~ubuntu-branches/ubuntu/quantal/psi/quantal

« back to all changes in this revision

Viewing changes to src/tools/openpgp/gpgop.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2006-01-20 00:20:36 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060120002036-7nw6yo6totip0ee5
Tags: 0.10-2
* Added upstream changelog (Closes: Bug#327748)
* Mention --no-gpg and --no-gpg-agent in manpage (Closes: Bug#204416)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include"gpgop.h"
 
2
 
 
3
#include<qstringlist.h>
 
4
#include<qtimer.h>
 
5
#include<qdatetime.h>
 
6
#include<stdlib.h>
 
7
#include"gpgproc/gpgproc.h"
 
8
 
 
9
static QByteArray stringToArray(const QString &str)
 
10
{
 
11
        QCString cs = str.utf8();
 
12
        QByteArray a(cs.length());
 
13
        memcpy(a.data(), cs.data(), a.size());
 
14
        return a;
 
15
}
 
16
 
 
17
class GpgOp::Private
 
18
{
 
19
public:
 
20
        Private() {}
 
21
 
 
22
        QString bin;
 
23
        int op;
 
24
        GPGProc *proc;
 
25
        bool tryAgent;
 
26
 
 
27
        QByteArray outbuf, errbuf;
 
28
        bool didPassphrase;
 
29
        QString sigKeyID;
 
30
        QDateTime sigTS;
 
31
        int verType;
 
32
 
 
33
        OpenPGP::KeyList keys;
 
34
        QString keyring;
 
35
        bool badpp;
 
36
 
 
37
        QString enc;
 
38
        QByteArray dec;
 
39
};
 
40
 
 
41
GpgOp::GpgOp(const QString &bin, QObject *parent)
 
42
:QObject(parent)
 
43
{
 
44
        d = new Private;
 
45
        d->bin = bin;
 
46
        d->proc = 0;
 
47
        d->op = -1;
 
48
        d->tryAgent = true;
 
49
}
 
50
 
 
51
GpgOp::~GpgOp()
 
52
{
 
53
        reset();
 
54
        delete d;
 
55
}
 
56
 
 
57
void GpgOp::reset()
 
58
{
 
59
        if(d->proc) {
 
60
                d->proc->disconnect(this);
 
61
                d->proc->deleteLater();
 
62
                d->proc = 0;
 
63
        }
 
64
 
 
65
        d->sigKeyID = "";
 
66
        d->sigTS = QDateTime();
 
67
        d->didPassphrase = false;
 
68
        d->outbuf.resize(0);
 
69
        d->errbuf.resize(0);
 
70
        d->keys.clear();
 
71
        d->keyring = "";
 
72
        d->verType = OpenPGP::VerifyError;
 
73
        d->badpp = false;
 
74
        d->enc = "";
 
75
        d->dec.resize(0);
 
76
}
 
77
 
 
78
void GpgOp::stop()
 
79
{
 
80
        reset();
 
81
}
 
82
 
 
83
bool GpgOp::isActive() const
 
84
{
 
85
        return (d->proc ? true: false);
 
86
}
 
87
 
 
88
int GpgOp::op() const
 
89
{
 
90
        return d->op;
 
91
}
 
92
 
 
93
const OpenPGP::KeyList & GpgOp::keys() const
 
94
{
 
95
        return d->keys;
 
96
}
 
97
 
 
98
const QString & GpgOp::keyringFile() const
 
99
{
 
100
        return d->keyring;
 
101
}
 
102
 
 
103
const QString & GpgOp::keyID() const
 
104
{
 
105
        return d->sigKeyID;
 
106
}
 
107
 
 
108
const QDateTime & GpgOp::timestamp() const
 
109
{
 
110
        return d->sigTS;
 
111
}
 
112
 
 
113
int GpgOp::verifyResult() const
 
114
{
 
115
        return d->verType;
 
116
}
 
117
 
 
118
bool GpgOp::badPassphrase() const
 
119
{
 
120
        return d->badpp;
 
121
}
 
122
 
 
123
const QString & GpgOp::encrypted() const
 
124
{
 
125
        return d->enc;
 
126
}
 
127
 
 
128
const QByteArray & GpgOp::decrypted() const
 
129
{
 
130
        return d->dec;
 
131
}
 
132
 
 
133
const QString & GpgOp::signature() const
 
134
{
 
135
        return d->enc;
 
136
}
 
137
 
 
138
void GpgOp::setTryAgent(bool b)
 
139
{
 
140
        d->tryAgent = b;
 
141
}
 
142
 
 
143
void GpgOp::doCheck()
 
144
{
 
145
        reset();
 
146
 
 
147
        d->op = Check;
 
148
        QStringList args;
 
149
        args += "--version";
 
150
        if(!launchGPG(args, false)) {
 
151
                QTimer::singleShot(0, this, SLOT(doFail()));
 
152
                return;
 
153
        }
 
154
}
 
155
 
 
156
void GpgOp::doSecretKeyringFile()
 
157
{
 
158
        reset();
 
159
 
 
160
        d->op = SecretKeyringFile;
 
161
        QStringList args;
 
162
        args += "--list-secret-keys";
 
163
        if(!launchGPG(args, false)) {
 
164
                QTimer::singleShot(0, this, SLOT(doFail()));
 
165
                return;
 
166
        }
 
167
}
 
168
 
 
169
void GpgOp::doPublicKeyringFile()
 
170
{
 
171
        reset();
 
172
 
 
173
        d->op = PublicKeyringFile;
 
174
        QStringList args;
 
175
        args += "--list-public-keys";
 
176
        if(!launchGPG(args, false)) {
 
177
                QTimer::singleShot(0, this, SLOT(doFail()));
 
178
                return;
 
179
        }
 
180
}
 
181
 
 
182
void GpgOp::doSecretKeys()
 
183
{
 
184
        reset();
 
185
 
 
186
        d->op = SecretKeys;
 
187
        QStringList args;
 
188
        args += "--fixed-list-mode";
 
189
        args += "--with-colons";
 
190
        args += "--list-secret-keys";
 
191
        if(!launchGPG(args, false)) {
 
192
                QTimer::singleShot(0, this, SLOT(doFail()));
 
193
                return;
 
194
        }
 
195
}
 
196
 
 
197
void GpgOp::doPublicKeys()
 
198
{
 
199
        reset();
 
200
 
 
201
        d->op = PublicKeys;
 
202
        QStringList args;
 
203
        args += "--fixed-list-mode";
 
204
        args += "--with-colons";
 
205
        args += "--list-public-keys";
 
206
        if(!launchGPG(args, false)) {
 
207
                QTimer::singleShot(0, this, SLOT(doFail()));
 
208
                return;
 
209
        }
 
210
}
 
211
 
 
212
void GpgOp::doEncrypt(const QByteArray &in, const QStringList &keys)
 
213
{
 
214
        reset();
 
215
 
 
216
        d->op = Encrypt;
 
217
        QStringList args;
 
218
        args += "--armor";
 
219
        args += "--always-trust";
 
220
        args += "--encrypt";
 
221
 
 
222
        // recipients
 
223
        for(QStringList::ConstIterator it = keys.begin(); it != keys.end(); ++it) {
 
224
                args += "--recipient";
 
225
                args += QString("0x") + *it;
 
226
        }
 
227
 
 
228
        if(!launchGPG(args)) {
 
229
                QTimer::singleShot(0, this, SLOT(doFail()));
 
230
                return;
 
231
        }
 
232
 
 
233
        if(!in.isEmpty())
 
234
                d->proc->writeToStdin(in);
 
235
        else
 
236
                d->proc->closeStdin();
 
237
}
 
238
 
 
239
void GpgOp::doDecrypt(const QString &in)
 
240
{
 
241
        reset();
 
242
 
 
243
        d->op = Decrypt;
 
244
        QStringList args;
 
245
        if(!d->tryAgent)
 
246
                args += "--no-use-agent";
 
247
        args += "--armor";
 
248
        args += "--decrypt";
 
249
        if(!launchGPG(args)) {
 
250
                QTimer::singleShot(0, this, SLOT(doFail()));
 
251
                return;
 
252
        }
 
253
 
 
254
        if(!in.isEmpty())
 
255
                d->proc->writeToStdin(stringToArray(fixOutgoingLines(in)));
 
256
        else
 
257
                d->proc->closeStdin();
 
258
}
 
259
 
 
260
void GpgOp::doSign(const QByteArray &in, const QString &keyID)
 
261
{
 
262
        reset();
 
263
 
 
264
        d->op = Sign;
 
265
        QStringList args;
 
266
        if(!d->tryAgent)
 
267
                args += "--no-use-agent";
 
268
        args += "--armor";
 
269
        args += "--default-key";
 
270
        args += QString("0x") + keyID;
 
271
        args += "--detach-sign";
 
272
        if(!launchGPG(args)) {
 
273
                QTimer::singleShot(0, this, SLOT(doFail()));
 
274
                return;
 
275
        }
 
276
 
 
277
        if(!in.isEmpty())
 
278
                d->proc->writeToStdin(in);
 
279
        else
 
280
                d->proc->closeStdin();
 
281
}
 
282
 
 
283
void GpgOp::doVerify(const QByteArray &in, const QString &sig)
 
284
{
 
285
        reset();
 
286
 
 
287
        d->op = Verify;
 
288
        d->verType = OpenPGP::VerifyError;
 
289
        QStringList args;
 
290
        args += "--armor";
 
291
        args += "--verify";
 
292
        args += "-";
 
293
        args += "-&?";
 
294
 
 
295
        if(!launchGPG(args)) {
 
296
                QTimer::singleShot(0, this, SLOT(doFail()));
 
297
                return;
 
298
        }
 
299
 
 
300
        // sig goes into stdin
 
301
        if(!sig.isEmpty())
 
302
                d->proc->writeToStdin(stringToArray(fixOutgoingLines(sig)));
 
303
        else
 
304
                d->proc->closeStdin();
 
305
 
 
306
        // data goes into aux
 
307
        d->proc->writeToAux(in);
 
308
        d->proc->closeAux();
 
309
}
 
310
 
 
311
void GpgOp::submitPassphrase(const QString &pp)
 
312
{
 
313
        QCString cs = pp.local8Bit() + '\n';
 
314
        QByteArray a(cs.length());
 
315
        memcpy(a.data(), cs.data(), a.size());
 
316
        d->proc->writeToCommand(a);
 
317
}
 
318
 
 
319
void GpgOp::doFail()
 
320
{
 
321
        finished(false);
 
322
}
 
323
 
 
324
bool GpgOp::launchGPG(const QStringList &args, bool useExtra)
 
325
{
 
326
        d->proc = new GPGProc;
 
327
        connect(d->proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout()));
 
328
        connect(d->proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr()));
 
329
        connect(d->proc, SIGNAL(processExited()), SLOT(proc_processExited()));
 
330
        connect(d->proc, SIGNAL(wroteToStdin()), SLOT(proc_wroteToStdin()));
 
331
        connect(d->proc, SIGNAL(statusLine(const QString &)), SLOT(proc_statusLine(const QString &)));
 
332
 
 
333
        if(!d->proc->start(d->bin, args, useExtra)) {
 
334
                d->proc->disconnect(this);
 
335
                d->proc->deleteLater();
 
336
                d->proc = 0;
 
337
                return false;
 
338
        }
 
339
 
 
340
        return true;
 
341
}
 
