• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE Support
  • Sitemap
  • Contact Us
 

qca

gpgop.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2003-2005  Justin Karneges <justin@affinix.com>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00017  *
00018  */
00019 
00020 #include "gpgop.h"
00021 
00022 #include "gpgproc.h"
00023 
00024 #include <QTimer>
00025 
00026 namespace gpgQCAPlugin {
00027 
00028 //----------------------------------------------------------------------------
00029 // LineConverter
00030 //----------------------------------------------------------------------------
00031 class LineConverter
00032 {
00033 public:
00034     enum Mode { Read, Write };
00035 
00036     void setup(Mode m)
00037     {
00038         state = Normal;
00039         mode = m;
00040 #ifdef Q_OS_WIN
00041         write_conv = true;
00042 #else
00043         write_conv = false;
00044 #endif
00045         prebytes = 0;
00046         list.clear();
00047     }
00048 
00049     QByteArray update(const QByteArray &buf)
00050     {
00051         if(mode == Read)
00052         {
00053             QByteArray out;
00054 
00055             if(state == Normal)
00056             {
00057                 out = buf;
00058             }
00059             else
00060             {
00061                 out.resize(buf.size() + 1);
00062                 out[0] = '\r';
00063                 memcpy(out.data() + 1, buf.data(), buf.size());
00064             }
00065 
00066             int n = 0;
00067             while(1)
00068             {
00069                 n = out.indexOf('\r', n);
00070                 // not found
00071                 if(n == -1)
00072                 {
00073                     break;
00074                 }
00075                 // found, not last character
00076                 if(n < (buf.size() - 1))
00077                 {
00078                     if(out[n + 1] == '\n')
00079                     {
00080                         // clip out the '\r'
00081                         memmove(out.data() + n, out.data() + n + 1, out.size() - n - 1);
00082                         out.resize(out.size() - 1);
00083                     }
00084                 }
00085                 // found, last character
00086                 else
00087                 {
00088                     state = Partial;
00089                     break;
00090                 }
00091                 ++n;
00092             }
00093 
00094             return out;
00095         }
00096         else
00097         {
00098             if(write_conv)
00099             {
00100                 QByteArray out;
00101                 int prev = 0;
00102                 int at = 0;
00103 
00104                 while(1)
00105                 {
00106                     int n = buf.indexOf('\n', at);
00107                     if(n == -1)
00108                         break;
00109 
00110                     int chunksize = n - at;
00111                     int oldsize = out.size();
00112                     out.resize(oldsize + chunksize + 2);
00113                     memcpy(out.data() + oldsize, buf.data() + at, chunksize);
00114                     memcpy(out.data() + oldsize + chunksize, "\r\n", 2);
00115 
00116                     list.append(prebytes + n + 1 - prev);
00117                     prebytes = 0;
00118                     prev = n;
00119 
00120                     at = n + 1;
00121                 }
00122                 if(at < buf.size())
00123                 {
00124                     int chunksize = buf.size() - at;
00125                     int oldsize = out.size();
00126                     out.resize(oldsize + chunksize);
00127                     memcpy(out.data() + oldsize, buf.data() + at, chunksize);
00128                 }
00129 
00130                 prebytes += buf.size() - prev;
00131                 return out;
00132             }
00133             else
00134                 return buf;
00135         }
00136     }
00137 
00138     QByteArray final()
00139     {
00140         if(mode == Read)
00141         {
00142             QByteArray out;
00143             if(state == Partial)
00144             {
00145                 out.resize(1);
00146                 out[0] = '\r';
00147             }
00148             return out;
00149         }
00150         else
00151         {
00152             return QByteArray();
00153         }
00154     }
00155 
00156     QByteArray process(const QByteArray &buf)
00157     {
00158         return update(buf) + final();
00159     }
00160 
00161     int writtenToActual(int bytes)
00162     {
00163         if(write_conv)
00164         {
00165             int n = 0;
00166             int counter = bytes;
00167             while(counter > 0)
00168             {
00169                 if(!list.isEmpty() && bytes >= list.first())
00170                 {
00171                     ++n;
00172                     counter -= list.takeFirst();
00173                 }
00174                 else
00175                 {
00176                     if(list.isEmpty())
00177                         prebytes -= counter;
00178                     else
00179                         list.first() -= counter;
00180 
00181                     if(prebytes < 0)
00182                     {
00183                         bytes += prebytes;
00184                         prebytes = 0;
00185                     }
00186 
00187                     break;
00188                 }
00189             }
00190             return bytes - n;
00191         }
00192         else
00193             return bytes;
00194     }
00195 
00196 private:
00197     enum State { Normal, Partial };
00198     Mode mode;
00199     State state;
00200     bool write_conv;
00201 public:
00202     int prebytes;
00203     QList<int> list;
00204 };
00205 
00206 //----------------------------------------------------------------------------
00207 // GpgAction
00208 //----------------------------------------------------------------------------
00209 static QDateTime getTimestamp(const QString &s)
00210 {
00211     if(s.isEmpty())
00212         return QDateTime();
00213 
00214     if(s.contains('T'))
00215     {
00216         return QDateTime::fromString(s, Qt::ISODate);
00217     }
00218     else
00219     {
00220         QDateTime dt;
00221         dt.setTime_t(s.toInt());
00222         return dt;
00223     }
00224 }
00225 
00226 static QByteArray getCString(const QByteArray &a)
00227 {
00228     QByteArray out;
00229 
00230     // convert the "backslash" C-string syntax
00231     for(int n = 0; n < a.size(); ++n)
00232     {
00233         if(a[n] == '\\' && n + 1 < a.size())
00234         {
00235             ++n;
00236             unsigned char c = (unsigned char)a[n];
00237             if(c == '\\')
00238             {
00239                 out += '\\';
00240             }
00241             else if(c == 'x' && n + 2 < a.size())
00242             {
00243                 ++n;
00244                 QByteArray hex = a.mid(n, 2);
00245                 ++n; // only skip one, loop will skip the next
00246 
00247                 bool ok;
00248                 uint val = hex.toInt(&ok, 16);
00249                 if(ok)
00250                 {
00251                     out += (unsigned char)val;
00252                 }
00253                 else
00254                 {
00255                     out += "\\x";
00256                     out += hex;
00257                 }
00258             }
00259         }
00260         else
00261         {
00262             out += a[n];
00263         }
00264     }
00265 
00266     return out;
00267 }
00268 
00269 static bool stringToKeyList(const QString &outstr, GpgOp::KeyList *_keylist, QString *_keyring)
00270 {
00271     GpgOp::KeyList keyList;
00272     QStringList lines = outstr.split('\n');
00273 
00274     if(lines.count() < 1)
00275         return false;
00276 
00277     QStringList::ConstIterator it = lines.begin();
00278 
00279     // first line is keyring file
00280     QString keyring = *(it++);
00281 
00282     // if the second line isn't a divider, we are dealing
00283     // with a new version of gnupg that doesn't give us
00284     // the keyring file on gpg --list-keys --with-colons
00285     if(it == lines.end() || (*it).isEmpty() || (*it).at(0) != '-')
00286     {
00287         // first line wasn't the keyring name...
00288         keyring.clear();
00289         // ...so read the first line again
00290         it--;
00291     }
00292     else
00293     {
00294         // this was the divider line - skip it
00295         it++;
00296     }
00297 
00298     for(; it != lines.end(); ++it)
00299     {
00300         QStringList f = (*it).split(':');
00301         if(f.count() < 1)
00302             continue;
00303         QString type = f[0];
00304 
00305         bool key = false; // key or not
00306         bool primary = false; // primary key or sub key
00307         bool sec = false; // private key or not
00308 
00309         if(type == "pub")
00310         {
00311             key = true;
00312             primary = true;
00313         }
00314         else if(type == "sec")
00315         {
00316             key = true;
00317             primary = true;
00318             sec = true;
00319         }
00320         else if(type == "sub")
00321         {
00322             key = true;
00323         }
00324         else if(type == "ssb")
00325         {
00326             key = true;
00327             sec = true;
00328         }
00329 
00330         if(key)
00331         {
00332             if(primary)
00333             {
00334                 keyList += GpgOp::Key();
00335 
00336                 QString trust = f[1];
00337                 if(trust == "f" || trust == "u")
00338                     keyList.last().isTrusted = true;
00339             }
00340 
00341             int key_type = f[3].toInt();
00342             QString caps = f[11];
00343 
00344             GpgOp::KeyItem item;
00345             item.bits = f[2].toInt();
00346             if(key_type == 1)
00347                 item.type = GpgOp::KeyItem::RSA;
00348             else if(key_type == 16)
00349                 item.type = GpgOp::KeyItem::ElGamal;
00350             else if(key_type == 17)
00351                 item.type = GpgOp::KeyItem::DSA;
00352             else
00353                 item.type = GpgOp::KeyItem::Unknown;
00354             item.id = f[4];
00355             item.creationDate = getTimestamp(f[5]);
00356             item.expirationDate = getTimestamp(f[6]);
00357             if(caps.contains('e'))
00358                 item.caps |= GpgOp::KeyItem::Encrypt;
00359             if(caps.contains('s'))
00360                 item.caps |= GpgOp::KeyItem::Sign;
00361             if(caps.contains('c'))
00362                 item.caps |= GpgOp::KeyItem::Certify;
00363             if(caps.contains('a'))
00364                 item.caps |= GpgOp::KeyItem::Auth;
00365 
00366             keyList.last().keyItems += item;
00367         }
00368         else if(type == "uid")
00369         {
00370             QByteArray uid = getCString(f[9].toLatin1());
00371             keyList.last().userIds.append(QString::fromUtf8(uid));
00372         }
00373         else if(type == "fpr")
00374         {
00375             QString s = f[9];
00376             keyList.last().keyItems.last().fingerprint = s;
00377         }
00378     }
00379 
00380     if(_keylist)
00381         *_keylist = keyList;
00382     if(_keyring)
00383         *_keyring = keyring;
00384 
00385     return true;
00386 }
00387 
00388 static bool findKeyringFilename(const QString &outstr, QString *_keyring)
00389 {
00390     QStringList lines = outstr.split('\n');
00391     if(lines.count() < 1)
00392         return false;
00393 
00394     *_keyring = lines[0];
00395     return true;
00396 }
00397 
00398 class GpgAction : public QObject
00399 {
00400     Q_OBJECT
00401 public:
00402     class Input
00403     {
00404     public:
00405         QString bin;
00406         GpgOp::Type op;
00407         bool opt_ascii, opt_noagent, opt_alwaystrust;
00408         QString opt_pubfile, opt_secfile;
00409         QStringList recip_ids;
00410         QString signer_id;
00411         QByteArray sig;
00412         QByteArray inkey;
00413         QString export_key_id;
00414         QString delete_key_fingerprint;
00415 
00416         Input() : opt_ascii(false), opt_noagent(false), opt_alwaystrust(false) {}
00417     };
00418 
00419     class Output
00420     {
00421     public:
00422         bool success;
00423         GpgOp::Error errorCode;
00424         GpgOp::KeyList keys;
00425         QString keyringFile;
00426         QString encryptedToId;
00427         bool wasSigned;
00428         QString signerId;
00429         QDateTime timestamp;
00430         GpgOp::VerifyResult verifyResult;
00431 
00432         Output() : success(false), errorCode(GpgOp::ErrorUnknown), wasSigned(false) {}
00433     };
00434 
00435     Input input;
00436     Output output;
00437 
00438     GPGProc proc;
00439     bool collectOutput, allowInput;
00440     LineConverter readConv, writeConv;
00441     bool readText, writeText;
00442     QByteArray buf_stdout, buf_stderr;
00443     bool useAux;
00444     QString passphraseKeyId;
00445     bool signing, signPartDone, decryptGood, signGood;
00446     GpgOp::Error curError;
00447     bool badPassphrase;
00448     bool need_submitPassphrase, need_cardOkay;
00449     QString diagnosticText;
00450     SafeTimer dtextTimer;
00451 
00452 #ifdef GPG_PROFILE
00453     QTime timer;
00454 #endif
00455 
00456     GpgAction(QObject *parent = 0) : QObject(parent), proc(this), dtextTimer(this)
00457     {
00458         dtextTimer.setSingleShot(true);
00459 
00460         connect(&proc, SIGNAL(error(gpgQCAPlugin::GPGProc::Error)), SLOT(proc_error(gpgQCAPlugin::GPGProc::Error)));
00461         connect(&proc, SIGNAL(finished(int)), SLOT(proc_finished(int)));
00462         connect(&proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout()));
00463         connect(&proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr()));
00464         connect(&proc, SIGNAL(readyReadStatusLines()), SLOT(proc_readyReadStatusLines()));
00465         connect(&proc, SIGNAL(bytesWrittenStdin(int)), SLOT(proc_bytesWrittenStdin(int)));
00466         connect(&proc, SIGNAL(bytesWrittenAux(int)), SLOT(proc_bytesWrittenAux(int)));
00467         connect(&proc, SIGNAL(bytesWrittenCommand(int)), SLOT(proc_bytesWrittenCommand(int)));
00468         connect(&proc, SIGNAL(debug(const QString &)), SLOT(proc_debug(const QString &)));
00469         connect(&dtextTimer, SIGNAL(timeout()), SLOT(t_dtext()));
00470 
00471         reset();
00472     }
00473 
00474     ~GpgAction()
00475     {
00476         reset();
00477     }
00478 
00479     void reset()
00480     {
00481         collectOutput = true;
00482         allowInput = false;
00483         readConv.setup(LineConverter::Read);
00484         writeConv.setup(LineConverter::Write);
00485         readText = false;
00486         writeText = false;
00487         useAux = false;
00488         passphraseKeyId = QString();
00489         signing = false;
00490         signPartDone = false;
00491         decryptGood = false;
00492         signGood = false;
00493         curError = GpgOp::ErrorUnknown;
00494         badPassphrase = false;
00495         need_submitPassphrase = false;
00496         need_cardOkay = false;
00497         diagnosticText = QString();
00498         dtextTimer.stop();
00499 
00500         output = Output();
00501 
00502         proc.reset();
00503     }
00504 
00505     void start()
00506     {
00507         reset();
00508 
00509         QStringList args;
00510         bool extra = false;
00511 
00512         if(input.opt_ascii)
00513             args += "--armor";
00514 
00515         if(input.opt_noagent)
00516             args += "--no-use-agent";
00517 
00518         if(input.opt_alwaystrust)
00519             args += "--always-trust";
00520 
00521         if(!input.opt_pubfile.isEmpty() && !input.opt_secfile.isEmpty())
00522         {
00523             args += "--no-default-keyring";
00524             args += "--keyring";
00525             args += input.opt_pubfile;
00526             args += "--secret-keyring";
00527             args += input.opt_secfile;
00528         }
00529 
00530         switch(input.op)
00531         {
00532             case GpgOp::Check:
00533             {
00534                 args += "--version";
00535                 readText = true;
00536                 break;
00537             }
00538             case GpgOp::SecretKeyringFile:
00539             {
00540                 args += "--list-secret-keys";
00541                 readText = true;
00542                 break;
00543             }
00544             case GpgOp::PublicKeyringFile:
00545             {
00546                 args += "--list-public-keys";
00547                 readText = true;
00548                 break;
00549             }
00550             case GpgOp::SecretKeys:
00551             {
00552                 args += "--fixed-list-mode";
00553                 args += "--with-colons";
00554                 args += "--with-fingerprint";
00555                 args += "--with-fingerprint";
00556                 args += "--list-secret-keys";
00557                 readText = true;
00558                 break;
00559             }
00560             case GpgOp::PublicKeys:
00561             {
00562                 args += "--fixed-list-mode";
00563                 args += "--with-colons";
00564                 args += "--with-fingerprint";
00565                 args += "--with-fingerprint";
00566                 args += "--list-public-keys";
00567                 readText = true;
00568                 break;
00569             }
00570             case GpgOp::Encrypt:
00571             {
00572                 args += "--encrypt";
00573 
00574                 // recipients
00575                 for(QStringList::ConstIterator it = input.recip_ids.begin(); it != input.recip_ids.end(); ++it)
00576                 {
00577                     args += "--recipient";
00578                     args += QString("0x") + *it;
00579                 }
00580                 extra = true;
00581                 collectOutput = false;
00582                 allowInput = true;
00583                 if(input.opt_ascii)
00584                     readText = true;
00585                 break;
00586             }
00587             case GpgOp::Decrypt:
00588             {
00589                 args += "--decrypt";
00590                 extra = true;
00591                 collectOutput = false;
00592                 allowInput = true;
00593                 if(input.opt_ascii)
00594                     writeText = true;
00595                 break;
00596             }
00597             case GpgOp::Sign:
00598             {
00599                 args += "--default-key";
00600                 args += QString("0x") + input.signer_id;
00601                 args += "--sign";
00602                 extra = true;
00603                 collectOutput = false;
00604                 allowInput = true;
00605                 if(input.opt_ascii)
00606                     readText = true;
00607                 signing = true;
00608                 break;
00609             }
00610             case GpgOp::SignAndEncrypt:
00611             {
00612                 args += "--default-key";
00613                 args += QString("0x") + input.signer_id;
00614                 args += "--sign";
00615                 args += "--encrypt";
00616 
00617                 // recipients
00618                 for(QStringList::ConstIterator it = input.recip_ids.begin(); it != input.recip_ids.end(); ++it)
00619                 {
00620                     args += "--recipient";
00621                     args += QString("0x") + *it;
00622                 }
00623                 extra = true;
00624                 collectOutput = false;
00625                 allowInput = true;
00626                 if(input.opt_ascii)
00627                     readText = true;
00628                 signing = true;
00629                 break;
00630             }
00631             case GpgOp::SignClearsign:
00632             {
00633                 args += "--default-key";
00634                 args += QString("0x") + input.signer_id;
00635                 args += "--clearsign";
00636                 extra = true;
00637                 collectOutput = false;
00638                 allowInput = true;
00639                 if(input.opt_ascii)
00640                     readText = true;
00641                 signing = true;
00642                 break;
00643             }
00644             case GpgOp::SignDetached:
00645             {
00646                 args += "--default-key";
00647                 args += QString("0x") + input.signer_id;
00648                 args += "--detach-sign";
00649                 extra = true;
00650                 collectOutput = false;
00651                 allowInput = true;
00652                 if(input.opt_ascii)
00653                     readText = true;
00654                 signing = true;
00655                 break;
00656             }
00657             case GpgOp::Verify:
00658             {
00659                 args += "--verify";
00660                 args += "-"; //krazy:exclude=doublequote_chars
00661                 extra = true;
00662                 allowInput = true;
00663                 if(input.opt_ascii)
00664                     writeText = true;
00665                 break;
00666             }
00667             case GpgOp::VerifyDetached:
00668             {
00669                 args += "--verify";
00670                 args += "-"; //krazy:exclude=doublequote_chars
00671                 args += "-&?";
00672                 extra = true;
00673                 allowInput = true;
00674                 useAux = true;
00675                 break;
00676             }
00677             case GpgOp::Import:
00678             {
00679                 args += "--import";
00680                 readText = true;
00681                 if(input.opt_ascii)
00682                     writeText = true;
00683                 break;
00684             }
00685             case GpgOp::Export:
00686             {
00687                 args += "--export";
00688                 args += QString("0x") + input.export_key_id;
00689                 collectOutput = false;
00690                 if(input.opt_ascii)
00691                     readText = true;
00692                 break;
00693             }
00694             case GpgOp::DeleteKey:
00695             {
00696                 args += "--batch";
00697                 args += "--delete-key";
00698                 args += QString("0x") + input.delete_key_fingerprint;
00699                 break;
00700             }
00701         }
00702 
00703 #ifdef GPG_PROFILE
00704         timer.start();
00705         printf("<< launch >>\n");
00706 #endif
00707         proc.start(input.bin, args, extra ? GPGProc::ExtendedMode : GPGProc::NormalMode);
00708 
00709         // detached sig
00710         if(input.op == GpgOp::VerifyDetached)
00711         {
00712             QByteArray a = input.sig;
00713             if(input.opt_ascii)
00714             {
00715                 LineConverter conv;
00716                 conv.setup(LineConverter::Write);
00717                 a = conv.process(a);
00718             }
00719             proc.writeStdin(a);
00720             proc.closeStdin();
00721         }
00722 
00723         // import
00724         if(input.op == GpgOp::Import)
00725         {
00726             QByteArray a = input.inkey;
00727             if(writeText)
00728             {
00729                 LineConverter conv;
00730                 conv.setup(LineConverter::Write);
00731                 a = conv.process(a);
00732             }
00733             proc.writeStdin(a);
00734             proc.closeStdin();
00735         }
00736     }
00737 
00738 #ifdef QPIPE_SECURE
00739     void submitPassphrase(const QCA::SecureArray &a)
00740 #else
00741     void submitPassphrase(const QByteArray &a)
00742 #endif
00743     {
00744         if(!need_submitPassphrase)
00745             return;
00746 
00747         need_submitPassphrase = false;
00748 
00749 #ifdef QPIPE_SECURE
00750         QCA::SecureArray b;
00751 #else
00752         QByteArray b;
00753 #endif
00754         // filter out newlines, since that's the delimiter used
00755         // to indicate a submitted passphrase
00756         b.resize(a.size());
00757         int at = 0;
00758         for(int n = 0; n < a.size(); ++n)
00759         {
00760             if(a[n] != '\n')
00761                 b[at++] = a[n];
00762         }
00763         b.resize(at);
00764 
00765         // append newline
00766         b.resize(b.size() + 1);
00767         b[b.size() - 1] = '\n';
00768         proc.writeCommand(b);
00769     }
00770 
00771 public slots:
00772     QByteArray read()
00773     {
00774         if(collectOutput)
00775             return QByteArray();
00776 
00777         QByteArray a = proc.readStdout();
00778         if(readText)
00779             a = readConv.update(a);
00780         if(!proc.isActive())
00781             a += readConv.final();
00782         return a;
00783     }
00784 
00785     void write(const QByteArray &in)
00786     {
00787         if(!allowInput)
00788             return;
00789 
00790         QByteArray a = in;
00791         if(writeText)
00792             a = writeConv.update(in);
00793 
00794         if(useAux)
00795             proc.writeAux(a);
00796         else
00797             proc.writeStdin(a);
00798     }
00799 
00800     void endWrite()
00801     {
00802         if(!allowInput)
00803             return;
00804 
00805         if(useAux)
00806             proc.closeAux();
00807         else
00808             proc.closeStdin();
00809     }
00810 
00811     void cardOkay()
00812     {
00813         if(need_cardOkay)
00814         {
00815             need_cardOkay = false;
00816             submitCommand("\n");
00817         }
00818     }
00819 
00820     QString readDiagnosticText()
00821     {
00822         QString s = diagnosticText;
00823         diagnosticText = QString();
00824         return s;
00825     }
00826 
00827 signals:
00828     void readyRead();
00829     void bytesWritten(int bytes);
00830     void finished();
00831     void needPassphrase(const QString &keyId);
00832     void needCard();
00833     void readyReadDiagnosticText();
00834 
00835 private:
00836     void submitCommand(const QByteArray &a)
00837     {
00838         proc.writeCommand(a);
00839     }
00840 
00841     // since str is taken as a value, it is ok to use the same variable for 'rest'
00842     QString nextArg(QString str, QString *rest = 0)
00843     {
00844         QString out;
00845         int n = str.indexOf(' ');
00846         if(n == -1)
00847         {
00848             if(rest)
00849                 *rest = QString();
00850             return str;
00851         }
00852         else
00853         {
00854             if(rest)
00855                 *rest = str.mid(n + 1);
00856             return str.mid(0, n);
00857         }
00858     }
00859 
00860     void processStatusLine(const QString &line)
00861     {
00862         diagnosticText += QString("{") + line + "}\n";
00863         ensureDTextEmit();
00864 
00865         if(!proc.isActive())
00866             return;
00867 
00868         QString s, rest;
00869         s = nextArg(line, &rest);
00870 
00871         if(s == "NODATA")
00872         {
00873             // only set this if it'll make it better
00874             if(curError == GpgOp::ErrorUnknown)
00875                 curError = GpgOp::ErrorFormat;
00876         }
00877         else if(s == "UNEXPECTED")
00878         {
00879             if(curError == GpgOp::ErrorUnknown)
00880                 curError = GpgOp::ErrorFormat;
00881         }
00882         else if(s == "KEYEXPIRED")
00883         {
00884             if(curError == GpgOp::ErrorUnknown)
00885             {
00886                 if(input.op == GpgOp::SignAndEncrypt)
00887                 {
00888                     if(!signPartDone)
00889                         curError = GpgOp::ErrorSignerExpired;
00890                     else
00891                         curError = GpgOp::ErrorEncryptExpired;
00892                 }
00893                 else
00894                 {
00895                     if(signing)
00896                         curError = GpgOp::ErrorSignerExpired;
00897                     else
00898                         curError = GpgOp::ErrorEncryptExpired;
00899                 }
00900             }
00901         }
00902         else if(s == "INV_RECP")
00903         {
00904             int r = nextArg(rest).toInt();
00905 
00906             if(curError == GpgOp::ErrorUnknown)
00907             {
00908                 if(r == 10)
00909                     curError = GpgOp::ErrorEncryptUntrusted;
00910                 else
00911                     curError = GpgOp::ErrorEncryptInvalid;
00912             }
00913         }
00914         else if(s == "NO_SECKEY")
00915         {
00916             output.encryptedToId = nextArg(rest);
00917 
00918             if(curError == GpgOp::ErrorUnknown)
00919                 curError = GpgOp::ErrorDecryptNoKey;
00920         }
00921         else if(s == "DECRYPTION_OKAY")
00922         {
00923             decryptGood = true;
00924 
00925             // message could be encrypted with several keys
00926             if(curError == GpgOp::ErrorDecryptNoKey)
00927                 curError = GpgOp::ErrorUnknown;
00928         }
00929         else if(s == "SIG_CREATED")
00930         {
00931             signGood = true;
00932         }
00933         else if(s == "USERID_HINT")
00934         {
00935             passphraseKeyId = nextArg(rest);
00936         }
00937         else if(s == "GET_HIDDEN")
00938         {
00939             QString arg = nextArg(rest);
00940             if(arg == "passphrase.enter" || arg == "passphrase.pin.ask")
00941             {
00942                 need_submitPassphrase = true;
00943 
00944                 // for signal-safety, emit later
00945                 QMetaObject::invokeMethod(this, "needPassphrase", Qt::QueuedConnection, Q_ARG(QString, passphraseKeyId));
00946             }
00947         }
00948         else if(s == "GET_LINE")
00949         {
00950             QString arg = nextArg(rest);
00951             if(arg == "cardctrl.insert_card.okay")
00952             {
00953                 need_cardOkay = true;
00954 
00955                 QMetaObject::invokeMethod(this, "needCard", Qt::QueuedConnection);
00956             }
00957         }
00958         else if(s == "GET_BOOL")
00959         {
00960             QString arg = nextArg(rest);
00961             if(arg == "untrusted_key.override")
00962                 submitCommand("no\n");
00963         }
00964         else if(s == "GOOD_PASSPHRASE")
00965         {
00966             badPassphrase = false;
00967 
00968             // a trick to determine what KEYEXPIRED should apply to
00969             signPartDone = true;
00970         }
00971         else if(s == "BAD_PASSPHRASE")
00972         {
00973             badPassphrase = true;
00974         }
00975         else if(s == "GOODSIG")
00976         {
00977             output.wasSigned = true;
00978             output.signerId = nextArg(rest);
00979             output.verifyResult = GpgOp::VerifyGood;
00980         }
00981         else if(s == "BADSIG")
00982         {
00983             output.wasSigned = true;
00984             output.signerId = nextArg(rest);
00985             output.verifyResult = GpgOp::VerifyBad;
00986         }
00987         else if(s == "ERRSIG")
00988         {
00989             output.wasSigned = true;
00990             QStringList list = rest.split(' ', QString::SkipEmptyParts);
00991             output.signerId = list[0];
00992             output.timestamp = getTimestamp(list[4]);
00993             output.verifyResult = GpgOp::VerifyNoKey;
00994         }
00995         else if(s == "VALIDSIG")
00996         {
00997             QStringList list = rest.split(' ', QString::SkipEmptyParts);
00998             output.timestamp = getTimestamp(list[2]);
00999         }
01000     }
01001 
01002     void processResult(int code)
01003     {
01004 #ifdef GPG_PROFILE
01005         printf("<< launch: %d >>\n", timer.elapsed());
01006 #endif
01007 
01008         // put stdout and stderr into QStrings
01009         QString outstr = QString::fromLatin1(buf_stdout);
01010         QString errstr = QString::fromLatin1(buf_stderr);
01011 
01012         if(collectOutput)
01013             diagnosticText += QString("stdout: [%1]\n").arg(outstr);
01014         diagnosticText += QString("stderr: [%1]\n").arg(errstr);
01015         ensureDTextEmit();
01016 
01017         if(badPassphrase)
01018         {
01019             output.errorCode = GpgOp::ErrorPassphrase;
01020         }
01021         else if(curError != GpgOp::ErrorUnknown)
01022         {
01023             output.errorCode = curError;
01024         }
01025         else if(code == 0)
01026         {
01027             if(input.op == GpgOp::SecretKeyringFile || input.op == GpgOp::PublicKeyringFile)
01028             {
01029                 if(findKeyringFilename(outstr, &output.keyringFile))
01030                     output.success = true;
01031             }
01032             else if(input.op == GpgOp::SecretKeys || input.op == GpgOp::PublicKeys)
01033             {
01034                 if(stringToKeyList(outstr, &output.keys, &output.keyringFile))
01035                     output.success = true;
01036             }
01037             else
01038                 output.success = true;
01039         }
01040         else
01041         {
01042             // decrypt and sign success based on status only.
01043             // this is mainly because gpg uses fatal return
01044             // values if there is trouble with gpg-agent, even
01045             // though the operation otherwise works.
01046 
01047             if(input.op == GpgOp::Decrypt && decryptGood)
01048                 output.success = true;
01049             if(signing && signGood)
01050                 output.success = true;
01051 
01052             // gpg will indicate failure for bad sigs, but we don't
01053             // consider this to be operation failure.
01054 
01055             bool signedMakesItGood = false;
01056             if(input.op == GpgOp::Verify || input.op == GpgOp::VerifyDetached)
01057                 signedMakesItGood = true;
01058 
01059             if(signedMakesItGood && output.wasSigned)
01060                 output.success = true;
01061         }
01062 
01063         emit finished();
01064     }
01065 
01066     void ensureDTextEmit()
01067     {
01068         if(!dtextTimer.isActive())
01069             dtextTimer.start();
01070     }
01071 
01072 private slots:
01073     void t_dtext()
01074     {
01075         emit readyReadDiagnosticText();
01076     }
01077 
01078     void proc_error(gpgQCAPlugin::GPGProc::Error e)
01079     {
01080         QString str;
01081         if(e == GPGProc::FailedToStart)
01082             str = "FailedToStart";
01083         else if(e == GPGProc::UnexpectedExit)
01084             str = "UnexpectedExit";
01085         else if(e == GPGProc::ErrorWrite)
01086             str = "ErrorWrite";
01087 
01088         diagnosticText += QString("GPG Process Error: %1\n").arg(str);
01089         ensureDTextEmit();
01090 
01091         output.errorCode = GpgOp::ErrorProcess;
01092         emit finished();
01093     }
01094 
01095     void proc_finished(int exitCode)
01096     {
01097         diagnosticText += QString("GPG Process Finished: exitStatus=%1\n").arg(exitCode);
01098         ensureDTextEmit();
01099 
01100         processResult(exitCode);
01101     }
01102 
01103     void proc_readyReadStdout()
01104     {
01105         if(collectOutput)
01106         {
01107             QByteArray a = proc.readStdout();
01108             if(readText)
01109                 a = readConv.update(a);
01110             buf_stdout.append(a);
01111         }
01112         else
01113             emit readyRead();
01114     }
01115 
01116     void proc_readyReadStderr()
01117     {
01118         buf_stderr.append(proc.readStderr());
01119     }
01120 
01121     void proc_readyReadStatusLines()
01122     {
01123         QStringList lines = proc.readStatusLines();
01124         for(int n = 0; n < lines.count(); ++n)
01125             processStatusLine(lines[n]);
01126     }
01127 
01128     void proc_bytesWrittenStdin(int bytes)
01129     {
01130         if(!useAux)
01131         {
01132             int actual = writeConv.writtenToActual(bytes);
01133             emit bytesWritten(actual);
01134         }
01135     }
01136 
01137     void proc_bytesWrittenAux(int bytes)
01138     {
01139         if(useAux)
01140         {
01141             int actual = writeConv.writtenToActual(bytes);
01142             emit bytesWritten(actual);
01143         }
01144     }
01145 
01146     void proc_bytesWrittenCommand(int)
01147     {
01148         // don't care about this
01149     }
01150 
01151     void proc_debug(const QString &str)
01152     {
01153         diagnosticText += "GPGProc: " + str + '\n';
01154         ensureDTextEmit();
01155     }
01156 };
01157 
01158 //----------------------------------------------------------------------------
01159 // GpgOp
01160 //----------------------------------------------------------------------------
01161 enum ResetMode
01162 {
01163     ResetSession        = 0,
01164     ResetSessionAndData = 1,
01165     ResetAll            = 2
01166 };
01167 
01168 class GpgOp::Private : public QObject
01169 {
01170     Q_OBJECT
01171 public:
01172     QCA::Synchronizer sync;
01173     GpgOp *q;
01174     GpgAction *act;
01175     QString bin;
01176     GpgOp::Type op;
01177     GpgAction::Output output;
01178     QByteArray result;
01179     QString diagnosticText;
01180     QList<GpgOp::Event> eventList;
01181     bool waiting;
01182 
01183     bool opt_ascii, opt_noagent, opt_alwaystrust;
01184     QString opt_pubfile, opt_secfile;
01185 
01186 #ifdef GPG_PROFILE
01187     QTime timer;
01188 #endif
01189 
01190     Private(GpgOp *_q) : QObject(_q), sync(_q), q(_q)
01191     {
01192         act = 0;
01193         waiting = false;
01194 
01195         reset(ResetAll);
01196     }
01197 
01198     ~Private()
01199     {
01200         reset(ResetAll);
01201     }
01202 
01203     void reset(ResetMode mode)
01204     {
01205         if(act)
01206         {
01207             releaseAndDeleteLater(this, act);
01208             act = 0;
01209         }
01210 
01211         if(mode >= ResetSessionAndData)
01212         {
01213             output = GpgAction::Output();
01214             result.clear();
01215             diagnosticText = QString();
01216             eventList.clear();
01217         }
01218 
01219         if(mode >= ResetAll)
01220         {
01221             opt_ascii = false;
01222             opt_noagent = false;
01223             opt_alwaystrust = false;
01224             opt_pubfile = QString();
01225             opt_secfile = QString();
01226         }
01227     }
01228 
01229     void make_act(GpgOp::Type _op)
01230     {
01231         reset(ResetSessionAndData);
01232 
01233         op = _op;
01234 
01235         act = new GpgAction(this);
01236 
01237         connect(act, SIGNAL(readyRead()), SLOT(act_readyRead()));
01238         connect(act, SIGNAL(bytesWritten(int)), SLOT(act_bytesWritten(int)));
01239         connect(act, SIGNAL(needPassphrase(const QString &)), SLOT(act_needPassphrase(const QString &)));
01240         connect(act, SIGNAL(needCard()), SLOT(act_needCard()));
01241         connect(act, SIGNAL(finished()), SLOT(act_finished()));
01242         connect(act, SIGNAL(readyReadDiagnosticText()), SLOT(act_readyReadDiagnosticText()));
01243 
01244         act->input.bin = bin;
01245         act->input.op = op;
01246         act->input.opt_ascii = opt_ascii;
01247         act->input.opt_noagent = opt_noagent;
01248         act->input.opt_alwaystrust = opt_alwaystrust;
01249         act->input.opt_pubfile = opt_pubfile;
01250         act->input.opt_secfile = opt_secfile;
01251     }
01252 
01253     void eventReady(const GpgOp::Event &e)
01254     {
01255         eventList += e;
01256         sync.conditionMet();
01257     }
01258 
01259     void eventReady(GpgOp::Event::Type type)
01260     {
01261         GpgOp::Event e;
01262         e.type = type;
01263         eventReady(e);
01264     }
01265 
01266     void eventReady(GpgOp::Event::Type type, int written)
01267     {
01268         GpgOp::Event e;
01269         e.type = type;
01270         e.written = written;
01271         eventReady(e);
01272     }
01273 
01274     void eventReady(GpgOp::Event::Type type, const QString &keyId)
01275     {
01276         GpgOp::Event e;
01277         e.type = type;
01278         e.keyId = keyId;
01279         eventReady(e);
01280     }
01281 
01282 public slots:
01283     void act_readyRead()
01284     {
01285         if(waiting)
01286             eventReady(GpgOp::Event::ReadyRead);
01287         else
01288             emit q->readyRead();
01289     }
01290 
01291     void act_bytesWritten(int bytes)
01292     {
01293         if(waiting)
01294             eventReady(GpgOp::Event::BytesWritten, bytes);
01295         else
01296             emit q->bytesWritten(bytes);
01297     }
01298 
01299     void act_needPassphrase(const QString &keyId)
01300     {
01301         if(waiting)
01302             eventReady(GpgOp::Event::NeedPassphrase, keyId);
01303         else
01304             emit q->needPassphrase(keyId);
01305     }
01306 
01307     void act_needCard()
01308     {
01309         if(waiting)
01310             eventReady(GpgOp::Event::NeedCard);
01311         else
01312             emit q->needCard();
01313     }
01314 
01315     void act_readyReadDiagnosticText()
01316     {
01317         QString s = act->readDiagnosticText();
01318         //printf("dtext ready: [%s]\n", qPrintable(s));
01319         diagnosticText += s;
01320 
01321         if(waiting)
01322             eventReady(GpgOp::Event::ReadyReadDiagnosticText);
01323         else
01324             emit q->readyReadDiagnosticText();
01325     }
01326 
01327     void act_finished()
01328     {
01329 #ifdef GPG_PROFILE
01330         if(op == GpgOp::Encrypt)
01331             printf("<< doEncrypt: %d >>\n", timer.elapsed());
01332 #endif
01333 
01334         result = act->read();
01335         diagnosticText += act->readDiagnosticText();
01336         output = act->output;
01337 
01338         QMap<int, QString> errmap;
01339         errmap[GpgOp::ErrorProcess] = "ErrorProcess";
01340         errmap[GpgOp::ErrorPassphrase] = "ErrorPassphrase";
01341         errmap[GpgOp::ErrorFormat] = "ErrorFormat";
01342         errmap[GpgOp::ErrorSignerExpired] = "ErrorSignerExpired";
01343         errmap[GpgOp::ErrorEncryptExpired] = "ErrorEncryptExpired";
01344         errmap[GpgOp::ErrorEncryptUntrusted] = "ErrorEncryptUntrusted";
01345         errmap[GpgOp::ErrorEncryptInvalid] = "ErrorEncryptInvalid";
01346         errmap[GpgOp::ErrorDecryptNoKey] = "ErrorDecryptNoKey";
01347         errmap[GpgOp::ErrorUnknown] = "ErrorUnknown";
01348         if(output.success)
01349             diagnosticText += "GpgAction success\n";
01350         else
01351             diagnosticText += QString("GpgAction error: %1\n").arg(errmap[output.errorCode]);
01352 
01353         if(output.wasSigned)
01354         {
01355             QString s;
01356             if(output.verifyResult == GpgOp::VerifyGood)
01357                 s = "VerifyGood";
01358             else if(output.verifyResult == GpgOp::VerifyBad)
01359                 s = "VerifyBad";
01360             else
01361                 s = "VerifyNoKey";
01362             diagnosticText += QString("wasSigned: verifyResult: %1\n").arg(s);
01363         }
01364 
01365         //printf("diagnosticText:\n%s", qPrintable(diagnosticText));
01366 
01367         reset(ResetSession);
01368 
01369         if(waiting)
01370             eventReady(GpgOp::Event::Finished);
01371         else
01372             emit q->finished();
01373     }
01374 };
01375 
01376 GpgOp::GpgOp(const QString &bin, QObject *parent)
01377 :QObject(parent)
01378 {
01379     d = new Private(this);
01380     d->bin = bin;
01381 }
01382 
01383 GpgOp::~GpgOp()
01384 {
01385     delete d;
01386 }
01387 
01388 void GpgOp::reset()
01389 {
01390     d->reset(ResetAll);
01391 }
01392 
01393 bool GpgOp::isActive() const
01394 {
01395     return (d->act ? true : false);
01396 }
01397 
01398 GpgOp::Type GpgOp::op() const
01399 {
01400     return d->op;
01401 }
01402 
01403 void GpgOp::setAsciiFormat(bool b)
01404 {
01405     d->opt_ascii = b;
01406 }
01407 
01408 void GpgOp::setDisableAgent(bool b)
01409 {
01410     d->opt_noagent = b;
01411 }
01412 
01413 void GpgOp::setAlwaysTrust(bool b)
01414 {
01415     d->opt_alwaystrust = b;
01416 }
01417 
01418 void GpgOp::setKeyrings(const QString &pubfile, const QString &secfile)
01419 {
01420     d->opt_pubfile = pubfile;
01421     d->opt_secfile = secfile;
01422 }
01423 
01424 void GpgOp::doCheck()
01425 {
01426     d->make_act(Check);
01427     d->act->start();
01428 }
01429 
01430 void GpgOp::doSecretKeyringFile()
01431 {
01432     d->make_act(SecretKeyringFile);
01433     d->act->start();
01434 }
01435 
01436 void GpgOp::doPublicKeyringFile()
01437 {
01438     d->make_act(PublicKeyringFile);
01439     d->act->start();
01440 }
01441 
01442 void GpgOp::doSecretKeys()
01443 {
01444     d->make_act(SecretKeys);
01445     d->act->start();
01446 }
01447 
01448 void GpgOp::doPublicKeys()
01449 {
01450     d->make_act(PublicKeys);
01451     d->act->start();
01452 }
01453 
01454 void GpgOp::doEncrypt(const QStringList &recip_ids)
01455 {
01456 #ifdef GPG_PROFILE
01457     d->timer.start();
01458     printf("<< doEncrypt >>\n");
01459 #endif
01460 
01461     d->make_act(Encrypt);
01462     d->act->input.recip_ids = recip_ids;
01463     d->act->start();
01464 }
01465 
01466 void GpgOp::doDecrypt()
01467 {
01468     d->make_act(Decrypt);
01469     d->act->start();
01470 }
01471 
01472 void GpgOp::doSign(const QString &signer_id)
01473 {
01474     d->make_act(Sign);
01475     d->act->input.signer_id = signer_id;
01476     d->act->start();
01477 }
01478 
01479 void GpgOp::doSignAndEncrypt(const QString &signer_id, const QStringList &recip_ids)
01480 {
01481     d->make_act(SignAndEncrypt);
01482     d->act->input.signer_id = signer_id;
01483     d->act->input.recip_ids = recip_ids;
01484     d->act->start();
01485 }
01486 
01487 void GpgOp::doSignClearsign(const QString &signer_id)
01488 {
01489     d->make_act(SignClearsign);
01490     d->act->input.signer_id = signer_id;
01491     d->act->start();
01492 }
01493 
01494 void GpgOp::doSignDetached(const QString &signer_id)
01495 {
01496     d->make_act(SignDetached);
01497     d->act->input.signer_id = signer_id;
01498     d->act->start();
01499 }
01500 
01501 void GpgOp::doVerify()
01502 {
01503     d->make_act(Verify);
01504     d->act->start();
01505 }
01506 
01507 void GpgOp::doVerifyDetached(const QByteArray &sig)
01508 {
01509     d->make_act(VerifyDetached);
01510     d->act->input.sig = sig;
01511     d->act->start();
01512 }
01513 
01514 void GpgOp::doImport(const QByteArray &in)
01515 {
01516     d->make_act(Import);
01517     d->act->input.inkey = in;
01518     d->act->start();
01519 }
01520 
01521 void GpgOp::doExport(const QString &key_id)
01522 {
01523     d->make_act(Export);
01524     d->act->input.export_key_id = key_id;
01525     d->act->start();
01526 }
01527 
01528 void GpgOp::doDeleteKey(const QString &key_fingerprint)
01529 {
01530     d->make_act(DeleteKey);
01531     d->act->input.delete_key_fingerprint = key_fingerprint;
01532     d->act->start();
01533 }
01534 
01535 #ifdef QPIPE_SECURE
01536 void GpgOp::submitPassphrase(const QCA::SecureArray &a)
01537 #else
01538 void GpgOp::submitPassphrase(const QByteArray &a)
01539 #endif
01540 {
01541     d->act->submitPassphrase(a);
01542 }
01543 
01544 void GpgOp::cardOkay()
01545 {
01546     d->act->cardOkay();
01547 }
01548 
01549 QByteArray GpgOp::read()
01550 {
01551     if(d->act)
01552     {
01553         return d->act->read();
01554     }
01555     else
01556     {
01557         QByteArray a = d->result;
01558         d->result.clear();
01559         return a;
01560     }
01561 }
01562 
01563 void GpgOp::write(const QByteArray &in)
01564 {
01565     d->act->write(in);
01566 }
01567 
01568 void GpgOp::endWrite()
01569 {
01570     d->act->endWrite();
01571 }
01572 
01573 QString GpgOp::readDiagnosticText()
01574 {
01575     QString s = d->diagnosticText;
01576     d->diagnosticText = QString();
01577     return s;
01578 }
01579 
01580 GpgOp::Event GpgOp::waitForEvent(int msecs)
01581 {
01582     if(!d->eventList.isEmpty())
01583         return d->eventList.takeFirst();
01584 
01585     if(!d->act)
01586         return GpgOp::Event();
01587 
01588     d->waiting = true;
01589     d->sync.waitForCondition(msecs);
01590     d->waiting = false;
01591     return d->eventList.takeFirst();
01592 }
01593 
01594 bool GpgOp::success() const
01595 {
01596     return d->output.success;
01597 }
01598 
01599 GpgOp::Error GpgOp::errorCode() const
01600 {
01601     return d->output.errorCode;
01602 }
01603 
01604 GpgOp::KeyList GpgOp::keys() const
01605 {
01606     return d->output.keys;
01607 }
01608 
01609 QString GpgOp::keyringFile() const
01610 {
01611     return d->output.keyringFile;
01612 }
01613 
01614 QString GpgOp::encryptedToId() const
01615 {
01616     return d->output.encryptedToId;
01617 }
01618 
01619 bool GpgOp::wasSigned() const
01620 {
01621     return d->output.wasSigned;
01622 }
01623 
01624 QString GpgOp::signerId() const
01625 {
01626     return d->output.signerId;
01627 }
01628 
01629 QDateTime GpgOp::timestamp() const
01630 {
01631     return d->output.timestamp;
01632 }
01633 
01634 GpgOp::VerifyResult GpgOp::verifyResult() const
01635 {
01636     return d->output.verifyResult;
01637 }
01638 
01639 }
01640 
01641 #include "gpgop.moc"

qca

Skip menu "qca"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE Support

Skip menu "KDE Support"
  • akonadi
  • Decibel
  • grantlee
  • kdewin
  • phonon
  •     Backend
  • polkit-qt
  • qca
  • qimageblitz
  • soprano
  • strigi
  •     searchclient
  •     streamanalyzer
  •     streams
Generated for KDE Support by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal