File indexing completed on 2024-11-10 04:50:05

0001 /*
0002  * SPDX-FileCopyrightText: 2017 Daniel Vrátil <dvratil@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  *
0006  */
0007 
0008 #include "filteractionwithcrypto.h"
0009 
0010 #include <QProcess>
0011 #include <QStandardPaths>
0012 
0013 using namespace MailCommon;
0014 
0015 QStringList FilterActionWithCrypto::getEncryptionKeysFromContent(const KMime::Message::Ptr &msg, GpgME::Protocol protocol) const
0016 {
0017     if (protocol == GpgME::CMS && mGpgSmPath.isNull()) {
0018         const auto path = QStandardPaths::findExecutable(QStringLiteral("gpgsm"));
0019         mGpgSmPath = path.isEmpty() ? QString() : path;
0020     } else if (protocol == GpgME::OpenPGP && mGpgPath.isNull()) {
0021         auto path = QStandardPaths::findExecutable(QStringLiteral("gpg2"));
0022         if (path.isEmpty()) {
0023             path = QStandardPaths::findExecutable(QStringLiteral("gpg"));
0024             mGpgPath = path.isEmpty() ? QString() : path;
0025         } else {
0026             mGpgPath = path;
0027         }
0028     }
0029 
0030     if ((protocol == GpgME::CMS && mGpgSmPath.isEmpty()) || (protocol == GpgME::OpenPGP && mGpgPath.isEmpty())) {
0031         return {};
0032     }
0033 
0034     QProcess gpg;
0035     QStringList keyIds;
0036     // TODO: contribute an API for this into gpgme
0037     if (protocol == GpgME::OpenPGP) {
0038         gpg.setProgram(mGpgPath);
0039         // --list-packets will give us list of keys used to encrypt the message
0040         // --batch will prevent gpg from asking for decryption password (we don't need it yet)
0041         gpg.setArguments({QStringLiteral("--list-packets"), QStringLiteral("--batch")});
0042         gpg.start(QIODevice::ReadWrite);
0043         gpg.waitForStarted();
0044         gpg.write(msg->encodedContent());
0045         gpg.closeWriteChannel();
0046         gpg.waitForFinished();
0047         while (!gpg.atEnd()) {
0048             const auto l = gpg.readLine();
0049             if (l.startsWith(":pubkey")) {
0050                 const int pos = l.indexOf("keyid ");
0051                 if (pos < 0) {
0052                     continue;
0053                 }
0054                 const int start = pos + 6; // strlen("keyid ")
0055                 const int len = l.size() - start - 1; // -1 to skip trailing \n
0056                 keyIds << QString::fromUtf8(l.mid(start, len));
0057             }
0058         }
0059     } else if (protocol == GpgME::CMS) {
0060         gpg.setProgram(mGpgSmPath);
0061         // --decrypt - the only way how to get the keys from gpgsm, sadly, is to decrypt the email
0062         // --status-fd 2 - make sure the status output is not mangled with the decrypted content
0063         // --assume-base64 - so that we don't have to decode it ourselves
0064         gpg.setArguments({QStringLiteral("--decrypt"),
0065                           QStringLiteral("--status-fd"),
0066                           QStringLiteral("2"),
0067                           QStringLiteral("--debug-level"),
0068                           QStringLiteral("basic"),
0069                           QStringLiteral("--assume-base64")});
0070         gpg.start(QIODevice::ReadWrite);
0071         gpg.waitForStarted();
0072         gpg.write(msg->encodedBody()); // just the body!
0073         gpg.closeWriteChannel();
0074         gpg.waitForFinished();
0075         gpg.setReadChannel(QProcess::StandardError);
0076         while (!gpg.atEnd()) {
0077             const auto l = gpg.readLine();
0078             if (l.startsWith("gpgsm: DBG: recp ")) {
0079                 const int pos = l.indexOf("serial: ");
0080                 if (pos < 0) {
0081                     continue;
0082                 }
0083                 const int start = pos + 8; // strlen("serial: ")
0084                 const int len = l.size() - start - 1; // -1 to skip trailing \n
0085                 keyIds << QString::fromUtf8(l.mid(start, len));
0086             }
0087         }
0088     }
0089 
0090     return keyIds;
0091 }
0092 
0093 #include "moc_filteractionwithcrypto.cpp"