342
 
 
343
void GpgOp::proc_readyReadStdout()
 
344
{
 
345
        QByteArray block = d->proc->readStdout();
 
346
        int oldsize = d->outbuf.size();
 
347
        d->outbuf.resize(oldsize + block.size());
 
348
        memcpy(d->outbuf.data() + oldsize, block.data(), block.size());
 
349
}
 
350
 
 
351
void GpgOp::proc_readyReadStderr()
 
352
{
 
353
        QByteArray block = d->proc->readStderr();
 
354
        int oldsize = d->errbuf.size();
 
355
        d->errbuf.resize(oldsize + block.size());
 
356
        memcpy(d->errbuf.data() + oldsize, block.data(), block.size());
 
357
}
 
358
 
 
359
void GpgOp::proc_wroteToStdin()
 
360
{
 
361
        d->proc->closeStdin();
 
362
}
 
363
 
 
364
void GpgOp::proc_statusLine(const QString &str)
 
365
{
 
366
#ifdef GPG_DEBUG
 
367
        printf("{%s}\n", str.latin1());
 
368
#endif
 
369
        QString s, rest;
 
370
        int n = str.find(' ');
 
371
        if(n == -1) {
 
372
                s = str;
 
373
                rest = "";
 
374
        }
 
375
        else {
 
376
                s = str.mid(0, n);
 
377
                rest = str.mid(n+1);
 
378
        }
 
379
 
 
380
        if(s == "NEED_PASSPHRASE") {
 
381
                if(!(d->tryAgent && getenv("GPG_AGENT_INFO"))) {
 
382
                        if(!d->didPassphrase) {
 
383
                                d->didPassphrase = true;
 
384
                                needPassphrase();
 
385
                        }
 
386
                        else {
 
387
                                QByteArray a(1);
 
388
                                a[0] = '\n';
 
389
                                d->proc->writeToCommand(a);
 
390
                        }
 
391
                }
 
392
        }
 
393
        else if(s == "BAD_PASSPHRASE") {
 
394
                d->badpp = true;
 
395
        }
 
396
        else if(s == "GOOD_PASSPHRASE") {
 
397
                d->badpp = false;
 
398
        }
 
399
        else if(s == "GOODSIG") {
 
400
                QString keyID;
 
401
                int n = rest.find(' ');
 
402
                if(n == -1)
 
403
                        keyID = rest;
 
404
                else
 
405
                        keyID = rest.mid(0, n);
 
406
                d->sigKeyID = keyID;
 
407
                d->verType = OpenPGP::VerifyGood;
 
408
        }
 
409
        else if(s == "BADSIG") {
 
410
                QString keyID;
 
411
                int n = rest.find(' ');
 
412
                if(n == -1)
 
413
                        keyID = rest;
 
414
                else
 
415
                        keyID = rest.mid(0, n);
 
416
                d->sigKeyID = keyID;
 
417
                d->verType = OpenPGP::VerifyBad;
 
418
        }
 
419
        else if(s == "ERRSIG") {
 
420
                QStringList list = QStringList::split(' ', rest, false);
 
421
                d->sigKeyID = list[0];
 
422
                d->sigTS.setTime_t(list[4].toInt());
 
423
                d->verType = OpenPGP::VerifyNoKey;
 
424
        }
 
425
        else if(s == "VALIDSIG") {
 
426
                QStringList list = QStringList::split(' ', rest, false);
 
427
                d->sigTS.setTime_t(list[2].toInt());
 
428
        }
 
429
}
 
