diff --git a/cmake/modules/FindAssuan2.cmake b/cmake/modules/FindAssuan2.cmake index ae8d62cde..27a194805 100644 --- a/cmake/modules/FindAssuan2.cmake +++ b/cmake/modules/FindAssuan2.cmake @@ -1,251 +1,251 @@ # - Try :to find the assuan v2 library # Variables set: # ASSUAN2_{INCLUDES,FOUND,LIBRARIES} will be set for each of the above #if this is built-in, please replace, if it isn't, export into a MacroToBool.cmake of it's own macro( macro_bool_to_bool FOUND_VAR ) foreach( _current_VAR ${ARGN} ) if ( ${FOUND_VAR} ) set( ${_current_VAR} TRUE ) else() set( ${_current_VAR} FALSE ) endif() endforeach() endmacro() if ( WIN32 ) # On Windows, we don't have a libassuan-config script, so we need to # look for the stuff ourselves: # in cmake, AND and OR have the same precedence, there's no # subexpressions, and expressions are evaluated short-circuit'ed # IOW: CMake if() suxx. set( _seem_to_have_cached_assuan2 false ) if ( ASSUAN2_INCLUDES ) if ( ASSUAN2_VANILLA_LIBRARIES )#OR ASSUAN2_QT_LIBRARIES OR ASSUAN2_GLIB_LIBRARIES ) set( _seem_to_have_cached_assuan2 true ) endif() endif() if ( _seem_to_have_cached_assuan2 ) macro_bool_to_bool( ASSUAN2_VANILLA_LIBRARIES ASSUAN2_VANILLA_FOUND ) # this would have been preferred: #set( ASSUAN2_*_FOUND macro_bool_to_bool(ASSUAN2_*_LIBRARIES) ) if ( ASSUAN2_VANILLA_FOUND ) #OR ASSUAN2_GLIB_FOUND OR ASSUAN2_QT_FOUND ) set( ASSUAN2_FOUND true ) else() set( ASSUAN2_FOUND false ) endif() else() set( ASSUAN2_FOUND false ) set( ASSUAN2_VANILLA_FOUND false ) #set( ASSUAN2_GLIB_FOUND false ) #set( ASSUAN2_QT_FOUND false ) find_path( ASSUAN2_INCLUDES assuan.h ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library( _assuan2_library NAMES assuan2 libassuan2 assuan-0 libassuan-0 #sic! PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) find_library( _gpg_error_library NAMES gpg-error libgpg-error gpg-error-0 libgpg-error-0 PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) set( ASSUAN2_INCLUDES ${ASSUAN2_INCLUDES} ) if ( _assuan2_library AND _gpg_error_library ) set( ASSUAN2_LIBRARIES ${_assuan2_library} ${_gpg_error_library} ws2_32 ) set( ASSUAN2_FOUND true ) endif() endif() if (ASSUAN2_FOUND) set (HAVE_ASSUAN2 1) else() set (HAVE_ASSUAN2 0) endif() else() # not WIN32 # On *nix, we have the libassuan-config script which can tell us all we # need to know: # see WIN32 case for an explanation of what this does: set( _seem_to_have_cached_assuan2 false ) if ( ASSUAN2_INCLUDES AND ASSUAN2_LIBRARIES ) set( _seem_to_have_cached_assuan2 true ) endif() if ( _seem_to_have_cached_assuan2 ) set( ASSUAN2_FOUND true ) else() set( ASSUAN2_FOUND false ) find_program( _ASSUAN2CONFIG_EXECUTABLE NAMES libassuan-config ) # if libassuan-config has been found if ( _ASSUAN2CONFIG_EXECUTABLE ) message( STATUS "Found libassuan-config at ${_ASSUAN2CONFIG_EXECUTABLE}" ) exec_program( ${_ASSUAN2CONFIG_EXECUTABLE} ARGS --version OUTPUT_VARIABLE ASSUAN2_VERSION ) set( _ASSUAN2_MIN_VERSION "2.0.0" ) if( ASSUAN2_VERSION VERSION_GREATER ${_ASSUAN2_MIN_VERSION} ) set( _ASSUAN2_INSTALLED_VERSION_OK TRUE ) endif() if ( NOT _ASSUAN2_INSTALLED_VERSION_OK ) message( STATUS "The installed version of assuan is too old: ${ASSUAN2_VERSION} (required: >= ${_ASSUAN2_MIN_VERSION})" ) else() message( STATUS "Found assuan v${ASSUAN2_VERSION}" ) exec_program( ${_ASSUAN2CONFIG_EXECUTABLE} ARGS --libs OUTPUT_VARIABLE _assuan2_config_libs RETURN_VALUE _ret ) if ( _ret ) set( _assuan2_config_libs ) endif() # append -lgpg-error to the list of libraries, if necessary if ( _assuan2_config_libs AND NOT _assuan2_config_libs MATCHES "lgpg-error" ) set( _assuan2_config_libs "${_assuan2_config_libs} -lgpg-error" ) endif() if ( _assuan2_config_libs ) exec_program( ${_ASSUAN2CONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE _ASSUAN2_CFLAGS ) if ( _ASSUAN2_CFLAGS ) string( REGEX REPLACE "(\r?\n)+$" " " _ASSUAN2_CFLAGS "${_ASSUAN2_CFLAGS}" ) string( REGEX REPLACE " *-I" ";" ASSUAN2_INCLUDES "${_ASSUAN2_CFLAGS}" ) endif() if ( _assuan2_config_libs ) set( _assuan2_library_dirs ) set( _assuan2_library_names ) string( REGEX REPLACE " +" ";" _assuan2_config_libs "${_assuan2_config_libs}" ) foreach( _flag ${_assuan2_config_libs} ) if ( "${_flag}" MATCHES "^-L" ) string( REGEX REPLACE "^-L" "" _dir "${_flag}" ) file( TO_CMAKE_PATH "${_dir}" _dir ) set( _assuan2_library_dirs ${_assuan2_library_dirs} "${_dir}" ) elseif( "${_flag}" MATCHES "^-l" ) string( REGEX REPLACE "^-l" "" _name "${_flag}" ) set( _assuan2_library_names ${_assuan2_library_names} "${_name}" ) endif() endforeach() set( ASSUAN2_FOUND true ) foreach( _name ${_assuan2_library_names} ) set( _assuan2_${_name}_lib ) # if -L options were given, look only there if ( _assuan2_library_dirs ) find_library( _assuan2_${_name}_lib NAMES ${_name} PATHS ${_assuan2_library_dirs} NO_DEFAULT_PATH ) endif() # if not found there, look in system directories if ( NOT _assuan2_${_name}_lib ) find_library( _assuan2_${_name}_lib NAMES ${_name} ) endif() # if still not found, then the whole flavour isn't found if ( NOT _assuan2_${_name}_lib ) if ( ASSUAN2_FOUND ) set( ASSUAN2_FOUND false ) - set( _not_found_reason "dependant library ${_name} wasn't found" ) + set( _not_found_reason "dependent library ${_name} wasn't found" ) endif() endif() set( ASSUAN2_LIBRARIES ${ASSUAN2_LIBRARIES} "${_assuan2_${_name}_lib}" ) endforeach() #check_c_library_exists_explicit( assuan assuan_check_version "${_ASSUAN2_CFLAGS}" "${ASSUAN2_LIBRARIES}" ASSUAN2_FOUND ) if ( ASSUAN2_FOUND ) message( STATUS " Checking whether assuan is usable...yes" ) else() message( STATUS " Checking whether assuan is usable...no" ) message( STATUS " (${_not_found_reason})" ) endif() endif() # ensure that they are cached set( ASSUAN2_INCLUDES ${ASSUAN2_INCLUDES} ) set( ASSUAN2_LIBRARIES ${ASSUAN2_LIBRARIES} ) endif() endif() endif() endif() if (ASSUAN2_FOUND) set (HAVE_ASSUAN2 1) else() set (HAVE_ASSUAN2 0) endif() endif() # WIN32 | Unix if ( NOT Assuan2_FIND_QUIETLY ) if ( ASSUAN2_FOUND ) message( STATUS "Usable assuan found." ) message( STATUS " Includes: ${ASSUAN2_INCLUDES}" ) message( STATUS " Libraries: ${ASSUAN2_LIBRARIES}" ) else() message( STATUS "No usable assuan found." ) endif() if( Assuan2_FIND_REQUIRED ) set( _ASSUAN2_TYPE "REQUIRED" ) else() set( _ASSUAN2_TYPE "OPTIONAL" ) endif() if ( WIN32 ) set( _assuan2_homepage "https://www.gpg4win.org" ) else() set( _assuan2_homepage "https://www.gnupg.org/related_software/libassuan" ) endif() set_package_properties(ASSUAN2 PROPERTIES DESCRIPTION "Assuan v2 IPC library" URL ${_assuan2_homepage} TYPE ${_ASSUAN2_TYPE} PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server" ) else() if ( Assuan2_FIND_REQUIRED AND NOT ASSUAN2_FOUND ) message( FATAL_ERROR "Assuan2 is required but was not found." ) endif() endif() diff --git a/src/conf/smimevalidationconfigurationwidget.ui b/src/conf/smimevalidationconfigurationwidget.ui index 19daf8df1..39d76b4a2 100644 --- a/src/conf/smimevalidationconfigurationwidget.ui +++ b/src/conf/smimevalidationconfigurationwidget.ui @@ -1,477 +1,477 @@ Kleo::Config::SMimeValidationConfigurationWidget 0 0 502 603 - This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is perfomed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. + This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is performed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. Check certificate validity every false - This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is perfomed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. + This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is performed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter hour hours 1 24 Qt::Horizontal 40 20 If this option is selected, S/MIME certificates are validated using Certificate Revocation Lists (CRLs). Validate certificates using CRLs true If this option is selected, S/MIME certificates are validated online using the Online Certificates Status Protocol (OCSP). Fill in the URL of the OCSP responder below. Validate certificates online (OCSP) false Online Certificate Validation OCSP responder URL: false Enter here the address of the server for online validation of certificates (OCSP responder). The URL is usually starting with http://. OCSP responder signature: false Ignore service URL of certificates Choose here the certificate with which the OCSP server signs its replies. By default, GnuPG uses the file ~/.gnupg/policies.txt to check if a certificate policy is allowed. If this option is selected, policies are not checked. Do not check certificate policies If this option is checked, Certificate Revocation Lists are never used to validate S/MIME certificates. Never consult a CRL If this option is checked while a root CA certificate is being imported, you will be asked to confirm its fingerprint and to state whether or not you consider this root certificate to be trusted. A root certificate needs to be trusted before the certificates it certified become trusted, but lightly allowing trusted root certificates into your certificate store will undermine the security of the system. Allow to mark root certificates as trusted If this option is checked, missing issuer certificates are fetched when necessary (this applies to both validation methods, CRLs and OCSP). Fetch missing issuer certificates &HTTP Requests Entirely disables the use of HTTP for S/MIME. Do not perform any HTTP requests When looking for the location of a CRL, the to-be-tested certificate usually contains what are known as "CRL Distribution Point" (DP) entries, which are URLs describing the way to access the CRL. The first-found DP entry is used. With this option, all entries using the HTTP scheme are ignored when looking for a suitable DP. Ignore HTTP CRL distribution point of certificates If this option is selected, the value of the HTTP proxy shown on the right (which comes from the environment variable http_proxy) will be used for any HTTP request. Use system HTTP proxy: no proxy false Use this proxy for HTTP requests: <p>If no system proxy is set, or you need to use a different proxy for GpgSM, you can enter its location here.</p><p>It will be used for all HTTP requests relating to S/MIME.</p><p>The syntax is host:port, for instance myproxy.nowhere.com:3128.</p> Qt::Vertical 320 16 &LDAP Requests Entirely disables the use of LDAP for S/MIME. Do not perform any LDAP requests When looking for the location of a CRL, the to-be-tested certificate usually contains what are known as "CRL Distribution Point" (DP) entries, which are URLs describing the way to access the CRL. The first found DP entry is used. With this option, all entries using the LDAP scheme are ignored when looking for a suitable DP. Ignore LDAP CRL distribution point of certificates Primary host for LDAP requests: false Entering an LDAP server here will make all LDAP requests go to that server first. More precisely, this setting overrides any specified host and port part in a LDAP URL and will also be used if host and port have been omitted from the URL. Other LDAP servers will be used only if the connection to the "proxy" failed. The syntax is "HOST" or "HOST:PORT". If PORT is omitted, port 389 (standard LDAP port) is used. Qt::Vertical 320 16 KleopatraClientCopy::Gui::CertificateRequester QWidget
libkleopatraclient/gui/certificaterequester.h
1
libkleo/keyrequester.h CRLRB toggled(bool) OCSPGroupBox setDisabled(bool) 20 20 29 99 useCustomHTTPProxyRB toggled(bool) customHTTPProxy setEnabled(bool) 44 542 481 542 disableLDAPCB toggled(bool) ignoreLDAPDPCB setDisabled(bool) 190 440 188 471 disableLDAPCB toggled(bool) customLDAPLabel setDisabled(bool) 136 440 146 508 disableLDAPCB toggled(bool) customLDAPProxy setDisabled(bool) 328 440 481 508 disableHTTPCB toggled(bool) ignoreHTTPDPCB setDisabled(bool) 338 440 338 471 disableHTTPCB toggled(bool) honorHTTPProxyRB setDisabled(bool) 116 440 126 507 disableHTTPCB toggled(bool) useCustomHTTPProxyRB setDisabled(bool) 92 440 98 542 intervalRefreshCB toggled(bool) intervalRefreshSB setEnabled(bool) 282 243 334 245
diff --git a/src/crypto/autodecryptverifyfilescontroller.cpp b/src/crypto/autodecryptverifyfilescontroller.cpp index adf9b4459..2f996002a 100644 --- a/src/crypto/autodecryptverifyfilescontroller.cpp +++ b/src/crypto/autodecryptverifyfilescontroller.cpp @@ -1,550 +1,550 @@ /* -*- mode: c++; c-basic-offset:4 -*- autodecryptverifyfilescontroller.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "autodecryptverifyfilescontroller.h" #include "fileoperationspreferences.h" #include #include #include #include #include "commands/decryptverifyfilescommand.h" #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class AutoDecryptVerifyFilesController::Private { AutoDecryptVerifyFilesController *const q; public: explicit Private(AutoDecryptVerifyFilesController *qq); ~Private() { qCDebug(KLEOPATRA_LOG); delete m_workDir; } void slotDialogCanceled(); void schedule(); void exec(); std::vector > buildTasks(const QStringList &, QStringList &); struct CryptoFile { QString baseName; QString fileName; GpgME::Protocol protocol = GpgME::UnknownProtocol; int classification = 0; std::shared_ptr output; }; QVector classifyAndSortFiles(const QStringList &files); void reportError(int err, const QString &details) { q->setLastError(err, details); q->emitDoneOrError(); } void cancelAllTasks(); QStringList m_passedFiles, m_filesAfterPreparation; std::vector > m_results; std::vector > m_runnableTasks, m_completedTasks; std::shared_ptr m_runningTask; bool m_errorDetected; DecryptVerifyOperation m_operation; DecryptVerifyFilesDialog *m_dialog; QTemporaryDir *m_workDir; }; AutoDecryptVerifyFilesController::Private::Private(AutoDecryptVerifyFilesController *qq) : q(qq), m_errorDetected(false), m_operation(DecryptVerify), m_dialog(nullptr), m_workDir(nullptr) { qRegisterMetaType(); } void AutoDecryptVerifyFilesController::Private::slotDialogCanceled() { qCDebug(KLEOPATRA_LOG); } void AutoDecryptVerifyFilesController::Private::schedule() { if (!m_runningTask && !m_runnableTasks.empty()) { const std::shared_ptr t = m_runnableTasks.back(); m_runnableTasks.pop_back(); t->start(); m_runningTask = t; } if (!m_runningTask) { kleo_assert(m_runnableTasks.empty()); for (const std::shared_ptr &i : qAsConst(m_results)) { Q_EMIT q->verificationResult(i->verificationResult()); } } } void AutoDecryptVerifyFilesController::Private::exec() { Q_ASSERT(!m_dialog); QStringList undetected; std::vector > tasks = buildTasks(m_passedFiles, undetected); if (!undetected.isEmpty()) { // Since GpgME 1.7.0 Classification is supposed to be reliable // so we really can't do anything with this data. reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to find encrypted or signed data in one or more files." "You can manually select what to do with the files now." "If they contain signed or encrypted data please report a bug (see Help->Report Bug).")); auto cmd = new Commands::DecryptVerifyFilesCommand(undetected, nullptr, true); cmd->start(); } if (tasks.empty()) { q->emitDoneOrError(); return; } Q_ASSERT(m_runnableTasks.empty()); m_runnableTasks.swap(tasks); std::shared_ptr coll(new TaskCollection); Q_FOREACH (const std::shared_ptr &i, m_runnableTasks) { q->connectTask(i); } coll->setTasks(m_runnableTasks); m_dialog = new DecryptVerifyFilesDialog(coll); m_dialog->setOutputLocation(heuristicBaseDirectory(m_passedFiles)); QTimer::singleShot(0, q, SLOT(schedule())); if (m_dialog->exec() == QDialog::Accepted && m_workDir) { // Without workdir there is nothing to move. const QDir workdir(m_workDir->path()); const QDir outDir(m_dialog->outputLocation()); bool overWriteAll = false; qCDebug(KLEOPATRA_LOG) << workdir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &fi: workdir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) { const auto inpath = fi.absoluteFilePath(); if (fi.isDir()) { // A directory. Assume that the input was an archive // and avoid directory merges by trying to find a non // existing directory. auto candidate = fi.baseName(); if (candidate.startsWith(QLatin1Char('-'))) { // Bug in GpgTar Extracts stdout passed archives to a dir named - candidate = QFileInfo(m_passedFiles.first()).baseName(); } QString suffix; QFileInfo ofi; int i = 0; do { ofi = QFileInfo(outDir.absoluteFilePath(candidate + suffix)); if (!ofi.exists()) { break; } suffix = QStringLiteral("_%1").arg(++i); } while (i < 1000); if (!moveDir(inpath, ofi.absoluteFilePath())) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move %1 to %2.", inpath, ofi.absoluteFilePath())); } continue; } const auto outpath = outDir.absoluteFilePath(fi.fileName()); qCDebug(KLEOPATRA_LOG) << "Moving " << inpath << " to " << outpath; const QFileInfo ofi(outpath); if (ofi.exists()) { int sel = KMessageBox::No; if (!overWriteAll) { sel = KMessageBox::questionYesNoCancel(m_dialog, i18n("The file %1 already exists.\n" "Overwrite?", outpath), i18n("Overwrite Existing File?"), KStandardGuiItem::overwrite(), KGuiItem(i18n("Overwrite All")), KStandardGuiItem::cancel()); } if (sel == KMessageBox::Cancel) { qCDebug(KLEOPATRA_LOG) << "Overwriting canceled for: " << outpath; continue; } if (sel == KMessageBox::No) { //Overwrite All overWriteAll = true; } if (!QFile::remove(outpath)) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to delete %1.", outpath)); continue; } } if (!QFile::rename(inpath, outpath)) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move %1 to %2.", inpath, outpath)); } } } q->emitDoneOrError(); delete m_dialog; m_dialog = nullptr; } QVector AutoDecryptVerifyFilesController::Private::classifyAndSortFiles(const QStringList &files) { const auto isSignature = [](int classification) -> bool { return mayBeDetachedSignature(classification) || mayBeOpaqueSignature(classification) || (classification & Class::TypeMask) == Class::ClearsignedMessage; }; QVector out; for (const auto &file : files) { CryptoFile cFile; cFile.fileName = file; cFile.baseName = file.left(file.length() - 4); cFile.classification = classify(file); cFile.protocol = findProtocol(cFile.classification); auto it = std::find_if(out.begin(), out.end(), [&cFile](const CryptoFile &other) { return other.protocol == cFile.protocol && other.baseName == cFile.baseName; }); if (it != out.end()) { // If we found a file with the same basename, make sure that encrypted // file is before the signature file, so that we first decrypt and then // verify if (isSignature(cFile.classification) && isCipherText(it->classification)) { out.insert(it + 1, cFile); } else if (isCipherText(cFile.classification) && isSignature(it->classification)) { out.insert(it, cFile); } else { // both are signatures or both are encrypted files, in which // case order does not matter out.insert(it, cFile); } } else { out.push_back(cFile); } } return out; } std::vector< std::shared_ptr > AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected) { // sort files so that we make sure we first decrypt and then verify QVector cryptoFiles = classifyAndSortFiles(fileNames); std::vector > tasks; for (auto it = cryptoFiles.begin(), end = cryptoFiles.end(); it != end; ++it) { auto &cFile = (*it); QFileInfo fi(cFile.fileName); qCDebug(KLEOPATRA_LOG) << "classified" << cFile.fileName << "as" << printableClassification(cFile.classification); if (!fi.isReadable()) { reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("Cannot open %1 for reading.", cFile.fileName)); continue; } if (mayBeAnyCertStoreType(cFile.classification)) { // Trying to verify a certificate. Possible because extensions are often similar // for PGP Keys. reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("The file %1 contains certificates and can't be decrypted or verified.", cFile.fileName)); qCDebug(KLEOPATRA_LOG) << "reported error"; continue; } // We can't reliably detect CMS detached signatures, so we will try to do // our best to use the current file as a detached signature and fallback to // opaque signature otherwise. if (cFile.protocol == GpgME::CMS && mayBeDetachedSignature(cFile.classification)) { // First, see if previous task was a decryption task for the same file // and "pipe" it's output into our input std::shared_ptr input; bool prepend = false; if (it != cryptoFiles.begin()) { const auto prev = it - 1; if (prev->protocol == cFile.protocol && prev->baseName == cFile.baseName) { input = Input::createFromOutput(prev->output); prepend = true; } } if (!input) { if (QFile::exists(cFile.baseName)) { input = Input::createFromFile(cFile.baseName); } } if (input) { qCDebug(KLEOPATRA_LOG) << "Detached CMS verify: " << cFile.fileName; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(cFile.fileName)); t->setSignedData(input); t->setProtocol(cFile.protocol); if (prepend) { // Put the verify task BEFORE the decrypt task in the tasks queue, // because the tasks are executed in reverse order! tasks.insert(tasks.end() - 1, t); } else { tasks.push_back(t); } continue; } else { // No signed data, maybe not a detached signature } } if (isDetachedSignature(cFile.classification)) { // Detached signature, try to find data or ask the user. QString signedDataFileName = cFile.baseName; if (signedDataFileName.isEmpty()) { signedDataFileName = QFileDialog::getOpenFileName(nullptr, xi18n("Select the file to verify with \"%1\"", fi.fileName()), fi.dir().dirName()); } if (signedDataFileName.isEmpty()) { - qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify abortet."; + qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify aborted."; } else { qCDebug(KLEOPATRA_LOG) << "Detached verify: " << cFile.fileName << " Data: " << signedDataFileName; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(cFile.fileName)); t->setSignedData(Input::createFromFile(signedDataFileName)); t->setProtocol(cFile.protocol); tasks.push_back(t); } continue; } if (!mayBeAnyMessageType(cFile.classification)) { // Not a Message? Maybe there is a signature for this file? const auto signatures = findSignatures(cFile.fileName); bool foundSig = false; if (!signatures.empty()) { for (const QString &sig : signatures) { const auto classification = classify(sig); qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << cFile.fileName << "Classification: " << classification; const auto proto = findProtocol(classification); if (proto == GpgME::UnknownProtocol) { qCDebug(KLEOPATRA_LOG) << "Could not determine protocol. Skipping guess."; continue; } foundSig = true; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(sig)); t->setSignedData(Input::createFromFile(cFile.fileName)); t->setProtocol(proto); tasks.push_back(t); } } if (!foundSig) { undetected << cFile.fileName; qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << cFile.fileName << " adding to undetected."; } } else { // Any Message type so we have input and output. const auto input = Input::createFromFile(cFile.fileName); const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions(); const auto ad = q->pick_archive_definition(cFile.protocol, archiveDefinitions, cFile.fileName); if (FileOperationsPreferences().dontUseTmpDir()) { if (!m_workDir) { m_workDir = new QTemporaryDir(heuristicBaseDirectory(fileNames) + QStringLiteral("/kleopatra-XXXXXX")); } if (!m_workDir->isValid()) { qCDebug(KLEOPATRA_LOG) << m_workDir->path() << "not a valid temporary directory."; delete m_workDir; m_workDir = new QTemporaryDir(); } } else if (!m_workDir) { m_workDir = new QTemporaryDir(); } qCDebug(KLEOPATRA_LOG) << "Using:" << m_workDir->path() << "as temporary directory."; const auto wd = QDir(m_workDir->path()); const auto output = ad ? ad->createOutputFromUnpackCommand(cFile.protocol, cFile.fileName, wd) : /*else*/ Output::createFromFile(wd.absoluteFilePath(outputFileName(fi.fileName())), false); // If this might be opaque CMS signature, then try that. We already handled // detached CMS signature above const auto isCMSOpaqueSignature = cFile.protocol == GpgME::CMS && mayBeOpaqueSignature(cFile.classification); if (isOpaqueSignature(cFile.classification) || isCMSOpaqueSignature) { qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask"; std::shared_ptr t(new VerifyOpaqueTask); t->setInput(input); t->setOutput(output); t->setProtocol(cFile.protocol); tasks.push_back(t); } else { // Any message. That is not an opaque signature needs to be // decrypted. Verify we always do because we can't know if // an encrypted message is also signed. qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask"; std::shared_ptr t(new DecryptVerifyTask); t->setInput(input); t->setOutput(output); t->setProtocol(cFile.protocol); cFile.output = output; tasks.push_back(t); } } } return tasks; } void AutoDecryptVerifyFilesController::setFiles(const QStringList &files) { d->m_passedFiles = files; } AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(QObject *parent) : DecryptVerifyFilesController(parent), d(new Private(this)) { } AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(const std::shared_ptr &ctx, QObject *parent) : DecryptVerifyFilesController(ctx, parent), d(new Private(this)) { } AutoDecryptVerifyFilesController::~AutoDecryptVerifyFilesController() { qCDebug(KLEOPATRA_LOG); } void AutoDecryptVerifyFilesController::start() { d->exec(); } void AutoDecryptVerifyFilesController::setOperation(DecryptVerifyOperation op) { d->m_operation = op; } DecryptVerifyOperation AutoDecryptVerifyFilesController::operation() const { return d->m_operation; } void AutoDecryptVerifyFilesController::Private::cancelAllTasks() { // we just kill all runnable tasks - this will not result in // signal emissions. m_runnableTasks.clear(); // a cancel() will result in a call to if (m_runningTask) { m_runningTask->cancel(); } } void AutoDecryptVerifyFilesController::cancel() { qCDebug(KLEOPATRA_LOG); try { d->m_errorDetected = true; if (d->m_dialog) { d->m_dialog->close(); } d->cancelAllTasks(); } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); } } void AutoDecryptVerifyFilesController::doTaskDone(const Task *task, const std::shared_ptr &result) { Q_ASSERT(task); Q_UNUSED(task); // We could just delete the tasks here, but we can't use // Qt::QueuedConnection here (we need sender()) and other slots // might not yet have executed. Therefore, we push completed tasks // into a burial container d->m_completedTasks.push_back(d->m_runningTask); d->m_runningTask.reset(); if (const std::shared_ptr &dvr = std::dynamic_pointer_cast(result)) { d->m_results.push_back(dvr); } QTimer::singleShot(0, this, SLOT(schedule())); } #include "moc_autodecryptverifyfilescontroller.cpp" diff --git a/src/crypto/newsignencryptemailcontroller.cpp b/src/crypto/newsignencryptemailcontroller.cpp index f8d06fd52..20b9410b5 100644 --- a/src/crypto/newsignencryptemailcontroller.cpp +++ b/src/crypto/newsignencryptemailcontroller.cpp @@ -1,655 +1,655 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/newsignencryptemailcontroller.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2009,2010 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "newsignencryptemailcontroller.h" #include "kleopatra_debug.h" #include "encryptemailtask.h" #include "signemailtask.h" #include "taskcollection.h" #include "sender.h" #include "recipient.h" #include "emailoperationspreferences.h" #include #include "utils/input.h" #include "utils/output.h" #include "utils/gnupg-helper.h" #include "utils/kleo_assert.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; using namespace GpgME; using namespace KMime::Types; // // BEGIN Conflict Detection // /* - This code implements the following conflict detection algortihm: + This code implements the following conflict detection algorithm: 1. There is no conflict if and only if we have a Perfect Match. 2. A Perfect Match is defined as: a. either a Perfect OpenPGP-Match and not even a Partial S/MIME Match b. or a Perfect S/MIME-Match and not even a Partial OpenPGP-Match c. or a Perfect OpenPGP-Match and preselected protocol=OpenPGP d. or a Perfect S/MIME-Match and preselected protocol=S/MIME 3. For Protocol \in {OpenPGP,S/MIME}, a Perfect Protocol-Match is defined as: a. If signing, \foreach Sender, there is exactly one Matching Protocol-Certificate with i. can-sign=true ii. has-secret=true b. and, if encrypting, \foreach Recipient, there is exactly one Matching Protocol-Certificate with i. can-encrypt=true ii. (validity is not considered, cf. msg 24059) 4. For Protocol \in {OpenPGP,S/MIME}, a Partial Protocol-Match is defined as: a. If signing, \foreach Sender, there is at least one Matching Protocol-Certificate with i. can-sign=true ii. has-secret=true b. and, if encrypting, \foreach Recipient, there is at least one Matching Protocol-Certificate with i. can-encrypt=true ii. (validity is not considered, cf. msg 24059) 5. For Protocol \in {OpenPGP,S/MIME}, a Matching Protocol-Certificate is defined as matching by email-address. A revoked, disabled, or expired certificate is not considered a match. 6. Sender is defined as those mailboxes that have been set with the SENDER command. 7. Recipient is defined as those mailboxes that have been set with either the SENDER or the RECIPIENT commands. */ namespace { static size_t count_signing_certificates(Protocol proto, const Sender &sender) { const size_t result = sender.signingCertificateCandidates(proto).size(); qDebug("count_signing_certificates( %9s %20s ) == %2lu", proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : ",", qPrintable(sender.mailbox().prettyAddress()), result); return result; } static size_t count_encrypt_certificates(Protocol proto, const Sender &sender) { const size_t result = sender.encryptToSelfCertificateCandidates(proto).size(); qDebug("count_encrypt_certificates( %9s %20s ) == %2lu", proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : ",", qPrintable(sender.mailbox().prettyAddress()), result); return result; } static size_t count_encrypt_certificates(Protocol proto, const Recipient &recipient) { const size_t result = recipient.encryptionCertificateCandidates(proto).size(); qDebug("count_encrypt_certificates( %9s %20s ) == %2lu", proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : ",", qPrintable(recipient.mailbox().prettyAddress()), result); return result; } } static bool has_perfect_match(bool sign, bool encrypt, Protocol proto, const std::vector &senders, const std::vector &recipients) { if (sign) if (!std::all_of(senders.cbegin(), senders.cend(), [proto](const Sender &sender) { return count_signing_certificates(proto, sender) == 1; })) { return false; } if (encrypt) if (!std::all_of(senders.cbegin(), senders.cend(), [proto](const Sender &sender) { return count_encrypt_certificates(proto, sender) == 1; }) || !std::all_of(recipients.cbegin(), recipients.cend(), [proto](const Recipient &rec) { return count_encrypt_certificates(proto, rec) == 1; })) { return false; } return true; } static bool has_partial_match(bool sign, bool encrypt, Protocol proto, const std::vector &senders, const std::vector &recipients) { if (sign) if (std::all_of(senders.cbegin(), senders.cend(), [proto](const Sender &sender) { return count_signing_certificates(proto, sender) >= 1; })) { return false; } if (encrypt) if (!std::all_of(senders.cbegin(), senders.cend(), [proto](const Sender &sender) { return count_encrypt_certificates(proto, sender) >= 1; }) || !std::all_of(recipients.cbegin(), recipients.cend(), [proto](const Recipient &rec) { return count_encrypt_certificates(proto, rec) >= 1; })) { return false; } return true; } static bool has_perfect_overall_match(bool sign, bool encrypt, const std::vector &senders, const std::vector &recipients, Protocol presetProtocol) { return (presetProtocol == OpenPGP && has_perfect_match(sign, encrypt, OpenPGP, senders, recipients)) || (presetProtocol == CMS && has_perfect_match(sign, encrypt, CMS, senders, recipients)) || (has_perfect_match(sign, encrypt, OpenPGP, senders, recipients) && !has_partial_match(sign, encrypt, CMS, senders, recipients)) || (has_perfect_match(sign, encrypt, CMS, senders, recipients) && !has_partial_match(sign, encrypt, OpenPGP, senders, recipients)); } static bool has_conflict(bool sign, bool encrypt, const std::vector &senders, const std::vector &recipients, Protocol presetProtocol) { return !has_perfect_overall_match(sign, encrypt, senders, recipients, presetProtocol); } static bool is_de_vs_compliant(bool sign, bool encrypt, const std::vector &senders, const std::vector &recipients, Protocol presetProtocol) { if (presetProtocol == Protocol::UnknownProtocol) { return false; } if (sign) { for (const auto &sender: senders) { const auto &key = sender.resolvedSigningKey(presetProtocol); if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) { return false; } } } if (encrypt) { for (const auto &sender: senders) { const auto &key = sender.resolvedSigningKey(presetProtocol); if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) { return false; } } for (const auto &recipient: recipients) { const auto &key = recipient.resolvedEncryptionKey(presetProtocol); if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) { return false; } } } return true; } // // END Conflict Detection // static std::vector mailbox2sender(const std::vector &mbs) { std::vector senders; senders.reserve(mbs.size()); for (const Mailbox &mb : mbs) { senders.push_back(Sender(mb)); } return senders; } static std::vector mailbox2recipient(const std::vector &mbs) { std::vector recipients; recipients.reserve(mbs.size()); for (const Mailbox &mb : mbs) { recipients.push_back(Recipient(mb)); } return recipients; } class NewSignEncryptEMailController::Private { friend class ::Kleo::Crypto::NewSignEncryptEMailController; NewSignEncryptEMailController *const q; public: explicit Private(NewSignEncryptEMailController *qq); ~Private(); private: void slotDialogAccepted(); void slotDialogRejected(); private: void ensureDialogVisible(); void cancelAllTasks(); void startSigning(); void startEncryption(); void schedule(); std::shared_ptr takeRunnable(GpgME::Protocol proto); private: bool sign : 1; bool encrypt : 1; bool resolvingInProgress : 1; bool certificatesResolved : 1; bool detached : 1; Protocol presetProtocol; std::vector signers, recipients; std::vector< std::shared_ptr > runnable, completed; std::shared_ptr cms, openpgp; QPointer dialog; }; NewSignEncryptEMailController::Private::Private(NewSignEncryptEMailController *qq) : q(qq), sign(false), encrypt(false), resolvingInProgress(false), certificatesResolved(false), detached(false), presetProtocol(UnknownProtocol), signers(), recipients(), runnable(), cms(), openpgp(), dialog(new SignEncryptEMailConflictDialog) { connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted())); connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected())); } NewSignEncryptEMailController::Private::~Private() { delete dialog; } NewSignEncryptEMailController::NewSignEncryptEMailController(const std::shared_ptr &xc, QObject *p) : Controller(xc, p), d(new Private(this)) { } NewSignEncryptEMailController::NewSignEncryptEMailController(QObject *p) : Controller(p), d(new Private(this)) { } NewSignEncryptEMailController::~NewSignEncryptEMailController() { qCDebug(KLEOPATRA_LOG); } void NewSignEncryptEMailController::setSubject(const QString &subject) { d->dialog->setSubject(subject); } void NewSignEncryptEMailController::setProtocol(Protocol proto) { d->presetProtocol = proto; d->dialog->setPresetProtocol(proto); } Protocol NewSignEncryptEMailController::protocol() const { return d->dialog->selectedProtocol(); } const char *NewSignEncryptEMailController::protocolAsString() const { switch (protocol()) { case OpenPGP: return "OpenPGP"; case CMS: return "CMS"; default: throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Call to NewSignEncryptEMailController::protocolAsString() is ambiguous.")); } } void NewSignEncryptEMailController::setSigning(bool sign) { d->sign = sign; d->dialog->setSign(sign); } bool NewSignEncryptEMailController::isSigning() const { return d->sign; } void NewSignEncryptEMailController::setEncrypting(bool encrypt) { d->encrypt = encrypt; d->dialog->setEncrypt(encrypt); } bool NewSignEncryptEMailController::isEncrypting() const { return d->encrypt; } void NewSignEncryptEMailController::setDetachedSignature(bool detached) { d->detached = detached; } bool NewSignEncryptEMailController::isResolvingInProgress() const { return d->resolvingInProgress; } bool NewSignEncryptEMailController::areCertificatesResolved() const { return d->certificatesResolved; } static bool is_dialog_quick_mode(bool sign, bool encrypt) { const EMailOperationsPreferences prefs; return (!sign || prefs.quickSignEMail()) && (!encrypt || prefs.quickEncryptEMail()) ; } static void save_dialog_quick_mode(bool on) { EMailOperationsPreferences prefs; prefs.setQuickSignEMail(on); prefs.setQuickEncryptEMail(on); prefs.save(); } void NewSignEncryptEMailController::startResolveCertificates(const std::vector &r, const std::vector &s) { d->certificatesResolved = false; d->resolvingInProgress = true; const std::vector senders = mailbox2sender(s); const std::vector recipients = mailbox2recipient(r); const bool quickMode = is_dialog_quick_mode(d->sign, d->encrypt); const bool conflict = quickMode && has_conflict(d->sign, d->encrypt, senders, recipients, d->presetProtocol); d->dialog->setQuickMode(quickMode); d->dialog->setSenders(senders); d->dialog->setRecipients(recipients); d->dialog->pickProtocol(); d->dialog->setConflict(conflict); const bool compliant = !Kleo::gpgComplianceP("de-vs") || is_de_vs_compliant(d->sign, d->encrypt, senders, recipients, d->presetProtocol); if (quickMode && !conflict && compliant) { QMetaObject::invokeMethod(this, "slotDialogAccepted", Qt::QueuedConnection); } else { d->ensureDialogVisible(); } } void NewSignEncryptEMailController::Private::slotDialogAccepted() { if (dialog->isQuickMode() != is_dialog_quick_mode(sign, encrypt)) { save_dialog_quick_mode(dialog->isQuickMode()); } resolvingInProgress = false; certificatesResolved = true; signers = dialog->resolvedSigningKeys(); recipients = dialog->resolvedEncryptionKeys(); QMetaObject::invokeMethod(q, "certificatesResolved", Qt::QueuedConnection); } void NewSignEncryptEMailController::Private::slotDialogRejected() { resolvingInProgress = false; certificatesResolved = false; QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, Q_ARG(int, gpg_error(GPG_ERR_CANCELED)), Q_ARG(QString, i18n("User cancel"))); } void NewSignEncryptEMailController::startEncryption(const std::vector< std::shared_ptr > &inputs, const std::vector< std::shared_ptr > &outputs) { kleo_assert(d->encrypt); kleo_assert(!d->resolvingInProgress); kleo_assert(!inputs.empty()); kleo_assert(outputs.size() == inputs.size()); std::vector< std::shared_ptr > tasks; tasks.reserve(inputs.size()); kleo_assert(!d->recipients.empty()); for (unsigned int i = 0, end = inputs.size(); i < end; ++i) { const std::shared_ptr task(new EncryptEMailTask); task->setInput(inputs[i]); task->setOutput(outputs[i]); task->setRecipients(d->recipients); tasks.push_back(task); } // append to runnable stack d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end()); d->startEncryption(); } void NewSignEncryptEMailController::Private::startEncryption() { std::shared_ptr coll(new TaskCollection); std::vector > tmp; tmp.reserve(runnable.size()); std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp)); coll->setTasks(tmp); #if 0 #warning use a new result dialog // ### use a new result dialog dialog->setTaskCollection(coll); #endif Q_FOREACH (const std::shared_ptr &t, tmp) { q->connectTask(t); } schedule(); } void NewSignEncryptEMailController::startSigning(const std::vector< std::shared_ptr > &inputs, const std::vector< std::shared_ptr > &outputs) { kleo_assert(d->sign); kleo_assert(!d->resolvingInProgress); kleo_assert(!inputs.empty()); kleo_assert(!outputs.empty()); std::vector< std::shared_ptr > tasks; tasks.reserve(inputs.size()); kleo_assert(!d->signers.empty()); kleo_assert(std::none_of(d->signers.cbegin(), d->signers.cend(), std::mem_fn(&Key::isNull))); for (unsigned int i = 0, end = inputs.size(); i < end; ++i) { const std::shared_ptr task(new SignEMailTask); task->setInput(inputs[i]); task->setOutput(outputs[i]); task->setSigners(d->signers); task->setDetachedSignature(d->detached); tasks.push_back(task); } // append to runnable stack d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end()); d->startSigning(); } void NewSignEncryptEMailController::Private::startSigning() { std::shared_ptr coll(new TaskCollection); std::vector > tmp; tmp.reserve(runnable.size()); std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp)); coll->setTasks(tmp); #if 0 #warning use a new result dialog // ### use a new result dialog dialog->setTaskCollection(coll); #endif Q_FOREACH (const std::shared_ptr &t, tmp) { q->connectTask(t); } schedule(); } void NewSignEncryptEMailController::Private::schedule() { if (!cms) if (const std::shared_ptr t = takeRunnable(CMS)) { t->start(); cms = t; } if (!openpgp) if (const std::shared_ptr t = takeRunnable(OpenPGP)) { t->start(); openpgp = t; } if (cms || openpgp) { return; } kleo_assert(runnable.empty()); q->emitDoneOrError(); } std::shared_ptr NewSignEncryptEMailController::Private::takeRunnable(GpgME::Protocol proto) { const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr &task) { return task->protocol() == proto; }); if (it == runnable.end()) { return std::shared_ptr(); } const std::shared_ptr result = *it; runnable.erase(it); return result; } void NewSignEncryptEMailController::doTaskDone(const Task *task, const std::shared_ptr &result) { Q_ASSERT(task); if (result && result->hasError()) { QPointer that = this; if (result->details().isEmpty()) KMessageBox:: sorry(nullptr, result->overview(), i18nc("@title:window", "Error")); else KMessageBox::detailedSorry(nullptr, result->overview(), result->details(), i18nc("@title:window", "Error")); if (!that) { return; } } // We could just delete the tasks here, but we can't use // Qt::QueuedConnection here (we need sender()) and other slots // might not yet have executed. Therefore, we push completed tasks // into a burial container if (task == d->cms.get()) { d->completed.push_back(d->cms); d->cms.reset(); } else if (task == d->openpgp.get()) { d->completed.push_back(d->openpgp); d->openpgp.reset(); } QTimer::singleShot(0, this, SLOT(schedule())); } void NewSignEncryptEMailController::cancel() { try { d->dialog->close(); d->cancelAllTasks(); } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); } } void NewSignEncryptEMailController::Private::cancelAllTasks() { // we just kill all runnable tasks - this will not result in // signal emissions. runnable.clear(); // a cancel() will result in a call to if (cms) { cms->cancel(); } if (openpgp) { openpgp->cancel(); } } void NewSignEncryptEMailController::Private::ensureDialogVisible() { q->bringToForeground(dialog, true); } #include "moc_newsignencryptemailcontroller.cpp" diff --git a/src/dialogs/ownertrustdialog.ui b/src/dialogs/ownertrustdialog.ui index 1f60fa9eb..286ad1619 100644 --- a/src/dialogs/ownertrustdialog.ui +++ b/src/dialogs/ownertrustdialog.ui @@ -1,496 +1,496 @@ OwnerTrustDialog 0 0 700 380 QLayout::SetMinimumSize true I do not know true true <i>(unknown trust)</i> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 20 20 true <font size="-1">Choose this if you have no opinion about the trustworthyness of the certificate's owner.<br>Certifications at this trust level are ignored when checking the validity of OpenPGP certificates.</font> true I do NOT trust them false <i>(never trust)</i> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 20 20 false <font size="-1">Choose this if you explicitly do <em>not</em> trust the certificate owner, e.g. because you have knowledge of him certifying without checking or without the certificate owner's consent.<br>Certifications at this trust level are ignored when checking the validity of OpenPGP certificates.</font> true I believe checks are casual false <i>(marginal trust)</i> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 20 20 false - <font size="-1">Choose this if you trust certifications are not done blindly, but not very accuratly, either.<br>Certificates will only become valid with multiple certifications (typically three) at this trust level. This is usually a good choice.</font> + <font size="-1">Choose this if you trust certifications are not done blindly, but not very accurately, either.<br>Certificates will only become valid with multiple certifications (typically three) at this trust level. This is usually a good choice.</font> true I believe checks are very accurate false <i>(full trust)</i> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 20 20 false <font size="-1">Choose this if you trust certifications are done very accurately.<br>Certificates will become valid with just a single certification at this trust level, so assign this much trust with care.</font> true false This is my certificate false <i>(ultimate trust)</i> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 20 20 false <font size="-1">Choose this if and only if this is your certificate. This is the default if the secret key is available, but if you imported this certificate, you might need to adjust the trust level yourself.<br>Certificates will become valid with just a single certification at this trust level.</font> true Qt::Vertical 501 40 unknownRB toggled(bool) label_2 setEnabled(bool) 258 50 282 82 neverRB toggled(bool) label_3 setEnabled(bool) 227 111 279 155 marginalRB toggled(bool) label_4 setEnabled(bool) 267 184 321 216 fullRB toggled(bool) label_5 setEnabled(bool) 321 245 381 277 ultimateRB toggled(bool) label_6 setEnabled(bool) 227 306 283 350 unknownRB toggled(bool) label_7 setEnabled(bool) 317 46 689 50 neverRB toggled(bool) label_8 setEnabled(bool) 329 111 689 111 marginalRB toggled(bool) label_9 setEnabled(bool) 331 184 689 184 fullRB toggled(bool) label_10 setEnabled(bool) 333 245 689 245 ultimateRB toggled(bool) label_11 setEnabled(bool) 335 306 689 306 slotTrustLevelChanged() diff --git a/src/kcfg/smimevalidationpreferences.kcfg b/src/kcfg/smimevalidationpreferences.kcfg index f905255a7..c6cae61ba 100644 --- a/src/kcfg/smimevalidationpreferences.kcfg +++ b/src/kcfg/smimevalidationpreferences.kcfg @@ -1,12 +1,12 @@ - This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is perfomed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. + This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is performed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity. 1 24 diff --git a/src/main.cpp b/src/main.cpp index 6d5bbdcf6..64ee20d53 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,264 +1,264 @@ /* main.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2001,2002,2004,2008 Klarävdalens Datakonsult AB 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "aboutdata.h" #include "kleopatraapplication.h" #include "mainwindow.h" #include #include #include #include #include #include "utils/kuniqueservice.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include "kleopatra_options.h" #include #include #include #include #include // for Qt::escape #include #include #include #include #include #include #include #include #include #include static bool selfCheck() { Kleo::Commands::SelfTestCommand cmd(nullptr); cmd.setAutoDelete(false); cmd.setAutomaticMode(true); QEventLoop loop; QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit); QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()... loop.exec(); if (cmd.isCanceled()) { return false; } else { return true; } } static void fillKeyCache(Kleo::UiServer *server) { Kleo::ReloadKeysCommand *cmd = new Kleo::ReloadKeysCommand(nullptr); QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands())); cmd->start(); } int main(int argc, char **argv) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); KleopatraApplication app(argc, argv); KCrash::initialize(); QTime timer; timer.start(); KLocalizedString::setApplicationDomain("kleopatra"); KUniqueService service; QObject::connect(&service, &KUniqueService::activateRequested, &app, &KleopatraApplication::slotActivateRequested); QObject::connect(&app, &KleopatraApplication::setExitValue, &service, [&service](int i) { service.setExitValue(i); }); // Delay init after KUniqueservice call as this might already // have terminated us and so we can avoid overhead (e.g. keycache // setup / systray icon). qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Service created"; app.init(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application initialized"; AboutData aboutData; KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); kleopatra_options(&parser); parser.process(QApplication::arguments()); aboutData.processCommandLine(&parser); Kdelibs4ConfigMigrator migrate(QStringLiteral("kleopatra")); migrate.setConfigFiles(QStringList() << QStringLiteral("kleopatrarc") << QStringLiteral("libkleopatrarc")); migrate.setUiFiles(QStringList() << QStringLiteral("kleopatra.rc")); migrate.migrate(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application created"; // Initialize GpgME const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0); { const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount(); QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads)); } if (gpgmeInitError) { KMessageBox::sorry(nullptr, xi18nc("@info", "The version of the GpgME library you are running against " "is older than the one that the GpgME++ library was built against." "Kleopatra will not function in this setting." "Please ask your administrator for help in resolving this issue."), i18nc("@title", "GpgME Too Old")); return EXIT_FAILURE; } - qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: GPGME Initalized"; + qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: GPGME Initialized"; Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath()); Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath()); int rc; Kleo::UiServer server(parser.value(QStringLiteral("uiserver-socket"))); try { qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer created"; QObject::connect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::connect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); #define REGISTER( Command ) server.registerCommandFactory( std::shared_ptr( new Kleo::GenericAssuanCommandFactory ) ) REGISTER(CreateChecksumsCommand); REGISTER(DecryptCommand); REGISTER(DecryptFilesCommand); REGISTER(DecryptVerifyFilesCommand); REGISTER(EchoCommand); REGISTER(EncryptCommand); REGISTER(EncryptFilesCommand); REGISTER(EncryptSignFilesCommand); REGISTER(ImportFilesCommand); REGISTER(PrepEncryptCommand); REGISTER(PrepSignCommand); REGISTER(SelectCertificateCommand); REGISTER(SignCommand); REGISTER(SignEncryptFilesCommand); REGISTER(SignFilesCommand); REGISTER(VerifyChecksumsCommand); REGISTER(VerifyCommand); REGISTER(VerifyFilesCommand); #undef REGISTER server.start(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer started"; } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what(); #ifdef Q_OS_WIN // Once there actually is a plugin for other systems then Windows this // error should probably be shown, too. But currently only Windows users need // to care. QMessageBox::information(nullptr, i18n("GPG UI Server Error"), i18n("The Kleopatra GPG UI Server Module could not be initialized.
" "The error given was: %1
" "You can use Kleopatra as a certificate manager, but cryptographic plugins that " "rely on a GPG UI Server being present might not work correctly, or at all.
", QString::fromUtf8(e.what()).toHtmlEscaped())); #endif } const bool daemon = parser.isSet(QStringLiteral("daemon")); if (!daemon && app.isSessionRestored()) { app.restoreMainWindow(); } if (!selfCheck()) { return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: SelfCheck completed"; fillKeyCache(&server); #ifndef QT_NO_SYSTEMTRAYICON app.startMonitoringSmartCard(); #endif app.setIgnoreNewInstance(false); if (!daemon) { const QString err = app.newInstance(parser); if (!err.isEmpty()) { std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n"; return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: new instance created"; } rc = app.exec(); app.setIgnoreNewInstance(true); QObject::disconnect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::disconnect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); server.stop(); server.waitForStopped(); return rc; } diff --git a/src/selftest/enginecheck.cpp b/src/selftest/enginecheck.cpp index c429fc49e..9bb2349c1 100644 --- a/src/selftest/enginecheck.cpp +++ b/src/selftest/enginecheck.cpp @@ -1,216 +1,216 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/enginecheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "enginecheck.h" #include "utils/gnupg-helper.h" #include "implementation_p.h" #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include using namespace Kleo; using namespace Kleo::_detail; using namespace GpgME; static QString engine_name(GpgME::Engine eng) { static const char *engines[] = { "gpg", "gpgsm", "gpgconf" }; return QString::fromLatin1(engines[eng]); } static QString test_name(GpgME::Engine eng) { static const char *names[] = { I18NC_NOOP("@title", "GPG (OpenPGP Backend) installation"), I18NC_NOOP("@title", "GpgSM (S/MIME Backend) installation"), I18NC_NOOP("@title", "GpgConf (Configuration) installation"), }; return i18nc("@title", names[eng]); } namespace { class EngineCheck : public SelfTestImplementation { public: explicit EngineCheck(GpgME::Engine eng) : SelfTestImplementation(test_name(eng)) { runTest(eng); } void runTest(GpgME::Engine eng) { // First use the crypto config which is much faster because it is only // created once and then kept in memory. Only if the crypoconfig is // bad we check into the engine info. const auto conf = QGpgME::cryptoConfig(); if (conf && eng == GpgME::GpgEngine) { m_passed = true; return; } else if (conf) { const auto comp = conf->component(engine_name(eng)); if (comp) { m_passed = true; return; } } // Problem with the config. Try to get more details: const Error err = GpgME::checkEngine(eng); Q_ASSERT(!err.code() || err.code() == GPG_ERR_INV_ENGINE); m_passed = !err; if (m_passed) { return; } - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "A problem was detected with the %1 backend.", engine_name(eng)); const EngineInfo ei = engineInfo(eng); if (ei.isNull()) { m_error = i18n("not supported"); - m_explaination += xi18nc("@info", + m_explanation += xi18nc("@info", "It seems that the gpgme library was compiled without " "support for this backend."); m_proposedFix += xi18nc("@info", "Replace the gpgme library with a version compiled " "with %1 support.", engine_name(eng)); } else if (ei.fileName() && (!ei.version() || !strcmp(ei.version(), "1.0.0"))) { // GPGSM only got the ei.version() working with 1.0.0 so 1.0.0 is returned as // a fallback if the version could not be checked. We assume that it's not properly // installed in that case. m_error = i18n("not properly installed"); - m_explaination += xi18nc("@info", + m_explanation += xi18nc("@info", "Backend %1 is not installed properly.", QFile::decodeName(ei.fileName())); m_proposedFix += xi18nc("@info", "Please check the output of %1 --version manually.", QFile::decodeName(ei.fileName())); } else if (ei.fileName() && ei.version() && ei.requiredVersion()) { m_error = i18n("too old"); - m_explaination += xi18nc("@info", + m_explanation += xi18nc("@info", "Backend %1 is installed in version %2, " "but at least version %3 is required.", QFile::decodeName(ei.fileName()), QString::fromUtf8(ei.version()), QString::fromUtf8(ei.requiredVersion())); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(eng), QString::fromUtf8(ei.requiredVersion())); } else { - m_error = m_explaination = i18n("unknown problem"); + m_error = m_explanation = i18n("unknown problem"); m_proposedFix += xi18nc("@info", "Make sure %1 is installed and " "in PATH.", engine_name(eng)); } } }; } std::shared_ptr Kleo::makeGpgEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgEngine)); } std::shared_ptr Kleo::makeGpgSmEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgSMEngine)); } std::shared_ptr Kleo::makeGpgConfEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgConfEngine)); } // // SelfTestImplementation (parts) // bool SelfTestImplementation::ensureEngineVersion(GpgME::Engine engine, int major, int minor, int patch) { const Error err = GpgME::checkEngine(engine); Q_ASSERT(!err || err.code() == GPG_ERR_INV_ENGINE); m_skipped = err || !engineIsVersion(major, minor, patch, engine); if (!m_skipped) { return true; } const char *version = GpgME::engineInfo(engine).version(); if (!err && version) { // properly installed, but too old - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "%1 v%2.%3.%4 is required for this test, but only %5 is installed.", engine_name(engine), major, minor, patch, QString::fromUtf8(version)); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(engine), QStringLiteral("%1.%2.%3").arg(major).arg(minor).arg(patch)); } else { // not properly installed - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "%1 is required for this test, but does not seem available." "See tests further up for more information.", engine_name(engine)); m_proposedFix = xi18nc("@info %1: test name", "See \"%1\" above.", test_name(engine)); } return false; } diff --git a/src/selftest/gpgagentcheck.cpp b/src/selftest/gpgagentcheck.cpp index f25acae2f..aeb72fc47 100644 --- a/src/selftest/gpgagentcheck.cpp +++ b/src/selftest/gpgagentcheck.cpp @@ -1,123 +1,123 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/gpgagentcheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2009 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "gpgagentcheck.h" #include "implementation_p.h" #include #include #include // for Qt::escape #include using namespace Kleo; using namespace Kleo::_detail; using namespace GpgME; namespace { class GpgAgentCheck : public SelfTestImplementation { public: explicit GpgAgentCheck() : SelfTestImplementation(i18nc("@title", "Gpg-Agent Connectivity")) { runTest(); } void runTest() { m_skipped = true; if (!hasFeature(AssuanEngineFeature, 0)) { m_error = i18n("GpgME library too old"); - m_explaination = i18nc("@info", + m_explanation = i18nc("@info", "Either the GpgME library itself is too old, " "or the GpgME++ library was compiled against " "an older GpgME that did not support connecting to gpg-agent."); m_proposedFix = xi18nc("@info", "Upgrade to gpgme 1.2.0 or higher, " "and ensure that gpgme++ was compiled against it."); } else if (ensureEngineVersion(GpgME::GpgConfEngine, 2, 1, 0)) { // 2.1 starts the agent on demand and requires it. So for 2.1.0 we can assume // autostart works and we don't need to care about the agent. m_skipped = false; m_passed = true; return; } else { Error error; const std::unique_ptr ctx = Context::createForEngine(AssuanEngine, &error); if (!ctx.get()) { m_error = i18n("GpgME does not support gpg-agent"); - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "The GpgME library is new " "enough to support gpg-agent, " "but does not seem to do so in this installation." "The error returned was: %1.", QString::fromLocal8Bit(error.asString()).toHtmlEscaped()); // PENDING(marc) proposed fix? } else { m_skipped = false; const Error error = ctx->assuanTransact("GETINFO version"); if (error) { m_passed = false; m_error = i18n("unexpected error"); - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "Unexpected error while asking gpg-agent " "for its version." "The error returned was: %1.", QString::fromLocal8Bit(error.asString()).toHtmlEscaped()); // PENDING(marc) proposed fix? } else { m_passed = true; } } } } }; } std::shared_ptr Kleo::makeGpgAgentConnectivitySelfTest() { return std::shared_ptr(new GpgAgentCheck); } diff --git a/src/selftest/gpgconfcheck.cpp b/src/selftest/gpgconfcheck.cpp index 1e26552ce..5a60f7be3 100644 --- a/src/selftest/gpgconfcheck.cpp +++ b/src/selftest/gpgconfcheck.cpp @@ -1,112 +1,112 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/gpgconfcheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "gpgconfcheck.h" #include "implementation_p.h" #include #include #include "kleopatra_debug.h" #include #include #include using namespace Kleo; using namespace Kleo::_detail; namespace { class GpgConfCheck : public SelfTestImplementation { QString m_component; public: explicit GpgConfCheck(const char *component) : SelfTestImplementation(i18nc("@title", "%1 Configuration Check", component && * component ? QLatin1String(component) : QLatin1String("gpgconf"))), m_component(QLatin1String(component)) { runTest(); } void runTest() { const auto conf = QGpgME::cryptoConfig(); QString message; m_passed = true; if (!conf) { message = QStringLiteral ("Could not be started."); m_passed = false; } else if (m_component.isEmpty() && conf->componentList().empty()) { message = QStringLiteral ("Could not list components."); m_passed = false; } else if (!m_component.isEmpty()) { const auto comp = conf->component (m_component); if (!comp) { message = QStringLiteral ("Binary could not be found."); m_passed = false; } else if (comp->groupList().empty()) { // If we don't have any group it means that list-options // for this component failed. message = QStringLiteral ("The configuration file is invalid."); m_passed = false; } } if (!m_passed) { m_error = i18nc("self-test did not pass", "Failed"); - m_explaination = + m_explanation = i18n("There was an error executing the GnuPG configuration self-check for %2:\n" " %1\n" "You might want to execute \"gpgconf %3\" on the command line.\n", message, m_component.isEmpty() ? QStringLiteral("GnuPG") : m_component, QStringLiteral("--check-options ") + (m_component.isEmpty() ? QStringLiteral("") : m_component)); // To avoid modifying the l10n - m_explaination.replace(QLatin1Char('\n'), QStringLiteral("
")); + m_explanation.replace(QLatin1Char('\n'), QStringLiteral("
")); } } }; } std::shared_ptr Kleo::makeGpgConfCheckConfigurationSelfTest(const char *component) { return std::shared_ptr(new GpgConfCheck(component)); } diff --git a/src/selftest/implementation_p.h b/src/selftest/implementation_p.h index 21832dd8b..1e85b3146 100644 --- a/src/selftest/implementation_p.h +++ b/src/selftest/implementation_p.h @@ -1,94 +1,94 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/implementation_p.h This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KLEOPATRA_SELFTEST_IMPLEMENTATION_P_H__ #define __KLEOPATRA_SELFTEST_IMPLEMENTATION_P_H__ #include #include #include namespace Kleo { namespace _detail { class SelfTestImplementation : public SelfTest { public: explicit SelfTestImplementation(const QString &name); ~SelfTestImplementation() override; QString name() const override { return m_name; } QString shortError() const override { return m_error; } QString longError() const override { - return m_explaination; + return m_explanation; } QString proposedFix() const override { return m_proposedFix; } bool skipped() const override { return m_skipped; } bool passed() const override { return m_passed; } protected: bool ensureEngineVersion(GpgME::Engine, int major, int minor, int patch); protected: const QString m_name; QString m_error; - QString m_explaination; + QString m_explanation; QString m_proposedFix; bool m_skipped : 1; bool m_passed : 1; }; } } #endif /* __KLEOPATRA_SELFTEST_IMPLEMENTATION_P_H__ */ diff --git a/src/selftest/libkleopatrarccheck.cpp b/src/selftest/libkleopatrarccheck.cpp index 0bac09bcb..7a58a9a17 100644 --- a/src/selftest/libkleopatrarccheck.cpp +++ b/src/selftest/libkleopatrarccheck.cpp @@ -1,92 +1,92 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/libkleopatrarccheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2010 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "libkleopatrarccheck.h" #include "implementation_p.h" #include #include #include using namespace Kleo; using namespace Kleo::_detail; namespace { class LibKleopatraRcCheck : public SelfTestImplementation { public: explicit LibKleopatraRcCheck() : SelfTestImplementation(i18nc("@title", "Config File 'libkleopatrarc'")) { runTest(); } void runTest() { QStringList errors; ArchiveDefinition::getArchiveDefinitions(errors); ChecksumDefinition::getChecksumDefinitions(errors); m_passed = errors.empty(); if (m_passed) { return; } m_error = i18n("Errors found"); // The building of the following string is a bit of a hack to avoid // that xi18nc does not escape the html tags while not breaking // the historic string. - m_explaination + m_explanation = xi18nc("@info", "Kleopatra detected the following errors in the libkleopatrarc configuration:" "%1", QStringLiteral("%1")).arg(QStringLiteral("
  1. ") + errors.join(QLatin1String("
  2. ")) + QStringLiteral("
