1/*
2 * Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 */
19
20// #define GPGOP_DEBUG
21
22#include "gpgaction.h"
23
24#ifdef GPGOP_DEBUG
25#include "stdio.h"
26#endif
27
28namespace gpgQCAPlugin {
29
30static QDateTime getTimestamp(const QString &s)
31{
32 if (s.isEmpty())
33 return QDateTime();
34
35 if (s.contains(c: QLatin1Char('T'))) {
36 return QDateTime::fromString(string: s, format: Qt::ISODate);
37 } else {
38 return QDateTime::fromSecsSinceEpoch(secs: s.toInt());
39 }
40}
41
42static QByteArray getCString(const QByteArray &a)
43{
44 QByteArray out;
45
46 // convert the "backslash" C-string syntax
47 for (int n = 0; n < a.size(); ++n) {
48 if (a[n] == '\\' && n + 1 < a.size()) {
49 ++n;
50 unsigned char c = (unsigned char)a[n];
51 if (c == '\\') {
52 out += '\\';
53 } else if (c == 'x' && n + 2 < a.size()) {
54 ++n;
55 const QByteArray hex = a.mid(index: n, len: 2);
56 ++n; // only skip one, loop will skip the next
57
58 bool ok;
59 uint val = hex.toInt(ok: &ok, base: 16);
60 if (ok) {
61 out += (unsigned char)val;
62 } else {
63 out += "\\x";
64 out += hex;
65 }
66 }
67 } else {
68 out += a[n];
69 }
70 }
71
72 return out;
73}
74
75static bool stringToKeyList(const QString &outstr, GpgOp::KeyList *_keylist, QString *_keyring)
76{
77 GpgOp::KeyList keyList;
78 const QStringList lines = outstr.split(sep: QLatin1Char('\n'));
79
80 if (lines.count() < 1)
81 return false;
82
83 QStringList::ConstIterator it = lines.constBegin();
84
85 // first line is keyring file
86 QString keyring = *(it++);
87
88 // if the second line isn't a divider, we are dealing
89 // with a new version of gnupg that doesn't give us
90 // the keyring file on gpg --list-keys --with-colons
91 if (it == lines.constEnd() || (*it).isEmpty() || (*it).at(i: 0) != QLatin1Char('-')) {
92 // first line wasn't the keyring name...
93 keyring.clear();
94 // ...so read the first line again
95 it--;
96 } else {
97 // this was the divider line - skip it
98 it++;
99 }
100
101 for (; it != lines.constEnd(); ++it) {
102 const QStringList f = (*it).split(sep: QLatin1Char(':'));
103 if (f.count() < 1)
104 continue;
105 const QString &type = f[0];
106
107 bool key = false; // key or not
108 bool primary = false; // primary key or sub key
109 // bool sec = false; // private key or not
110
111 if (type == QLatin1String("pub")) {
112 key = true;
113 primary = true;
114 } else if (type == QLatin1String("sec")) {
115 key = true;
116 primary = true;
117 // sec = true;
118 } else if (type == QLatin1String("sub")) {
119 key = true;
120 } else if (type == QLatin1String("ssb")) {
121 key = true;
122 // sec = true;
123 }
124
125 if (key) {
126 if (primary) {
127 keyList += GpgOp::Key();
128
129 const QString &trust = f[1];
130 if (trust == QLatin1String("f") || trust == QLatin1String("u"))
131 keyList.last().isTrusted = true;
132 }
133
134 const int key_type = f[3].toInt();
135 const QString &caps = f[11];
136
137 GpgOp::KeyItem item;
138 item.bits = f[2].toInt();
139 if (key_type == 1)
140 item.type = GpgOp::KeyItem::RSA;
141 else if (key_type == 16)
142 item.type = GpgOp::KeyItem::ElGamal;
143 else if (key_type == 17)
144 item.type = GpgOp::KeyItem::DSA;
145 else
146 item.type = GpgOp::KeyItem::Unknown;
147 item.id = f[4];
148 item.creationDate = getTimestamp(s: f[5]);
149 item.expirationDate = getTimestamp(s: f[6]);
150 if (caps.contains(c: QLatin1Char('e')))
151 item.caps |= GpgOp::KeyItem::Encrypt;
152 if (caps.contains(c: QLatin1Char('s')))
153 item.caps |= GpgOp::KeyItem::Sign;
154 if (caps.contains(c: QLatin1Char('c')))
155 item.caps |= GpgOp::KeyItem::Certify;
156 if (caps.contains(c: QLatin1Char('a')))
157 item.caps |= GpgOp::KeyItem::Auth;
158
159 keyList.last().keyItems += item;
160 } else if (type == QLatin1String("uid")) {
161 const QByteArray uid = getCString(a: f[9].toUtf8());
162 keyList.last().userIds.append(t: QString::fromUtf8(ba: uid));
163 } else if (type == QLatin1String("fpr")) {
164 const QString &s = f[9];
165 keyList.last().keyItems.last().fingerprint = s;
166 }
167 }
168
169 if (_keylist)
170 *_keylist = keyList;
171 if (_keyring)
172 *_keyring = keyring;
173
174 return true;
175}
176
177static bool findKeyringFilename(const QString &outstr, QString *_keyring)
178{
179 const QStringList lines = outstr.split(sep: QLatin1Char('\n'));
180 if (lines.count() < 1)
181 return false;
182
183 *_keyring = lines[0];
184 return true;
185}
186
187GpgAction::GpgAction(QObject *parent)
188 : QObject(parent)
189 , proc(this)
190 , dtextTimer(this)
191 , utf8Output(false)
192{
193 dtextTimer.setSingleShot(true);
194
195 connect(sender: &proc, signal: &GPGProc::error, context: this, slot: &GpgAction::proc_error);
196 connect(sender: &proc, signal: &GPGProc::finished, context: this, slot: &GpgAction::proc_finished);
197 connect(sender: &proc, signal: &GPGProc::readyReadStdout, context: this, slot: &GpgAction::proc_readyReadStdout);
198 connect(sender: &proc, signal: &GPGProc::readyReadStderr, context: this, slot: &GpgAction::proc_readyReadStderr);
199 connect(sender: &proc, signal: &GPGProc::readyReadStatusLines, context: this, slot: &GpgAction::proc_readyReadStatusLines);
200 connect(sender: &proc, signal: &GPGProc::bytesWrittenStdin, context: this, slot: &GpgAction::proc_bytesWrittenStdin);
201 connect(sender: &proc, signal: &GPGProc::bytesWrittenAux, context: this, slot: &GpgAction::proc_bytesWrittenAux);
202 connect(sender: &proc, signal: &GPGProc::bytesWrittenCommand, context: this, slot: &GpgAction::proc_bytesWrittenCommand);
203 connect(sender: &proc, signal: &GPGProc::debug, context: this, slot: &GpgAction::proc_debug);
204 connect(sender: &dtextTimer, signal: &QCA::SafeTimer::timeout, context: this, slot: &GpgAction::t_dtext);
205
206 reset();
207}
208
209GpgAction::~GpgAction()
210{
211 reset();
212}
213
214void GpgAction::reset()
215{
216 collectOutput = true;
217 allowInput = false;
218 readConv.setup(LineConverter::Read);
219 writeConv.setup(LineConverter::Write);
220 readText = false;
221 writeText = false;
222 useAux = false;
223 passphraseKeyId = QString();
224 signing = false;
225 decryptGood = false;
226 signGood = false;
227 curError = GpgOp::ErrorUnknown;
228 badPassphrase = false;
229 need_submitPassphrase = false;
230 need_cardOkay = false;
231 diagnosticText = QString();
232 dtextTimer.stop();
233
234 output = Output();
235
236 proc.reset();
237}
238
239void GpgAction::start()
240{
241 reset();
242
243 QStringList args;
244 bool extra = false;
245
246 if (input.opt_ascii)
247 args += QStringLiteral("--armor");
248
249 if (input.opt_noagent)
250 args += QStringLiteral("--no-use-agent");
251
252 if (input.opt_alwaystrust)
253 args += QStringLiteral("--always-trust");
254
255 if (!input.opt_pubfile.isEmpty() && !input.opt_secfile.isEmpty()) {
256 args += QStringLiteral("--no-default-keyring");
257 args += QStringLiteral("--keyring");
258 args += input.opt_pubfile;
259 args += QStringLiteral("--secret-keyring");
260 args += input.opt_secfile;
261 }
262
263 switch (input.op) {
264 case GpgOp::Check:
265 {
266 args += QStringLiteral("--version");
267 readText = true;
268 break;
269 }
270 case GpgOp::SecretKeyringFile:
271 {
272#ifndef Q_OS_WIN
273 args += QStringLiteral("--display-charset=utf-8");
274#endif
275 args += QStringLiteral("--list-secret-keys");
276 readText = true;
277 break;
278 }
279 case GpgOp::PublicKeyringFile:
280 {
281#ifndef Q_OS_WIN
282 args += QStringLiteral("--display-charset=utf-8");
283#endif
284 args += QStringLiteral("--list-public-keys");
285 readText = true;
286 break;
287 }
288 case GpgOp::SecretKeys:
289 {
290 args += QStringLiteral("--fixed-list-mode");
291 args += QStringLiteral("--with-colons");
292 args += QStringLiteral("--with-fingerprint");
293 args += QStringLiteral("--with-fingerprint");
294 args += QStringLiteral("--list-secret-keys");
295 utf8Output = true;
296 readText = true;
297 break;
298 }
299 case GpgOp::PublicKeys:
300 {
301 args += QStringLiteral("--fixed-list-mode");
302 args += QStringLiteral("--with-colons");
303 args += QStringLiteral("--with-fingerprint");
304 args += QStringLiteral("--with-fingerprint");
305 args += QStringLiteral("--list-public-keys");
306 utf8Output = true;
307 readText = true;
308 break;
309 }
310 case GpgOp::Encrypt:
311 {
312 args += QStringLiteral("--encrypt");
313
314 // recipients
315 for (QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it) {
316 args += QStringLiteral("--recipient");
317 args += QStringLiteral("0x") + *it;
318 }
319 extra = true;
320 collectOutput = false;
321 allowInput = true;
322 if (input.opt_ascii)
323 readText = true;
324 break;
325 }
326 case GpgOp::Decrypt:
327 {
328 args += QStringLiteral("--decrypt");
329 extra = true;
330 collectOutput = false;
331 allowInput = true;
332 if (input.opt_ascii)
333 writeText = true;
334 break;
335 }
336 case GpgOp::Sign:
337 {
338 args += QStringLiteral("--default-key");
339 args += QStringLiteral("0x") + input.signer_id;
340 args += QStringLiteral("--sign");
341 extra = true;
342 collectOutput = false;
343 allowInput = true;
344 if (input.opt_ascii)
345 readText = true;
346 signing = true;
347 break;
348 }
349 case GpgOp::SignAndEncrypt:
350 {
351 args += QStringLiteral("--default-key");
352 args += QStringLiteral("0x") + input.signer_id;
353 args += QStringLiteral("--sign");
354 args += QStringLiteral("--encrypt");
355
356 // recipients
357 for (QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it) {
358 args += QStringLiteral("--recipient");
359 args += QStringLiteral("0x") + *it;
360 }
361 extra = true;
362 collectOutput = false;
363 allowInput = true;
364 if (input.opt_ascii)
365 readText = true;
366 signing = true;
367 break;
368 }
369 case GpgOp::SignClearsign:
370 {
371 args += QStringLiteral("--default-key");
372 args += QStringLiteral("0x") + input.signer_id;
373 args += QStringLiteral("--clearsign");
374 extra = true;
375 collectOutput = false;
376 allowInput = true;
377 if (input.opt_ascii)
378 readText = true;
379 signing = true;
380 break;
381 }
382 case GpgOp::SignDetached:
383 {
384 args += QStringLiteral("--default-key");
385 args += QStringLiteral("0x") + input.signer_id;
386 args += QStringLiteral("--detach-sign");
387 extra = true;
388 collectOutput = false;
389 allowInput = true;
390 if (input.opt_ascii)
391 readText = true;
392 signing = true;
393 break;
394 }
395 case GpgOp::Verify:
396 {
397 args += QStringLiteral("--verify");
398 args += QStringLiteral("-"); // krazy:exclude=doublequote_chars
399 extra = true;
400 allowInput = true;
401 if (input.opt_ascii)
402 writeText = true;
403 break;
404 }
405 case GpgOp::VerifyDetached:
406 {
407 args += QStringLiteral("--verify");
408 args += QStringLiteral("-"); // krazy:exclude=doublequote_chars
409 args += QStringLiteral("-&?");
410 extra = true;
411 allowInput = true;
412 useAux = true;
413 break;
414 }
415 case GpgOp::Import:
416 {
417 args += QStringLiteral("--import");
418 readText = true;
419 if (input.opt_ascii)
420 writeText = true;
421 break;
422 }
423 case GpgOp::Export:
424 {
425 args += QStringLiteral("--export");
426 args += QStringLiteral("0x") + input.export_key_id;
427 collectOutput = false;
428 if (input.opt_ascii)
429 readText = true;
430 break;
431 }
432 case GpgOp::DeleteKey:
433 {
434 args += QStringLiteral("--batch");
435 args += QStringLiteral("--delete-key");
436 args += QStringLiteral("0x") + input.delete_key_fingerprint;
437 break;
438 }
439 }
440
441#ifdef GPG_PROFILE
442 timer.start();
443 printf("<< launch >>\n");
444#endif
445 proc.start(bin: input.bin, args, m: extra ? GPGProc::ExtendedMode : GPGProc::NormalMode);
446
447 // detached sig
448 if (input.op == GpgOp::VerifyDetached) {
449 QByteArray a = input.sig;
450 if (input.opt_ascii) {
451 LineConverter conv;
452 conv.setup(LineConverter::Write);
453 a = conv.process(buf: a);
454 }
455 proc.writeStdin(a);
456 proc.closeStdin();
457 }
458
459 // import
460 if (input.op == GpgOp::Import) {
461 QByteArray a = input.inkey;
462 if (writeText) {
463 LineConverter conv;
464 conv.setup(LineConverter::Write);
465 a = conv.process(buf: a);
466 }
467 proc.writeStdin(a);
468 proc.closeStdin();
469 }
470}
471
472#ifdef QPIPE_SECURE
473void GpgAction::submitPassphrase(const QCA::SecureArray &a)
474#else
475void GpgAction::submitPassphrase(const QByteArray &a)
476#endif
477{
478 if (!need_submitPassphrase)
479 return;
480
481 need_submitPassphrase = false;
482
483#ifdef QPIPE_SECURE
484 QCA::SecureArray b;
485#else
486 QByteArray b;
487#endif
488 // filter out newlines, since that's the delimiter used
489 // to indicate a submitted passphrase
490 b.resize(size: a.size());
491 int at = 0;
492 for (int n = 0; n < a.size(); ++n) {
493 if (a[n] != '\n')
494 b[at++] = a[n];
495 }
496 b.resize(size: at);
497
498 // append newline
499 b.resize(size: b.size() + 1);
500 b[b.size() - 1] = '\n';
501 proc.writeCommand(a: b);
502}
503
504QByteArray GpgAction::read()
505{
506 if (collectOutput)
507 return QByteArray();
508
509 QByteArray a = proc.readStdout();
510 if (readText)
511 a = readConv.update(buf: a);
512 if (!proc.isActive())
513 a += readConv.final();
514 return a;
515}
516
517void GpgAction::write(const QByteArray &in)
518{
519 if (!allowInput)
520 return;
521
522 QByteArray a = in;
523 if (writeText)
524 a = writeConv.update(buf: in);
525
526 if (useAux)
527 proc.writeAux(a);
528 else
529 proc.writeStdin(a);
530}
531
532void GpgAction::endWrite()
533{
534 if (!allowInput)
535 return;
536
537 if (useAux)
538 proc.closeAux();
539 else
540 proc.closeStdin();
541}
542
543void GpgAction::cardOkay()
544{
545 if (need_cardOkay) {
546 need_cardOkay = false;
547 submitCommand(a: "\n");
548 }
549}
550
551QString GpgAction::readDiagnosticText()
552{
553 QString s = diagnosticText;
554 diagnosticText = QString();
555 return s;
556}
557
558void GpgAction::submitCommand(const QByteArray &a)
559{
560 proc.writeCommand(a);
561}
562
563// since str is taken as a value, it is ok to use the same variable for 'rest'
564QString GpgAction::nextArg(QString str, QString *rest)
565{
566 int n = str.indexOf(c: QLatin1Char(' '));
567 if (n == -1) {
568 if (rest)
569 *rest = QString();
570 return str;
571 } else {
572 if (rest)
573 *rest = str.mid(position: n + 1);
574 return str.mid(position: 0, n);
575 }
576}
577
578void GpgAction::processStatusLine(const QString &line)
579{
580 appendDiagnosticText(QStringLiteral("{") + line + QStringLiteral("}"));
581 ensureDTextEmit();
582
583 if (!proc.isActive())
584 return;
585
586 QString s, rest;
587 s = nextArg(str: line, rest: &rest);
588
589 if (s == QLatin1String("NODATA")) {
590 // only set this if it'll make it better
591 if (curError == GpgOp::ErrorUnknown)
592 curError = GpgOp::ErrorFormat;
593 } else if (s == QLatin1String("UNEXPECTED")) {
594 if (curError == GpgOp::ErrorUnknown)
595 curError = GpgOp::ErrorFormat;
596 } else if (s == QLatin1String("EXPKEYSIG")) {
597 curError = GpgOp::ErrorSignerExpired;
598 } else if (s == QLatin1String("REVKEYSIG")) {
599 curError = GpgOp::ErrorSignerRevoked;
600 } else if (s == QLatin1String("EXPSIG")) {
601 curError = GpgOp::ErrorSignatureExpired;
602 } else if (s == QLatin1String("INV_RECP")) {
603 const int r = nextArg(str: rest).toInt();
604
605 if (curError == GpgOp::ErrorUnknown) {
606 if (r == 10)
607 curError = GpgOp::ErrorEncryptUntrusted;
608 else if (r == 4)
609 curError = GpgOp::ErrorEncryptRevoked;
610 else if (r == 5)
611 curError = GpgOp::ErrorEncryptExpired;
612 else
613 // due to GnuPG bug #1650
614 // <https://bugs.g10code.com/gnupg/issue1650>
615 // encrypting to expired and revoked keys will
616 // not specify any reason for failing,
617 // defaulting to this
618 curError = GpgOp::ErrorEncryptInvalid;
619 }
620 } else if (s == QLatin1String("NO_SECKEY")) {
621 output.encryptedToId = nextArg(str: rest);
622
623 if (curError == GpgOp::ErrorUnknown)
624 curError = GpgOp::ErrorDecryptNoKey;
625 } else if (s == QLatin1String("DECRYPTION_OKAY")) {
626 decryptGood = true;
627
628 // message could be encrypted with several keys
629 if (curError == GpgOp::ErrorDecryptNoKey)
630 curError = GpgOp::ErrorUnknown;
631 } else if (s == QLatin1String("SIG_CREATED")) {
632 signGood = true;
633 } else if (s == QLatin1String("USERID_HINT")) {
634 passphraseKeyId = nextArg(str: rest);
635 } else if (s == QLatin1String("GET_HIDDEN")) {
636 QString arg = nextArg(str: rest);
637 if (arg == QLatin1String("passphrase.enter") || arg == QLatin1String("passphrase.pin.ask")) {
638 need_submitPassphrase = true;
639
640 // for signal-safety, emit later
641 QMetaObject::invokeMethod(obj: this, member: "needPassphrase", c: Qt::QueuedConnection, Q_ARG(QString, passphraseKeyId));
642 }
643 } else if (s == QLatin1String("GET_LINE")) {
644 QString arg = nextArg(str: rest);
645 if (arg == QLatin1String("cardctrl.insert_card.okay")) {
646 need_cardOkay = true;
647
648 QMetaObject::invokeMethod(obj: this, member: "needCard", c: Qt::QueuedConnection);
649 }
650 } else if (s == QLatin1String("GET_BOOL")) {
651 QString arg = nextArg(str: rest);
652 if (arg == QLatin1String("untrusted_key.override"))
653 submitCommand(a: "no\n");
654 } else if (s == QLatin1String("GOOD_PASSPHRASE")) {
655 badPassphrase = false;
656 } else if (s == QLatin1String("BAD_PASSPHRASE")) {
657 badPassphrase = true;
658 } else if (s == QLatin1String("GOODSIG")) {
659 output.wasSigned = true;
660 output.signerId = nextArg(str: rest);
661 output.verifyResult = GpgOp::VerifyGood;
662 } else if (s == QLatin1String("BADSIG")) {
663 output.wasSigned = true;
664 output.signerId = nextArg(str: rest);
665 output.verifyResult = GpgOp::VerifyBad;
666 } else if (s == QLatin1String("ERRSIG")) {
667 output.wasSigned = true;
668 const QStringList list = rest.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
669 output.signerId = list[0];
670 output.timestamp = getTimestamp(s: list[4]);
671 output.verifyResult = GpgOp::VerifyNoKey;
672 } else if (s == QLatin1String("VALIDSIG")) {
673 const QStringList list = rest.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
674 output.timestamp = getTimestamp(s: list[2]);
675 }
676}
677
678void GpgAction::processResult(int code)
679{
680#ifdef GPG_PROFILE
681 printf("<< launch: %d >>\n", timer.elapsed());
682#endif
683
684 // put stdout and stderr into QStrings
685
686 QString outstr;
687 QString errstr;
688
689#ifdef Q_OS_WIN
690 if (!utf8Output) {
691 outstr = QString::fromLocal8Bit(buf_stdout);
692 errstr = QString::fromLocal8Bit(buf_stderr);
693 } else {
694#endif
695 outstr = QString::fromUtf8(ba: buf_stdout);
696 errstr = QString::fromUtf8(ba: buf_stderr);
697#ifdef Q_OS_WIN
698 }
699#endif
700
701 if (collectOutput)
702 appendDiagnosticText(QStringLiteral("stdout: [%1]").arg(a: outstr));
703 appendDiagnosticText(QStringLiteral("stderr: [%1]").arg(a: errstr));
704 ensureDTextEmit();
705
706 if (badPassphrase) {
707 output.errorCode = GpgOp::ErrorPassphrase;
708 } else if (curError != GpgOp::ErrorUnknown) {
709 output.errorCode = curError;
710 } else if (code == 0) {
711 if (input.op == GpgOp::Check) {
712 const QStringList strList = outstr.split(QStringLiteral("\n"));
713 foreach (const QString &str, strList) {
714 if (!str.startsWith(s: QLatin1String("Home: ")))
715 continue;
716
717 output.homeDir = str.section(asep: QLatin1Char(' '), astart: 1);
718 break;
719 }
720 output.success = true;
721 } else if (input.op == GpgOp::SecretKeyringFile || input.op == GpgOp::PublicKeyringFile) {
722 if (findKeyringFilename(outstr, keyring: &output.keyringFile))
723 output.success = true;
724 } else if (input.op == GpgOp::SecretKeys || input.op == GpgOp::PublicKeys) {
725 if (stringToKeyList(outstr, keylist: &output.keys, keyring: &output.keyringFile))
726 output.success = true;
727 } else
728 output.success = true;
729 } else {
730 // decrypt and sign success based on status only.
731 // this is mainly because gpg uses fatal return
732 // values if there is trouble with gpg-agent, even
733 // though the operation otherwise works.
734
735 if (input.op == GpgOp::Decrypt && decryptGood)
736 output.success = true;
737 if (signing && signGood)
738 output.success = true;
739
740 // gpg will indicate failure for bad sigs, but we don't
741 // consider this to be operation failure.
742
743 bool signedMakesItGood = false;
744 if (input.op == GpgOp::Verify || input.op == GpgOp::VerifyDetached)
745 signedMakesItGood = true;
746
747 if (signedMakesItGood && output.wasSigned)
748 output.success = true;
749 }
750
751 emit finished();
752}
753
754void GpgAction::ensureDTextEmit()
755{
756 if (!dtextTimer.isActive())
757 dtextTimer.start();
758}
759
760void GpgAction::t_dtext()
761{
762 emit readyReadDiagnosticText();
763}
764
765void GpgAction::proc_error(gpgQCAPlugin::GPGProc::Error e)
766{
767 QString str;
768 if (e == GPGProc::FailedToStart)
769 str = QStringLiteral("FailedToStart");
770 else if (e == GPGProc::UnexpectedExit)
771 str = QStringLiteral("UnexpectedExit");
772 else if (e == GPGProc::ErrorWrite)
773 str = QStringLiteral("ErrorWrite");
774
775 appendDiagnosticText(QStringLiteral("GPG Process Error: %1").arg(a: str));
776 ensureDTextEmit();
777
778 output.errorCode = GpgOp::ErrorProcess;
779 emit finished();
780}
781
782void GpgAction::proc_finished(int exitCode)
783{
784 appendDiagnosticText(QStringLiteral("GPG Process Finished: exitStatus=%1").arg(a: exitCode));
785 ensureDTextEmit();
786
787 processResult(code: exitCode);
788}
789
790void GpgAction::proc_readyReadStdout()
791{
792 if (collectOutput) {
793 QByteArray a = proc.readStdout();
794 if (readText)
795 a = readConv.update(buf: a);
796 buf_stdout.append(a);
797 } else
798 emit readyRead();
799}
800
801void GpgAction::proc_readyReadStderr()
802{
803 buf_stderr.append(a: proc.readStderr());
804}
805
806void GpgAction::proc_readyReadStatusLines()
807{
808 const QStringList lines = proc.readStatusLines();
809 for (int n = 0; n < lines.count(); ++n)
810 processStatusLine(line: lines[n]);
811}
812
813void GpgAction::proc_bytesWrittenStdin(int bytes)
814{
815 if (!useAux) {
816 int actual = writeConv.writtenToActual(bytes);
817 emit bytesWritten(bytes: actual);
818 }
819}
820
821void GpgAction::proc_bytesWrittenAux(int bytes)
822{
823 if (useAux) {
824 int actual = writeConv.writtenToActual(bytes);
825 emit bytesWritten(bytes: actual);
826 }
827}
828
829void GpgAction::proc_bytesWrittenCommand(int)
830{
831 // don't care about this
832}
833
834void GpgAction::proc_debug(const QString &str)
835{
836 appendDiagnosticText(QStringLiteral("GPGProc: ") + str);
837 ensureDTextEmit();
838}
839
840void GpgAction::appendDiagnosticText(const QString &line)
841{
842#ifdef GPGOP_DEBUG
843 printf("%s\n", qPrintable(line));
844#endif
845 diagnosticText += line;
846}
847
848} // end namespace gpgQCAPlugin
849

source code of qca/plugins/qca-gnupg/gpgaction.cpp