2005-07-06 23:25:51 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2007-01-05 06:45:57 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2005-07-06 23:25:51 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gpgop.h"
|
|
|
|
|
|
|
|
#include "gpgproc.h"
|
2014-09-25 01:03:18 +06:00
|
|
|
#include "qca_safetimer.h"
|
2005-07-06 23:25:51 +00:00
|
|
|
|
2007-04-17 12:03:37 +00:00
|
|
|
#include <QTimer>
|
|
|
|
|
2005-07-06 23:25:51 +00:00
|
|
|
namespace gpgQCAPlugin {
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// LineConverter
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class LineConverter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum Mode { Read, Write };
|
|
|
|
|
|
|
|
void setup(Mode m)
|
|
|
|
{
|
|
|
|
state = Normal;
|
|
|
|
mode = m;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
write_conv = true;
|
|
|
|
#else
|
|
|
|
write_conv = false;
|
|
|
|
#endif
|
|
|
|
prebytes = 0;
|
|
|
|
list.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray update(const QByteArray &buf)
|
|
|
|
{
|
|
|
|
if(mode == Read)
|
|
|
|
{
|
|
|
|
QByteArray out;
|
|
|
|
|
|
|
|
if(state == Normal)
|
|
|
|
{
|
|
|
|
out = buf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out.resize(buf.size() + 1);
|
|
|
|
out[0] = '\r';
|
|
|
|
memcpy(out.data() + 1, buf.data(), buf.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
n = out.indexOf('\r', n);
|
|
|
|
// not found
|
|
|
|
if(n == -1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// found, not last character
|
|
|
|
if(n < (buf.size() - 1))
|
|
|
|
{
|
|
|
|
if(out[n + 1] == '\n')
|
|
|
|
{
|
|
|
|
// clip out the '\r'
|
|
|
|
memmove(out.data() + n, out.data() + n + 1, out.size() - n - 1);
|
|
|
|
out.resize(out.size() - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// found, last character
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state = Partial;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(write_conv)
|
|
|
|
{
|
|
|
|
QByteArray out;
|
|
|
|
int prev = 0;
|
|
|
|
int at = 0;
|
|
|
|
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
int n = buf.indexOf('\n', at);
|
|
|
|
if(n == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
int chunksize = n - at;
|
|
|
|
int oldsize = out.size();
|
|
|
|
out.resize(oldsize + chunksize + 2);
|
|
|
|
memcpy(out.data() + oldsize, buf.data() + at, chunksize);
|
|
|
|
memcpy(out.data() + oldsize + chunksize, "\r\n", 2);
|
|
|
|
|
|
|
|
list.append(prebytes + n + 1 - prev);
|
|
|
|
prebytes = 0;
|
|
|
|
prev = n;
|
|
|
|
|
|
|
|
at = n + 1;
|
|
|
|
}
|
|
|
|
if(at < buf.size())
|
|
|
|
{
|
|
|
|
int chunksize = buf.size() - at;
|
|
|
|
int oldsize = out.size();
|
|
|
|
out.resize(oldsize + chunksize);
|
|
|
|
memcpy(out.data() + oldsize, buf.data() + at, chunksize);
|
|
|
|
}
|
|
|
|
|
|
|
|
prebytes += buf.size() - prev;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray final()
|
|
|
|
{
|
|
|
|
if(mode == Read)
|
|
|
|
{
|
|
|
|
QByteArray out;
|
|
|
|
if(state == Partial)
|
|
|
|
{
|
|
|
|
out.resize(1);
|
|
|
|
out[0] = '\r';
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray process(const QByteArray &buf)
|
|
|
|
{
|
|
|
|
return update(buf) + final();
|
|
|
|
}
|
|
|
|
|
|
|
|
int writtenToActual(int bytes)
|
|
|
|
{
|
|
|
|
if(write_conv)
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
int counter = bytes;
|
|
|
|
while(counter > 0)
|
|
|
|
{
|
|
|
|
if(!list.isEmpty() && bytes >= list.first())
|
|
|
|
{
|
|
|
|
++n;
|
|
|
|
counter -= list.takeFirst();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(list.isEmpty())
|
|
|
|
prebytes -= counter;
|
|
|
|
else
|
|
|
|
list.first() -= counter;
|
|
|
|
|
|
|
|
if(prebytes < 0)
|
|
|
|
{
|
|
|
|
bytes += prebytes;
|
|
|
|
prebytes = 0;
|
|
|
|
}
|
2007-04-02 11:47:31 +00:00
|
|
|
|
2005-07-06 23:25:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bytes - n;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum State { Normal, Partial };
|
|
|
|
Mode mode;
|
|
|
|
State state;
|
|
|
|
bool write_conv;
|
|
|
|
public:
|
|
|
|
int prebytes;
|
|
|
|
QList<int> list;
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// GpgAction
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
static QDateTime getTimestamp(const QString &s)
|
|
|
|
{
|
|
|
|
if(s.isEmpty())
|
|
|
|
return QDateTime();
|
|
|
|
|
|
|
|
if(s.contains('T'))
|
|
|
|
{
|
|
|
|
return QDateTime::fromString(s, Qt::ISODate);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QDateTime dt;
|
|
|
|
dt.setTime_t(s.toInt());
|
|
|
|
return dt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static QByteArray getCString(const QByteArray &a)
|
|
|
|
{
|
|
|
|
QByteArray out;
|
|
|
|
|
|
|
|
// convert the "backslash" C-string syntax
|
|
|
|
for(int n = 0; n < a.size(); ++n)
|
|
|
|
{
|
|
|
|
if(a[n] == '\\' && n + 1 < a.size())
|
|
|
|
{
|
|
|
|
++n;
|
|
|
|
unsigned char c = (unsigned char)a[n];
|
|
|
|
if(c == '\\')
|
|
|
|
{
|
|
|
|
out += '\\';
|
|
|
|
}
|
|
|
|
else if(c == 'x' && n + 2 < a.size())
|
|
|
|
{
|
|
|
|
++n;
|
|
|
|
QByteArray hex = a.mid(n, 2);
|
|
|
|
++n; // only skip one, loop will skip the next
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
uint val = hex.toInt(&ok, 16);
|
|
|
|
if(ok)
|
|
|
|
{
|
|
|
|
out += (unsigned char)val;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out += "\\x";
|
|
|
|
out += hex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out += a[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool stringToKeyList(const QString &outstr, GpgOp::KeyList *_keylist, QString *_keyring)
|
|
|
|
{
|
|
|
|
GpgOp::KeyList keyList;
|
|
|
|
QStringList lines = outstr.split('\n');
|
|
|
|
|
|
|
|
if(lines.count() < 1)
|
|
|
|
return false;
|
|
|
|
|
2009-08-31 09:31:02 +00:00
|
|
|
QStringList::ConstIterator it = lines.constBegin();
|
2005-07-06 23:25:51 +00:00
|
|
|
|
|
|
|
// first line is keyring file
|
|
|
|
QString keyring = *(it++);
|
|
|
|
|
|
|
|
// if the second line isn't a divider, we are dealing
|
|
|
|
// with a new version of gnupg that doesn't give us
|
|
|
|
// the keyring file on gpg --list-keys --with-colons
|
2009-08-31 09:31:02 +00:00
|
|
|
if(it == lines.constEnd() || (*it).isEmpty() || (*it).at(0) != '-')
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
// first line wasn't the keyring name...
|
2007-04-02 11:47:31 +00:00
|
|
|
keyring.clear();
|
2005-07-06 23:25:51 +00:00
|
|
|
// ...so read the first line again
|
|
|
|
it--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// this was the divider line - skip it
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
|
2009-08-31 09:31:02 +00:00
|
|
|
for(; it != lines.constEnd(); ++it)
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
QStringList f = (*it).split(':');
|
|
|
|
if(f.count() < 1)
|
|
|
|
continue;
|
|
|
|
QString type = f[0];
|
|
|
|
|
|
|
|
bool key = false; // key or not
|
|
|
|
bool primary = false; // primary key or sub key
|
2013-07-24 23:52:37 +06:00
|
|
|
// bool sec = false; // private key or not
|
2005-07-06 23:25:51 +00:00
|
|
|
|
|
|
|
if(type == "pub")
|
|
|
|
{
|
|
|
|
key = true;
|
|
|
|
primary = true;
|
|
|
|
}
|
|
|
|
else if(type == "sec")
|
|
|
|
{
|
|
|
|
key = true;
|
|
|
|
primary = true;
|
2013-07-24 23:52:37 +06:00
|
|
|
// sec = true;
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
else if(type == "sub")
|
|
|
|
{
|
|
|
|
key = true;
|
|
|
|
}
|
|
|
|
else if(type == "ssb")
|
|
|
|
{
|
|
|
|
key = true;
|
2013-07-24 23:52:37 +06:00
|
|
|
// sec = true;
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(key)
|
|
|
|
{
|
|
|
|
if(primary)
|
|
|
|
{
|
|
|
|
keyList += GpgOp::Key();
|
|
|
|
|
|
|
|
QString trust = f[1];
|
|
|
|
if(trust == "f" || trust == "u")
|
|
|
|
keyList.last().isTrusted = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int key_type = f[3].toInt();
|
|
|
|
QString caps = f[11];
|
|
|
|
|
|
|
|
GpgOp::KeyItem item;
|
|
|
|
item.bits = f[2].toInt();
|
|
|
|
if(key_type == 1)
|
|
|
|
item.type = GpgOp::KeyItem::RSA;
|
|
|
|
else if(key_type == 16)
|
|
|
|
item.type = GpgOp::KeyItem::ElGamal;
|
|
|
|
else if(key_type == 17)
|
|
|
|
item.type = GpgOp::KeyItem::DSA;
|
|
|
|
else
|
|
|
|
item.type = GpgOp::KeyItem::Unknown;
|
|
|
|
item.id = f[4];
|
|
|
|
item.creationDate = getTimestamp(f[5]);
|
|
|
|
item.expirationDate = getTimestamp(f[6]);
|
|
|
|
if(caps.contains('e'))
|
|
|
|
item.caps |= GpgOp::KeyItem::Encrypt;
|
|
|
|
if(caps.contains('s'))
|
|
|
|
item.caps |= GpgOp::KeyItem::Sign;
|
|
|
|
if(caps.contains('c'))
|
|
|
|
item.caps |= GpgOp::KeyItem::Certify;
|
|
|
|
if(caps.contains('a'))
|
|
|
|
item.caps |= GpgOp::KeyItem::Auth;
|
|
|
|
|
|
|
|
keyList.last().keyItems += item;
|
|
|
|
}
|
|
|
|
else if(type == "uid")
|
|
|
|
{
|
2013-06-14 16:16:45 +00:00
|
|
|
QByteArray uid = getCString(f[9].toUtf8());
|
2005-07-06 23:25:51 +00:00
|
|
|
keyList.last().userIds.append(QString::fromUtf8(uid));
|
|
|
|
}
|
|
|
|
else if(type == "fpr")
|
|
|
|
{
|
|
|
|
QString s = f[9];
|
|
|
|
keyList.last().keyItems.last().fingerprint = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_keylist)
|
|
|
|
*_keylist = keyList;
|
|
|
|
if(_keyring)
|
|
|
|
*_keyring = keyring;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool findKeyringFilename(const QString &outstr, QString *_keyring)
|
|
|
|
{
|
|
|
|
QStringList lines = outstr.split('\n');
|
|
|
|
if(lines.count() < 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*_keyring = lines[0];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class GpgAction : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
class Input
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QString bin;
|
|
|
|
GpgOp::Type op;
|
|
|
|
bool opt_ascii, opt_noagent, opt_alwaystrust;
|
|
|
|
QString opt_pubfile, opt_secfile;
|
|
|
|
QStringList recip_ids;
|
|
|
|
QString signer_id;
|
|
|
|
QByteArray sig;
|
|
|
|
QByteArray inkey;
|
|
|
|
QString export_key_id;
|
2008-04-08 03:01:19 +00:00
|
|
|
QString delete_key_fingerprint;
|
2005-07-06 23:25:51 +00:00
|
|
|
|
|
|
|
Input() : opt_ascii(false), opt_noagent(false), opt_alwaystrust(false) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Output
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool success;
|
|
|
|
GpgOp::Error errorCode;
|
|
|
|
GpgOp::KeyList keys;
|
|
|
|
QString keyringFile;
|
|
|
|
QString encryptedToId;
|
|
|
|
bool wasSigned;
|
|
|
|
QString signerId;
|
|
|
|
QDateTime timestamp;
|
|
|
|
GpgOp::VerifyResult verifyResult;
|
|
|
|
|
|
|
|
Output() : success(false), errorCode(GpgOp::ErrorUnknown), wasSigned(false) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
Input input;
|
|
|
|
Output output;
|
|
|
|
|
|
|
|
GPGProc proc;
|
|
|
|
bool collectOutput, allowInput;
|
|
|
|
LineConverter readConv, writeConv;
|
|
|
|
bool readText, writeText;
|
|
|
|
QByteArray buf_stdout, buf_stderr;
|
|
|
|
bool useAux;
|
|
|
|
QString passphraseKeyId;
|
2014-06-01 13:47:11 +02:00
|
|
|
bool signing, decryptGood, signGood;
|
2005-07-06 23:25:51 +00:00
|
|
|
GpgOp::Error curError;
|
|
|
|
bool badPassphrase;
|
|
|
|
bool need_submitPassphrase, need_cardOkay;
|
|
|
|
QString diagnosticText;
|
2014-09-25 01:03:18 +06:00
|
|
|
QCA::SafeTimer dtextTimer;
|
2005-07-06 23:25:51 +00:00
|
|
|
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
QTime timer;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
GpgAction(QObject *parent = 0) : QObject(parent), proc(this), dtextTimer(this)
|
|
|
|
{
|
|
|
|
dtextTimer.setSingleShot(true);
|
|
|
|
|
2006-03-30 09:41:01 +00:00
|
|
|
connect(&proc, SIGNAL(error(gpgQCAPlugin::GPGProc::Error)), SLOT(proc_error(gpgQCAPlugin::GPGProc::Error)));
|
2005-07-06 23:25:51 +00:00
|
|
|
connect(&proc, SIGNAL(finished(int)), SLOT(proc_finished(int)));
|
|
|
|
connect(&proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout()));
|
|
|
|
connect(&proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr()));
|
|
|
|
connect(&proc, SIGNAL(readyReadStatusLines()), SLOT(proc_readyReadStatusLines()));
|
|
|
|
connect(&proc, SIGNAL(bytesWrittenStdin(int)), SLOT(proc_bytesWrittenStdin(int)));
|
|
|
|
connect(&proc, SIGNAL(bytesWrittenAux(int)), SLOT(proc_bytesWrittenAux(int)));
|
|
|
|
connect(&proc, SIGNAL(bytesWrittenCommand(int)), SLOT(proc_bytesWrittenCommand(int)));
|
|
|
|
connect(&proc, SIGNAL(debug(const QString &)), SLOT(proc_debug(const QString &)));
|
|
|
|
connect(&dtextTimer, SIGNAL(timeout()), SLOT(t_dtext()));
|
|
|
|
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
~GpgAction()
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
collectOutput = true;
|
|
|
|
allowInput = false;
|
|
|
|
readConv.setup(LineConverter::Read);
|
|
|
|
writeConv.setup(LineConverter::Write);
|
|
|
|
readText = false;
|
|
|
|
writeText = false;
|
|
|
|
useAux = false;
|
|
|
|
passphraseKeyId = QString();
|
|
|
|
signing = false;
|
|
|
|
decryptGood = false;
|
|
|
|
signGood = false;
|
|
|
|
curError = GpgOp::ErrorUnknown;
|
|
|
|
badPassphrase = false;
|
|
|
|
need_submitPassphrase = false;
|
|
|
|
need_cardOkay = false;
|
|
|
|
diagnosticText = QString();
|
|
|
|
dtextTimer.stop();
|
|
|
|
|
|
|
|
output = Output();
|
|
|
|
|
|
|
|
proc.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void start()
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
|
|
|
|
QStringList args;
|
|
|
|
bool extra = false;
|
|
|
|
|
|
|
|
if(input.opt_ascii)
|
|
|
|
args += "--armor";
|
|
|
|
|
|
|
|
if(input.opt_noagent)
|
|
|
|
args += "--no-use-agent";
|
|
|
|
|
|
|
|
if(input.opt_alwaystrust)
|
|
|
|
args += "--always-trust";
|
|
|
|
|
|
|
|
if(!input.opt_pubfile.isEmpty() && !input.opt_secfile.isEmpty())
|
|
|
|
{
|
|
|
|
args += "--no-default-keyring";
|
|
|
|
args += "--keyring";
|
|
|
|
args += input.opt_pubfile;
|
|
|
|
args += "--secret-keyring";
|
|
|
|
args += input.opt_secfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(input.op)
|
|
|
|
{
|
|
|
|
case GpgOp::Check:
|
|
|
|
{
|
|
|
|
args += "--version";
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::SecretKeyringFile:
|
|
|
|
{
|
|
|
|
args += "--list-secret-keys";
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::PublicKeyringFile:
|
|
|
|
{
|
|
|
|
args += "--list-public-keys";
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::SecretKeys:
|
|
|
|
{
|
|
|
|
args += "--fixed-list-mode";
|
|
|
|
args += "--with-colons";
|
|
|
|
args += "--with-fingerprint";
|
|
|
|
args += "--with-fingerprint";
|
|
|
|
args += "--list-secret-keys";
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::PublicKeys:
|
|
|
|
{
|
|
|
|
args += "--fixed-list-mode";
|
|
|
|
args += "--with-colons";
|
|
|
|
args += "--with-fingerprint";
|
|
|
|
args += "--with-fingerprint";
|
|
|
|
args += "--list-public-keys";
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Encrypt:
|
|
|
|
{
|
|
|
|
args += "--encrypt";
|
|
|
|
|
|
|
|
// recipients
|
2009-08-31 09:31:02 +00:00
|
|
|
for(QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it)
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
args += "--recipient";
|
|
|
|
args += QString("0x") + *it;
|
|
|
|
}
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Decrypt:
|
|
|
|
{
|
|
|
|
args += "--decrypt";
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
writeText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Sign:
|
|
|
|
{
|
|
|
|
args += "--default-key";
|
|
|
|
args += QString("0x") + input.signer_id;
|
|
|
|
args += "--sign";
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
signing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::SignAndEncrypt:
|
|
|
|
{
|
|
|
|
args += "--default-key";
|
|
|
|
args += QString("0x") + input.signer_id;
|
|
|
|
args += "--sign";
|
|
|
|
args += "--encrypt";
|
|
|
|
|
|
|
|
// recipients
|
2009-08-31 09:31:02 +00:00
|
|
|
for(QStringList::ConstIterator it = input.recip_ids.constBegin(); it != input.recip_ids.constEnd(); ++it)
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
args += "--recipient";
|
|
|
|
args += QString("0x") + *it;
|
|
|
|
}
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
signing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::SignClearsign:
|
|
|
|
{
|
|
|
|
args += "--default-key";
|
|
|
|
args += QString("0x") + input.signer_id;
|
|
|
|
args += "--clearsign";
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
signing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::SignDetached:
|
|
|
|
{
|
|
|
|
args += "--default-key";
|
|
|
|
args += QString("0x") + input.signer_id;
|
|
|
|
args += "--detach-sign";
|
|
|
|
extra = true;
|
|
|
|
collectOutput = false;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
signing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Verify:
|
|
|
|
{
|
|
|
|
args += "--verify";
|
2007-01-05 06:45:57 +00:00
|
|
|
args += "-"; //krazy:exclude=doublequote_chars
|
2005-07-06 23:25:51 +00:00
|
|
|
extra = true;
|
|
|
|
allowInput = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
writeText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::VerifyDetached:
|
|
|
|
{
|
|
|
|
args += "--verify";
|
2007-01-05 06:45:57 +00:00
|
|
|
args += "-"; //krazy:exclude=doublequote_chars
|
2005-07-06 23:25:51 +00:00
|
|
|
args += "-&?";
|
|
|
|
extra = true;
|
|
|
|
allowInput = true;
|
|
|
|
useAux = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Import:
|
|
|
|
{
|
|
|
|
args += "--import";
|
|
|
|
readText = true;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
writeText = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GpgOp::Export:
|
|
|
|
{
|
|
|
|
args += "--export";
|
|
|
|
args += QString("0x") + input.export_key_id;
|
2007-04-01 01:38:21 +00:00
|
|
|
collectOutput = false;
|
2005-07-06 23:25:51 +00:00
|
|
|
if(input.opt_ascii)
|
|
|
|
readText = true;
|
|
|
|
break;
|
|
|
|
}
|
2008-04-08 03:01:19 +00:00
|
|
|
case GpgOp::DeleteKey:
|
|
|
|
{
|
|
|
|
args += "--batch";
|
|
|
|
args += "--delete-key";
|
|
|
|
args += QString("0x") + input.delete_key_fingerprint;
|
|
|
|
break;
|
|
|
|
}
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
timer.start();
|
|
|
|
printf("<< launch >>\n");
|
|
|
|
#endif
|
|
|
|
proc.start(input.bin, args, extra ? GPGProc::ExtendedMode : GPGProc::NormalMode);
|
|
|
|
|
|
|
|
// detached sig
|
|
|
|
if(input.op == GpgOp::VerifyDetached)
|
|
|
|
{
|
|
|
|
QByteArray a = input.sig;
|
|
|
|
if(input.opt_ascii)
|
|
|
|
{
|
|
|
|
LineConverter conv;
|
|
|
|
conv.setup(LineConverter::Write);
|
|
|
|
a = conv.process(a);
|
|
|
|
}
|
|
|
|
proc.writeStdin(a);
|
|
|
|
proc.closeStdin();
|
|
|
|
}
|
|
|
|
|
|
|
|
// import
|
|
|
|
if(input.op == GpgOp::Import)
|
|
|
|
{
|
|
|
|
QByteArray a = input.inkey;
|
|
|
|
if(writeText)
|
|
|
|
{
|
|
|
|
LineConverter conv;
|
|
|
|
conv.setup(LineConverter::Write);
|
|
|
|
a = conv.process(a);
|
|
|
|
}
|
|
|
|
proc.writeStdin(a);
|
|
|
|
proc.closeStdin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef QPIPE_SECURE
|
2007-04-14 01:16:13 +00:00
|
|
|
void submitPassphrase(const QCA::SecureArray &a)
|
2005-07-06 23:25:51 +00:00
|
|
|
#else
|
|
|
|
void submitPassphrase(const QByteArray &a)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if(!need_submitPassphrase)
|
|
|
|
return;
|
|
|
|
|
|
|
|
need_submitPassphrase = false;
|
|
|
|
|
|
|
|
#ifdef QPIPE_SECURE
|
2007-04-24 21:00:11 +00:00
|
|
|
QCA::SecureArray b;
|
2005-07-06 23:25:51 +00:00
|
|
|
#else
|
2007-04-24 21:00:11 +00:00
|
|
|
QByteArray b;
|
2005-07-06 23:25:51 +00:00
|
|
|
#endif
|
2007-04-24 21:00:11 +00:00
|
|
|
// filter out newlines, since that's the delimiter used
|
|
|
|
// to indicate a submitted passphrase
|
|
|
|
b.resize(a.size());
|
|
|
|
int at = 0;
|
|
|
|
for(int n = 0; n < a.size(); ++n)
|
|
|
|
{
|
|
|
|
if(a[n] != '\n')
|
|
|
|
b[at++] = a[n];
|
|
|
|
}
|
|
|
|
b.resize(at);
|
|
|
|
|
|
|
|
// append newline
|
2005-07-06 23:25:51 +00:00
|
|
|
b.resize(b.size() + 1);
|
|
|
|
b[b.size() - 1] = '\n';
|
|
|
|
proc.writeCommand(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
QByteArray read()
|
|
|
|
{
|
|
|
|
if(collectOutput)
|
|
|
|
return QByteArray();
|
|
|
|
|
|
|
|
QByteArray a = proc.readStdout();
|
|
|
|
if(readText)
|
|
|
|
a = readConv.update(a);
|
|
|
|
if(!proc.isActive())
|
|
|
|
a += readConv.final();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(const QByteArray &in)
|
|
|
|
{
|
|
|
|
if(!allowInput)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QByteArray a = in;
|
|
|
|
if(writeText)
|
|
|
|
a = writeConv.update(in);
|
|
|
|
|
|
|
|
if(useAux)
|
|
|
|
proc.writeAux(a);
|
|
|
|
else
|
|
|
|
proc.writeStdin(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void endWrite()
|
|
|
|
{
|
|
|
|
if(!allowInput)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(useAux)
|
|
|
|
proc.closeAux();
|
|
|
|
else
|
|
|
|
proc.closeStdin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cardOkay()
|
|
|
|
{
|
|
|
|
if(need_cardOkay)
|
|
|
|
{
|
|
|
|
need_cardOkay = false;
|
|
|
|
submitCommand("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString readDiagnosticText()
|
|
|
|
{
|
|
|
|
QString s = diagnosticText;
|
|
|
|
diagnosticText = QString();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void readyRead();
|
|
|
|
void bytesWritten(int bytes);
|
|
|
|
void finished();
|
|
|
|
void needPassphrase(const QString &keyId);
|
|
|
|
void needCard();
|
|
|
|
void readyReadDiagnosticText();
|
|
|
|
|
|
|
|
private:
|
|
|
|
void submitCommand(const QByteArray &a)
|
|
|
|
{
|
|
|
|
proc.writeCommand(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
// since str is taken as a value, it is ok to use the same variable for 'rest'
|
|
|
|
QString nextArg(QString str, QString *rest = 0)
|
|
|
|
{
|
|
|
|
QString out;
|
|
|
|
int n = str.indexOf(' ');
|
|
|
|
if(n == -1)
|
|
|
|
{
|
|
|
|
if(rest)
|
|
|
|
*rest = QString();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(rest)
|
|
|
|
*rest = str.mid(n + 1);
|
|
|
|
return str.mid(0, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void processStatusLine(const QString &line)
|
|
|
|
{
|
|
|
|
diagnosticText += QString("{") + line + "}\n";
|
|
|
|
ensureDTextEmit();
|
|
|
|
|
|
|
|
if(!proc.isActive())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QString s, rest;
|
|
|
|
s = nextArg(line, &rest);
|
|
|
|
|
|
|
|
if(s == "NODATA")
|
|
|
|
{
|
|
|
|
// only set this if it'll make it better
|
|
|
|
if(curError == GpgOp::ErrorUnknown)
|
|
|
|
curError = GpgOp::ErrorFormat;
|
|
|
|
}
|
|
|
|
else if(s == "UNEXPECTED")
|
|
|
|
{
|
|
|
|
if(curError == GpgOp::ErrorUnknown)
|
|
|
|
curError = GpgOp::ErrorFormat;
|
|
|
|
}
|
2014-06-01 13:47:11 +02:00
|
|
|
else if(s == "EXPKEYSIG")
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
2014-06-01 13:47:11 +02:00
|
|
|
curError = GpgOp::ErrorSignerExpired;
|
|
|
|
}
|
|
|
|
else if(s == "REVKEYSIG")
|
|
|
|
{
|
|
|
|
curError = GpgOp::ErrorSignerRevoked;
|
|
|
|
}
|
|
|
|
else if(s == "EXPSIG")
|
|
|
|
{
|
|
|
|
curError = GpgOp::ErrorSignatureExpired;
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
else if(s == "INV_RECP")
|
|
|
|
{
|
|
|
|
int r = nextArg(rest).toInt();
|
|
|
|
|
|
|
|
if(curError == GpgOp::ErrorUnknown)
|
|
|
|
{
|
|
|
|
if(r == 10)
|
|
|
|
curError = GpgOp::ErrorEncryptUntrusted;
|
2014-06-01 13:47:11 +02:00
|
|
|
else if(r == 4)
|
|
|
|
curError = GpgOp::ErrorEncryptRevoked;
|
|
|
|
else if(r == 5)
|
|
|
|
curError = GpgOp::ErrorEncryptExpired;
|
2005-07-06 23:25:51 +00:00
|
|
|
else
|
2014-06-01 13:47:11 +02:00
|
|
|
// due to GnuPG bug #1650
|
|
|
|
// <https://bugs.g10code.com/gnupg/issue1650>
|
|
|
|
// encrypting to expired and revoked keys will
|
|
|
|
// not specify any reason for failing,
|
|
|
|
// defaulting to this
|
2005-07-06 23:25:51 +00:00
|
|
|
curError = GpgOp::ErrorEncryptInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(s == "NO_SECKEY")
|
|
|
|
{
|
|
|
|
output.encryptedToId = nextArg(rest);
|
|
|
|
|
|
|
|
if(curError == GpgOp::ErrorUnknown)
|
|
|
|
curError = GpgOp::ErrorDecryptNoKey;
|
|
|
|
}
|
|
|
|
else if(s == "DECRYPTION_OKAY")
|
|
|
|
{
|
|
|
|
decryptGood = true;
|
2007-04-02 20:12:56 +00:00
|
|
|
|
|
|
|
// message could be encrypted with several keys
|
|
|
|
if(curError == GpgOp::ErrorDecryptNoKey)
|
|
|
|
curError = GpgOp::ErrorUnknown;
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
else if(s == "SIG_CREATED")
|
|
|
|
{
|
|
|
|
signGood = true;
|
|
|
|
}
|
|
|
|
else if(s == "USERID_HINT")
|
|
|
|
{
|
|
|
|
passphraseKeyId = nextArg(rest);
|
|
|
|
}
|
|
|
|
else if(s == "GET_HIDDEN")
|
|
|
|
{
|
|
|
|
QString arg = nextArg(rest);
|
2008-07-26 18:29:27 +00:00
|
|
|
if(arg == "passphrase.enter" || arg == "passphrase.pin.ask")
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
need_submitPassphrase = true;
|
|
|
|
|
|
|
|
// for signal-safety, emit later
|
|
|
|
QMetaObject::invokeMethod(this, "needPassphrase", Qt::QueuedConnection, Q_ARG(QString, passphraseKeyId));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(s == "GET_LINE")
|
|
|
|
{
|
|
|
|
QString arg = nextArg(rest);
|
|
|
|
if(arg == "cardctrl.insert_card.okay")
|
|
|
|
{
|
|
|
|
need_cardOkay = true;
|
|
|
|
|
|
|
|
QMetaObject::invokeMethod(this, "needCard", Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(s == "GET_BOOL")
|
|
|
|
{
|
|
|
|
QString arg = nextArg(rest);
|
|
|
|
if(arg == "untrusted_key.override")
|
|
|
|
submitCommand("no\n");
|
|
|
|
}
|
|
|
|
else if(s == "GOOD_PASSPHRASE")
|
|
|
|
{
|
|
|
|
badPassphrase = false;
|
|
|
|
}
|
|
|
|
else if(s == "BAD_PASSPHRASE")
|
|
|
|
{
|
|
|
|
badPassphrase = true;
|
|
|
|
}
|
|
|
|
else if(s == "GOODSIG")
|
|
|
|
{
|
|
|
|
output.wasSigned = true;
|
|
|
|
output.signerId = nextArg(rest);
|
|
|
|
output.verifyResult = GpgOp::VerifyGood;
|
|
|
|
}
|
|
|
|
else if(s == "BADSIG")
|
|
|
|
{
|
|
|
|
output.wasSigned = true;
|
|
|
|
output.signerId = nextArg(rest);
|
|
|
|
output.verifyResult = GpgOp::VerifyBad;
|
|
|
|
}
|
|
|
|
else if(s == "ERRSIG")
|
|
|
|
{
|
|
|
|
output.wasSigned = true;
|
|
|
|
QStringList list = rest.split(' ', QString::SkipEmptyParts);
|
|
|
|
output.signerId = list[0];
|
|
|
|
output.timestamp = getTimestamp(list[4]);
|
|
|
|
output.verifyResult = GpgOp::VerifyNoKey;
|
|
|
|
}
|
|
|
|
else if(s == "VALIDSIG")
|
|
|
|
{
|
|
|
|
QStringList list = rest.split(' ', QString::SkipEmptyParts);
|
|
|
|
output.timestamp = getTimestamp(list[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void processResult(int code)
|
|
|
|
{
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
printf("<< launch: %d >>\n", timer.elapsed());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// put stdout and stderr into QStrings
|
2013-06-14 08:30:12 +00:00
|
|
|
|
|
|
|
// FIXME: on Windows gpg returns --with-colons in
|
|
|
|
// utf-8 charset but for -k or -K it uses
|
|
|
|
// console output charset (which may will be differs
|
|
|
|
// then system charse). Will be wait a resolving of
|
|
|
|
// QTBUG-13303 https://bugreports.qt-project.org/browse/QTBUG-13303
|
|
|
|
// After it need to make some changes.
|
|
|
|
QString outstr = QString::fromUtf8(buf_stdout);
|
|
|
|
QString errstr = QString::fromUtf8(buf_stderr);
|
2005-07-06 23:25:51 +00:00
|
|
|
|
|
|
|
if(collectOutput)
|
|
|
|
diagnosticText += QString("stdout: [%1]\n").arg(outstr);
|
|
|
|
diagnosticText += QString("stderr: [%1]\n").arg(errstr);
|
|
|
|
ensureDTextEmit();
|
|
|
|
|
|
|
|
if(badPassphrase)
|
|
|
|
{
|
|
|
|
output.errorCode = GpgOp::ErrorPassphrase;
|
|
|
|
}
|
|
|
|
else if(curError != GpgOp::ErrorUnknown)
|
|
|
|
{
|
|
|
|
output.errorCode = curError;
|
|
|
|
}
|
|
|
|
else if(code == 0)
|
|
|
|
{
|
|
|
|
if(input.op == GpgOp::SecretKeyringFile || input.op == GpgOp::PublicKeyringFile)
|
|
|
|
{
|
|
|
|
if(findKeyringFilename(outstr, &output.keyringFile))
|
|
|
|
output.success = true;
|
|
|
|
}
|
|
|
|
else if(input.op == GpgOp::SecretKeys || input.op == GpgOp::PublicKeys)
|
|
|
|
{
|
|
|
|
if(stringToKeyList(outstr, &output.keys, &output.keyringFile))
|
|
|
|
output.success = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
output.success = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// decrypt and sign success based on status only.
|
|
|
|
// this is mainly because gpg uses fatal return
|
|
|
|
// values if there is trouble with gpg-agent, even
|
|
|
|
// though the operation otherwise works.
|
|
|
|
|
|
|
|
if(input.op == GpgOp::Decrypt && decryptGood)
|
|
|
|
output.success = true;
|
|
|
|
if(signing && signGood)
|
|
|
|
output.success = true;
|
|
|
|
|
|
|
|
// gpg will indicate failure for bad sigs, but we don't
|
|
|
|
// consider this to be operation failure.
|
|
|
|
|
|
|
|
bool signedMakesItGood = false;
|
|
|
|
if(input.op == GpgOp::Verify || input.op == GpgOp::VerifyDetached)
|
|
|
|
signedMakesItGood = true;
|
|
|
|
|
|
|
|
if(signedMakesItGood && output.wasSigned)
|
|
|
|
output.success = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit finished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensureDTextEmit()
|
|
|
|
{
|
|
|
|
if(!dtextTimer.isActive())
|
|
|
|
dtextTimer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
void t_dtext()
|
|
|
|
{
|
|
|
|
emit readyReadDiagnosticText();
|
|
|
|
}
|
|
|
|
|
2006-03-30 09:41:01 +00:00
|
|
|
void proc_error(gpgQCAPlugin::GPGProc::Error e)
|
2005-07-06 23:25:51 +00:00
|
|
|
{
|
|
|
|
QString str;
|
|
|
|
if(e == GPGProc::FailedToStart)
|
|
|
|
str = "FailedToStart";
|
|
|
|
else if(e == GPGProc::UnexpectedExit)
|
|
|
|
str = "UnexpectedExit";
|
|
|
|
else if(e == GPGProc::ErrorWrite)
|
|
|
|
str = "ErrorWrite";
|
|
|
|
|
|
|
|
diagnosticText += QString("GPG Process Error: %1\n").arg(str);
|
|
|
|
ensureDTextEmit();
|
|
|
|
|
|
|
|
output.errorCode = GpgOp::ErrorProcess;
|
|
|
|
emit finished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_finished(int exitCode)
|
|
|
|
{
|
|
|
|
diagnosticText += QString("GPG Process Finished: exitStatus=%1\n").arg(exitCode);
|
|
|
|
ensureDTextEmit();
|
|
|
|
|
|
|
|
processResult(exitCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_readyReadStdout()
|
|
|
|
{
|
|
|
|
if(collectOutput)
|
|
|
|
{
|
|
|
|
QByteArray a = proc.readStdout();
|
|
|
|
if(readText)
|
|
|
|
a = readConv.update(a);
|
|
|
|
buf_stdout.append(a);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
emit readyRead();
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_readyReadStderr()
|
|
|
|
{
|
|
|
|
buf_stderr.append(proc.readStderr());
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_readyReadStatusLines()
|
|
|
|
{
|
|
|
|
QStringList lines = proc.readStatusLines();
|
|
|
|
for(int n = 0; n < lines.count(); ++n)
|
|
|
|
processStatusLine(lines[n]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_bytesWrittenStdin(int bytes)
|
|
|
|
{
|
|
|
|
if(!useAux)
|
|
|
|
{
|
|
|
|
int actual = writeConv.writtenToActual(bytes);
|
|
|
|
emit bytesWritten(actual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_bytesWrittenAux(int bytes)
|
|
|
|
{
|
|
|
|
if(useAux)
|
|
|
|
{
|
|
|
|
int actual = writeConv.writtenToActual(bytes);
|
|
|
|
emit bytesWritten(actual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_bytesWrittenCommand(int)
|
|
|
|
{
|
|
|
|
// don't care about this
|
|
|
|
}
|
|
|
|
|
|
|
|
void proc_debug(const QString &str)
|
|
|
|
{
|
|
|
|
diagnosticText += "GPGProc: " + str + '\n';
|
|
|
|
ensureDTextEmit();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// GpgOp
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
enum ResetMode
|
|
|
|
{
|
|
|
|
ResetSession = 0,
|
|
|
|
ResetSessionAndData = 1,
|
|
|
|
ResetAll = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
class GpgOp::Private : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
QCA::Synchronizer sync;
|
|
|
|
GpgOp *q;
|
|
|
|
GpgAction *act;
|
|
|
|
QString bin;
|
|
|
|
GpgOp::Type op;
|
|
|
|
GpgAction::Output output;
|
|
|
|
QByteArray result;
|
|
|
|
QString diagnosticText;
|
|
|
|
QList<GpgOp::Event> eventList;
|
|
|
|
bool waiting;
|
|
|
|
|
|
|
|
bool opt_ascii, opt_noagent, opt_alwaystrust;
|
|
|
|
QString opt_pubfile, opt_secfile;
|
|
|
|
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
QTime timer;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Private(GpgOp *_q) : QObject(_q), sync(_q), q(_q)
|
|
|
|
{
|
|
|
|
act = 0;
|
|
|
|
waiting = false;
|
|
|
|
|
|
|
|
reset(ResetAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
~Private()
|
|
|
|
{
|
|
|
|
reset(ResetAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset(ResetMode mode)
|
|
|
|
{
|
|
|
|
if(act)
|
|
|
|
{
|
2014-09-25 01:03:18 +06:00
|
|
|
act->disconnect(this);
|
|
|
|
act->setParent(0);
|
|
|
|
act->deleteLater();
|
|
|
|
|
2005-07-06 23:25:51 +00:00
|
|
|
act = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mode >= ResetSessionAndData)
|
|
|
|
{
|
|
|
|
output = GpgAction::Output();
|
|
|
|
result.clear();
|
|
|
|
diagnosticText = QString();
|
|
|
|
eventList.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mode >= ResetAll)
|
|
|
|
{
|
|
|
|
opt_ascii = false;
|
|
|
|
opt_noagent = false;
|
|
|
|
opt_alwaystrust = false;
|
|
|
|
opt_pubfile = QString();
|
|
|
|
opt_secfile = QString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void make_act(GpgOp::Type _op)
|
|
|
|
{
|
|
|
|
reset(ResetSessionAndData);
|
|
|
|
|
|
|
|
op = _op;
|
|
|
|
|
|
|
|
act = new GpgAction(this);
|
|
|
|
|
|
|
|
connect(act, SIGNAL(readyRead()), SLOT(act_readyRead()));
|
|
|
|
connect(act, SIGNAL(bytesWritten(int)), SLOT(act_bytesWritten(int)));
|
|
|
|
connect(act, SIGNAL(needPassphrase(const QString &)), SLOT(act_needPassphrase(const QString &)));
|
|
|
|
connect(act, SIGNAL(needCard()), SLOT(act_needCard()));
|
|
|
|
connect(act, SIGNAL(finished()), SLOT(act_finished()));
|
|
|
|
connect(act, SIGNAL(readyReadDiagnosticText()), SLOT(act_readyReadDiagnosticText()));
|
|
|
|
|
|
|
|
act->input.bin = bin;
|
|
|
|
act->input.op = op;
|
|
|
|
act->input.opt_ascii = opt_ascii;
|
|
|
|
act->input.opt_noagent = opt_noagent;
|
|
|
|
act->input.opt_alwaystrust = opt_alwaystrust;
|
|
|
|
act->input.opt_pubfile = opt_pubfile;
|
|
|
|
act->input.opt_secfile = opt_secfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void eventReady(const GpgOp::Event &e)
|
|
|
|
{
|
|
|
|
eventList += e;
|
|
|
|
sync.conditionMet();
|
|
|
|
}
|
|
|
|
|
|
|
|
void eventReady(GpgOp::Event::Type type)
|
|
|
|
{
|
|
|
|
GpgOp::Event e;
|
|
|
|
e.type = type;
|
|
|
|
eventReady(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void eventReady(GpgOp::Event::Type type, int written)
|
|
|
|
{
|
|
|
|
GpgOp::Event e;
|
|
|
|
e.type = type;
|
|
|
|
e.written = written;
|
|
|
|
eventReady(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void eventReady(GpgOp::Event::Type type, const QString &keyId)
|
|
|
|
{
|
|
|
|
GpgOp::Event e;
|
|
|
|
e.type = type;
|
|
|
|
e.keyId = keyId;
|
|
|
|
eventReady(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void act_readyRead()
|
|
|
|
{
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::ReadyRead);
|
|
|
|
else
|
|
|
|
emit q->readyRead();
|
|
|
|
}
|
|
|
|
|
|
|
|
void act_bytesWritten(int bytes)
|
|
|
|
{
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::BytesWritten, bytes);
|
|
|
|
else
|
|
|
|
emit q->bytesWritten(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void act_needPassphrase(const QString &keyId)
|
|
|
|
{
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::NeedPassphrase, keyId);
|
|
|
|
else
|
|
|
|
emit q->needPassphrase(keyId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void act_needCard()
|
|
|
|
{
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::NeedCard);
|
|
|
|
else
|
|
|
|
emit q->needCard();
|
|
|
|
}
|
|
|
|
|
|
|
|
void act_readyReadDiagnosticText()
|
|
|
|
{
|
|
|
|
QString s = act->readDiagnosticText();
|
|
|
|
//printf("dtext ready: [%s]\n", qPrintable(s));
|
|
|
|
diagnosticText += s;
|
|
|
|
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::ReadyReadDiagnosticText);
|
|
|
|
else
|
|
|
|
emit q->readyReadDiagnosticText();
|
|
|
|
}
|
|
|
|
|
|
|
|
void act_finished()
|
|
|
|
{
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
if(op == GpgOp::Encrypt)
|
|
|
|
printf("<< doEncrypt: %d >>\n", timer.elapsed());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
result = act->read();
|
|
|
|
diagnosticText += act->readDiagnosticText();
|
|
|
|
output = act->output;
|
|
|
|
|
|
|
|
QMap<int, QString> errmap;
|
|
|
|
errmap[GpgOp::ErrorProcess] = "ErrorProcess";
|
|
|
|
errmap[GpgOp::ErrorPassphrase] = "ErrorPassphrase";
|
|
|
|
errmap[GpgOp::ErrorFormat] = "ErrorFormat";
|
|
|
|
errmap[GpgOp::ErrorSignerExpired] = "ErrorSignerExpired";
|
|
|
|
errmap[GpgOp::ErrorEncryptExpired] = "ErrorEncryptExpired";
|
|
|
|
errmap[GpgOp::ErrorEncryptUntrusted] = "ErrorEncryptUntrusted";
|
|
|
|
errmap[GpgOp::ErrorEncryptInvalid] = "ErrorEncryptInvalid";
|
|
|
|
errmap[GpgOp::ErrorDecryptNoKey] = "ErrorDecryptNoKey";
|
|
|
|
errmap[GpgOp::ErrorUnknown] = "ErrorUnknown";
|
|
|
|
if(output.success)
|
|
|
|
diagnosticText += "GpgAction success\n";
|
|
|
|
else
|
|
|
|
diagnosticText += QString("GpgAction error: %1\n").arg(errmap[output.errorCode]);
|
|
|
|
|
|
|
|
if(output.wasSigned)
|
|
|
|
{
|
|
|
|
QString s;
|
|
|
|
if(output.verifyResult == GpgOp::VerifyGood)
|
|
|
|
s = "VerifyGood";
|
|
|
|
else if(output.verifyResult == GpgOp::VerifyBad)
|
|
|
|
s = "VerifyBad";
|
|
|
|
else
|
|
|
|
s = "VerifyNoKey";
|
|
|
|
diagnosticText += QString("wasSigned: verifyResult: %1\n").arg(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
//printf("diagnosticText:\n%s", qPrintable(diagnosticText));
|
|
|
|
|
|
|
|
reset(ResetSession);
|
|
|
|
|
|
|
|
if(waiting)
|
|
|
|
eventReady(GpgOp::Event::Finished);
|
|
|
|
else
|
|
|
|
emit q->finished();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
GpgOp::GpgOp(const QString &bin, QObject *parent)
|
|
|
|
:QObject(parent)
|
|
|
|
{
|
|
|
|
d = new Private(this);
|
|
|
|
d->bin = bin;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::~GpgOp()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::reset()
|
|
|
|
{
|
|
|
|
d->reset(ResetAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GpgOp::isActive() const
|
|
|
|
{
|
|
|
|
return (d->act ? true : false);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::Type GpgOp::op() const
|
|
|
|
{
|
|
|
|
return d->op;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::setAsciiFormat(bool b)
|
|
|
|
{
|
|
|
|
d->opt_ascii = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::setDisableAgent(bool b)
|
|
|
|
{
|
|
|
|
d->opt_noagent = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::setAlwaysTrust(bool b)
|
|
|
|
{
|
|
|
|
d->opt_alwaystrust = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::setKeyrings(const QString &pubfile, const QString &secfile)
|
|
|
|
{
|
|
|
|
d->opt_pubfile = pubfile;
|
|
|
|
d->opt_secfile = secfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doCheck()
|
|
|
|
{
|
|
|
|
d->make_act(Check);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSecretKeyringFile()
|
|
|
|
{
|
|
|
|
d->make_act(SecretKeyringFile);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doPublicKeyringFile()
|
|
|
|
{
|
|
|
|
d->make_act(PublicKeyringFile);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSecretKeys()
|
|
|
|
{
|
|
|
|
d->make_act(SecretKeys);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doPublicKeys()
|
|
|
|
{
|
|
|
|
d->make_act(PublicKeys);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doEncrypt(const QStringList &recip_ids)
|
|
|
|
{
|
|
|
|
#ifdef GPG_PROFILE
|
|
|
|
d->timer.start();
|
|
|
|
printf("<< doEncrypt >>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
d->make_act(Encrypt);
|
|
|
|
d->act->input.recip_ids = recip_ids;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doDecrypt()
|
|
|
|
{
|
|
|
|
d->make_act(Decrypt);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSign(const QString &signer_id)
|
|
|
|
{
|
|
|
|
d->make_act(Sign);
|
|
|
|
d->act->input.signer_id = signer_id;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSignAndEncrypt(const QString &signer_id, const QStringList &recip_ids)
|
|
|
|
{
|
|
|
|
d->make_act(SignAndEncrypt);
|
|
|
|
d->act->input.signer_id = signer_id;
|
|
|
|
d->act->input.recip_ids = recip_ids;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSignClearsign(const QString &signer_id)
|
|
|
|
{
|
|
|
|
d->make_act(SignClearsign);
|
|
|
|
d->act->input.signer_id = signer_id;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doSignDetached(const QString &signer_id)
|
|
|
|
{
|
|
|
|
d->make_act(SignDetached);
|
|
|
|
d->act->input.signer_id = signer_id;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doVerify()
|
|
|
|
{
|
|
|
|
d->make_act(Verify);
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doVerifyDetached(const QByteArray &sig)
|
|
|
|
{
|
|
|
|
d->make_act(VerifyDetached);
|
|
|
|
d->act->input.sig = sig;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doImport(const QByteArray &in)
|
|
|
|
{
|
|
|
|
d->make_act(Import);
|
|
|
|
d->act->input.inkey = in;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::doExport(const QString &key_id)
|
|
|
|
{
|
|
|
|
d->make_act(Export);
|
|
|
|
d->act->input.export_key_id = key_id;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
2008-04-08 03:01:19 +00:00
|
|
|
void GpgOp::doDeleteKey(const QString &key_fingerprint)
|
|
|
|
{
|
|
|
|
d->make_act(DeleteKey);
|
|
|
|
d->act->input.delete_key_fingerprint = key_fingerprint;
|
|
|
|
d->act->start();
|
|
|
|
}
|
|
|
|
|
2005-07-06 23:25:51 +00:00
|
|
|
#ifdef QPIPE_SECURE
|
2007-04-14 01:16:13 +00:00
|
|
|
void GpgOp::submitPassphrase(const QCA::SecureArray &a)
|
2005-07-06 23:25:51 +00:00
|
|
|
#else
|
|
|
|
void GpgOp::submitPassphrase(const QByteArray &a)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
d->act->submitPassphrase(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::cardOkay()
|
|
|
|
{
|
|
|
|
d->act->cardOkay();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray GpgOp::read()
|
|
|
|
{
|
|
|
|
if(d->act)
|
|
|
|
{
|
|
|
|
return d->act->read();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QByteArray a = d->result;
|
|
|
|
d->result.clear();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::write(const QByteArray &in)
|
|
|
|
{
|
|
|
|
d->act->write(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GpgOp::endWrite()
|
|
|
|
{
|
|
|
|
d->act->endWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString GpgOp::readDiagnosticText()
|
|
|
|
{
|
|
|
|
QString s = d->diagnosticText;
|
|
|
|
d->diagnosticText = QString();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::Event GpgOp::waitForEvent(int msecs)
|
|
|
|
{
|
|
|
|
if(!d->eventList.isEmpty())
|
|
|
|
return d->eventList.takeFirst();
|
|
|
|
|
|
|
|
if(!d->act)
|
|
|
|
return GpgOp::Event();
|
|
|
|
|
|
|
|
d->waiting = true;
|
|
|
|
d->sync.waitForCondition(msecs);
|
|
|
|
d->waiting = false;
|
2010-05-17 05:02:18 +00:00
|
|
|
if(!d->eventList.isEmpty())
|
|
|
|
return d->eventList.takeFirst();
|
|
|
|
else
|
|
|
|
return GpgOp::Event();
|
2005-07-06 23:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GpgOp::success() const
|
|
|
|
{
|
|
|
|
return d->output.success;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::Error GpgOp::errorCode() const
|
|
|
|
{
|
|
|
|
return d->output.errorCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::KeyList GpgOp::keys() const
|
|
|
|
{
|
|
|
|
return d->output.keys;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString GpgOp::keyringFile() const
|
|
|
|
{
|
|
|
|
return d->output.keyringFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString GpgOp::encryptedToId() const
|
|
|
|
{
|
|
|
|
return d->output.encryptedToId;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GpgOp::wasSigned() const
|
|
|
|
{
|
|
|
|
return d->output.wasSigned;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString GpgOp::signerId() const
|
|
|
|
{
|
|
|
|
return d->output.signerId;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime GpgOp::timestamp() const
|
|
|
|
{
|
|
|
|
return d->output.timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpgOp::VerifyResult GpgOp::verifyResult() const
|
|
|
|
{
|
|
|
|
return d->output.verifyResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "gpgop.moc"
|