")); } ///* reimp */ bool canFixAutomatically() const { return false; } }; } std::shared_ptr Kleo::makeLibKleopatraRcSelfTest() { return std::shared_ptr(new LibKleopatraRcCheck); } diff --git a/src/selftest/registrycheck.cpp b/src/selftest/registrycheck.cpp index db0e1a1a1..76c83a962 100644 --- a/src/selftest/registrycheck.cpp +++ b/src/selftest/registrycheck.cpp @@ -1,126 +1,126 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/registrycheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "registrycheck.h" #include "implementation_p.h" #include #include #include using namespace Kleo; using namespace Kleo::_detail; static QString gnupg_path = QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\GNU\\GnuPG"); static QString gnupg_key = QStringLiteral("gpgProgram"); namespace { class RegistryCheck : public SelfTestImplementation { public: explicit RegistryCheck() : SelfTestImplementation(i18nc("@title", "Windows Registry")) { runTest(); } void runTest() { m_passed = !QSettings(gnupg_path, QSettings::NativeFormat).contains(gnupg_key); if (m_passed) { return; } m_error = i18n("Obsolete registry entries found"); - m_explaination + m_explanation = xi18nc("@info", "Kleopatra detected an obsolete registry key (%1\\%2), " "added by either a previous Gpg4win version or " "applications such as WinPT or EnigMail." "Keeping the entry might lead to an old GnuPG backend being used.", gnupg_path, gnupg_key); m_proposedFix = xi18nc("@info", "Delete registry key %1\\%2.", gnupg_path, gnupg_key); } /* reimp */ bool canFixAutomatically() const { return true; } /* reimp */ bool fix() { QSettings settings(gnupg_path, QSettings::NativeFormat); if (!settings.contains(gnupg_key)) { return true; } settings.remove(gnupg_key); settings.sync(); if (settings.status() != QSettings::NoError) { KMessageBox::error( 0, xi18nc("@info", "Could not delete the registry key %1\\%2", gnupg_path, gnupg_key), i18nc("@title", "Error Deleting Registry Key")); return false; } m_passed = true; m_error.clear(); - m_explaination.clear(); + m_explanation.clear(); m_proposedFix.clear(); return true; } }; } std::shared_ptr Kleo::makeGpgProgramRegistryCheckSelfTest() { return std::shared_ptr(new RegistryCheck); } diff --git a/src/selftest/selftest.cpp b/src/selftest/selftest.cpp index 37e2bd897..700d68978 100644 --- a/src/selftest/selftest.cpp +++ b/src/selftest/selftest.cpp @@ -1,67 +1,67 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/selftest.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "selftest.h" #include "implementation_p.h" using namespace Kleo; using namespace Kleo::_detail; SelfTest::~SelfTest() {} bool SelfTest::canFixAutomatically() const { return false; } bool SelfTest::fix() { return false; } SelfTestImplementation::SelfTestImplementation(const QString &title) : SelfTest(), m_name(title), m_error(), - m_explaination(), + m_explanation(), m_proposedFix(), m_skipped(false), m_passed(false) { } SelfTestImplementation::~SelfTestImplementation() {} // bool SelfTestImplementation::ensureEngineVersion( GpgME::Engine engine, int major, int minor, int patch ) // in enginecheck.cpp, since it reuses the instrumentation there diff --git a/src/selftest/uiservercheck.cpp b/src/selftest/uiservercheck.cpp index 9617fe459..8095db526 100644 --- a/src/selftest/uiservercheck.cpp +++ b/src/selftest/uiservercheck.cpp @@ -1,104 +1,104 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/uiservercheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "uiservercheck.h" #include "implementation_p.h" #include #include // for Qt::escape #include #include #include using namespace Kleo; using namespace Kleo::_detail; namespace { class UiServerCheck : public SelfTestImplementation { public: explicit UiServerCheck() : SelfTestImplementation(i18nc("@title", "UiServer Connectivity")) { runTest(); } void runTest() { KleopatraClientCopy::Command command; { QEventLoop loop; loop.connect(&command, SIGNAL(finished()), SLOT(quit())); QMetaObject::invokeMethod(&command, "start", Qt::QueuedConnection); loop.exec(); } if (command.error()) { m_passed = false; m_error = i18n("not reachable"); - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "Could not connect to UiServer: %1", command.errorString().toHtmlEscaped()); m_proposedFix = xi18nc("@info", "Check that your firewall is not set to block local connections " "(allow connections to localhost or 127.0.0.1)."); } else if (command.serverPid() != QCoreApplication::applicationPid()) { m_passed = false; m_error = i18n("multiple instances"); - m_explaination = xi18nc("@info", + m_explanation = xi18nc("@info", "It seems another Kleopatra is running (with process-id %1)", command.serverPid()); m_proposedFix = xi18nc("@info", "Quit any other running instances of Kleopatra."); } else { m_passed = true; } } }; } std::shared_ptr Kleo::makeUiServerConnectivitySelfTest() { return std::shared_ptr(new UiServerCheck); } diff --git a/src/uiserver/assuancommand.h b/src/uiserver/assuancommand.h index 99408794b..d5f1da55f 100644 --- a/src/uiserver/assuancommand.h +++ b/src/uiserver/assuancommand.h @@ -1,410 +1,410 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/assuancommand.h This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__ #define __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__ #include #include #include #include #ifdef HAVE_ASSUAN2 #include #endif #include #include // for WId #include #include #include #include class QVariant; class QObject; class QStringList; struct assuan_context_s; namespace Kleo { class Input; class Output; class AssuanCommandFactory; /*! \brief Base class for GnuPG UI Server commands \note large parts of this are outdated by now!