430
 
 
431
void GpgOp::proc_processExited()
 
432
{
 
433
        if(!d->proc)
 
434
                return;
 
435
 
 
436
#ifdef GPG_DEBUG
 
437
        printf("GPG Finished: ");
 
438
#endif
 
439
 
 
440
        bool clean = true;
 
441
        int exitStatus = -1;
 
442
        if(!d->proc->normalExit()) {
 
443
                clean = false;
 
444
#ifdef GPG_DEBUG
 
445
                printf("bad exit.. crash?\n");
 
446
#endif
 
447
        }
 
448
        else {
 
449
                exitStatus = d->proc->exitStatus();
 
450
#ifdef GPG_DEBUG
 
451
                printf("exitStatus=%d\n", exitStatus);
 
452
#endif
 
453
        }
 
454
 
 
455
        d->proc->disconnect(this);
 
456
        d->proc->deleteLater();
 
457
        d->proc = 0;
 
458
 
 
459
        processResult(clean, exitStatus, d->outbuf, d->errbuf);
 
460
}
 
461
 
 
462
void GpgOp::processResult(bool clean, int code, const QByteArray &out, const QByteArray &err)
 
463
{
 
464
        // put stdout and stderr into QStrings
 
465
        QCString cs;
 
466
        cs.resize(out.size()+1);
 
467
        memcpy(cs.data(), out.data(), out.size());
 
468
        QString outstr = QString::fromLatin1(cs);
 
469
        cs.resize(err.size()+1);
 
470
        memcpy(cs.data(), err.data(), err.size());
 
471
        QString errstr = QString::fromLatin1(cs);
 
472
 
 
473
#ifdef GPG_DEBUG
 
474
        printf("stdout: [%s]\n", outstr.latin1());
 
475
        printf("stderr: [%s]\n", errstr.latin1());
 
476
#endif
 
477
 
 
478
        if(d->op == Check) {
 
479
                if(!clean || code != 0) {
 
480
                        doFail();
 
481
                        return;
 
482
                }
 
483
                finished(true);
 
484
        }
 
485
        else if(d->op == SecretKeyringFile || d->op == PublicKeyringFile) {
 
486
                if(!clean || code != 0) {
 
487
                        doFail();
 
488
                        return;
 
489
                }
 
490
                if(!findKeyringFilename(fixIncomingLines(outstr), &d->keyring)) {
 
491
                        doFail();
 
492
                        return;
 
493
                }
 
494
                finished(true);
 
495
        }
 
496
        else if(d->op == SecretKeys || d->op == PublicKeys) {
 
497
                if(!clean || code != 0) {
 
498
                        doFail();
 
499
                        return;
 
500
                }
 
501
                if(!stringToKeyList(fixIncomingLines(outstr), &d->keys, &d->keyring)) {
 
502
                        doFail();
 
503
                        return;
 
504
                }
 
505
                finished(true);
 
506
        }
 
507
        else if(d->op == Encrypt) {
 
508
                if(!clean || code != 0) {
 
509
                        doFail();
 
510
                        return;
 
511
                }
 
512
                d->enc = fixIncomingLines(outstr);
 
513
                finished(true);
 
514
        }
 
515
        else if(d->op == Decrypt) {
 
516
                if(!clean || code != 0) {
 
517
                        doFail();
 
518
                        return;
 
519
                }
 
520
                d->dec = out;
 
521
                finished(true);
 
522
        }
 
523
        else if(d->op == Sign) {
 
524
                if(!clean || code != 0) {
 
525
                        doFail();
 
526
                        return;
 
527
                }
 
528
                d->enc = fixIncomingLines(outstr);
 
529
                finished(true);
 
530
        }
 
531
        else if(d->op == Verify) {
 
532
                if(!clean || code != 0) {
 
533
                        doFail();
 
534
                        return;
 
535
                }
 
536
                finished(true);
 
537
        }
 
538
}
 
539
 
 
540
QString GpgOp::fixIncomingLines(const QString &str)
 
541
{
 
542
#ifdef Q_WS_WIN
 
543
        QString out;
 
544
        for(int n = 0; n < (int)str.length(); ++n) {
 
545
                if(str.at(n) == '\r' && (n + 1) < (int)str.length() && str.at(n+1) == '\n') {
 
546
                        out += '\n';
 
547
                        ++n;
 
548
                }
 
549
                else
 
550
                        out += str.at(n);
 
551
        }
 
552
        return out;
 
553
#else
 
554
        return str;
 
555
#endif
 
556
}
 
557
 
 
558
QString GpgOp::fixOutgoingLines(const QString &str)
 
559
{
 
560
#ifdef Q_WS_WIN
 
561
        QString out;
 
562
        for(int n = 0; n < (int)str.length(); ++n) {
 
563
                if(str.at(n) == '\n' && (n == 0 || str.at(n-1) != '\r'))
 
564
                        out += "\r\n";
 
565
                else
 
566
                        out += str.at(n);
 
567
        }
 
568
        return out;
 
569
#else
 
570
        return str;
 
571
#endif
 
572
}
 
573
 
 
574
bool GpgOp::stringToKeyList(const QString &outstr, OpenPGP::KeyList *_keylist, QString *_keyring)
 
