sailfish-safe

Sailfish frontend for safe(1)
git clone git://git.z3bra.org/sailfish-safe.git
Log | Files | Refs | README | LICENSE

gpg.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 }