diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ec4c41d0..381f95894 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,410 +1,411 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(icons) add_subdirectory(mimetypes) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp utils/userinfo_win.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() if(KF5IdentityManagement_FOUND AND KF5MailTransport_FOUND AND KF5MailTransportAkonadi_FOUND) set(_kleopatra_mail_libs KF5::IdentityManagement # Export OpenPGP keys using WKS KF5::MailTransport KF5::MailTransportAkonadi ) add_definitions(-DMAILAKONADI_ENABLED) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS utils/accessibility.cpp utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp utils/tags.cpp utils/writecertassuantransaction.cpp utils/keyparameters.cpp utils/userinfo.cpp utils/keys.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp selftest/compliancecheck.cpp ${_kleopatra_extra_SRCS} view/errorlabel.cpp view/formtextinput.cpp view/htmllabel.cpp view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/openpgpkeycardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/p15cardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/urllabel.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/revokecertificationwidget.cpp dialogs/revokecertificationdialog.cpp dialogs/adduseriddialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailsdialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.cpp dialogs/certificatedetailsinputwidget.cpp dialogs/createcsrforcardkeydialog.cpp dialogs/groupdetailsdialog.cpp dialogs/editgroupdialog.cpp dialogs/revokekeydialog.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp + crypto/checksumsutils_p.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/exportgroupscommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/importcertificatefromkeyservercommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshcertificatecommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/revokecertificationcommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportsecretkeycommand_old.cpp commands/exportsecretsubkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/exportopenpgpcerttoprovidercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp commands/certificatetopivcardcommand.cpp commands/importcertificatefrompivcardcommand.cpp commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createcsrforcardkeycommand.cpp commands/revokekeycommand.cpp commands/revokeuseridcommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp conf/groupsconfigdialog.cpp conf/groupsconfigpage.cpp conf/groupsconfigwidget.cpp newcertificatewizard/advancedsettingsdialog.cpp newcertificatewizard/chooseprotocolpage.cpp newcertificatewizard/enterdetailspage.cpp newcertificatewizard/keyalgo.cpp newcertificatewizard/keycreationpage.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp newcertificatewizard/resultpage.cpp newcertificatewizard/wizardpage.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp smartcard/p15card.cpp smartcard/keypairinfo.cpp smartcard/utils.cpp smartcard/deviceinfowatcher.cpp accessibility/accessiblerichtextlabel.cpp accessibility/accessiblewidgetfactory.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp kleopatra.qrc ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/setinitialpindialog.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc kcfg/tagspreferences.kcfgc kcfg/settings.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) # For the ConfigureDialog & KCMs target_link_libraries(kleopatra_bin kcm_kleopatra_static) #if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) # set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON) #endif() set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) if (WIN32) set(_kleopatra_platform_libs "secur32") endif () target_link_libraries(kleopatra_bin Gpgmepp QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash ${_kleopatra_mail_libs} Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ${_kleopatra_platform_libs} ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( PROGRAMS data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus ) diff --git a/src/crypto/checksumsutils_p.h b/src/crypto/checksumsutils_p.cpp similarity index 71% copy from src/crypto/checksumsutils_p.h copy to src/crypto/checksumsutils_p.cpp index 1b1e61221..6b213c73b 100644 --- a/src/crypto/checksumsutils_p.h +++ b/src/crypto/checksumsutils_p.cpp @@ -1,136 +1,108 @@ /* -*- mode: c++; c-basic-offset:4 -*- - crypto/checksumsutils_p.h + crypto/checksumsutils_p.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma once - +#include "checksumsutils_p.h" #include #include "kleopatra_debug.h" #include -#include #include -#ifdef Q_OS_UNIX -// Can we use QAbstractFileEngine::caseSensitive()? -static const Qt::CaseSensitivity fs_cs = Qt::CaseSensitive; - -static const QRegularExpression::PatternOption s_regex_cs = QRegularExpression::NoPatternOption; -#else -static const Qt::CaseSensitivity fs_cs = Qt::CaseInsensitive; -static const QRegularExpression::PatternOption s_regex_cs = QRegularExpression::CaseInsensitiveOption; -#endif - -static std::vector get_patterns(const std::vector> &checksumDefinitions) +std::vector ChecksumsUtils::get_patterns(const std::vector> &checksumDefinitions) { std::vector result; for (const auto &cd : checksumDefinitions) { if (!cd) { continue; } const QStringList &patterns = cd->patterns(); result.reserve(result.size() + patterns.size()); std::transform(patterns.cbegin(), patterns.cend(), std::back_inserter(result), [](const QString &pattern) { return QRegularExpression(QRegularExpression::anchoredPattern(pattern), s_regex_cs); }); } return result; } -struct matches_any : std::unary_function { - const std::vector m_regexps; - explicit matches_any(const std::vector ®exps) : m_regexps(regexps) {} - bool operator()(const QString &s) const - { - return std::any_of(m_regexps.cbegin(), m_regexps.cend(), - [&s](const QRegularExpression &rx) { return rx.match(s).hasMatch(); }); - } -}; - -struct File { - QString name; - QByteArray checksum; - bool binary; -}; - static QString decode(const QString &encoded) { QString decoded; decoded.reserve(encoded.size()); bool shift = false; for (const QChar ch : encoded) if (shift) { switch (ch.toLatin1()) { case '\\': decoded += QLatin1Char('\\'); break; case 'n': decoded += QLatin1Char('\n'); break; default: qCDebug(KLEOPATRA_LOG) << "invalid escape sequence" << '\\' << ch << "(interpreted as '" << ch << "')"; decoded += ch; break; } shift = false; } else { if (ch == QLatin1Char('\\')) { shift = true; } else { decoded += ch; } } return decoded; } -static std::vector parse_sum_file(const QString &fileName) +std::vector ChecksumsUtils::parse_sum_file(const QString &fileName) { std::vector files; QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) { return {}; } QTextStream s(&f); static const QRegularExpression rx(QRegularExpression::anchoredPattern(uR"((\\?)([a-f0-9A-F]+) ([ *])([^\\n]+)\\n*)")); while (!s.atEnd()) { const QString line = s.readLine(); QRegularExpressionMatch match = rx.match(line); if (!match.hasMatch()) { continue; } Q_ASSERT(!match.capturedView(4).endsWith(QLatin1Char('\n'))); const File file = { match.capturedView(1) == QLatin1Char('\\') ? decode(match.captured(4)) : match.captured(4), match.capturedView(2).toLatin1(), match.capturedView(3) == QLatin1Char('*'), }; files.push_back(file); } return files; } -static std::shared_ptr filename2definition(const QString &fileName, +std::shared_ptr ChecksumsUtils::filename2definition(const QString &fileName, const std::vector> &checksumDefinitions) { auto matchFileName = [&fileName](const std::shared_ptr &cd) { if (!cd) { return false; } const QStringList &patterns = cd->patterns(); return std::any_of(patterns.cbegin(), patterns.cend(), [&fileName](const QString &pattern) { const QRegularExpression re(QRegularExpression::anchoredPattern(pattern), s_regex_cs); return re.match(fileName).hasMatch(); }); }; auto it = std::find_if(checksumDefinitions.cbegin(), checksumDefinitions.cend(), matchFileName); return it != checksumDefinitions.cend() ? *it : std::shared_ptr{}; } diff --git a/src/crypto/checksumsutils_p.h b/src/crypto/checksumsutils_p.h index 1b1e61221..1e89f84a5 100644 --- a/src/crypto/checksumsutils_p.h +++ b/src/crypto/checksumsutils_p.h @@ -1,136 +1,55 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/checksumsutils_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once -#include - #include "kleopatra_debug.h" -#include #include -#include +namespace Kleo +{ +class ChecksumDefinition; +} + +namespace ChecksumsUtils { #ifdef Q_OS_UNIX // Can we use QAbstractFileEngine::caseSensitive()? static const Qt::CaseSensitivity fs_cs = Qt::CaseSensitive; static const QRegularExpression::PatternOption s_regex_cs = QRegularExpression::NoPatternOption; #else static const Qt::CaseSensitivity fs_cs = Qt::CaseInsensitive; static const QRegularExpression::PatternOption s_regex_cs = QRegularExpression::CaseInsensitiveOption; #endif -static std::vector get_patterns(const std::vector> &checksumDefinitions) -{ - std::vector result; - for (const auto &cd : checksumDefinitions) { - if (!cd) { - continue; - } - const QStringList &patterns = cd->patterns(); - result.reserve(result.size() + patterns.size()); - std::transform(patterns.cbegin(), patterns.cend(), std::back_inserter(result), [](const QString &pattern) { - return QRegularExpression(QRegularExpression::anchoredPattern(pattern), s_regex_cs); - }); - } - - return result; -} +std::vector get_patterns(const std::vector> &checksumDefinitions); struct matches_any : std::unary_function { const std::vector m_regexps; explicit matches_any(const std::vector ®exps) : m_regexps(regexps) {} bool operator()(const QString &s) const { return std::any_of(m_regexps.cbegin(), m_regexps.cend(), [&s](const QRegularExpression &rx) { return rx.match(s).hasMatch(); }); } }; struct File { QString name; QByteArray checksum; bool binary; }; -static QString decode(const QString &encoded) -{ - QString decoded; - decoded.reserve(encoded.size()); - bool shift = false; - for (const QChar ch : encoded) - if (shift) { - switch (ch.toLatin1()) { - case '\\': decoded += QLatin1Char('\\'); break; - case 'n': decoded += QLatin1Char('\n'); break; - default: - qCDebug(KLEOPATRA_LOG) << "invalid escape sequence" << '\\' << ch << "(interpreted as '" << ch << "')"; - decoded += ch; - break; - } - shift = false; - } else { - if (ch == QLatin1Char('\\')) { - shift = true; - } else { - decoded += ch; - } - } - return decoded; -} - -static std::vector parse_sum_file(const QString &fileName) -{ - std::vector files; - QFile f(fileName); - if (!f.open(QIODevice::ReadOnly)) { - return {}; - } - - QTextStream s(&f); - static const QRegularExpression rx(QRegularExpression::anchoredPattern(uR"((\\?)([a-f0-9A-F]+) ([ *])([^\\n]+)\\n*)")); - while (!s.atEnd()) { - const QString line = s.readLine(); - QRegularExpressionMatch match = rx.match(line); - if (!match.hasMatch()) { - continue; - } +std::vector parse_sum_file(const QString &fileName); - Q_ASSERT(!match.capturedView(4).endsWith(QLatin1Char('\n'))); - const File file = { - match.capturedView(1) == QLatin1Char('\\') ? decode(match.captured(4)) : match.captured(4), - match.capturedView(2).toLatin1(), - match.capturedView(3) == QLatin1Char('*'), - }; - files.push_back(file); - } - - return files; -} +std::shared_ptr filename2definition(const QString &fileName, + const std::vector> &checksumDefinitions); -static std::shared_ptr filename2definition(const QString &fileName, - const std::vector> &checksumDefinitions) -{ - auto matchFileName = [&fileName](const std::shared_ptr &cd) { - if (!cd) { - return false; - } - - const QStringList &patterns = cd->patterns(); - return std::any_of(patterns.cbegin(), patterns.cend(), [&fileName](const QString &pattern) { - const QRegularExpression re(QRegularExpression::anchoredPattern(pattern), s_regex_cs); - return re.match(fileName).hasMatch(); - }); - }; - - auto it = std::find_if(checksumDefinitions.cbegin(), checksumDefinitions.cend(), matchFileName); - - return it != checksumDefinitions.cend() ? *it : std::shared_ptr{}; -} +} // namespace ChecksumsUtils diff --git a/src/crypto/createchecksumscontroller.cpp b/src/crypto/createchecksumscontroller.cpp index a7bfb23d3..8568581dd 100644 --- a/src/crypto/createchecksumscontroller.cpp +++ b/src/crypto/createchecksumscontroller.cpp @@ -1,609 +1,610 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/createchecksumscontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "createchecksumscontroller.h" #include "checksumsutils_p.h" #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; namespace { class ResultDialog : public QDialog { Q_OBJECT public: ResultDialog(const QStringList &created, const QStringList &errors, QWidget *parent = nullptr, Qt::WindowFlags f = {}) : QDialog(parent, f), createdLB(created.empty() ? i18nc("@info", "No checksum files have been created.") : i18nc("@info", "These checksum files have been successfully created:"), this), createdLW(this), errorsLB(errors.empty() ? i18nc("@info", "There were no errors.") : i18nc("@info", "The following errors were encountered:"), this), errorsLW(this), buttonBox(QDialogButtonBox::Ok, Qt::Horizontal, this), vlay(this) { KDAB_SET_OBJECT_NAME(createdLB); KDAB_SET_OBJECT_NAME(createdLW); KDAB_SET_OBJECT_NAME(errorsLB); KDAB_SET_OBJECT_NAME(errorsLW); KDAB_SET_OBJECT_NAME(buttonBox); KDAB_SET_OBJECT_NAME(vlay); createdLW.addItems(created); QRect r; for (int i = 0; i < created.size(); ++i) { r = r.united(createdLW.visualRect(createdLW.model()->index(0, i))); } createdLW.setMinimumWidth(qMin(1024, r.width() + 4 * createdLW.frameWidth())); errorsLW.addItems(errors); vlay.addWidget(&createdLB); vlay.addWidget(&createdLW, 1); vlay.addWidget(&errorsLB); vlay.addWidget(&errorsLW, 1); vlay.addWidget(&buttonBox); if (created.empty()) { createdLW.hide(); } if (errors.empty()) { errorsLW.hide(); } connect(&buttonBox, &QDialogButtonBox::accepted, this, &ResultDialog::accept); connect(&buttonBox, &QDialogButtonBox::rejected, this, &ResultDialog::reject); readConfig(); } ~ResultDialog() override { writeConfig(); } void readConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), "ResultDialog"); const QSize size = dialog.readEntry("Size", QSize(600, 400)); if (size.isValid()) { resize(size); } } void writeConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), "ResultDialog"); dialog.writeEntry("Size", size()); dialog.sync(); } private: QLabel createdLB; QListWidget createdLW; QLabel errorsLB; QListWidget errorsLW; QDialogButtonBox buttonBox; QVBoxLayout vlay; }; } static QStringList fs_sort(QStringList l) { std::sort(l.begin(), l.end(), [](const QString &lhs, const QString &rhs) { - return QString::compare(lhs, rhs, fs_cs) < 0; + return QString::compare(lhs, rhs, ChecksumsUtils::fs_cs) < 0; }); return l; } static QStringList fs_intersect(QStringList l1, QStringList l2) { fs_sort(l1); fs_sort(l2); QStringList result; std::set_intersection(l1.begin(), l1.end(), l2.begin(), l2.end(), std::back_inserter(result), [](const QString &lhs, const QString &rhs) { - return QString::compare(lhs, rhs, fs_cs) < 0; + return QString::compare(lhs, rhs, ChecksumsUtils::fs_cs) < 0; }); return result; } class CreateChecksumsController::Private : public QThread { Q_OBJECT friend class ::Kleo::Crypto::CreateChecksumsController; CreateChecksumsController *const q; public: explicit Private(CreateChecksumsController *qq); ~Private() override; Q_SIGNALS: void progress(int, int, const QString &); private: void slotOperationFinished() { #ifndef QT_NO_PROGRESSDIALOG if (progressDialog) { progressDialog->setValue(progressDialog->maximum()); progressDialog->close(); } #endif // QT_NO_PROGRESSDIALOG auto const dlg = new ResultDialog(created, errors); dlg->setAttribute(Qt::WA_DeleteOnClose); q->bringToForeground(dlg); if (!errors.empty()) q->setLastError(gpg_error(GPG_ERR_GENERAL), errors.join(QLatin1Char('\n'))); q->emitDoneOrError(); } void slotProgress(int current, int total, const QString &what) { qCDebug(KLEOPATRA_LOG) << "progress: " << current << "/" << total << ": " << qPrintable(what); #ifndef QT_NO_PROGRESSDIALOG if (!progressDialog) { return; } progressDialog->setMaximum(total); progressDialog->setValue(current); progressDialog->setLabelText(what); #endif // QT_NO_PROGRESSDIALOG } private: void run() override; private: #ifndef QT_NO_PROGRESSDIALOG QPointer progressDialog; #endif mutable QMutex mutex; const std::vector< std::shared_ptr > checksumDefinitions; std::shared_ptr checksumDefinition; QStringList files; QStringList errors, created; bool allowAddition; volatile bool canceled; }; CreateChecksumsController::Private::Private(CreateChecksumsController *qq) : q(qq), #ifndef QT_NO_PROGRESSDIALOG progressDialog(), #endif mutex(), checksumDefinitions(ChecksumDefinition::getChecksumDefinitions()), checksumDefinition(ChecksumDefinition::getDefaultChecksumDefinition(checksumDefinitions)), files(), errors(), created(), allowAddition(false), canceled(false) { connect(this, SIGNAL(progress(int,int,QString)), q, SLOT(slotProgress(int,int,QString))); connect(this, &Private::progress, q, &Controller::progress); connect(this, SIGNAL(finished()), q, SLOT(slotOperationFinished())); } CreateChecksumsController::Private::~Private() { qCDebug(KLEOPATRA_LOG); } CreateChecksumsController::CreateChecksumsController(QObject *p) : Controller(p), d(new Private(this)) { } CreateChecksumsController::CreateChecksumsController(const std::shared_ptr &ctx, QObject *p) : Controller(ctx, p), d(new Private(this)) { } CreateChecksumsController::~CreateChecksumsController() { qCDebug(KLEOPATRA_LOG); } void CreateChecksumsController::setFiles(const QStringList &files) { kleo_assert(!d->isRunning()); kleo_assert(!files.empty()); - const std::vector patterns = get_patterns(d->checksumDefinitions); - if (!std::all_of(files.cbegin(), files.cend(), matches_any(patterns)) && - !std::none_of(files.cbegin(), files.cend(), matches_any(patterns))) { + const std::vector patterns = ChecksumsUtils::get_patterns(d->checksumDefinitions); + if (!std::all_of(files.cbegin(), files.cend(), ChecksumsUtils::matches_any(patterns)) && + !std::none_of(files.cbegin(), files.cend(), ChecksumsUtils::matches_any(patterns))) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Create Checksums: input files must be either all checksum files or all files to be checksummed, not a mixture of both.")); } const QMutexLocker locker(&d->mutex); d->files = files; } void CreateChecksumsController::setAllowAddition(bool allow) { kleo_assert(!d->isRunning()); const QMutexLocker locker(&d->mutex); d->allowAddition = allow; } bool CreateChecksumsController::allowAddition() const { const QMutexLocker locker(&d->mutex); return d->allowAddition; } void CreateChecksumsController::start() { { const QMutexLocker locker(&d->mutex); #ifndef QT_NO_PROGRESSDIALOG d->progressDialog = new QProgressDialog(i18n("Initializing..."), i18n("Cancel"), 0, 0); applyWindowID(d->progressDialog); d->progressDialog->setAttribute(Qt::WA_DeleteOnClose); d->progressDialog->setMinimumDuration(1000); d->progressDialog->setWindowTitle(i18nc("@title:window", "Create Checksum Progress")); connect(d->progressDialog.data(), &QProgressDialog::canceled, this, &CreateChecksumsController::cancel); #endif // QT_NO_PROGRESSDIALOG d->canceled = false; d->errors.clear(); d->created.clear(); } d->start(); } void CreateChecksumsController::cancel() { qCDebug(KLEOPATRA_LOG); const QMutexLocker locker(&d->mutex); d->canceled = true; } namespace { struct Dir { QDir dir; QString sumFile; QStringList inputFiles; quint64 totalSize; std::shared_ptr checksumDefinition; }; } static QStringList remove_checksum_files(QStringList l, const std::vector &rxs) { QStringList::iterator end = l.end(); for (const auto &rx : rxs) { end = std::remove_if(l.begin(), end, [rx](const QString &str) { return rx.match(str).hasMatch(); }); } l.erase(end, l.end()); return l; } static quint64 aggregate_size(const QDir &dir, const QStringList &files) { quint64 n = 0; for (const QString &file : files) { n += QFileInfo(dir.absoluteFilePath(file)).size(); } return n; } static std::vector find_dirs_by_sum_files(const QStringList &files, bool allowAddition, const std::function &progress, const std::vector< std::shared_ptr > &checksumDefinitions) { - const std::vector patterns = get_patterns(checksumDefinitions); + const std::vector patterns = ChecksumsUtils::get_patterns(checksumDefinitions); std::vector dirs; dirs.reserve(files.size()); int i = 0; for (const QString &file : files) { const QFileInfo fi(file); const QDir dir = fi.dir(); const QStringList entries = remove_checksum_files(dir.entryList(QDir::Files), patterns); QStringList inputFiles; if (allowAddition) { inputFiles = entries; } else { - const std::vector parsed = parse_sum_file(fi.absoluteFilePath()); + const std::vector parsed = ChecksumsUtils::parse_sum_file(fi.absoluteFilePath()); QStringList oldInputFiles; oldInputFiles.reserve(parsed.size()); std::transform(parsed.cbegin(), parsed.cend(), std::back_inserter(oldInputFiles), - std::mem_fn(&File::name)); + std::mem_fn(&ChecksumsUtils::File::name)); inputFiles = fs_intersect(oldInputFiles, entries); } const Dir item = { dir, fi.fileName(), inputFiles, aggregate_size(dir, inputFiles), - filename2definition(fi.fileName(), checksumDefinitions) + ChecksumsUtils::filename2definition(fi.fileName(), checksumDefinitions) }; dirs.push_back(item); if (progress) { progress(++i); } } return dirs; } namespace { struct less_dir : std::binary_function { bool operator()(const QDir &lhs, const QDir &rhs) const { - return QString::compare(lhs.absolutePath(), rhs.absolutePath(), fs_cs) < 0; + return QString::compare(lhs.absolutePath(), rhs.absolutePath(), ChecksumsUtils::fs_cs) < 0; } }; } static std::vector find_dirs_by_input_files(const QStringList &files, const std::shared_ptr &checksumDefinition, bool allowAddition, const std::function &progress, const std::vector< std::shared_ptr > &checksumDefinitions) { Q_UNUSED(allowAddition) if (!checksumDefinition) { return std::vector(); } - const std::vector patterns = get_patterns(checksumDefinitions); + const std::vector patterns = ChecksumsUtils::get_patterns(checksumDefinitions); std::map dirs2files; // Step 1: sort files by the dir they're contained in: std::deque inputs(files.begin(), files.end()); int i = 0; while (!inputs.empty()) { const QString file = inputs.front(); inputs.pop_front(); const QFileInfo fi(file); if (fi.isDir()) { QDir dir(file); dirs2files[ dir ] = remove_checksum_files(dir.entryList(QDir::Files), patterns); const auto entryList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); std::transform(entryList.cbegin(), entryList.cend(), std::inserter(inputs, inputs.begin()), [&dir](const QString &entry) { return dir.absoluteFilePath(entry); }); } else { dirs2files[fi.dir()].push_back(file); } if (progress) { progress(++i); } } // Step 2: convert into vector: std::vector dirs; dirs.reserve(dirs2files.size()); for (auto it = dirs2files.begin(), end = dirs2files.end(); it != end; ++it) { const QStringList inputFiles = remove_checksum_files(it->second, patterns); if (inputFiles.empty()) { continue; } const Dir dir = { it->first, checksumDefinition->outputFileName(), inputFiles, aggregate_size(it->first, inputFiles), checksumDefinition }; dirs.push_back(dir); if (progress) { progress(++i); } } return dirs; } static QString process(const Dir &dir, bool *fatal) { const QString absFilePath = dir.dir.absoluteFilePath(dir.sumFile); QTemporaryFile out; QProcess p; if (!out.open()) { return QStringLiteral("Failed to open Temporary file."); } p.setWorkingDirectory(dir.dir.absolutePath()); p.setStandardOutputFile(out.fileName()); const QString program = dir.checksumDefinition->createCommand(); dir.checksumDefinition->startCreateCommand(&p, dir.inputFiles); p.waitForFinished(); qCDebug(KLEOPATRA_LOG) << "[" << &p << "] Exit code " << p.exitCode(); if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) { if (fatal && p.error() == QProcess::FailedToStart) { *fatal = true; } if (p.error() == QProcess::UnknownError) return i18n("Error while running %1: %2", program, QString::fromLocal8Bit(p.readAllStandardError().trimmed().constData())); else { return i18n("Failed to execute %1: %2", program, p.errorString()); } } QFileInfo fi(absFilePath); if (!(fi.exists() && !QFile::remove(absFilePath)) && QFile::copy(out.fileName(), absFilePath)) { return QString(); } return xi18n("Failed to overwrite %1.", dir.sumFile); } namespace { static QDebug operator<<(QDebug s, const Dir &dir) { return s << "Dir(" << dir.dir << "->" << dir.sumFile << "<-(" << dir.totalSize << ')' << dir.inputFiles << ")\n"; } } void CreateChecksumsController::Private::run() { QMutexLocker locker(&mutex); const QStringList files = this->files; const std::vector< std::shared_ptr > checksumDefinitions = this->checksumDefinitions; const std::shared_ptr checksumDefinition = this->checksumDefinition; const bool allowAddition = this->allowAddition; locker.unlock(); QStringList errors; QStringList created; if (!checksumDefinition) { errors.push_back(i18n("No checksum programs defined.")); locker.relock(); this->errors = errors; return; } else { qCDebug(KLEOPATRA_LOG) << "using checksum-definition" << checksumDefinition->id(); } // // Step 1: build a list of work to do (no progress): // const QString scanning = i18n("Scanning directories..."); Q_EMIT progress(0, 0, scanning); - const bool haveSumFiles = std::all_of(files.cbegin(), files.cend(), matches_any(get_patterns(checksumDefinitions))); + const bool haveSumFiles = std::all_of(files.cbegin(), files.cend(), ChecksumsUtils::matches_any(ChecksumsUtils::get_patterns(checksumDefinitions))); const auto progressCb = [this, &scanning](int c) { Q_EMIT progress(c, 0, scanning); }; const std::vector dirs = haveSumFiles ? find_dirs_by_sum_files(files, allowAddition, progressCb, checksumDefinitions) : find_dirs_by_input_files(files, checksumDefinition, allowAddition, progressCb, checksumDefinitions); for (const Dir &dir : dirs) { qCDebug(KLEOPATRA_LOG) << dir; } if (!canceled) { Q_EMIT progress(0, 0, i18n("Calculating total size...")); const quint64 total = kdtools::accumulate_transform(dirs.cbegin(), dirs.cend(), std::mem_fn(&Dir::totalSize), Q_UINT64_C(0)); if (!canceled) { // // Step 2: perform work (with progress reporting): // // re-scale 'total' to fit into ints (wish QProgressDialog would use quint64...) const quint64 factor = total / std::numeric_limits::max() + 1; quint64 done = 0; for (const Dir &dir : dirs) { Q_EMIT progress(done / factor, total / factor, i18n("Checksumming (%2) in %1", dir.checksumDefinition->label(), dir.dir.path())); bool fatal = false; const QString error = process(dir, &fatal); if (!error.isEmpty()) { errors.push_back(error); } else { created.push_back(dir.dir.absoluteFilePath(dir.sumFile)); } done += dir.totalSize; if (fatal || canceled) { break; } } Q_EMIT progress(done / factor, total / factor, i18n("Done.")); } } locker.relock(); this->errors = errors; this->created = created; // mutex unlocked by QMutexLocker } #include "moc_createchecksumscontroller.cpp" #include "createchecksumscontroller.moc" diff --git a/src/crypto/verifychecksumscontroller.cpp b/src/crypto/verifychecksumscontroller.cpp index e84f307b8..25f8edf3a 100644 --- a/src/crypto/verifychecksumscontroller.cpp +++ b/src/crypto/verifychecksumscontroller.cpp @@ -1,586 +1,587 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/verifychecksumscontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "verifychecksumscontroller.h" #include "checksumsutils_p.h" #ifndef QT_NO_DIRMODEL #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; static const QLatin1String CHECKSUM_DEFINITION_ID_ENTRY("checksum-definition-id"); #if 0 static QStringList fs_sort(QStringList l) { int (*QString_compare)(const QString &, const QString &, Qt::CaseSensitivity) = &QString::compare; std::sort(l.begin(), l.end(), [](const QString &lhs, const QString &rhs) { - return QString::compare(lhs, rhs, fs_cs) < 0; + return QString::compare(lhs, rhs, ChecksumsUtils::fs_cs) < 0; }); return l; } static QStringList fs_intersect(QStringList l1, QStringList l2) { int (*QString_compare)(const QString &, const QString &, Qt::CaseSensitivity) = &QString::compare; fs_sort(l1); fs_sort(l2); QStringList result; std::set_intersection(l1.begin(), l1.end(), l2.begin(), l2.end(), std::back_inserter(result), [](const QString &lhs, const QString &rhs) { - return QString::compare(lhs, rhs, fs_cs) < 0; + return QString::compare(lhs, rhs, ChecksumsUtils::fs_cs) < 0; }); return result; } #endif namespace { struct matches_none_of : std::unary_function { const std::vector m_regexps; explicit matches_none_of(const std::vector ®exps) : m_regexps(regexps) {} bool operator()(const QString &s) const { return std::none_of(m_regexps.cbegin(), m_regexps.cend(), [&s](const QRegularExpression &rx) { return rx.match(s).hasMatch(); }); } }; } class VerifyChecksumsController::Private : public QThread { Q_OBJECT friend class ::Kleo::Crypto::VerifyChecksumsController; VerifyChecksumsController *const q; public: explicit Private(VerifyChecksumsController *qq); ~Private() override; Q_SIGNALS: void baseDirectories(const QStringList &); void progress(int, int, const QString &); void status(const QString &file, Kleo::Crypto::Gui::VerifyChecksumsDialog::Status); private: void slotOperationFinished() { if (dialog) { dialog->setProgress(100, 100); dialog->setErrors(errors); } if (!errors.empty()) q->setLastError(gpg_error(GPG_ERR_GENERAL), errors.join(QLatin1Char('\n'))); q->emitDoneOrError(); } private: void run() override; private: QPointer dialog; mutable QMutex mutex; const std::vector< std::shared_ptr > checksumDefinitions; QStringList files; QStringList errors; volatile bool canceled; }; VerifyChecksumsController::Private::Private(VerifyChecksumsController *qq) : q(qq), dialog(), mutex(), checksumDefinitions(ChecksumDefinition::getChecksumDefinitions()), files(), errors(), canceled(false) { connect(this, &Private::progress, q, &Controller::progress); connect(this, SIGNAL(finished()), q, SLOT(slotOperationFinished())); } VerifyChecksumsController::Private::~Private() { qCDebug(KLEOPATRA_LOG); } VerifyChecksumsController::VerifyChecksumsController(QObject *p) : Controller(p), d(new Private(this)) { } VerifyChecksumsController::VerifyChecksumsController(const std::shared_ptr &ctx, QObject *p) : Controller(ctx, p), d(new Private(this)) { } VerifyChecksumsController::~VerifyChecksumsController() { qCDebug(KLEOPATRA_LOG); } void VerifyChecksumsController::setFiles(const QStringList &files) { kleo_assert(!d->isRunning()); kleo_assert(!files.empty()); const QMutexLocker locker(&d->mutex); d->files = files; } void VerifyChecksumsController::start() { { const QMutexLocker locker(&d->mutex); d->dialog = new VerifyChecksumsDialog; d->dialog->setAttribute(Qt::WA_DeleteOnClose); d->dialog->setWindowTitle(i18nc("@title:window", "Verify Checksum Results")); connect(d->dialog.data(), &VerifyChecksumsDialog::canceled, this, &VerifyChecksumsController::cancel); connect(d.get(), &Private::baseDirectories, d->dialog.data(), &VerifyChecksumsDialog::setBaseDirectories); connect(d.get(), &Private::progress, d->dialog.data(), &VerifyChecksumsDialog::setProgress); connect(d.get(), &Private::status, d->dialog.data(), &VerifyChecksumsDialog::setStatus); d->canceled = false; d->errors.clear(); } d->start(); d->dialog->show(); } void VerifyChecksumsController::cancel() { qCDebug(KLEOPATRA_LOG); const QMutexLocker locker(&d->mutex); d->canceled = true; } namespace { struct SumFile { QDir dir; QString sumFile; quint64 totalSize; std::shared_ptr checksumDefinition; }; } static QStringList filter_checksum_files(QStringList l, const std::vector &rxs) { l.erase(std::remove_if(l.begin(), l.end(), matches_none_of(rxs)), l.end()); return l; } static quint64 aggregate_size(const QDir &dir, const QStringList &files) { quint64 n = 0; for (const QString &file : files) { n += QFileInfo(dir.absoluteFilePath(file)).size(); } return n; } namespace { struct less_dir : std::binary_function { bool operator()(const QDir &lhs, const QDir &rhs) const { - return QString::compare(lhs.absolutePath(), rhs.absolutePath(), fs_cs) < 0; + return QString::compare(lhs.absolutePath(), rhs.absolutePath(), ChecksumsUtils::fs_cs) < 0; } }; struct less_file : std::binary_function { bool operator()(const QString &lhs, const QString &rhs) const { - return QString::compare(lhs, rhs, fs_cs) < 0; + return QString::compare(lhs, rhs, ChecksumsUtils::fs_cs) < 0; } }; struct sumfile_contains_file : std::unary_function { const QDir dir; const QString fileName; sumfile_contains_file(const QDir &dir_, const QString &fileName_) : dir(dir_), fileName(fileName_) {} bool operator()(const QString &sumFile) const { - const std::vector files = parse_sum_file(dir.absoluteFilePath(sumFile)); + const std::vector files = ChecksumsUtils::parse_sum_file(dir.absoluteFilePath(sumFile)); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << files.size() << " files listed in " << qPrintable(dir.absoluteFilePath(sumFile)); - for (const File &file : files) { - const bool isSameFileName = (QString::compare(file.name, fileName, fs_cs) == 0); + for (const ChecksumsUtils::File &file : files) { + const bool isSameFileName = (QString::compare(file.name, fileName, ChecksumsUtils::fs_cs) == 0); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: " << qPrintable(file.name) << " == " << qPrintable(fileName) << " ? " << isSameFileName; if (isSameFileName) { return true; } } return false; } }; } // IF is_dir(file) // add all sumfiles \in dir(file) // inputs.prepend( all dirs \in dir(file) ) // ELSE IF is_sum_file(file) // add // ELSE IF \exists sumfile in dir(file) \where sumfile \contains file // add sumfile // ELSE // error: no checksum found for "file" static QStringList find_base_directories(const QStringList &files) { // Step 1: find base dirs: std::set dirs; for (const QString &file : files) { const QFileInfo fi(file); const QDir dir = fi.isDir() ? QDir(file) : fi.dir(); dirs.insert(dir); } // Step 1a: collapse direct child directories bool changed; do { changed = false; auto it = dirs.begin(); while (it != dirs.end()) { QDir dir = *it; if (dir.cdUp() && dirs.count(dir)) { dirs.erase(it++); changed = true; } else { ++it; } } } while (changed); QStringList rv; rv.reserve(dirs.size()); std::transform(dirs.cbegin(), dirs.cend(), std::back_inserter(rv), std::mem_fn(&QDir::absolutePath)); return rv; } static std::vector find_sums_by_input_files(const QStringList &files, QStringList &errors, const std::function &progress, const std::vector< std::shared_ptr > &checksumDefinitions) { - const std::vector patterns = get_patterns(checksumDefinitions); + const std::vector patterns = ChecksumsUtils::get_patterns(checksumDefinitions); - const matches_any is_sum_file(patterns); + const ChecksumsUtils::matches_any is_sum_file(patterns); std::map, less_dir> dirs2sums; // Step 1: find the sumfiles we need to check: std::deque inputs(files.begin(), files.end()); int i = 0; while (!inputs.empty()) { const QString file = inputs.front(); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: considering " << qPrintable(file); inputs.pop_front(); const QFileInfo fi(file); const QString fileName = fi.fileName(); if (fi.isDir()) { qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's a directory"; QDir dir(file); const QStringList sumfiles = filter_checksum_files(dir.entryList(QDir::Files), patterns); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << sumfiles.size() << " sum files: " << qPrintable(sumfiles.join(QLatin1String(", "))); dirs2sums[ dir ].insert(sumfiles.begin(), sumfiles.end()); const QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << dirs.size() << " subdirs, prepending"; std::transform(dirs.cbegin(), dirs.cend(), std::inserter(inputs, inputs.begin()), [&dir](const QString &path) { return dir.absoluteFilePath(path); }); } else if (is_sum_file(fileName)) { qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's a sum file"; dirs2sums[fi.dir()].insert(fileName); } else { qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: it's something else; checking whether we'll find a sumfile for it..."; const QDir dir = fi.dir(); const QStringList sumfiles = filter_checksum_files(dir.entryList(QDir::Files), patterns); qCDebug(KLEOPATRA_LOG) << "find_sums_by_input_files: found " << sumfiles.size() << " potential sumfiles: " << qPrintable(sumfiles.join(QLatin1String(", "))); const auto it = std::find_if(sumfiles.cbegin(), sumfiles.cend(), sumfile_contains_file(dir, fileName)); if (it == sumfiles.end()) { errors.push_back(i18n("Cannot find checksums file for file %1", file)); } else { dirs2sums[dir].insert(*it); } } if (progress) { progress(++i); } } // Step 2: convert into vector: std::vector sumfiles; sumfiles.reserve(dirs2sums.size()); for (auto it = dirs2sums.begin(), end = dirs2sums.end(); it != end; ++it) { if (it->second.empty()) { continue; } const QDir &dir = it->first; for (const QString &sumFileName : std::as_const(it->second)) { - const std::vector summedfiles = parse_sum_file(dir.absoluteFilePath(sumFileName)); + const std::vector summedfiles = ChecksumsUtils::parse_sum_file(dir.absoluteFilePath(sumFileName)); QStringList files; files.reserve(summedfiles.size()); std::transform(summedfiles.cbegin(), summedfiles.cend(), - std::back_inserter(files), std::mem_fn(&File::name)); + std::back_inserter(files), std::mem_fn(&ChecksumsUtils::File::name)); const SumFile sumFile = { it->first, sumFileName, aggregate_size(it->first, files), - filename2definition(sumFileName, checksumDefinitions), + ChecksumsUtils::filename2definition(sumFileName, checksumDefinitions), }; sumfiles.push_back(sumFile); } if (progress) { progress(++i); } } return sumfiles; } static QStringList c_lang_environment() { - static const QRegularExpression re(QRegularExpression::anchoredPattern(u"LANG=.*"), s_regex_cs); + static const QRegularExpression re(QRegularExpression::anchoredPattern(u"LANG=.*"), ChecksumsUtils::s_regex_cs); QStringList env = QProcess::systemEnvironment(); env.erase(std::remove_if(env.begin(), env.end(), [](const QString &str) { return re.match(str).hasMatch(); }), env.end()); env.push_back(QStringLiteral("LANG=C")); return env; } static const struct { const char *string; VerifyChecksumsDialog::Status status; } statusStrings[] = { { "OK", VerifyChecksumsDialog::OK }, { "FAILED", VerifyChecksumsDialog::Failed }, }; static const size_t numStatusStrings = sizeof statusStrings / sizeof * statusStrings; static VerifyChecksumsDialog::Status string2status(const QByteArray &str) { for (unsigned int i = 0; i < numStatusStrings; ++i) if (str == statusStrings[i].string) { return statusStrings[i].status; } return VerifyChecksumsDialog::Unknown; } static QString process(const SumFile &sumFile, bool *fatal, const QStringList &env, const std::function &status) { QProcess p; p.setEnvironment(env); p.setWorkingDirectory(sumFile.dir.absolutePath()); p.setReadChannel(QProcess::StandardOutput); const QString absFilePath = sumFile.dir.absoluteFilePath(sumFile.sumFile); const QString program = sumFile.checksumDefinition->verifyCommand(); sumFile.checksumDefinition->startVerifyCommand(&p, QStringList(absFilePath)); QByteArray remainder; // used for filenames with newlines in them while (p.state() != QProcess::NotRunning) { p.waitForReadyRead(); while (p.canReadLine()) { const QByteArray line = p.readLine(); const int colonIdx = line.lastIndexOf(':'); if (colonIdx < 0) { remainder += line; // no colon -> probably filename with a newline continue; } const QString file = QFile::decodeName(remainder + line.left(colonIdx)); remainder.clear(); const VerifyChecksumsDialog::Status result = string2status(line.mid(colonIdx + 1).trimmed()); status(sumFile.dir.absoluteFilePath(file), result); } } qCDebug(KLEOPATRA_LOG) << "[" << &p << "] Exit code " << p.exitCode(); if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) { if (fatal && p.error() == QProcess::FailedToStart) { *fatal = true; } if (p.error() == QProcess::UnknownError) return i18n("Error while running %1: %2", program, QString::fromLocal8Bit(p.readAllStandardError().trimmed().constData())); else { return i18n("Failed to execute %1: %2", program, p.errorString()); } } return QString(); } namespace { static QDebug operator<<(QDebug s, const SumFile &sum) { return s << "SumFile(" << sum.dir << "->" << sum.sumFile << "<-(" << sum.totalSize << ')' << ")\n"; } } void VerifyChecksumsController::Private::run() { QMutexLocker locker(&mutex); const QStringList files = this->files; const std::vector< std::shared_ptr > checksumDefinitions = this->checksumDefinitions; locker.unlock(); QStringList errors; // // Step 0: find base directories: // Q_EMIT baseDirectories(find_base_directories(files)); // // Step 1: build a list of work to do (no progress): // const QString scanning = i18n("Scanning directories..."); Q_EMIT progress(0, 0, scanning); const auto progressCb = [this, scanning](int arg) { Q_EMIT progress(arg, 0, scanning); }; const auto statusCb = [this](const QString &str, VerifyChecksumsDialog::Status st) { Q_EMIT status(str, st); }; const std::vector sumfiles = find_sums_by_input_files(files, errors, progressCb, checksumDefinitions); for (const SumFile &sumfile : sumfiles) { qCDebug(KLEOPATRA_LOG) << sumfile; } if (!canceled) { Q_EMIT progress(0, 0, i18n("Calculating total size...")); const quint64 total = kdtools::accumulate_transform(sumfiles.cbegin(), sumfiles.cend(), std::mem_fn(&SumFile::totalSize), Q_UINT64_C(0)); if (!canceled) { // // Step 2: perform work (with progress reporting): // const QStringList env = c_lang_environment(); // re-scale 'total' to fit into ints (wish QProgressDialog would use quint64...) const quint64 factor = total / std::numeric_limits::max() + 1; quint64 done = 0; for (const SumFile &sumFile : sumfiles) { Q_EMIT progress(done / factor, total / factor, i18n("Verifying checksums (%2) in %1", sumFile.checksumDefinition->label(), sumFile.dir.path())); bool fatal = false; const QString error = process(sumFile, &fatal, env, statusCb); if (!error.isEmpty()) { errors.push_back(error); } done += sumFile.totalSize; if (fatal || canceled) { break; } } Q_EMIT progress(done / factor, total / factor, i18n("Done.")); } } locker.relock(); this->errors = errors; // mutex unlocked by QMutexLocker } #include "moc_verifychecksumscontroller.cpp" #include "verifychecksumscontroller.moc" #endif // QT_NO_DIRMODEL