1 /*
2 SPDX-FileCopyrightText: 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2018 Rolf Eike Beer <kde@opensource.sf-tec.de>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "kgpgtransaction.h"
7 #include "kgpg_transactions_debug.h"
8 #include "kgpg_general_debug.h"
9 #include "kgpgtransactionprivate.h"
10
11 #include "gpgproc.h"
12 #include "kgpginterface.h"
13
14 #include <QByteArray>
15
16 #include <QStringList>
17 #include <QUrl>
18 #include <QWidget>
19
20 #include <KConfigGroup>
21 #include <KNewPasswordDialog>
22 #include <KLocalizedString>
23 #include <KPasswordDialog>
24
KGpgTransaction(QObject * parent,const bool allowChaining)25 KGpgTransaction::KGpgTransaction(QObject *parent, const bool allowChaining)
26 : QObject(parent),
27 d(new KGpgTransactionPrivate(this, allowChaining))
28 {
29 }
30
~KGpgTransaction()31 KGpgTransaction::~KGpgTransaction()
32 {
33 delete d;
34 }
35
36 void
start()37 KGpgTransaction::start()
38 {
39 d->m_inputProcessResult = false;
40 d->m_inputProcessDone = (d->m_inputTransaction == nullptr);
41
42 setSuccess(TS_OK);
43 d->m_idhints.clear();
44 d->m_tries = 3;
45 if (preStart()) {
46 d->m_ownProcessFinished = false;
47 if (d->m_inputTransaction != nullptr)
48 d->m_inputTransaction->start();
49 #ifdef KGPG_DEBUG_TRANSACTIONS
50 qCDebug(KGPG_LOG_TRANSACTIONS) << this << d->m_process->program();
51 #endif /* KGPG_DEBUG_TRANSACTIONS */
52 d->m_process->start();
53 Q_EMIT infoProgress(0, 1);
54 } else {
55 Q_EMIT done(d->m_success);
56 }
57 }
58
59 void
write(const QByteArray & a,const bool lf)60 KGpgTransaction::write(const QByteArray &a, const bool lf)
61 {
62 if (lf)
63 d->write(a + '\n');
64 else
65 d->write(a);
66 }
67
68 void
write(const int i)69 KGpgTransaction::write(const int i)
70 {
71 write(QByteArray::number(i));
72 }
73
74 void
askNewPassphrase(const QString & text)75 KGpgTransaction::askNewPassphrase(const QString& text)
76 {
77 Q_EMIT statusMessage(i18n("Requesting Passphrase"));
78
79 d->m_newPasswordDialog = new KNewPasswordDialog(qobject_cast<QWidget *>(parent()));
80 d->m_newPasswordDialog->setPrompt(text);
81 d->m_newPasswordDialog->setAllowEmptyPasswords(false);
82 connect(d->m_newPasswordDialog, &KNewPasswordDialog::newPassword, d, &KGpgTransactionPrivate::slotPassphraseEntered);
83 connect(d->m_newPasswordDialog, &KNewPasswordDialog::rejected, d, &KGpgTransactionPrivate::slotPassphraseAborted);
84 connect(d->m_process, &GPGProc::processExited, d->m_newPasswordDialog, &KNewPasswordDialog::rejected);
85 d->m_newPasswordDialog->show();
86 }
87
88 int
getSuccess() const89 KGpgTransaction::getSuccess() const
90 {
91 return d->m_success;
92 }
93
94 void
setSuccess(const int v)95 KGpgTransaction::setSuccess(const int v)
96 {
97 #ifdef KGPG_DEBUG_TRANSACTIONS
98 qCDebug(KGPG_LOG_TRANSACTIONS) << "old" << d->m_success << "new" << v;
99 #endif /* KGPG_DEBUG_TRANSACTIONS */
100 d->m_success = v;
101 }
102
103 KGpgTransaction::ts_boolanswer
boolQuestion(const QString & line)104 KGpgTransaction::boolQuestion(const QString& line)
105 {
106 Q_UNUSED(line)
107
108 return BA_UNKNOWN;
109 }
110
111 KGpgTransaction::ts_boolanswer
confirmOverwrite(QUrl & currentFile)112 KGpgTransaction::confirmOverwrite(QUrl ¤tFile)
113 {
114 Q_UNUSED(currentFile)
115
116 return BA_UNKNOWN;
117 }
118
119 bool
hintLine(const ts_hintType hint,const QString & args)120 KGpgTransaction::hintLine(const ts_hintType hint, const QString &args)
121 {
122 switch (hint) {
123 case HT_KEYEXPIRED:
124 case HT_PINENTRY_LAUNCHED:
125 return !args.isEmpty();
126 default:
127 return true;
128 }
129 }
130
131 void
finish()132 KGpgTransaction::finish()
133 {
134 }
135
136 void
setDescription(const QString & description)137 KGpgTransaction::setDescription(const QString &description)
138 {
139 d->m_description = description;
140 }
141
142 void
waitForInputTransaction()143 KGpgTransaction::waitForInputTransaction()
144 {
145 Q_ASSERT(d->m_inputTransaction != nullptr);
146
147 if (d->m_inputProcessDone)
148 return;
149
150 d->m_inputTransaction->waitForFinished();
151 }
152
153 void
unexpectedLine(const QString & line)154 KGpgTransaction::unexpectedLine(const QString &line)
155 {
156 qCDebug(KGPG_LOG_GENERAL) << this << "unexpected input line" << line << "for command" << d->m_process->program();
157 }
158
159 bool
passphraseRequested()160 KGpgTransaction::passphraseRequested()
161 {
162 return askPassphrase();
163 }
164
165 bool
passphraseReceived()166 KGpgTransaction::passphraseReceived()
167 {
168 return true;
169 }
170
171 bool
preStart()172 KGpgTransaction::preStart()
173 {
174 return true;
175 }
176
177 void
postStart()178 KGpgTransaction::postStart()
179 {
180 }
181
182 void
addIdHint(QString txt)183 KGpgTransaction::addIdHint(QString txt)
184 {
185 int cut = txt.indexOf(QLatin1Char( ' ' ), 22, Qt::CaseInsensitive);
186 txt.remove(0, cut);
187
188 if (txt.contains(QLatin1Char( '(' ), Qt::CaseInsensitive))
189 txt = txt.section(QLatin1Char( '(' ), 0, 0) + txt.section(QLatin1Char( ')' ), -1);
190
191 txt.replace(QLatin1Char( '<' ), QLatin1String( "<" ));
192
193 if (!d->m_idhints.contains(txt))
194 d->m_idhints << txt;
195 }
196
197 QString
getIdHints() const198 KGpgTransaction::getIdHints() const
199 {
200 return d->m_idhints.join( i18n(" or " ));
201 }
202
203 GPGProc *
getProcess()204 KGpgTransaction::getProcess()
205 {
206 return d->m_process;
207 }
208
209 int
addArgument(const QString & arg)210 KGpgTransaction::addArgument(const QString &arg)
211 {
212 int r = d->m_process->program().count();
213
214 *d->m_process << arg;
215
216 return r;
217 }
218
219 void
addArguments(const QStringList & args)220 KGpgTransaction::addArguments(const QStringList &args)
221 {
222 *d->m_process << args;
223 }
224
225 void
replaceArgument(const int pos,const QString & arg)226 KGpgTransaction::replaceArgument(const int pos, const QString &arg)
227 {
228 QStringList args(d->m_process->program());
229 d->m_process->clearProgram();
230
231 args.replace(pos, arg);
232
233 d->m_process->setProgram(args);
234 }
235
236 void
insertArgument(const int pos,const QString & arg)237 KGpgTransaction::insertArgument(const int pos, const QString &arg)
238 {
239 insertArguments(pos, QStringList(arg));
240 }
241
242 void
insertArguments(const int pos,const QStringList & args)243 KGpgTransaction::insertArguments(const int pos, const QStringList &args)
244 {
245 QStringList tmp(d->m_process->program());
246
247 int tmppos = pos;
248 for (const QString &s : args) {
249 tmp.insert(tmppos++, s);
250 }
251 d->m_process->setProgram(tmp);
252
253 int move = args.count();
254 for (int *ref : qAsConst(d->m_argRefs)) {
255 if (*ref >= pos)
256 *ref += move;
257 }
258 }
259
260 void
addArgumentRef(int * ref)261 KGpgTransaction::addArgumentRef(int *ref)
262 {
263 d->m_argRefs.append(ref);
264 }
265
266 bool
askPassphrase(const QString & message)267 KGpgTransaction::askPassphrase(const QString &message)
268 {
269 Q_EMIT statusMessage(i18n("Requesting Passphrase"));
270
271 if (d->m_passwordDialog == nullptr) {
272 d->m_passwordDialog = new KPasswordDialog(qobject_cast<QWidget *>(parent()));
273
274 QString passdlgmessage;
275 if (message.isEmpty()) {
276 QString userIDs(getIdHints());
277 if (userIDs.isEmpty())
278 userIDs = i18n("[No user id found]");
279 else
280 userIDs.replace(QLatin1Char( '<' ), QLatin1String( "<" ));
281
282 passdlgmessage = i18n("Enter passphrase for <b>%1</b>", userIDs);
283 } else {
284 passdlgmessage = message;
285 }
286
287 d->m_passwordDialog->setPrompt(passdlgmessage);
288
289 connect(d->m_passwordDialog, &KPasswordDialog::gotPassword, d, &KGpgTransactionPrivate::slotPassphraseEntered);
290 connect(d->m_passwordDialog, &KPasswordDialog::rejected, d, &KGpgTransactionPrivate::slotPassphraseAborted);
291 connect(d->m_process, &GPGProc::processExited, d->m_passwordDialog, &KPasswordDialog::rejected);
292 } else {
293 // we already have a dialog, so this is a "bad passphrase" situation
294 --d->m_tries;
295
296 d->m_passwordDialog->showErrorMessage(i18np("<p><b>Bad passphrase</b>. You have 1 try left.</p>",
297 "<p><b>Bad passphrase</b>. You have %1 tries left.</p>", d->m_tries),
298 KPasswordDialog::PasswordError);
299 }
300
301 d->m_passwordDialog->show();
302
303 return true;
304 }
305
306 void
setExpectedFingerprints(const QStringList & fingerprints)307 KGpgTransaction::setExpectedFingerprints(const QStringList &fingerprints)
308 {
309 d->m_expectedFingerprints = fingerprints;
310 }
311
312 void
setGnuPGHome(const QString & home)313 KGpgTransaction::setGnuPGHome(const QString &home)
314 {
315 QStringList tmp(d->m_process->program());
316
317 Q_ASSERT(tmp.count() > 3);
318 int homepos = tmp.indexOf(QLatin1String("--options"), 1);
319 if (homepos == -1)
320 homepos = tmp.indexOf(QLatin1String("--homedir"), 1);
321 Q_ASSERT(homepos != -1);
322 Q_ASSERT(homepos + 1 < tmp.count());
323
324 tmp[homepos] = QLatin1String("--homedir");
325 tmp[homepos + 1] = home;
326
327 d->m_process->setProgram(tmp);
328 }
329
330 int
waitForFinished(const int msecs)331 KGpgTransaction::waitForFinished(const int msecs)
332 {
333 int ret = TS_OK;
334
335 if (d->m_inputTransaction != nullptr) {
336 int ret = d->m_inputTransaction->waitForFinished(msecs);
337 if ((ret != TS_OK) && (msecs != -1))
338 return ret;
339 }
340
341 bool b = d->m_process->waitForFinished(msecs);
342
343 if (ret != TS_OK)
344 return ret;
345
346 if (!b)
347 return TS_USER_ABORTED;
348 else
349 return getSuccess();
350 }
351
352 const QString &
getDescription() const353 KGpgTransaction::getDescription() const
354 {
355 return d->m_description;
356 }
357
358 void
setInputTransaction(KGpgTransaction * ta)359 KGpgTransaction::setInputTransaction(KGpgTransaction *ta)
360 {
361 Q_ASSERT(d->m_chainingAllowed);
362
363 if (d->m_inputTransaction != nullptr)
364 clearInputTransaction();
365 d->m_inputTransaction = ta;
366
367 GPGProc *proc = ta->getProcess();
368 proc->setStandardOutputProcess(d->m_process);
369 connect(ta, &KGpgTransaction::done, d, &KGpgTransactionPrivate::slotInputTransactionDone);
370 }
371
372 void
clearInputTransaction()373 KGpgTransaction::clearInputTransaction()
374 {
375 disconnect(d->m_inputTransaction, &KGpgTransaction::done, d, &KGpgTransactionPrivate::slotInputTransactionDone);
376 d->m_inputTransaction = nullptr;
377 }
378
379 bool
hasInputTransaction() const380 KGpgTransaction::hasInputTransaction() const
381 {
382 return (d->m_inputTransaction != nullptr);
383 }
384
385 void
kill()386 KGpgTransaction::kill()
387 {
388 d->m_process->kill();
389 }
390
391 void
newPassphraseEntered()392 KGpgTransaction::newPassphraseEntered()
393 {
394 }
395