File indexing completed on 2024-05-05 04:45:04

0001 /*
0002  * Copyright (C) 2003-2005  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
0017  *
0018  */
0019 
0020 // #define GPGOP_DEBUG
0021 
0022 #include "gpgaction.h"
0023 
0024 #ifdef GPGOP_DEBUG
0025 #include "stdio.h"
0026 #endif
0027 
0028 namespace gpgQCAPlugin {
0029 
0030 static QDateTime getTimestamp(const QString &s)
0031 {
0032     if (s.isEmpty())
0033         return QDateTime();
0034 
0035     if (s.contains(QLatin1Char('T'))) {
0036         return QDateTime::fromString(s, Qt::ISODate);
0037     } else {
0038         return QDateTime::fromSecsSinceEpoch(s.toInt());
0039     }
0040 }
0041 
0042 static QByteArray getCString(const QByteArray &a)
0043 {
0044     QByteArray out;
0045 
0046     // convert the "backslash" C-string syntax
0047     for (int n = 0; n < a.size(); ++n) {
0048         if (a[n] == '\\' && n + 1 < a.size()) {
0049             ++n;
0050             unsigned char c = (unsigned char)a[n];
0051             if (c == '\\') {
0052                 out += '\\';
0053             } else if (c == 'x' && n + 2 < a.size()) {
0054                 ++n;
0055                 const QByteArray hex = a.mid(n, 2);
0056                 ++n; // only skip one, loop will skip the next
0057 
0058                 bool ok;
0059                 uint val = hex.toInt(&ok, 16);
0060                 if (ok) {
0061                     out += (unsigned char)val;
0062                 } else {
0063                     out += "\\x";
0064                     out += hex;
0065                 }
0066             }
0067         } else {
0068             out += a[n];
0069         }
0070     }
0071 
0072     return out;
0073 }
0074 
0075 static bool stringToKeyList(const QString &outstr, GpgOp::KeyList *_keylist, QString *_keyring)
0076 {
0077     GpgOp::KeyList    keyList;
0078     const QStringList lines = outstr.split(QLatin1Char('\n'));
0079 
0080     if (lines.count() < 1)
0081         return false;
0082 
0083     QStringList::ConstIterator it = lines.constBegin();
0084 
0085     // first line is keyring file
0086     QString keyring = *(it++);
0087 
0088     // if the second line isn't a divider, we are dealing
0089     // with a new version of gnupg that doesn't give us
0090     // the keyring file on gpg --list-keys --with-colons
0091     if (it == lines.constEnd() || (*it).isEmpty() || (*it).at(0) != QLatin1Char('-')) {
0092         // first line wasn't the keyring name...
0093         keyring.clear();
0094         // ...so read the first line again
0095         it--;
0096     } else {
0097         // this was the divider line - skip it
0098         it++;
0099     }
0100 
0101     for (; it != lines.constEnd(); ++it) {
0102         const QStringList f = (*it).split(QLatin1Char(':'));
0103         if (f.count() < 1)
0104             continue;
0105         const QString &type = f[0];
0106 
0107         bool key     = false; // key or not
0108         bool primary = false; // primary key or sub key
0109         // bool sec = false; // private key or not
0110 
0111         if (type == QLatin1String("pub")) {
0112             key     = true;
0113             primary = true;
0114         } else if (type == QLatin1String("sec")) {
0115             key     = true;
0116             primary = true;
0117             // sec = true;
0118         } else if (type == QLatin1String("sub")) {
0119             key = true;
0120         } else if (type == QLatin1String("ssb")) {
0121             key = true;
0122             // sec = true;
0123         }
0124 
0125         if (key) {
0126             if (primary) {
0127                 keyList += GpgOp::Key();
0128 
0129                 const QString &trust = f[1];
0130                 if (trust == QLatin1String("f") || trust == QLatin1String("u"))
0131                     keyList.last().isTrusted = true;
0132             }
0133 
0134             const int      key_type = f[3].toInt();
0135             const QString &caps     = f[11];
0136 
0137             GpgOp::KeyItem item;
0138             item.bits = f[2].toInt();
0139             if (key_type == 1)
0140                 item.type = GpgOp::KeyItem::RSA;
0141             else if (key_type == 16)
0142                 item.type = GpgOp::KeyItem::ElGamal;
0143             else if (key_type == 17)
0144                 item.type = GpgOp::KeyItem::DSA;
0145             else
0146                 item.type = GpgOp::KeyItem::Unknown;
0147             item.id             = f[4];
0148             item.creationDate   = getTimestamp(f[5]);
0149             item.expirationDate = getTimestamp(f[6]);
0150             if (caps.contains(QLatin1Char('e')))
0151                 item.caps |= GpgOp::KeyItem::Encrypt;
0152             if (caps.contains(QLatin1Char('s')))
0153                 item.caps |= GpgOp::KeyItem::Sign;
0154             if (caps.contains(QLatin1Char('c')))
0155                 item.caps |= GpgOp::KeyItem::Certify;
0156             if (caps.contains(QLatin1Char('a')))
0157                 item.caps |= GpgOp::KeyItem::Auth;
0158 
0159             keyList.last().keyItems += item;
0160         } else if (type == QLatin1String("uid")) {
0161             const QByteArray uid = getCString(f[9].toUtf8());
0162             keyList.last().userIds.append(QString::fromUtf8(uid));
0163         } else if (type == QLatin1String("fpr")) {
0164             const QString &s                           = f[9];
0165             keyList.last().keyItems.last().fingerprint = s;
0166         }
0167     }
0168 
0169     if (_keylist)
0170         *_keylist = keyList;
0171     if (_keyring)
0172         *_keyring = keyring;
0173 
0174     return true;
0175 }
0176 
0177 static bool findKeyringFilename(const QString &outstr, QString *_keyring)
0178 {
0179     const QStringList lines = outstr.split(QLatin1Char('\n'));
0180     if (lines.count() < 1)
0181         return false;
0182 
0183     *_keyring = lines[0];
0184     return true;
0185 }
0186 
0187 GpgAction::GpgAction(QObject *parent)
0188     : QObject(parent)
0189     , proc(this)
0190     , dtextTimer(this)
0191     , utf8Output(false)
0192 {
0193     dtextTimer.setSingleShot(true);
0194 
0195     connect(&proc, &GPGProc::error, this, &GpgAction::proc_error);
0196     connect(&proc, &GPGProc::finished, this, &GpgAction::proc_finished);
0197     connect(&proc, &GPGProc::readyReadStdout, this, &GpgAction::proc_readyReadStdout);
0198     connect(&proc, &GPGProc::readyReadStderr, this, &GpgAction::proc_readyReadStderr);
0199     connect(&proc, &GPGProc::readyReadStatusLines, this, &GpgAction::proc_readyReadStatusLines);
0200     connect(&proc, &GPGProc::bytesWrittenStdin, this, &GpgAction::proc_bytesWrittenStdin);
0201     connect(&proc, &GPGProc::bytesWrittenAux, this, &GpgAction::proc_bytesWrittenAux);
0202     connect(&proc, &GPGProc::bytesWrittenCommand, this, &GpgAction::proc_bytesWrittenCommand);
0203     connect(&proc, &GPGProc::debug, this, &GpgAction::proc_debug);
0204     connect(&dtextTimer, &QCA::SafeTimer::timeout, this, &GpgAction::t_dtext);
0205 
0206     reset();
0207 }
0208 
0209 GpgAction::~GpgAction()
0210 {
0211     reset();
0212 }
0213 
0214 void GpgAction::reset()
0215 {
0216     collectOutput = true;
0217     allowInput    = false;
0218     readConv.setup(LineConverter::Read);
0219     writeConv.setup(LineConverter::Write);
0220     readText              = false;
0221     writeText             = false;
0222     useAux                = false;
0223     passphraseKeyId       = QString();
0224     signing               = false;
0225     decryptGood           = false;
0226     signGood              = false;
0227     curError              = GpgOp::ErrorUnknown;
0228     badPassphrase         = false;
0229     need_submitPassphrase = false;
0230     need_cardOkay         = false;
0231     diagnosticText        = QString();
0232     dtextTimer.stop();
0233 
0234     output = Output();
0235 
0236     proc.reset();
0237 }
0238 
0239 void GpgAction::start()
0240 {
0241     reset();
0242 
0243     QStringList args;
0244     bool        extra = false;
0245 
0246     if (input.opt_ascii)
0247         args += QStringLiteral("--armor");
0248 
0249     if (input.opt_noagent)
0250         args += QStringLiteral("--no-use-agent");
0251 
0252     if (input.opt_alwaystrust)
0253         args += QStringLiteral("--always-trust");
0254 
0255     if (!input.opt_pubfile.isEmpty() && !input.opt_secfile.isEmpty()) {
0256         args += QStringLiteral("--no-default-keyring");
0257         args += QStringLiteral("--keyring");
0258         args += input.opt_pubfile;
0259         args += QStringLiteral("--secret-keyring");
0260         args += input.opt_secfile;
0261     }
0262 
0263     switch (input.op) {
0264     case GpgOp::Check:
0265     {
0266         args += QStringLiteral("--version");
0267         readText = true;
0268         break;
0269     }
0270     case GpgOp::SecretKeyringFile:
0271     {
0272 #ifndef Q_OS_WIN
0273         args += QStringLiteral("--display-charset=utf-8");
0274 #endif
0275         args += QStringLiteral("--list-secret-keys");
0276         readText = true;
0277         break;
0278     }
0279     case GpgOp::PublicKeyringFile:
0280     {
0281 #ifndef Q_OS_WIN
0282         args += QStringLiteral("--display-charset=utf-8");
0283 #endif
0284         args += QStringLiteral("--list-public-keys");
0285         readText = true;
0286         break;
0287     }
0288     case GpgOp::SecretKeys:
0289     {
0290         args += QStringLiteral("--fixed-list-mode");
0291         args += QStringLiteral("--with-colons");
0292         args += QStringLiteral("--with-fingerprint");
0293         args += QStringLiteral("--with-fingerprint");
0294         args += QStringLiteral("--list-secret-keys");
0295         utf8Output = true;
0296         readText   = true;
0297         break;
0298     }
0299     case GpgOp::PublicKeys:
0300     {
0301         args += QStringLiteral("--fixed-list-mode");
0302         args += QStringLiteral("--with-colons");
0303         args += QStringLiteral("--with-fingerprint");
0304         args += QStringLiteral("--with-fingerprint");
0305         args += QStringLiteral("--list-public-keys");
0306         utf8Output = true;
0307         readText   = true;
0308         break;
0309     }
0310     case GpgOp::Encrypt:
0311     {
0312         args += QStringLiteral("--encrypt");
0313 
0314         // recipients
0315         for (QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it) {
0316             args += QStringLiteral("--recipient");
0317             args += QStringLiteral("0x") + *it;
0318         }
0319         extra         = true;
0320         collectOutput = false;
0321         allowInput    = true;
0322         if (input.opt_ascii)
0323             readText = true;
0324         break;
0325     }
0326     case GpgOp::Decrypt:
0327     {
0328         args += QStringLiteral("--decrypt");
0329         extra         = true;
0330         collectOutput = false;
0331         allowInput    = true;
0332         if (input.opt_ascii)
0333             writeText = true;
0334         break;
0335     }
0336     case GpgOp::Sign:
0337     {
0338         args += QStringLiteral("--default-key");
0339         args += QStringLiteral("0x") + input.signer_id;
0340         args += QStringLiteral("--sign");
0341         extra         = true;
0342         collectOutput = false;
0343         allowInput    = true;
0344         if (input.opt_ascii)
0345             readText = true;
0346         signing = true;
0347         break;
0348     }
0349     case GpgOp::SignAndEncrypt:
0350     {
0351         args += QStringLiteral("--default-key");
0352         args += QStringLiteral("0x") + input.signer_id;
0353         args += QStringLiteral("--sign");
0354         args += QStringLiteral("--encrypt");
0355 
0356         // recipients
0357         for (QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it) {
0358             args += QStringLiteral("--recipient");
0359             args += QStringLiteral("0x") + *it;
0360         }
0361         extra         = true;
0362         collectOutput = false;
0363         allowInput    = true;
0364         if (input.opt_ascii)
0365             readText = true;
0366         signing = true;
0367         break;
0368     }
0369     case GpgOp::SignClearsign:
0370     {
0371         args += QStringLiteral("--default-key");
0372         args += QStringLiteral("0x") + input.signer_id;
0373         args += QStringLiteral("--clearsign");
0374         extra         = true;
0375         collectOutput = false;
0376         allowInput    = true;
0377         if (input.opt_ascii)
0378             readText = true;
0379         signing = true;
0380         break;
0381     }
0382     case GpgOp::SignDetached:
0383     {
0384         args += QStringLiteral("--default-key");
0385         args += QStringLiteral("0x") + input.signer_id;
0386         args += QStringLiteral("--detach-sign");
0387         extra         = true;
0388         collectOutput = false;
0389         allowInput    = true;
0390         if (input.opt_ascii)
0391             readText = true;
0392         signing = true;
0393         break;
0394     }
0395     case GpgOp::Verify:
0396     {
0397         args += QStringLiteral("--verify");
0398         args += QStringLiteral("-"); // krazy:exclude=doublequote_chars
0399         extra      = true;
0400         allowInput = true;
0401         if (input.opt_ascii)
0402             writeText = true;
0403         break;
0404     }
0405     case GpgOp::VerifyDetached:
0406     {
0407         args += QStringLiteral("--verify");
0408         args += QStringLiteral("-"); // krazy:exclude=doublequote_chars
0409         args += QStringLiteral("-&?");
0410         extra      = true;
0411         allowInput = true;
0412         useAux     = true;
0413         break;
0414     }
0415     case GpgOp::Import:
0416     {
0417         args += QStringLiteral("--import");
0418         readText = true;
0419         if (input.opt_ascii)
0420             writeText = true;
0421         break;
0422     }
0423     case GpgOp::Export:
0424     {
0425         args += QStringLiteral("--export");
0426         args += QStringLiteral("0x") + input.export_key_id;
0427         collectOutput = false;
0428         if (input.opt_ascii)
0429             readText = true;
0430         break;
0431     }
0432     case GpgOp::DeleteKey:
0433     {
0434         args += QStringLiteral("--batch");
0435         args += QStringLiteral("--delete-key");
0436         args += QStringLiteral("0x") + input.delete_key_fingerprint;
0437         break;
0438     }
0439     }
0440 
0441 #ifdef GPG_PROFILE
0442     timer.start();
0443     printf("<< launch >>\n");
0444 #endif
0445     proc.start(input.bin, args, extra ? GPGProc::ExtendedMode : GPGProc::NormalMode);
0446 
0447     // detached sig
0448     if (input.op == GpgOp::VerifyDetached) {
0449         QByteArray a = input.sig;
0450         if (input.opt_ascii) {
0451             LineConverter conv;
0452             conv.setup(LineConverter::Write);
0453             a = conv.process(a);
0454         }
0455         proc.writeStdin(a);
0456         proc.closeStdin();
0457     }
0458 
0459     // import
0460     if (input.op == GpgOp::Import) {
0461         QByteArray a = input.inkey;
0462         if (writeText) {
0463             LineConverter conv;
0464             conv.setup(LineConverter::Write);
0465             a = conv.process(a);
0466         }
0467         proc.writeStdin(a);
0468         proc.closeStdin();
0469     }
0470 }
0471 
0472 #ifdef QPIPE_SECURE
0473 void GpgAction::submitPassphrase(const QCA::SecureArray &a)
0474 #else
0475 void GpgAction::submitPassphrase(const QByteArray &a)
0476 #endif
0477 {
0478     if (!need_submitPassphrase)
0479         return;
0480 
0481     need_submitPassphrase = false;
0482 
0483 #ifdef QPIPE_SECURE
0484     QCA::SecureArray b;
0485 #else
0486     QByteArray b;
0487 #endif
0488     // filter out newlines, since that's the delimiter used
0489     // to indicate a submitted passphrase
0490     b.resize(a.size());
0491     int at = 0;
0492     for (int n = 0; n < a.size(); ++n) {
0493         if (a[n] != '\n')
0494             b[at++] = a[n];
0495     }
0496     b.resize(at);
0497 
0498     // append newline
0499     b.resize(b.size() + 1);
0500     b[b.size() - 1] = '\n';
0501     proc.writeCommand(b);
0502 }
0503 
0504 QByteArray GpgAction::read()
0505 {
0506     if (collectOutput)
0507         return QByteArray();
0508 
0509     QByteArray a = proc.readStdout();
0510     if (readText)
0511         a = readConv.update(a);
0512     if (!proc.isActive())
0513         a += readConv.final();
0514     return a;
0515 }
0516 
0517 void GpgAction::write(const QByteArray &in)
0518 {
0519     if (!allowInput)
0520         return;
0521 
0522     QByteArray a = in;
0523     if (writeText)
0524         a = writeConv.update(in);
0525 
0526     if (useAux)
0527         proc.writeAux(a);
0528     else
0529         proc.writeStdin(a);
0530 }
0531 
0532 void GpgAction::endWrite()
0533 {
0534     if (!allowInput)
0535         return;
0536 
0537     if (useAux)
0538         proc.closeAux();
0539     else
0540         proc.closeStdin();
0541 }
0542 
0543 void GpgAction::cardOkay()
0544 {
0545     if (need_cardOkay) {
0546         need_cardOkay = false;
0547         submitCommand("\n");
0548     }
0549 }
0550 
0551 QString GpgAction::readDiagnosticText()
0552 {
0553     QString s      = diagnosticText;
0554     diagnosticText = QString();
0555     return s;
0556 }
0557 
0558 void GpgAction::submitCommand(const QByteArray &a)
0559 {
0560     proc.writeCommand(a);
0561 }
0562 
0563 // since str is taken as a value, it is ok to use the same variable for 'rest'
0564 QString GpgAction::nextArg(QString str, QString *rest)
0565 {
0566     int n = str.indexOf(QLatin1Char(' '));
0567     if (n == -1) {
0568         if (rest)
0569             *rest = QString();
0570         return str;
0571     } else {
0572         if (rest)
0573             *rest = str.mid(n + 1);
0574         return str.mid(0, n);
0575     }
0576 }
0577 
0578 void GpgAction::processStatusLine(const QString &line)
0579 {
0580     appendDiagnosticText(QStringLiteral("{") + line + QStringLiteral("}"));
0581     ensureDTextEmit();
0582 
0583     if (!proc.isActive())
0584         return;
0585 
0586     QString s, rest;
0587     s = nextArg(line, &rest);
0588 
0589     if (s == QLatin1String("NODATA")) {
0590         // only set this if it'll make it better
0591         if (curError == GpgOp::ErrorUnknown)
0592             curError = GpgOp::ErrorFormat;
0593     } else if (s == QLatin1String("UNEXPECTED")) {
0594         if (curError == GpgOp::ErrorUnknown)
0595             curError = GpgOp::ErrorFormat;
0596     } else if (s == QLatin1String("EXPKEYSIG")) {
0597         curError = GpgOp::ErrorSignerExpired;
0598     } else if (s == QLatin1String("REVKEYSIG")) {
0599         curError = GpgOp::ErrorSignerRevoked;
0600     } else if (s == QLatin1String("EXPSIG")) {
0601         curError = GpgOp::ErrorSignatureExpired;
0602     } else if (s == QLatin1String("INV_RECP")) {
0603         const int r = nextArg(rest).toInt();
0604 
0605         if (curError == GpgOp::ErrorUnknown) {
0606             if (r == 10)
0607                 curError = GpgOp::ErrorEncryptUntrusted;
0608             else if (r == 4)
0609                 curError = GpgOp::ErrorEncryptRevoked;
0610             else if (r == 5)
0611                 curError = GpgOp::ErrorEncryptExpired;
0612             else
0613                 // due to GnuPG bug #1650
0614                 // <https://bugs.g10code.com/gnupg/issue1650>
0615                 // encrypting to expired and revoked keys will
0616                 // not specify any reason for failing,
0617                 // defaulting to this
0618                 curError = GpgOp::ErrorEncryptInvalid;
0619         }
0620     } else if (s == QLatin1String("NO_SECKEY")) {
0621         output.encryptedToId = nextArg(rest);
0622 
0623         if (curError == GpgOp::ErrorUnknown)
0624             curError = GpgOp::ErrorDecryptNoKey;
0625     } else if (s == QLatin1String("DECRYPTION_OKAY")) {
0626         decryptGood = true;
0627 
0628         // message could be encrypted with several keys
0629         if (curError == GpgOp::ErrorDecryptNoKey)
0630             curError = GpgOp::ErrorUnknown;
0631     } else if (s == QLatin1String("SIG_CREATED")) {
0632         signGood = true;
0633     } else if (s == QLatin1String("USERID_HINT")) {
0634         passphraseKeyId = nextArg(rest);
0635     } else if (s == QLatin1String("GET_HIDDEN")) {
0636         QString arg = nextArg(rest);
0637         if (arg == QLatin1String("passphrase.enter") || arg == QLatin1String("passphrase.pin.ask")) {
0638             need_submitPassphrase = true;
0639 
0640             // for signal-safety, emit later
0641             QMetaObject::invokeMethod(this, "needPassphrase", Qt::QueuedConnection, Q_ARG(QString, passphraseKeyId));
0642         }
0643     } else if (s == QLatin1String("GET_LINE")) {
0644         QString arg = nextArg(rest);
0645         if (arg == QLatin1String("cardctrl.insert_card.okay")) {
0646             need_cardOkay = true;
0647 
0648             QMetaObject::invokeMethod(this, "needCard", Qt::QueuedConnection);
0649         }
0650     } else if (s == QLatin1String("GET_BOOL")) {
0651         QString arg = nextArg(rest);
0652         if (arg == QLatin1String("untrusted_key.override"))
0653             submitCommand("no\n");
0654     } else if (s == QLatin1String("GOOD_PASSPHRASE")) {
0655         badPassphrase = false;
0656     } else if (s == QLatin1String("BAD_PASSPHRASE")) {
0657         badPassphrase = true;
0658     } else if (s == QLatin1String("GOODSIG")) {
0659         output.wasSigned    = true;
0660         output.signerId     = nextArg(rest);
0661         output.verifyResult = GpgOp::VerifyGood;
0662     } else if (s == QLatin1String("BADSIG")) {
0663         output.wasSigned    = true;
0664         output.signerId     = nextArg(rest);
0665         output.verifyResult = GpgOp::VerifyBad;
0666     } else if (s == QLatin1String("ERRSIG")) {
0667         output.wasSigned       = true;
0668         const QStringList list = rest.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0669         output.signerId        = list[0];
0670         output.timestamp       = getTimestamp(list[4]);
0671         output.verifyResult    = GpgOp::VerifyNoKey;
0672     } else if (s == QLatin1String("VALIDSIG")) {
0673         const QStringList list = rest.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0674         output.timestamp       = getTimestamp(list[2]);
0675     }
0676 }
0677 
0678 void GpgAction::processResult(int code)
0679 {
0680 #ifdef GPG_PROFILE
0681     printf("<< launch: %d >>\n", timer.elapsed());
0682 #endif
0683 
0684     // put stdout and stderr into QStrings
0685 
0686     QString outstr;
0687     QString errstr;
0688 
0689 #ifdef Q_OS_WIN
0690     if (!utf8Output) {
0691         outstr = QString::fromLocal8Bit(buf_stdout);
0692         errstr = QString::fromLocal8Bit(buf_stderr);
0693     } else {
0694 #endif
0695         outstr = QString::fromUtf8(buf_stdout);
0696         errstr = QString::fromUtf8(buf_stderr);
0697 #ifdef Q_OS_WIN
0698     }
0699 #endif
0700 
0701     if (collectOutput)
0702         appendDiagnosticText(QStringLiteral("stdout: [%1]").arg(outstr));
0703     appendDiagnosticText(QStringLiteral("stderr: [%1]").arg(errstr));
0704     ensureDTextEmit();
0705 
0706     if (badPassphrase) {
0707         output.errorCode = GpgOp::ErrorPassphrase;
0708     } else if (curError != GpgOp::ErrorUnknown) {
0709         output.errorCode = curError;
0710     } else if (code == 0) {
0711         if (input.op == GpgOp::Check) {
0712             const QStringList strList = outstr.split(QStringLiteral("\n"));
0713             foreach (const QString &str, strList) {
0714                 if (!str.startsWith(QLatin1String("Home: ")))
0715                     continue;
0716 
0717                 output.homeDir = str.section(QLatin1Char(' '), 1);
0718                 break;
0719             }
0720             output.success = true;
0721         } else if (input.op == GpgOp::SecretKeyringFile || input.op == GpgOp::PublicKeyringFile) {
0722             if (findKeyringFilename(outstr, &output.keyringFile))
0723                 output.success = true;
0724         } else if (input.op == GpgOp::SecretKeys || input.op == GpgOp::PublicKeys) {
0725             if (stringToKeyList(outstr, &output.keys, &output.keyringFile))
0726                 output.success = true;
0727         } else
0728             output.success = true;
0729     } else {
0730         // decrypt and sign success based on status only.
0731         // this is mainly because gpg uses fatal return
0732         // values if there is trouble with gpg-agent, even
0733         // though the operation otherwise works.
0734 
0735         if (input.op == GpgOp::Decrypt && decryptGood)
0736             output.success = true;
0737         if (signing && signGood)
0738             output.success = true;
0739 
0740         // gpg will indicate failure for bad sigs, but we don't
0741         // consider this to be operation failure.
0742 
0743         bool signedMakesItGood = false;
0744         if (input.op == GpgOp::Verify || input.op == GpgOp::VerifyDetached)
0745             signedMakesItGood = true;
0746 
0747         if (signedMakesItGood && output.wasSigned)
0748             output.success = true;
0749     }
0750 
0751     emit finished();
0752 }
0753 
0754 void GpgAction::ensureDTextEmit()
0755 {
0756     if (!dtextTimer.isActive())
0757         dtextTimer.start();
0758 }
0759 
0760 void GpgAction::t_dtext()
0761 {
0762     emit readyReadDiagnosticText();
0763 }
0764 
0765 void GpgAction::proc_error(gpgQCAPlugin::GPGProc::Error e)
0766 {
0767     QString str;
0768     if (e == GPGProc::FailedToStart)
0769         str = QStringLiteral("FailedToStart");
0770     else if (e == GPGProc::UnexpectedExit)
0771         str = QStringLiteral("UnexpectedExit");
0772     else if (e == GPGProc::ErrorWrite)
0773         str = QStringLiteral("ErrorWrite");
0774 
0775     appendDiagnosticText(QStringLiteral("GPG Process Error: %1").arg(str));
0776     ensureDTextEmit();
0777 
0778     output.errorCode = GpgOp::ErrorProcess;
0779     emit finished();
0780 }
0781 
0782 void GpgAction::proc_finished(int exitCode)
0783 {
0784     appendDiagnosticText(QStringLiteral("GPG Process Finished: exitStatus=%1").arg(exitCode));
0785     ensureDTextEmit();
0786 
0787     processResult(exitCode);
0788 }
0789 
0790 void GpgAction::proc_readyReadStdout()
0791 {
0792     if (collectOutput) {
0793         QByteArray a = proc.readStdout();
0794         if (readText)
0795             a = readConv.update(a);
0796         buf_stdout.append(a);
0797     } else
0798         emit readyRead();
0799 }
0800 
0801 void GpgAction::proc_readyReadStderr()
0802 {
0803     buf_stderr.append(proc.readStderr());
0804 }
0805 
0806 void GpgAction::proc_readyReadStatusLines()
0807 {
0808     const QStringList lines = proc.readStatusLines();
0809     for (int n = 0; n < lines.count(); ++n)
0810         processStatusLine(lines[n]);
0811 }
0812 
0813 void GpgAction::proc_bytesWrittenStdin(int bytes)
0814 {
0815     if (!useAux) {
0816         int  actual = writeConv.writtenToActual(bytes);
0817         emit bytesWritten(actual);
0818     }
0819 }
0820 
0821 void GpgAction::proc_bytesWrittenAux(int bytes)
0822 {
0823     if (useAux) {
0824         int  actual = writeConv.writtenToActual(bytes);
0825         emit bytesWritten(actual);
0826     }
0827 }
0828 
0829 void GpgAction::proc_bytesWrittenCommand(int)
0830 {
0831     // don't care about this
0832 }
0833 
0834 void GpgAction::proc_debug(const QString &str)
0835 {
0836     appendDiagnosticText(QStringLiteral("GPGProc: ") + str);
0837     ensureDTextEmit();
0838 }
0839 
0840 void GpgAction::appendDiagnosticText(const QString &line)
0841 {
0842 #ifdef GPGOP_DEBUG
0843     printf("%s\n", qPrintable(line));
0844 #endif
0845     diagnosticText += line;
0846 }
0847 
0848 } // end namespace gpgQCAPlugin