tgpg.cpp - sailfish-safe - Sailfish frontend for safe(1)
 (HTM) git clone git://git.z3bra.org/sailfish-safe.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tgpg.cpp (7202B)
       ---
            1 #include "gpg.h"
            2 
            3 #include <QStandardPaths>
            4 #include <QProcess>
            5 #include <QIODevice>
            6 #include <QRegularExpression>
            7 #include <QRegularExpressionMatch>
            8 #include <QTimer>
            9 #include <QtConcurrent>
           10 #include <QFutureWatcher>
           11 
           12 namespace {
           13 
           14 struct GpgExecutable {
           15     GpgExecutable(const QString &path, int major, int minor)
           16         : path(path), major_version(major), minor_version(minor)
           17     {}
           18     QString path = {};
           19     int major_version = 0;
           20     int minor_version = 0;
           21 };
           22 
           23 GpgExecutable findGpgExecutable()
           24 {
           25     auto gpgExe = QStandardPaths::findExecutable(QStringLiteral("gpg2"));
           26     if (gpgExe.isEmpty()) {
           27         gpgExe = QStandardPaths::findExecutable(QStringLiteral("gpg"));
           28     }
           29 
           30     QProcess process;
           31     process.start(gpgExe, {QStringLiteral("--version")}, QIODevice::ReadOnly);
           32     process.waitForFinished();
           33     const auto line = process.readLine();
           34     static const QRegularExpression rex(QStringLiteral("([0-9]+).([0-9]+).([0-9]+)"));
           35     const auto match = rex.match(QString::fromUtf8(line));
           36 
           37     return {gpgExe, match.captured(1).toInt(), match.captured(2).toInt()};
           38 }
           39 
           40 } // namespace
           41 
           42 Gpg::GetKeyTrustTask *Gpg::getKeyTrust(const Key &key)
           43 {
           44     return new GetKeyTrustTask(key);
           45 }
           46 
           47 Gpg::UpdateKeyTrustTask *Gpg::updateKeyTrust(const Key &key, Key::Trust trust)
           48 {
           49     return new UpdateKeyTrustTask(key, trust);
           50 }
           51 
           52 Gpg::EncryptTask *Gpg::encrypt(const QString &file, const Key &key, const QString &content)
           53 {
           54     return new EncryptTask(file, key, content);
           55 }
           56 
           57 Gpg::DecryptTask *Gpg::decrypt(const QString &file, const Key &key, const QString &passphrase)
           58 {
           59     return new DecryptTask(file, key, passphrase);
           60 }
           61 
           62 
           63 Gpg::Task::Task(QObject *parent)
           64     : QObject(parent)
           65 {
           66     QTimer::singleShot(0, this, &Task::start);
           67 }
           68 
           69 bool Gpg::Task::error() const
           70 {
           71     return !mError.isNull();
           72 }
           73 
           74 QString Gpg::Task::errorString() const
           75 {
           76     return mError;
           77 }
           78 
           79 void Gpg::Task::setError(const QString &error)
           80 {
           81     mError = error;
           82 }
           83 
           84 void Gpg::Task::start()
           85 {
           86     qDebug() << "Starting task" << this;
           87     auto future = QtConcurrent::run(this, &Task::run);
           88     auto *watcher = new QFutureWatcher<void>;
           89     connect(watcher, &QFutureWatcher<void>::finished, watcher, &QObject::deleteLater);
           90     connect(watcher, &QFutureWatcher<void>::finished, this, &Gpg::Task::finished);
           91     connect(watcher, &QFutureWatcher<void>::finished, this, &QObject::deleteLater);
           92     watcher->setFuture(future);
           93 }
           94 
           95 Gpg::GetKeyTrustTask::GetKeyTrustTask(const Key &key)
           96     : mKey(key)
           97 {}
           98 
           99 Gpg::Key::Trust Gpg::GetKeyTrustTask::trust() const
          100 {
          101     return mTrust;
          102 }
          103 
          104 void Gpg::GetKeyTrustTask::run()
          105 {
          106     const auto gpg = findGpgExecutable();
          107     QProcess process;
          108     process.setProgram(gpg.path);
          109     process.setArguments({QStringLiteral("--list-key \"%1\"").arg(mKey.id), QStringLiteral("--with-colons")});
          110     process.start(QIODevice::ReadOnly);
          111     process.waitForFinished();
          112     while (!process.atEnd()) {
          113         const auto line = process.readLine();
          114         const auto cols = line.split(':');
          115         if (cols.size() < 8) {
          116             continue;
          117         }
          118         if (cols[0] == "uid") {
          119             if (cols[1].isEmpty()) {
          120                 mTrust = Key::Trust::Unknown;
          121             }
          122             switch (cols[1][0]) {
          123             case 'u':
          124                 mTrust = Key::Trust::Ultimate;
          125                 break;
          126             case 'f':
          127                 mTrust = Key::Trust::Full;
          128                 break;
          129             case 'm':
          130                 mTrust = Key::Trust::Marginal;
          131                 break;
          132             case 'n':
          133                 mTrust = Key::Trust::Never;
          134                 break;
          135             case '-':
          136             default:
          137                 mTrust = Key::Trust::Unknown;
          138                 break;
          139             }
          140             break;
          141         }
          142     }
          143 }
          144 
          145 Gpg::UpdateKeyTrustTask::UpdateKeyTrustTask(const Key &key, Key::Trust trust)
          146     : Task()
          147     , mKey(key)
          148     , mTrust(trust)
          149 {}
          150 
          151 void Gpg::UpdateKeyTrustTask::run()
          152 {
          153     const auto gpg = findGpgExecutable();
          154     QProcess process;
          155     process.setProgram(gpg.path);
          156     process.setArguments({QStringLiteral("--command-fd=1"),
          157                           QStringLiteral("--status-fd=1"),
          158                           QStringLiteral("--batch"),
          159                           QStringLiteral("--edit-key"),
          160                           mKey.id,
          161                           QStringLiteral("trust")});
          162     process.start();
          163     process.waitForStarted();
          164     while (process.state() == QProcess::Running) {
          165         process.waitForReadyRead();
          166         const auto line = process.readLine();
          167         if (line == "[GNUPG:] GET_LINE edit_ownertrust.value\n") {
          168             process.write(QByteArray::number(static_cast<int>(mTrust)) + "\n");
          169             process.closeWriteChannel();
          170             break;
          171         }
          172     }
          173 
          174     process.waitForFinished();
          175 }
          176 
          177 Gpg::EncryptTask::EncryptTask(const QString &file, const Key &key, const QString &content)
          178     : mFile(file), mKey(key), mContent(content)
          179 {}
          180 
          181 void Gpg::EncryptTask::run()
          182 {
          183     const auto gpg = findGpgExecutable();
          184     QProcess process;
          185     process.setProgram(gpg.path);
          186     process.setArguments({QStringLiteral("--quiet"),
          187                           QStringLiteral("--status-fd=1"),
          188                           QStringLiteral("--command-fd=1"),
          189                           QStringLiteral("--batch"),
          190                           QStringLiteral("--encrypt"),
          191                           QStringLiteral("--no-encrypt-to"),
          192                           QStringLiteral("-r %1").arg(mKey.id),
          193                           QStringLiteral("-o%1").arg(mFile)});
          194     process.start();
          195     process.waitForStarted();
          196     process.write(mContent.toUtf8());
          197     process.closeWriteChannel();
          198     process.waitForFinished();
          199     if (process.exitCode() != 0) {
          200         const auto err = process.readAllStandardError();
          201         qWarning() << "Failed to encrypt data:" << err;
          202         setError(QString::fromUtf8(err));
          203     }
          204 }
          205 
          206 Gpg::DecryptTask::DecryptTask(const QString &file, const Key &key, const QString &passphrase)
          207     : Task(), mFile(file), mPassphrase(passphrase), mKey(key)
          208 {}
          209 
          210 QString Gpg::DecryptTask::content() const
          211 {
          212     return mContent;
          213 }
          214 
          215 void Gpg::DecryptTask::run()
          216 {
          217     const auto gpg = findGpgExecutable();
          218     QProcess process;
          219     process.setProgram(gpg.path);
          220     QStringList arguments{
          221         QStringLiteral("--quiet"),
          222         QStringLiteral("--batch"),
          223         QStringLiteral("--decrypt"),
          224         QStringLiteral("--no-tty"),
          225         QStringLiteral("--command-fd=1"),
          226         QStringLiteral("--no-encrypt-to"),
          227         QStringLiteral("--compress-algo=none"),
          228         QStringLiteral("--passphrase-fd=0")
          229     };
          230 
          231     if (gpg.minor_version >= 1) {
          232         arguments << QStringLiteral("--pinentry-mode=loopback");
          233     }
          234 
          235     arguments << QStringLiteral ("-r %1").arg (mKey.id) << mFile;
          236 
          237     process.setArguments(arguments);
          238     process.start();
          239     process.waitForStarted();
          240     process.write(mPassphrase.toUtf8());
          241     process.closeWriteChannel();
          242     process.waitForFinished();
          243     if (process.exitCode() != 0) {
          244         const auto err = process.readAllStandardError();
          245         qWarning() << "Failed to decrypt data:" << err;
          246         setError(QString::fromUtf8(err));
          247     } else {
          248         mContent = QString::fromUtf8(process.readAllStandardOutput());
          249     }
          250 }