diff --git a/transactions/kgpgchangedisable.cpp b/transactions/kgpgchangedisable.cpp index 6748a0b3..37fc4a5a 100644 --- a/transactions/kgpgchangedisable.cpp +++ b/transactions/kgpgchangedisable.cpp @@ -1,56 +1,47 @@ /* * Copyright (C) 2008,2009,2012 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #include "kgpgchangedisable.h" KGpgChangeDisable::KGpgChangeDisable(QObject *parent, const QString &keyid, const bool disable) : KGpgEditKeyTransaction(parent, keyid, QString(), false) { setDisable(disable); } KGpgChangeDisable::~KGpgChangeDisable() { } void KGpgChangeDisable::setDisable(bool disable) { QString cmd; if (disable) cmd = QLatin1String( "disable" ); else cmd = QLatin1String( "enable" ); replaceCommand(cmd); } bool KGpgChangeDisable::preStart() { if (!KGpgEditKeyTransaction::preStart()) return false; setSuccess(TS_OK); return true; } - -bool -KGpgChangeDisable::nextLine(const QString &line) -{ - if(keyConsidered(line, QStringList(getKeyid()))) - return false; - - return KGpgEditKeyTransaction::nextLine(line); -} diff --git a/transactions/kgpgchangedisable.h b/transactions/kgpgchangedisable.h index 82bb54b5..c06ff61d 100644 --- a/transactions/kgpgchangedisable.h +++ b/transactions/kgpgchangedisable.h @@ -1,41 +1,40 @@ /* * Copyright (C) 2008,2009 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #ifndef KGPGCHANGEDISABLE_H #define KGPGCHANGEDISABLE_H #include #include "kgpgeditkeytransaction.h" /** * @brief enable of disable a key */ class KGpgChangeDisable: public KGpgEditKeyTransaction { Q_OBJECT Q_DISABLE_COPY(KGpgChangeDisable) KGpgChangeDisable() = delete; public: KGpgChangeDisable(QObject *parent, const QString &keyid, const bool disable); virtual ~KGpgChangeDisable(); void setDisable(bool disable); protected: bool preStart() override; - bool nextLine(const QString &line) override; }; #endif // KGPGCHANGEDISABLE_H diff --git a/transactions/kgpgdecrypt.cpp b/transactions/kgpgdecrypt.cpp index 5be9b23c..e2adbf51 100644 --- a/transactions/kgpgdecrypt.cpp +++ b/transactions/kgpgdecrypt.cpp @@ -1,139 +1,155 @@ /* * Copyright (C) 2010,2011,2012 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #include "kgpgdecrypt.h" #include "gpgproc.h" #include "kgpgsettings.h" #include KGpgDecrypt::KGpgDecrypt(QObject *parent, const QString &text) : KGpgTextOrFileTransaction(parent, text), m_fileIndex(-1), m_plainLength(-1) { } KGpgDecrypt::KGpgDecrypt(QObject *parent, const QList &files) : KGpgTextOrFileTransaction(parent, files), m_fileIndex(0), m_plainLength(-1) { } KGpgDecrypt::KGpgDecrypt(QObject* parent, const QUrl& infile, const QUrl& outfile) : KGpgTextOrFileTransaction(parent, QList({infile})), m_fileIndex(0), m_plainLength(-1), m_outFilename(outfile.toLocalFile()) { } KGpgDecrypt::~KGpgDecrypt() { } QStringList KGpgDecrypt::command() const { QStringList ret; ret << QLatin1String("--decrypt") << QLatin1String("--command-fd=0"); if (!m_outFilename.isEmpty()) ret << QLatin1String("-o") << m_outFilename; ret << KGpgSettings::customDecrypt().simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); return ret; } QStringList KGpgDecrypt::decryptedText() const { QStringList result; int txtlength = 0; for (const QString &line : getMessages()) if (!line.startsWith(QLatin1String("[GNUPG:] "))) { result.append(line); txtlength += line.length() + 1; } if (result.isEmpty()) return result; QString last = result.last(); // this may happen when the original text did not end with a newline if (last.endsWith(QLatin1String("[GNUPG:] DECRYPTION_OKAY"))) { // if GnuPG doesn't tell us the length assume that this happend // if it told us the length then check if it _really_ happend if (((m_plainLength != -1) && (txtlength != m_plainLength)) || (m_plainLength == -1)) { last.chop(24); result[result.count() - 1] = last; } } return result; } bool KGpgDecrypt::isEncryptedText(const QString &text, int *startPos, int *endPos) { int posStart = text.indexOf(QLatin1String("-----BEGIN PGP MESSAGE-----")); if (posStart == -1) return false; int posEnd = text.indexOf(QLatin1String("-----END PGP MESSAGE-----"), posStart); if (posEnd == -1) return false; if (startPos != nullptr) *startPos = posStart; if (endPos != nullptr) *endPos = posEnd; return true; } bool KGpgDecrypt::nextLine(const QString& line) { const QList &inputFiles = getInputFiles(); - if (!inputFiles.isEmpty()) { + if (line == QLatin1String("[GNUPG:] DECRYPTION_OKAY")) { + // Assume Gpg decryption was successful even if gpg returns + // an error code. + decryptSuccess = true; + } else if (!inputFiles.isEmpty()) { if (line == QLatin1String("[GNUPG:] BEGIN_DECRYPTION")) { emit statusMessage(i18nc("Status message 'Decrypting ' (operation starts)", "Decrypting %1", inputFiles.at(m_fileIndex).fileName())); emit infoProgress(2 * m_fileIndex + 1, inputFiles.count() * 2); } else if (line == QLatin1String("[GNUPG:] END_DECRYPTION")) { emit statusMessage(i18nc("Status message 'Decrypted ' (operation was completed)", "Decrypted %1", inputFiles.at(m_fileIndex).fileName())); m_fileIndex++; emit infoProgress(2 * m_fileIndex, inputFiles.count() * 2); } } else { if (line.startsWith(QLatin1String("[GNUPG:] PLAINTEXT_LENGTH "))) { bool ok; - m_plainLength = line.mid(26).toInt(&ok); + m_plainLength = line.midRef(26).toInt(&ok); if (!ok) m_plainLength = -1; } else if (line == QLatin1String("[GNUPG:] BEGIN_DECRYPTION")) { // close the command channel (if any) to signal GnuPG that it // can start sending the output. getProcess()->closeWriteChannel(); } } return KGpgTextOrFileTransaction::nextLine(line); } + +void +KGpgDecrypt::finish() +{ + if (decryptSuccess) { + // Gpg error code is ignored. + // https://bugs.kde.org/show_bug.cgi?id=357462 + setSuccess(TS_OK); + } else { + KGpgTextOrFileTransaction::finish(); + } +} diff --git a/transactions/kgpgdecrypt.h b/transactions/kgpgdecrypt.h index 5ab59f53..5c4abe0a 100644 --- a/transactions/kgpgdecrypt.h +++ b/transactions/kgpgdecrypt.h @@ -1,85 +1,88 @@ /* * Copyright (C) 2010,2011 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #ifndef KGPGDECRYPT_H #define KGPGDECRYPT_H #include #include #include "kgpgtextorfiletransaction.h" class QStringList; /** * @brief decrypt the given text or files */ class KGpgDecrypt: public KGpgTextOrFileTransaction { Q_OBJECT Q_DISABLE_COPY(KGpgDecrypt) KGpgDecrypt() = delete; public: /** * @brief decrypt given text * @param parent parent object * @param text text to decrypt */ explicit KGpgDecrypt(QObject *parent, const QString &text = QString()); /** * @brief decrypt file(s) * @param parent parent object * @param files list of file locations to decrypt */ KGpgDecrypt(QObject *parent, const QList &files); /** * @brief decrypt file to given output filename * @param parent parent object * @param infile name of file to decrypt * @param outfile name of file to write output to (will be overwritten) */ KGpgDecrypt(QObject *parent, const QUrl &infile, const QUrl &outfile); /** * @brief destructor */ virtual ~KGpgDecrypt(); /** * @brief get decryption result * @return decrypted text */ QStringList decryptedText() const; /** * @brief check if the given text contains an encoded message * @param text text to check * @param startPos if not nullptr start offset of encoded text will be returned here * @param endPos if not nullptr end offset of encoded text will be returned here */ static bool isEncryptedText(const QString &text, int *startPos = nullptr, int *endPos = nullptr); protected: QStringList command() const override; bool nextLine(const QString &line) override; + void finish() override; + private: int m_fileIndex; int m_plainLength; ///< length of decrypted plain text if given by GnuPG const QString m_outFilename; ///< name of file to write output to + bool decryptSuccess = false; //< flag to determine if decryption succeeded }; #endif // KGPGDECRYPT_H diff --git a/transactions/kgpgdelkey.cpp b/transactions/kgpgdelkey.cpp index c3716641..2d5143ec 100644 --- a/transactions/kgpgdelkey.cpp +++ b/transactions/kgpgdelkey.cpp @@ -1,96 +1,94 @@ /* * Copyright (C) 2008,2009,2012,2016,2017 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #include "kgpgdelkey.h" #include "gpgproc.h" #include #include static QStringList keyFingerprints(const KGpgKeyNode::List &keys) { QStringList ret; ret.reserve(keys.count()); for (const KGpgKeyNode *key : keys) ret << key->getFingerprint(); return ret; } KGpgDelKey::KGpgDelKey(QObject *parent, KGpgKeyNode *key) : KGpgTransaction(parent) , keys({key}) , fingerprints(keyFingerprints(keys)) { setCmdLine(); } KGpgDelKey::KGpgDelKey(QObject *parent, const KGpgKeyNode::List &keys) : KGpgTransaction(parent) , keys(keys) , fingerprints(keyFingerprints(keys)) { setCmdLine(); } KGpgDelKey::~KGpgDelKey() { } bool KGpgDelKey::nextLine(const QString &line) { - if (keyConsidered(line, fingerprints)) { - // nothing - } else if (!line.startsWith(QLatin1String("[GNUPG:] GOT_IT"))) + if (!line.startsWith(QLatin1String("[GNUPG:] GOT_IT"))) setSuccess(KGpgTransaction::TS_MSG_SEQUENCE); return false; } KGpgTransaction::ts_boolanswer KGpgDelKey::boolQuestion(const QString &line) { if (line.startsWith(QLatin1String("delete_key.okay"))) return KGpgTransaction::BA_YES; if (line.startsWith(QLatin1String("delete_key.secret.okay"))) return KGpgTransaction::BA_YES; return KGpgTransaction::boolQuestion(line); } bool KGpgDelKey::preStart() { GPGProc *proc = getProcess(); const QStringList args = proc->program() + fingerprints; proc->setProgram(args); setSuccess(KGpgTransaction::TS_OK); return true; } void KGpgDelKey::setCmdLine() { addArgument(QLatin1String( "--status-fd=1" )); addArgument(QLatin1String( "--command-fd=0" )); addArgument(QLatin1String( "--delete-secret-and-public-key" )); m_argscount = getProcess()->program().count(); } diff --git a/transactions/kgpgsignkey.cpp b/transactions/kgpgsignkey.cpp index 8cf7cba8..668b2de5 100644 --- a/transactions/kgpgsignkey.cpp +++ b/transactions/kgpgsignkey.cpp @@ -1,89 +1,85 @@ /* * Copyright (C) 2009,2012 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #include "kgpgsignkey.h" #include "model/kgpgitemnode.h" KGpgSignKey::KGpgSignKey(QObject *parent, const QString &signer, KGpgKeyNode *key, const bool local, const carefulCheck checking) : KGpgEditKeyTransaction(parent, key->getId(), QString(), false, false), KGpgSignTransactionHelper(signer, !local, checking) { insertArgument(1, QLatin1String( "-u" )); insertArgument(2, signer); m_signerPos = 2; addArgumentRef(&m_signerPos); addArgument(QLatin1String("save")); setKey(key); setLocal(local); } KGpgSignKey::~KGpgSignKey() { } bool KGpgSignKey::nextLine(const QString &line) { - if (keyConsidered(line, QStringList())) - // could be any private key, so just ignore them - return false; - switch (KGpgSignTransactionHelper::nextLine(line)) { case KGpgSignTransactionHelper::handledFalse: return false; case KGpgSignTransactionHelper::handledTrue: return true; default: // just to keep the compiler happy Q_ASSERT(0); case KGpgSignTransactionHelper::notHandled: return KGpgEditKeyTransaction::nextLine(line); } } KGpgTransaction::ts_boolanswer KGpgSignKey::boolQuestion(const QString& line) { if (line.contains(QLatin1String("sign_all.okay"))) return BA_YES; ts_boolanswer ret = KGpgSignTransactionHelper::boolQuestion(line); if (ret == BA_UNKNOWN) ret = KGpgTransaction::boolQuestion(line); return ret; } bool KGpgSignKey::passphraseReceived() { setSuccess(KGpgTransaction::TS_OK); return true; } KGpgTransaction * KGpgSignKey::asTransaction() { return this; } void KGpgSignKey::replaceCmd(const QString &cmd) { replaceCommand(cmd); } diff --git a/transactions/kgpgtransactionprivate.cpp b/transactions/kgpgtransactionprivate.cpp index 6b012f0a..6c6b86f8 100644 --- a/transactions/kgpgtransactionprivate.cpp +++ b/transactions/kgpgtransactionprivate.cpp @@ -1,320 +1,321 @@ /* * Copyright (C) 2009,2010,2012,2014,2016 Rolf Eike Beer */ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ #include "kgpgtransactionprivate.h" #include "kgpgtransaction.h" #include "kgpg_debug.h" #include #include #include #include #include KGpgTransactionPrivate::KGpgTransactionPrivate(KGpgTransaction *parent, bool allowChaining) : m_parent(parent), m_process(new GPGProc()), m_inputTransaction(nullptr), m_newPasswordDialog(nullptr), m_passwordDialog(nullptr), m_success(KGpgTransaction::TS_OK), m_tries(3), m_chainingAllowed(allowChaining), m_inputProcessDone(false), m_inputProcessResult(KGpgTransaction::TS_OK), m_ownProcessFinished(false), m_quitTries(0) { connect(m_process, &GPGProc::readReady, this, &KGpgTransactionPrivate::slotReadReady); connect(m_process, &GPGProc::processExited, this, &KGpgTransactionPrivate::slotProcessExited); connect(m_process, &GPGProc::started, this, &KGpgTransactionPrivate::slotProcessStarted); } KGpgTransactionPrivate::~KGpgTransactionPrivate() { if (m_newPasswordDialog) { m_newPasswordDialog->close(); m_newPasswordDialog->deleteLater(); } if (m_process->state() == QProcess::Running) { m_process->closeWriteChannel(); m_process->terminate(); } delete m_inputTransaction; delete m_process; } void KGpgTransactionPrivate::slotReadReady() { QString line; QPointer process(m_process); QPointer par(m_parent); while (!process.isNull() && (m_process->readln(line, true) >= 0)) { if (m_quitTries) m_quitLines << line; #ifdef KGPG_DEBUG_TRANSACTIONS qCDebug(KGPG_LOG_TRANSACTIONS) << m_parent << line; #endif /* KGPG_DEBUG_TRANSACTIONS */ static const QString getBool = QLatin1String("[GNUPG:] GET_BOOL "); - - if (line.startsWith(QLatin1String("[GNUPG:] USERID_HINT "))) { + if (m_parent->keyConsidered(line, QStringList())) { + // already handled by keyConsidered - skip the line + } else if (line.startsWith(QLatin1String("[GNUPG:] USERID_HINT "))) { m_parent->addIdHint(line); } else if (line.startsWith(QLatin1String("[GNUPG:] BAD_PASSPHRASE "))) { // the MISSING_PASSPHRASE line comes first, in that case ignore a // following BAD_PASSPHRASE if (m_success != KGpgTransaction::TS_USER_ABORTED) m_success = KGpgTransaction::TS_BAD_PASSPHRASE; } else if (line.startsWith(QLatin1String("[GNUPG:] GET_HIDDEN passphrase.enter"))) { const bool goOn = m_parent->passphraseRequested(); // Check if the object was deleted while waiting for the result if (!goOn || par.isNull()) return; } else if (line.startsWith(QLatin1String("[GNUPG:] GOOD_PASSPHRASE"))) { emit m_parent->statusMessage(i18n("Got Passphrase")); if (m_passwordDialog != nullptr) { m_passwordDialog->close(); m_passwordDialog->deleteLater(); m_passwordDialog = nullptr; } if (m_parent->passphraseReceived()) { // signal GnuPG that there will be no further input and it can // begin sending output. m_process->closeWriteChannel(); } } else if (line.startsWith(getBool)) { static const QString overwrite = QLatin1String("openfile.overwrite.okay"); const QString question = line.mid(getBool.length()); KGpgTransaction::ts_boolanswer answer; if (question.startsWith(overwrite)) { m_overwriteUrl.clear(); answer = m_parent->confirmOverwrite(m_overwriteUrl); if ((answer == KGpgTransaction::BA_UNKNOWN) && !m_overwriteUrl.isEmpty()) { QPointer over = new KIO::RenameDialog(qobject_cast(m_parent->parent()), i18n("File Already Exists"), QUrl(), m_overwriteUrl, KIO::RenameDialog_Overwrite); m_overwriteUrl.clear(); switch (over->exec()) { case KIO::R_OVERWRITE: answer = KGpgTransaction::BA_YES; break; case KIO::R_RENAME: answer = KGpgTransaction::BA_NO; m_overwriteUrl = over->newDestUrl(); break; default: answer = KGpgTransaction::BA_UNKNOWN; m_parent->setSuccess(KGpgTransaction::TS_USER_ABORTED); // Close the pipes, otherwise GnuPG will try to answer // further questions about this file. m_process->closeWriteChannel(); m_process->closeReadChannel(QProcess::StandardOutput); break; } delete over; if (answer == KGpgTransaction::BA_UNKNOWN) continue; } } else { answer = m_parent->boolQuestion(question); } switch (answer) { case KGpgTransaction::BA_YES: write("YES\n"); break; case KGpgTransaction::BA_NO: write("NO\n"); break; case KGpgTransaction::BA_UNKNOWN: m_parent->setSuccess(KGpgTransaction::TS_MSG_SEQUENCE); m_parent->unexpectedLine(line); sendQuit(); } } else if (!m_overwriteUrl.isEmpty() && line.startsWith(QLatin1String("[GNUPG:] GET_LINE openfile.askoutname"))) { write(m_overwriteUrl.toLocalFile().toUtf8() + '\n'); m_overwriteUrl.clear(); } else if (line.startsWith(QLatin1String("[GNUPG:] MISSING_PASSPHRASE"))) { m_success = KGpgTransaction::TS_USER_ABORTED; } else if (line.startsWith(QLatin1String("[GNUPG:] CARDCTRL "))) { // just ignore them, pinentry should handle that } else { // all known hints int i = 0; bool matched = false; for (const QString &hintName : hintNames()) { const KGpgTransaction::ts_hintType h = static_cast(i++); if (!line.startsWith(hintName)) continue; matched = true; bool r; const int skip = hintName.length(); if (line.length() == skip) { r = m_parent->hintLine(h, QString()); } else { r = m_parent->hintLine(h, line.mid(skip + 1).trimmed()); } if (!r) { m_parent->setSuccess(KGpgTransaction::TS_MSG_SEQUENCE); sendQuit(); } break; } if (!matched) { if (m_parent->nextLine(line)) sendQuit(); } } } } void KGpgTransactionPrivate::slotProcessExited() { Q_ASSERT(sender() == m_process); m_ownProcessFinished = true; if (m_inputProcessDone) processDone(); } void KGpgTransactionPrivate::slotProcessStarted() { m_parent->postStart(); } void KGpgTransactionPrivate::sendQuit(void) { write("quit\n"); #ifdef KGPG_DEBUG_TRANSACTIONS if (m_quitTries == 0) qCDebug(KGPG_LOG_TRANSACTIONS) << "sending quit"; #endif /* KGPG_DEBUG_TRANSACTIONS */ if (m_quitTries++ >= 3) { qCDebug(KGPG_LOG_GENERAL) << "tried" << m_quitTries << "times to quit the GnuPG session"; qCDebug(KGPG_LOG_GENERAL) << "last input was" << m_quitLines; qCDebug(KGPG_LOG_GENERAL) << "please file a bug report at https://bugs.kde.org"; m_process->closeWriteChannel(); m_success = KGpgTransaction::TS_MSG_SEQUENCE; } } void KGpgTransactionPrivate::slotInputTransactionDone(int result) { Q_ASSERT(sender() == m_inputTransaction); m_inputProcessDone = true; m_inputProcessResult = result; if (m_ownProcessFinished) processDone(); } void KGpgTransactionPrivate::slotPassphraseEntered(const QString &passphrase) { // not calling KGpgTransactionPrivate::write() here for obvious privacy reasons m_process->write(passphrase.toUtf8() + '\n'); if (sender() == m_newPasswordDialog) { m_newPasswordDialog->deleteLater(); m_newPasswordDialog = nullptr; m_parent->newPassphraseEntered(); } else { Q_ASSERT(sender() == m_passwordDialog); } } void KGpgTransactionPrivate::slotPassphraseAborted() { Q_ASSERT((sender() == m_passwordDialog) ^ (sender() == m_newPasswordDialog)); sender()->deleteLater(); m_newPasswordDialog = nullptr; m_passwordDialog = nullptr; handlePassphraseAborted(); } void KGpgTransactionPrivate::handlePassphraseAborted() { // sending "quit" here is useless as it would be interpreted as the passphrase m_process->closeWriteChannel(); m_success = KGpgTransaction::TS_USER_ABORTED; } void KGpgTransactionPrivate::write(const QByteArray &a) { m_process->write(a); #ifdef KGPG_DEBUG_TRANSACTIONS qCDebug(KGPG_LOG_TRANSACTIONS) << m_parent << a; #endif /* KGPG_DEBUG_TRANSACTIONS */ } const QStringList & KGpgTransactionPrivate::hintNames (void) { static QStringList hints; if (hints.isEmpty()) { hints.insert(KGpgTransaction::HT_KEYEXPIRED, QLatin1String("[GNUPG:] KEYEXPIRED")); hints.insert(KGpgTransaction::HT_SIGEXPIRED, QLatin1String("[GNUPG:] SIGEXPIRED")); hints.insert(KGpgTransaction::HT_NOSECKEY, QLatin1String("[GNUPG:] NO_SECKEY")); hints.insert(KGpgTransaction::HT_ENCTO, QLatin1String("[GNUPG:] ENC_TO")); hints.insert(KGpgTransaction::HT_PINENTRY_LAUNCHED, QLatin1String("[GNUPG:] PINENTRY_LAUNCHED")); } return hints; } void KGpgTransactionPrivate::processDone() { m_parent->finish(); emit m_parent->infoProgress(100, 100); emit m_parent->done(m_success); #ifdef KGPG_DEBUG_TRANSACTIONS qCDebug(KGPG_LOG_TRANSACTIONS) << this << "result:" << m_success; #endif /* KGPG_DEBUG_TRANSACTIONS */ }