File indexing completed on 2024-11-10 04:50:01
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Daniel Vrátil <dvratil@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only 0005 */ 0006 0007 #include "gpghelper.h" 0008 0009 #include <QDebug> 0010 #include <QFileInfo> 0011 #include <QProcess> 0012 #include <QTest> 0013 0014 namespace 0015 { 0016 bool copyRecursively(const QString &src, const QString &dest) 0017 { 0018 QFileInfo srcInfo(src); 0019 if (srcInfo.isDir()) { 0020 QDir destDir(dest); 0021 destDir.cdUp(); 0022 if (!destDir.mkdir(QFileInfo(src).fileName())) { 0023 qWarning() << "Failed to create directory" << QFileInfo(src).fileName() << "in" << destDir.path(); 0024 return false; 0025 } 0026 QDir srcDir(src); 0027 const auto srcFiles = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); 0028 for (const auto &fileName : srcFiles) { 0029 const QString srcFile = src + QLatin1Char('/') + fileName; 0030 const QString dstFile = dest + QLatin1Char('/') + fileName; 0031 if (!copyRecursively(srcFile, dstFile)) { 0032 return false; 0033 } 0034 } 0035 } else { 0036 if (!QFile::copy(src, dest)) { 0037 qWarning() << "Failed to copy" << src << "into" << dest; 0038 return false; 0039 } 0040 } 0041 return true; 0042 } 0043 0044 QString gpgexe(GPGHelper::CryptoType crypto) 0045 { 0046 return (crypto == GPGHelper::OpenPGP) ? QStringLiteral("gpg2") : QStringLiteral("gpgsm"); 0047 } 0048 } // namespace 0049 0050 GPGHelper::GPGHelper(const QString &templateGnupgHome) 0051 : mValid(false) 0052 { 0053 const auto home = gnupgHome(); 0054 mValid = copyRecursively(templateGnupgHome, home); 0055 if (mValid) { 0056 qputenv("GNUPGHOME", home.toUtf8()); 0057 } 0058 } 0059 0060 GPGHelper::~GPGHelper() 0061 { 0062 // shutdown gpg-agent 0063 QProcess gpgshutdown; 0064 auto env = gpgshutdown.processEnvironment(); 0065 env.insert(QStringLiteral("GNUPGHOME"), gnupgHome()); 0066 gpgshutdown.setProcessEnvironment(env); 0067 gpgshutdown.start(QStringLiteral("gpg-connect-agent"), QStringList()); 0068 QVERIFY(gpgshutdown.waitForStarted()); 0069 gpgshutdown.write("KILLAGENT"); 0070 gpgshutdown.closeWriteChannel(); 0071 QVERIFY(gpgshutdown.waitForFinished()); 0072 } 0073 0074 QString GPGHelper::gnupgHome() const 0075 { 0076 return mTmpDir.path() + QStringLiteral("/gpghome"); 0077 } 0078 0079 QByteArray GPGHelper::runGpg(const QByteArray &in, GPGHelper::CryptoType crypto, const QStringList &args) const 0080 { 0081 QProcess gpg; 0082 gpg.setReadChannel(QProcess::StandardOutput); 0083 auto env = gpg.processEnvironment(); 0084 env.insert(QStringLiteral("GNUPGHOME"), gnupgHome()); 0085 gpg.setProcessEnvironment(env); 0086 gpg.start(gpgexe(crypto), args); 0087 if (!gpg.waitForStarted()) { 0088 return {}; 0089 } 0090 gpg.write(in); 0091 gpg.closeWriteChannel(); 0092 if (!gpg.waitForReadyRead()) { 0093 return {}; 0094 } 0095 const auto out = gpg.readAllStandardOutput(); 0096 0097 if (!gpg.waitForFinished()) { 0098 return {}; 0099 } 0100 0101 return out; 0102 } 0103 0104 QByteArray GPGHelper::decrypt(const QByteArray &enc, GPGHelper::CryptoType crypto) const 0105 { 0106 return runGpg(enc, crypto, {QStringLiteral("-d")}); 0107 } 0108 0109 QByteArray GPGHelper::encrypt(const QByteArray &dec, GPGHelper::CryptoType crypto) const 0110 { 0111 return runGpg(dec, crypto, {QStringLiteral("-e")}); 0112 } 0113 0114 QString GPGHelper::encryptionKeyFp(const QByteArray &enc, GPGHelper::CryptoType crypto) const 0115 { 0116 const auto data = runGpg(enc, crypto, {QStringLiteral("--fingerprint"), QStringLiteral("--with-colons")}); 0117 int idx = data.indexOf("\nfpr:"); 0118 if (idx == -1) { 0119 return {}; 0120 } 0121 0122 // Find first non-colon character after "fpr" 0123 for (idx = idx + 4; idx < data.size() && data[idx] == ':'; ++idx) { } 0124 const int end = data.indexOf(':', idx); 0125 0126 return QString::fromLatin1(data.constData() + idx, end - idx); 0127 }