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 }