Implementing a new AssuanCommand

You do not directly inherit AssuanCommand, unless you want to - deal with implementing low-level, repetetive things like name() + deal with implementing low-level, repetitive things like name() in terms of staticName(). Assuming you don't, then you inherit your command class from AssuanCommandMixin, passing your class as the template argument to AssuanCommandMixin, like this: \code class MyFooCommand : public AssuanCommandMixin { \endcode (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) You then choose a command name, and return that from the static method staticName(), which is by convention queried by both AssuanCommandMixin<> and GenericAssuanCommandFactory<>: \code static const char * staticName() { return "MYFOO"; } \endcode The string should be all-uppercase by convention, but the UiServer implementation doesn't enforce this. The next step is to implement start(), the starting point of command execution:

Executing the command

\code int start( const std::string & line ) { \endcode This should set everything up and check the parameters in \a line and any options this command understands. If there's an error, choose one of the gpg-error codes and create a gpg_error_t from it using the protected makeError() function: \code return makeError( GPG_ERR_NOT_IMPLEMENTED ); \endcode But usually, you will want to create a dialog, or call some GpgME function from here. In case of errors from GpgME, you shouldn't pipe them through makeError(), but return them as-is. This will preserve the error source. Error created using makeError() will have Kleopatra as their error source, so watch out what you're doing :) In addition to options and the command line, your command might require bulk data input or output. That's what the bulk input and output channels are for. You can check whether the client handed you an input channel by checking that bulkInputDevice() isn't NULL, likewise for bulkOutputDevice(). If everything is ok, you return 0. This indicates to the client that the command has been accepted and is now in progress. In this mode (start() returned 0), there are a bunch of options for your command to do. Some commands may require additional information from the client. The options passed to start() are designed to be persistent across commands, and rather limited in length (there's a strict line length limit in the assuan protocol with no line continuation mechanism). The same is true for command line arguments, which, in addition, you have to parse yourself. Those usually apply only to this command, and not to following ones. If you need data that might be larger than the line length limit, you can either expect it on the bulkInputDevice(), or, if you have the need for more than one such data channel, or the data is optional or conditional on some condition that can only be determined during command execution, you can \em inquire the missing information from the client. As an example, a VERIFY command would expect the signed data on the bulkInputDevice(). But if the input stream doesn't contain an embedded (opaque) signature, indicating a \em detached signature, it would go and inquire that data from the client. Here's how it works: \code const int err = inquire( "DETACHED_SIGNATURE", this, SLOT(slotDetachedSignature(int,QByteArray,QByteArray)) ); if ( err ) done( err ); \endcode This should be self-explanatory: You give a slot to call when the data has arrived. The slot's first argument is an error code. The second the data (if any), and the third is just repeating what you gave as inquire()'s first argument. As usual, you can leave argument off of the end, if you are not interested in them. You can do as many inquiries as you want, but only one at a time. You should periodically send status updates to the client. You do that by calling sendStatus(). Once your command has finished executing, call done(). If it's with an error code, call done(err) like above. Do not forget to call done() when done!. It will close bulkInputDevice(), bulkOutputDevice(), and send an OK or ERR message back to the client. At that point, your command has finished executing, and a new one can be accepted, or the connection closed. Apropos connection closed. The only way for the client to cancel an operation is to shut down the connection. In this case, the canceled() function will be called. At that point, the connection to the client will have been broken already, and all you can do is pack your things and go down gracefully. If _you_ detect that the user has canceled (your dialog contains a cancel button, doesn't it?), then you should instead call done( GPG_ERR_CANCELED ), like for normal operation.

