37
// for SafeTimer and release function
42
namespace gpgQCAPlugin {
44
static int qVersionInt()
50
QString str = QString::fromLatin1(qVersion());
51
QStringList parts = str.split('.', QString::KeepEmptyParts);
52
if(parts.count() != 3)
59
for(int n = 0; n < 3; ++n)
62
int x = parts[n].toInt(&ok);
63
if(ok && x >= 0 && x <= 0xff)
80
static bool qt_buggy_fsw()
82
// fixed in 4.3.5 and 4.4.1
83
int ver = qVersionInt();
84
int majmin = ver >> 8;
87
else if(majmin == 0x0403 && ver < 0x040305)
89
else if(majmin == 0x0404 && ver < 0x040401)
94
static bool qt_buggy_fsw()
100
// begin ugly hack for qca 2.0.0 with broken dirwatch support
103
// 1) we must construct with a valid file to watch. passing an empty
104
// string doesn't work. this means we can't create the objects in
105
// advance. instead we'll use new/delete as necessary.
106
// 2) there's a wrong internal connect() statement in the qca source.
107
// assuming fixed internals for qca 2.0.0, we can fix that connect from
110
#include <QFileSystemWatcher>
112
// some structures below to give accessible interface to qca 2.0.0 internals
114
class DirWatch2 : public QObject
118
explicit DirWatch2(const QString &dir = QString(), QObject *parent = 0)
128
QString dirName() const
133
void setDirName(const QString &dir)
142
Q_DISABLE_COPY(DirWatch2)
145
friend class Private;
149
/*class FileWatch2 : public QObject
153
explicit FileWatch2(const QString &file = QString(), QObject *parent = 0)
163
QString fileName() const
168
void setFileName(const QString &file)
177
Q_DISABLE_COPY(FileWatch2)
180
friend class Private;
184
class QFileSystemWatcherRelay2 : public QObject
190
class DirWatch2::Private : public QObject
195
QFileSystemWatcher *watcher;
196
QFileSystemWatcherRelay2 *watcher_relay;
200
void watcher_changed(const QString &path)
206
/*class FileWatch2::Private : public QObject
211
QFileSystemWatcher *watcher;
212
QFileSystemWatcherRelay2 *watcher_relay;
216
void watcher_changed(const QString &path)
222
static void hack_fix(DirWatch *dw)
224
DirWatch2 *dw2 = reinterpret_cast<DirWatch2*>(dw);
225
QObject::connect(dw2->d->watcher_relay, SIGNAL(directoryChanged(const QString &)), dw2->d, SLOT(watcher_changed(const QString &)));
226
fprintf(stderr, "qca-gnupg: patching DirWatch to fix failed connect\n");
32
232
static QString find_reg_gpgProgram()
139
371
virtual QByteArray toBinary() const
375
GpgOp gpg(find_bin());
376
gpg.setAsciiFormat(false);
377
gpg.doExport(_props.keyId);
378
gpg_waitForFinished(&gpg);
379
gpg_keyStoreLog(gpg.readDiagnosticText());
385
return cacheExportBinary;
145
388
virtual QString toAscii() const
392
GpgOp gpg(find_bin());
393
gpg.setAsciiFormat(true);
394
gpg.doExport(_props.keyId);
395
gpg_waitForFinished(&gpg);
396
gpg_keyStoreLog(gpg.readDiagnosticText());
399
return QString::fromLocal8Bit(gpg.read());
402
return cacheExportAscii;
405
static void cleanup_temp_keyring(const QString &name)
408
QFile::remove(name + '~'); // remove possible backup file
411
virtual ConvertResult fromBinary(const QByteArray &a)
416
// temporary keyrings
417
QString pubname, secname;
419
QTemporaryFile pubtmp(QDir::tempPath() + QLatin1String("/qca_gnupg_tmp.XXXXXX.gpg"));
423
QTemporaryFile sectmp(QDir::tempPath() + QLatin1String("/qca_gnupg_tmp.XXXXXX.gpg"));
427
pubname = pubtmp.fileName();
428
secname = sectmp.fileName();
430
// we turn off autoRemove so that we can close the files
431
// without them getting deleted
432
pubtmp.setAutoRemove(false);
433
sectmp.setAutoRemove(false);
437
// import key into temporary keyring
147
438
GpgOp gpg(find_bin());
439
gpg.setKeyrings(pubname, secname);
441
gpg_waitForFinished(&gpg);
442
gpg_keyStoreLog(gpg.readDiagnosticText());
443
// comment this out. apparently gpg will report failure for
444
// an import if there are trust issues, even though the
445
// key actually did get imported
448
cleanup_temp_keyring(pubname);
449
cleanup_temp_keyring(secname);
453
// now extract the key from gpg like normal
455
// is it a public key?
457
gpg_waitForFinished(&gpg);
458
gpg_keyStoreLog(gpg.readDiagnosticText());
461
cleanup_temp_keyring(pubname);
462
cleanup_temp_keyring(secname);
466
GpgOp::KeyList pubkeys = gpg.keys();
467
if(!pubkeys.isEmpty())
469
key = pubkeys.first();
473
// is it a secret key?
475
gpg_waitForFinished(&gpg);
476
gpg_keyStoreLog(gpg.readDiagnosticText());
479
cleanup_temp_keyring(pubname);
480
cleanup_temp_keyring(secname);
484
GpgOp::KeyList seckeys = gpg.keys();
485
if(!seckeys.isEmpty())
487
key = seckeys.first();
493
cleanup_temp_keyring(pubname);
494
cleanup_temp_keyring(secname);
499
// export binary/ascii and cache
501
gpg.setAsciiFormat(false);
502
gpg.doExport(key.keyItems.first().id);
503
gpg_waitForFinished(&gpg);
504
gpg_keyStoreLog(gpg.readDiagnosticText());
507
cleanup_temp_keyring(pubname);
508
cleanup_temp_keyring(secname);
511
cacheExportBinary = gpg.read();
148
513
gpg.setAsciiFormat(true);
149
gpg.doExport(_props.keyId);
514
gpg.doExport(key.keyItems.first().id);
515
gpg_waitForFinished(&gpg);
516
gpg_keyStoreLog(gpg.readDiagnosticText());
152
GpgOp::Event e = gpg.waitForEvent(-1);
153
if(e.type == GpgOp::Event::Finished)
519
cleanup_temp_keyring(pubname);
520
cleanup_temp_keyring(secname);
158
QString str = QString::fromLocal8Bit(gpg.read());
162
virtual ConvertResult fromBinary(const QByteArray &a)
523
cacheExportAscii = QString::fromLocal8Bit(gpg.read());
527
cleanup_temp_keyring(pubname);
528
cleanup_temp_keyring(secname);
530
set(key, sec, false, false);
169
534
virtual ConvertResult fromAscii(const QString &s)
536
// GnuPG does ascii/binary detection for imports, so for
537
// simplicity we consider an ascii import to just be a
538
// binary import that happens to be comprised of ascii
539
return fromBinary(s.toLocal8Bit());
251
class MyMessageContext;
255
/*class MyKeyStore : public KeyStoreContext
619
// since keyring files are often modified by creating a new copy and
620
// overwriting the original file, this messes up Qt's file watching
621
// capability since the original file goes away. to work around this
622
// problem, we'll watch the directories containing the keyring files
623
// instead of watching the actual files themselves.
625
// FIXME: consider moving this logic into FileWatch
626
class RingWatch : public QObject
259
friend class MyMessageContext;
261
MyKeyStore(Provider *p) : KeyStoreContext(p) {}
263
virtual Provider::Context *clone() const
268
virtual int contextId() const
271
return 0; // there is only 1 context, so this can be static
274
virtual QString deviceId() const
277
return "qca-gnupg-(gpg)";
280
virtual KeyStore::Type type() const
282
return KeyStore::PGPKeyring;
285
virtual QString name() const
287
return "GnuPG Keyring";
290
virtual QList<KeyStoreEntryContext*> entryList() const
292
QList<KeyStoreEntryContext*> out;
294
GpgOp::KeyList seckeys;
296
GpgOp gpg(find_bin());
300
GpgOp::Event e = gpg.waitForEvent(-1);
301
if(e.type == GpgOp::Event::Finished)
306
seckeys = gpg.keys();
309
GpgOp::KeyList pubkeys;
311
GpgOp gpg(find_bin());
315
GpgOp::Event e = gpg.waitForEvent(-1);
316
if(e.type == GpgOp::Event::Finished)
321
pubkeys = gpg.keys();
324
for(int n = 0; n < pubkeys.count(); ++n)
326
QString id = pubkeys[n].keyItems.first().id;
327
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
328
kc->_props.keyId = id;
329
kc->_props.userIds = QStringList() << pubkeys[n].userIds.first();
332
for(int i = 0; i < seckeys.count(); ++i)
334
if(seckeys[i].keyItems.first().id == id)
634
SafeTimer *changeTimer;
644
QDateTime lastModified;
648
QList<FileItem> files;
650
RingWatch(QObject *parent = 0) :
660
void add(const QString &filePath)
662
QFileInfo fi(filePath);
663
QString path = fi.absolutePath();
665
// watching this path already?
666
DirWatch *dirWatch = 0;
667
foreach(const DirItem &di, dirs)
669
if(di.dirWatch->dirName() == path)
671
dirWatch = di.dirWatch;
676
// if not, make a watcher
679
//printf("creating dirwatch for [%s]\n", qPrintable(path));
682
di.dirWatch = new DirWatch(path, this);
683
connect(di.dirWatch, SIGNAL(changed()), SLOT(dirChanged()));
685
if(qcaVersion() == 0x020000)
686
hack_fix(di.dirWatch);
688
di.changeTimer = new SafeTimer(this);
689
di.changeTimer->setSingleShot(true);
690
connect(di.changeTimer, SIGNAL(timeout()), SLOT(handleChanged()));
692
dirWatch = di.dirWatch;
697
i.dirWatch = dirWatch;
698
i.fileName = fi.fileName();
699
i.exists = fi.exists();
703
i.lastModified = fi.lastModified();
707
//printf("watching [%s] in [%s]\n", qPrintable(fi.fileName()), qPrintable(i.dirWatch->dirName()));
714
foreach(const DirItem &di, dirs)
716
delete di.changeTimer;
724
void changed(const QString &filePath);
729
DirWatch *dirWatch = (DirWatch *)sender();
732
for(int n = 0; n < dirs.count(); ++n)
734
if(dirs[n].dirWatch == dirWatch)
743
// we get a ton of change notifications for the dir when
744
// something happens.. let's collect them and only
745
// report after 100ms
747
if(!dirs[at].changeTimer->isActive())
748
dirs[at].changeTimer->start(100);
753
SafeTimer *t = (SafeTimer *)sender();
756
for(int n = 0; n < dirs.count(); ++n)
758
if(dirs[n].changeTimer == t)
767
DirWatch *dirWatch = dirs[at].dirWatch;
768
QString dir = dirWatch->dirName();
770
// see which files changed
771
QStringList changeList;
772
for(int n = 0; n < files.count(); ++n)
774
FileItem &i = files[n];
775
QString filePath = dir + '/' + i.fileName;
776
QFileInfo fi(filePath);
778
// if the file didn't exist, and still doesn't, skip
779
if(!i.exists && !fi.exists())
782
// size/lastModified should only get checked here if
783
// the file existed and still exists
784
if(fi.exists() != i.exists || fi.size() != i.size || fi.lastModified() != i.lastModified)
786
changeList += filePath;
788
i.exists = fi.exists();
336
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
337
kc->_props.keyId = id;
338
kc->_props.userIds = QStringList() << pubkeys[n].userIds.first();
792
i.lastModified = fi.lastModified();
343
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
350
virtual QList<KeyStoreEntry::Type> entryTypes() const
352
QList<KeyStoreEntry::Type> list;
353
list += KeyStoreEntry::TypePGPSecretKey;
354
list += KeyStoreEntry::TypePGPPublicKey;
358
virtual void submitPassphrase(const SecureArray &a)
360
global_gpg->submitPassphrase(a.toByteArray());
364
class MyKeyStoreList;
365
class MyMessageContext;
367
static MyKeyStoreList *keyStoreList = 0;
797
foreach(const QString &s, changeList)
369
802
class MyKeyStoreList : public KeyStoreListContext
373
friend class MyMessageContext;
809
GpgOp::KeyList pubkeys, seckeys;
810
QString pubring, secring;
811
bool pubdirty, secdirty;
376
MyKeyStoreList(Provider *p) : KeyStoreListContext(p)
815
MyKeyStoreList(Provider *p) :
816
KeyStoreListContext(p),
818
gpg(find_bin(), this),
823
QMutexLocker locker(ksl_mutex());
378
824
keyStoreList = this;
382
//ks = new MyKeyStore(provider());
826
connect(&gpg, SIGNAL(finished()), SLOT(gpg_finished()));
827
connect(&ringWatch, SIGNAL(changed(const QString &)), SLOT(ring_changed(const QString &)));
385
830
~MyKeyStoreList()
832
QMutexLocker locker(ksl_mutex());
389
833
keyStoreList = 0;
433
899
virtual QList<KeyStoreEntryContext*> entryList(int)
901
QMutexLocker locker(&ringMutex);
435
903
QList<KeyStoreEntryContext*> out;
437
GpgOp::KeyList seckeys;
439
GpgOp gpg(find_bin());
443
GpgOp::Event e = gpg.waitForEvent(-1);
444
if(e.type == GpgOp::Event::Finished)
449
seckeys = gpg.keys();
452
GpgOp::KeyList pubkeys;
454
GpgOp gpg(find_bin());
458
GpgOp::Event e = gpg.waitForEvent(-1);
459
if(e.type == GpgOp::Event::Finished)
464
pubkeys = gpg.keys();
467
for(int n = 0; n < pubkeys.count(); ++n)
469
QString id = pubkeys[n].keyItems.first().id;
905
foreach(const GpgOp::Key &pkey, pubkeys)
909
QString id = pkey.keyItems.first().id;
470
911
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
471
kc->_props.keyId = id;
472
kc->_props.userIds = QStringList() << pubkeys[n].userIds.first();
912
// not secret, in keyring
913
kc->set(pkey, false, true, pkey.isTrusted);
475
for(int i = 0; i < seckeys.count(); ++i)
477
if(seckeys[i].keyItems.first().id == id)
479
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
480
kc->_props.keyId = id;
481
kc->_props.userIds = QStringList() << pubkeys[n].userIds.first();
482
kc->_props.isSecret = true;
917
sec = getSecKey(id, pkey.userIds);
487
919
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
488
920
c->_storeId = storeId(0);
928
virtual KeyStoreEntryContext *entry(int, const QString &entryId)
930
QMutexLocker locker(&ringMutex);
932
PGPKey pub = getPubKey(entryId);
937
PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
939
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
940
c->_storeId = storeId(0);
941
c->_storeName = name(0);
496
945
virtual KeyStoreEntryContext *entryPassive(const QString &serialized)
947
QMutexLocker locker(&ringMutex);
498
949
QStringList parts = serialized.split(':');
499
950
if(parts.count() < 2)
501
952
if(unescape_string(parts[0]) != "qca-gnupg-1")
504
QString keyId = unescape_string(parts[1]);
506
GpgOp::KeyList seckeys;
508
GpgOp gpg(find_bin());
512
GpgOp::Event e = gpg.waitForEvent(-1);
513
if(e.type == GpgOp::Event::Finished)
518
seckeys = gpg.keys();
521
GpgOp::KeyList pubkeys;
523
GpgOp gpg(find_bin());
527
GpgOp::Event e = gpg.waitForEvent(-1);
528
if(e.type == GpgOp::Event::Finished)
533
pubkeys = gpg.keys();
537
for(int n = 0; n < pubkeys.count(); ++n)
539
QString id = pubkeys[n].keyItems.first().id;
549
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
550
kc->_props.keyId = keyId;
551
kc->_props.userIds = QStringList() << pubkeys[at].userIds.first();
554
for(int i = 0; i < seckeys.count(); ++i)
556
if(seckeys[i].keyItems.first().id == keyId)
558
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
559
kc->_props.keyId = keyId;
560
kc->_props.userIds = QStringList() << pubkeys[at].userIds.first();
561
kc->_props.isSecret = true;
955
QString entryId = unescape_string(parts[1]);
956
if(entryId.isEmpty())
959
PGPKey pub = getPubKey(entryId);
964
PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
566
966
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
567
967
c->_storeId = storeId(0);
572
virtual void submitPassphrase(int, int, const SecureArray &a)
574
global_gpg->submitPassphrase(a.toByteArray());
972
// TODO: cache should reflect this change immediately
973
virtual QString writeEntry(int, const PGPKey &key)
975
const MyPGPKeyContext *kc = static_cast<const MyPGPKeyContext *>(key.context());
976
QByteArray buf = kc->toBinary();
978
GpgOp gpg(find_bin());
980
gpg_waitForFinished(&gpg);
981
gpg_keyStoreLog(gpg.readDiagnosticText());
985
return kc->_props.keyId;
988
// TODO: cache should reflect this change immediately
989
virtual bool removeEntry(int, const QString &entryId)
992
PGPKey pub = getPubKey(entryId);
995
const MyPGPKeyContext *kc = static_cast<const MyPGPKeyContext *>(pub.context());
996
QString fingerprint = kc->_props.fingerprint;
998
GpgOp gpg(find_bin());
999
gpg.doDeleteKey(fingerprint);
1000
gpg_waitForFinished(&gpg);
1001
gpg_keyStoreLog(gpg.readDiagnosticText());
1002
return gpg.success();
1006
PGPKey getPubKey(const QString &keyId) const
1009
for(int n = 0; n < pubkeys.count(); ++n)
1011
if(pubkeys[n].keyItems.first().id == keyId)
1020
const GpgOp::Key &pkey = pubkeys[at];
1023
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
1024
// not secret, in keyring
1025
kc->set(pkey, false, true, pkey.isTrusted);
1032
PGPKey getSecKey(const QString &keyId, const QStringList &userIdsOverride) const
1034
Q_UNUSED(userIdsOverride);
1037
for(int n = 0; n < seckeys.count(); ++n)
1039
if(seckeys[n].keyItems.first().id == keyId)
1048
const GpgOp::Key &skey = seckeys[at];
1051
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
1052
// secret, in keyring, trusted
1053
kc->set(skey, true, true, true);
1054
//kc->_props.userIds = userIdsOverride;
1060
PGPKey publicKeyFromId(const QString &keyId)
1062
QMutexLocker locker(&ringMutex);
1065
for(int n = 0; n < pubkeys.count(); ++n)
1067
const GpgOp::Key &pkey = pubkeys[n];
1068
for(int k = 0; k < pkey.keyItems.count(); ++k)
1070
const GpgOp::KeyItem &ki = pkey.keyItems[k];
1083
const GpgOp::Key &pkey = pubkeys[at];
1086
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
1087
// not secret, in keyring
1088
kc->set(pkey, false, true, pkey.isTrusted);
1094
PGPKey secretKeyFromId(const QString &keyId)
1096
QMutexLocker locker(&ringMutex);
1099
for(int n = 0; n < seckeys.count(); ++n)
1101
const GpgOp::Key &skey = seckeys[n];
1102
for(int k = 0; k < skey.keyItems.count(); ++k)
1104
const GpgOp::KeyItem &ki = skey.keyItems[k];
1117
const GpgOp::Key &skey = seckeys[at];
1120
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
1121
// secret, in keyring, trusted
1122
kc->set(skey, true, true, true);
1131
gpg_keyStoreLog(gpg.readDiagnosticText());
1135
// any steps that fail during init, just give up completely
1146
// obtain keyring file names for monitoring
1148
gpg.doSecretKeyringFile();
1150
// secret keyring filename
1151
else if(init_step == 1)
1153
secring = gpg.keyringFile();
1156
fprintf(stderr, "qca-gnupg: disabling keyring monitoring in Qt version < 4.3.5 or 4.4.1\n");
1158
if(!secring.isEmpty())
1161
ringWatch.add(secring);
1164
// obtain keyring file names for monitoring
1166
gpg.doPublicKeyringFile();
1168
// public keyring filename
1169
else if(init_step == 2)
1171
pubring = gpg.keyringFile();
1172
if(!pubring.isEmpty())
1175
ringWatch.add(pubring);
1178
// cache initial keyrings
1182
else if(init_step == 3)
1185
seckeys = gpg.keys();
1188
// cache initial keyrings
1192
else if(init_step == 4)
1195
pubkeys = gpg.keys();
1208
GpgOp::Type op = gpg.op();
1209
if(op == GpgOp::SecretKeys)
1212
seckeys = gpg.keys();
1217
else if(op == GpgOp::PublicKeys)
1220
pubkeys = gpg.keys();
1226
if(!secdirty && !pubdirty)
1228
emit storeUpdated(0);
1236
void ring_changed(const QString &filePath)
1238
ext_keyStoreLog(QString("ring_changed: [%1]\n").arg(filePath));
1240
if(filePath == secring)
1242
else if(filePath == pubring)
1259
void handleDirtyRings()
1261
if(!initialized || gpg.isActive())
585
static PGPKey publicKeyFromId(const QString &id, Provider *p)
587
GpgOp::KeyList pubkeys;
589
GpgOp gpg(find_bin());
593
GpgOp::Event e = gpg.waitForEvent(-1);
594
if(e.type == GpgOp::Event::Finished)
598
return PGPKey(); // FIXME
599
pubkeys = gpg.keys();
603
for(int n = 0; n < pubkeys.count(); ++n)
605
if(pubkeys[n].keyItems.first().id == id)
612
return PGPKey(); // FIXME
614
MyPGPKeyContext *kc = new MyPGPKeyContext(p);
615
kc->_props.keyId = pubkeys[at].keyItems.first().id;
616
kc->_props.userIds = QStringList() << pubkeys[at].userIds.first();
623
static PGPKey secretKeyFromId(const QString &id, Provider *p)
625
GpgOp::KeyList seckeys;
627
GpgOp gpg(find_bin());
631
GpgOp::Event e = gpg.waitForEvent(-1);
632
if(e.type == GpgOp::Event::Finished)
636
return PGPKey(); // FIXME
637
seckeys = gpg.keys();
641
for(int n = 0; n < seckeys.count(); ++n)
643
const GpgOp::Key &key = seckeys[n];
644
for(int k = 0; k < key.keyItems.count(); ++k)
646
const GpgOp::KeyItem &ki = key.keyItems[k];
657
return PGPKey(); // FIXME
659
MyPGPKeyContext *kc = new MyPGPKeyContext(p);
660
kc->_props.keyId = seckeys[at].keyItems.first().id;
661
kc->_props.userIds = QStringList() << seckeys[at].userIds.first();
1271
static void gpg_keyStoreLog(const QString &str)
1273
MyKeyStoreList *ksl = MyKeyStoreList::instance();
1275
ksl->ext_keyStoreLog(str);
1278
static PGPKey publicKeyFromId(const QString &id)
1280
MyKeyStoreList *ksl = MyKeyStoreList::instance();
1284
return ksl->publicKeyFromId(id);
1287
static PGPKey secretKeyFromId(const QString &id)
1289
MyKeyStoreList *ksl = MyKeyStoreList::instance();
1293
return ksl->secretKeyFromId(id);
668
1296
class MyOpenPGPContext : public SMSContext