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 &currentFile)
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( "&lt;" ));
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( "&lt;" ));
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