575
{
 
576
        OpenPGP::KeyList keyList;
 
577
        QStringList lines = QStringList::split('\n', outstr, true);
 
578
 
 
579
        if(lines.count() < 1)
 
580
                return false;
 
581
 
 
582
        QStringList::ConstIterator it = lines.begin();
 
583
 
 
584
        // first line is keyring file
 
585
        QString keyring = *(it++);
 
586
 
 
587
        // if the second line isn't a divider, we are dealing
 
588
        // with a new version of gnupg that doesn't give us
 
589
        // the keyring file on gpg --list-keys --with-colons
 
590
        if(it == lines.end() || (*it).at(0) != '-') {
 
591
                // first line wasn't the keyring name...
 
592
                keyring = "";
 
593
                // ...so read the first line again
 
594
                it--;
 
595
        }
 
596
        else {
 
597
                // this was the divider line - skip it
 
598
                it++;
 
599
        }
 
600
 
 
601
        OpenPGP::Key *k = 0;
 
602
        for(; it != lines.end(); ++it) {
 
603
                QStringList f = QStringList::split(':', *it, true);
 
604
                QString type = f[0];
 
605
 
 
606
                if(type == "pub") {
 
607
                        if(k) {
 
608
                                keyList += (*k);
 
609
                                delete k;
 
610
                                k = 0;
 
611
                        }
 
612
                        k = new OpenPGP::Key;
 
613
 
 
614
                        k->setKeyID(f[4]);
 
615
                }
 
616
                else if(type == "sec") {
 
617
                        if(k) {
 
618
                                keyList += (*k);
 
619
                                delete k;
 
620
                                k = 0;
 
621
                        }
 
622
                        k = new OpenPGP::Key;
 
623
 
 
624
                        k->setKeyID(f[4]);
 
625
                }
 
626
                else if(type == "uid") {
 
627
                        if(!k)
 
628
                                continue;
 
629
                        // ignore a uid if we already have one
 
630
                        if(!k->userID().isEmpty())
 
631
                                continue;
 
632
                        QString s = f[9];
 
633
                        QCString uid;
 
634
                        // convert the "backslash" C-string syntax
 
635
                        for(int n = 0; n < (int)s.length(); ++n) {
 
636
                                if(s.at(n) == '\\' && n + 1 < (int)s.length()) {
 
637
                                        ++n;
 
638
                                        unsigned char c = (unsigned char)s.at(n).latin1();
 
639
                                        if(c == '\\')
 
640
                                                uid += '\\';
 
641
                                        else if(c == 'x' && n + 2 < (int)s.length()) {
 
642
                                                ++n;
 
643
                                                QString hex = s.mid(n, 2);
 
644
                                                bool ok;
 
645
                                                uint val = hex.toInt(&ok, 16);
 
646
                                                uid += (unsigned char)val;
 
647
                                                ++n; // only skip one, the for-loop will skip the next
 
648
                                        }
 
649
                                }
 
650
                                else {
 
651
                                        uid += (unsigned char)s.at(n).latin1();
 
652
                                }
 
653
                        }
 
654
                        k->setUserID(QString::fromUtf8(uid));
 
655
                }
 
656
        }
 
657
        if(k) {
 
658
                keyList += (*k);
 
659
                delete k;
 
660
                k = 0;
 
661
        }
 
662
 
 
663
        if(_keylist)
 
664
                *_keylist = keyList;
 
665
        if(_keyring)
 
666
                *_keyring = keyring;
 
667
 
 
668
        return true;
 
669
}
 
670
 
 
671
bool GpgOp::findKeyringFilename(const QString &outstr, QString *_keyring)
 
672
{
 
673
        QStringList lines = QStringList::split('\n', outstr, true);
 
674
        if(lines.count() < 1)
 
675
                return false;
 
676
 
 
677
        *_keyring = lines[0];
 
678
        return true;
 
679
}
 
680