Registering the command with UiServer

To register a command, you implement a AssuanCommandFactory for your AssuanCommand subclass, and register it with the UiServer. This can be made considerably easier using GenericAssuanCommandFactory: \code UiServer server; server.registerCommandFactory( shared_ptr( new GenericAssuanCommandFactory ) ); // more registerCommandFactory calls... server.start(); \endcode */ class AssuanCommand : public ExecutionContext, public std::enable_shared_from_this { // defined in assuanserverconnection.cpp! public: AssuanCommand(); virtual ~AssuanCommand(); int start(); void canceled(); virtual const char *name() const = 0; class Memento { public: virtual ~Memento() {} }; template class TypedMemento : public Memento { T m_t; public: explicit TypedMemento(const T &t) : m_t(t) {} const T &get() const { return m_t; } T &get() { return m_t; } }; template static std::shared_ptr< TypedMemento > make_typed_memento(const T &t) { return std::shared_ptr< TypedMemento >(new TypedMemento(t)); } static int makeError(int code); // convenience methods: enum Mode { NoMode, EMail, FileManager }; Mode checkMode() const; enum CheckProtocolOption { AllowProtocolMissing = 0x01 }; GpgME::Protocol checkProtocol(Mode mode, int options = 0) const; void applyWindowID(QWidget *w) const override { doApplyWindowID(w); } WId parentWId() const; void setNohup(bool on); bool isNohup() const; bool isDone() const; QString sessionTitle() const; unsigned int sessionId() const; bool informativeRecipients() const; bool informativeSenders() const; const std::vector &recipients() const; const std::vector &senders() const; bool hasMemento(const QByteArray &tag) const; std::shared_ptr memento(const QByteArray &tag) const; template std::shared_ptr mementoAs(const QByteArray &tag) const { return std::dynamic_pointer_cast(this->memento(tag)); } QByteArray registerMemento(const std::shared_ptr &mem); QByteArray registerMemento(const QByteArray &tag, const std::shared_ptr &mem); void removeMemento(const QByteArray &tag); template T mementoContent(const QByteArray &tag) const { if (std::shared_ptr< TypedMemento > m = mementoAs< TypedMemento >(tag)) { return m->get(); } else { return T(); } } bool hasOption(const char *opt) const; QVariant option(const char *opt) const; const std::map &options() const; const std::vector< std::shared_ptr > &inputs() const; const std::vector< std::shared_ptr > &messages() const; const std::vector< std::shared_ptr > &outputs() const; QStringList fileNames() const; unsigned int numFiles() const; void sendStatus(const char *keyword, const QString &text); void sendStatusEncoded(const char *keyword, const std::string &text); void sendData(const QByteArray &data, bool moreToCome = false); int inquire(const char *keyword, QObject *receiver, const char *slot, unsigned int maxSize = 0); void done(const GpgME::Error &err = GpgME::Error()); void done(const GpgME::Error &err, const QString &details); void done(int err) { done(GpgME::Error(err)); } void done(int err, const QString &details) { done(GpgME::Error(err), details); } private: virtual void doCanceled() = 0; virtual int doStart() = 0; private: void doApplyWindowID(QWidget *w) const; private: const std::map< QByteArray, std::shared_ptr > &mementos() const; private: friend class ::Kleo::AssuanCommandFactory; class Private; kdtools::pimpl_ptr d; }; class AssuanCommandFactory { public: virtual ~AssuanCommandFactory() {} virtual std::shared_ptr create() const = 0; virtual const char *name() const = 0; #ifndef HAVE_ASSUAN2 typedef int(*_Handler)(assuan_context_s *, char *); #else typedef gpg_error_t(*_Handler)(assuan_context_s *, char *); #endif virtual _Handler _handler() const = 0; #ifndef HAVE_ASSUAN2 static int _handle(assuan_context_s *, char *, const char *); #else static gpg_error_t _handle(assuan_context_s *, char *, const char *); #endif }; template class GenericAssuanCommandFactory : public AssuanCommandFactory { AssuanCommandFactory::_Handler _handler() const override { return &GenericAssuanCommandFactory::_handle; } #ifndef HAVE_ASSUAN2 static int _handle(assuan_context_s *_ctx, char *_line) { #else static gpg_error_t _handle(assuan_context_s *_ctx, char *_line) { #endif return AssuanCommandFactory::_handle(_ctx, _line, Command::staticName()); } std::shared_ptr create() const override { return make(); } const char *name() const override { return Command::staticName(); } public: static std::shared_ptr make() { return std::shared_ptr(new Command); } }; template class AssuanCommandMixin : public Base { protected: /* reimp */ const char *name() const override { return Derived::staticName(); } }; } #endif /* __KLEOPATRA_UISERVER_ASSUANCOMMAND_H__ */