Cpp / Qt: Reduce boost usage (memory and tuple)
[gpgme.git] / lang / qt / src / qgpgmebackend.cpp
1 /*
2     qgpgmebackend.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2004,2005 Klarälvdalens Datakonsult AB
6     Copyright (c) 2016 Intevation GmbH
7
8     Libkleopatra is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.
12
13     Libkleopatra is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
22     In addition, as a special exception, the copyright holders give
23     permission to link the code of this program with any edition of
24     the Qt library by Trolltech AS, Norway (or with modified versions
25     of Qt that use the same license as Qt), and distribute linked
26     combinations including the two.  You must obey the GNU General
27     Public License in all respects for all of the code used other than
28     Qt.  If you modify this file, you may extend this exception to
29     your version of the file, but you are not obligated to do so.  If
30     you do not wish to do so, delete this exception statement from
31     your version.
32 */
33
34 #include "qgpgmebackend.h"
35
36 #include "qgpgmenewcryptoconfig.h"
37
38 #include "qgpgmekeygenerationjob.h"
39 #include "qgpgmekeylistjob.h"
40 #include "qgpgmelistallkeysjob.h"
41 #include "qgpgmedecryptjob.h"
42 #include "qgpgmedecryptverifyjob.h"
43 #include "qgpgmerefreshkeysjob.h"
44 #include "qgpgmedeletejob.h"
45 #include "qgpgmesecretkeyexportjob.h"
46 #include "qgpgmedownloadjob.h"
47 #include "qgpgmesignencryptjob.h"
48 #include "qgpgmeencryptjob.h"
49 #include "qgpgmesignjob.h"
50 #include "qgpgmesignkeyjob.h"
51 #include "qgpgmeexportjob.h"
52 #include "qgpgmeverifydetachedjob.h"
53 #include "qgpgmeimportjob.h"
54 #include "qgpgmeimportfromkeyserverjob.h"
55 #include "qgpgmeverifyopaquejob.h"
56 #include "qgpgmechangeexpiryjob.h"
57 #include "qgpgmechangeownertrustjob.h"
58 #include "qgpgmechangepasswdjob.h"
59 #include "qgpgmeadduseridjob.h"
60
61 #include "error.h"
62 #include "engineinfo.h"
63
64 #include <QFile>
65 #include <QString>
66
67 const char QGpgME::QGpgMEBackend::OpenPGP[] = "OpenPGP";
68 const char QGpgME::QGpgMEBackend::SMIME[] = "SMIME";
69
70 namespace
71 {
72
73 class Protocol : public QGpgME::Protocol
74 {
75     GpgME::Protocol mProtocol;
76 public:
77     explicit Protocol(GpgME::Protocol proto) : mProtocol(proto) {}
78
79     QString name() const Q_DECL_OVERRIDE
80     {
81         switch (mProtocol) {
82         case GpgME::OpenPGP: return QStringLiteral("OpenPGP");
83         case GpgME::CMS:     return QStringLiteral("SMIME");
84         default:             return QString();
85         }
86     }
87
88     QString displayName() const Q_DECL_OVERRIDE
89     {
90         // ah (2.4.16): Where is this used and isn't this inverted
91         // with name
92         switch (mProtocol) {
93         case GpgME::OpenPGP: return QStringLiteral("gpg");
94         case GpgME::CMS:     return QStringLiteral("gpgsm");
95         default:             return QStringLiteral("unknown");
96         }
97     }
98
99     QGpgME::SpecialJob *specialJob(const char *, const QMap<QString, QVariant> &) const Q_DECL_OVERRIDE
100     {
101         return 0;
102     }
103
104     QGpgME::KeyListJob *keyListJob(bool remote, bool includeSigs, bool validate) const Q_DECL_OVERRIDE
105     {
106         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
107         if (!context) {
108             return 0;
109         }
110
111         unsigned int mode = context->keyListMode();
112         if (remote) {
113             mode |= GpgME::Extern;
114             mode &= ~GpgME::Local;
115         } else {
116             mode |= GpgME::Local;
117             mode &= ~GpgME::Extern;
118         }
119         if (includeSigs) {
120             mode |= GpgME::Signatures;
121         }
122         if (validate) {
123             mode |= GpgME::Validate;
124         }
125         context->setKeyListMode(mode);
126         return new QGpgME::QGpgMEKeyListJob(context);
127     }
128
129     QGpgME::ListAllKeysJob *listAllKeysJob(bool includeSigs, bool validate) const Q_DECL_OVERRIDE
130     {
131         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
132         if (!context) {
133             return 0;
134         }
135
136         unsigned int mode = context->keyListMode();
137         mode |= GpgME::Local;
138         mode &= ~GpgME::Extern;
139         if (includeSigs) {
140             mode |= GpgME::Signatures;
141         }
142         if (validate) {
143             mode |= GpgME::Validate;
144             /* Setting the context to offline mode disables CRL / OCSP checks in
145                this Job. Otherwise we would try to fetch the CRL's for all CMS
146                keys in the users keyring because GpgME::Validate includes remote
147                resources by default in the validity check.
148                This setting only has any effect if gpgsm >= 2.1.6 is used.
149                */
150             context->setOffline(true);
151         }
152         context->setKeyListMode(mode);
153         return new QGpgME::QGpgMEListAllKeysJob(context);
154     }
155
156     QGpgME::EncryptJob *encryptJob(bool armor, bool textmode) const Q_DECL_OVERRIDE
157     {
158         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
159         if (!context) {
160             return 0;
161         }
162
163         context->setArmor(armor);
164         context->setTextMode(textmode);
165         return new QGpgME::QGpgMEEncryptJob(context);
166     }
167
168     QGpgME::DecryptJob *decryptJob() const Q_DECL_OVERRIDE
169     {
170         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
171         if (!context) {
172             return 0;
173         }
174         return new QGpgME::QGpgMEDecryptJob(context);
175     }
176
177     QGpgME::SignJob *signJob(bool armor, bool textMode) const Q_DECL_OVERRIDE
178     {
179         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
180         if (!context) {
181             return 0;
182         }
183
184         context->setArmor(armor);
185         context->setTextMode(textMode);
186         return new QGpgME::QGpgMESignJob(context);
187     }
188
189     QGpgME::VerifyDetachedJob *verifyDetachedJob(bool textMode) const Q_DECL_OVERRIDE
190     {
191         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
192         if (!context) {
193             return 0;
194         }
195
196         context->setTextMode(textMode);
197         return new QGpgME::QGpgMEVerifyDetachedJob(context);
198     }
199
200     QGpgME::VerifyOpaqueJob *verifyOpaqueJob(bool textMode) const Q_DECL_OVERRIDE
201     {
202         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
203         if (!context) {
204             return 0;
205         }
206
207         context->setTextMode(textMode);
208         return new QGpgME::QGpgMEVerifyOpaqueJob(context);
209     }
210
211     QGpgME::KeyGenerationJob *keyGenerationJob() const Q_DECL_OVERRIDE
212     {
213         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
214         if (!context) {
215             return 0;
216         }
217         return new QGpgME::QGpgMEKeyGenerationJob(context);
218     }
219
220     QGpgME::ImportJob *importJob() const Q_DECL_OVERRIDE
221     {
222         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
223         if (!context) {
224             return 0;
225         }
226         return new QGpgME::QGpgMEImportJob(context);
227     }
228
229     QGpgME::ImportFromKeyserverJob *importFromKeyserverJob() const Q_DECL_OVERRIDE
230     {
231         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
232         if (!context) {
233             return 0;
234         }
235         return new QGpgME::QGpgMEImportFromKeyserverJob(context);
236     }
237
238     QGpgME::ExportJob *publicKeyExportJob(bool armor) const Q_DECL_OVERRIDE
239     {
240         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
241         if (!context) {
242             return 0;
243         }
244
245         context->setArmor(armor);
246         return new QGpgME::QGpgMEExportJob(context);
247     }
248
249     QGpgME::ExportJob *secretKeyExportJob(bool armor, const QString &charset) const Q_DECL_OVERRIDE
250     {
251         if (mProtocol != GpgME::CMS) { // fixme: add support for gpg, too
252             return 0;
253         }
254
255         // this operation is not supported by gpgme, so we have to call gpgsm ourselves:
256         return new QGpgME::QGpgMESecretKeyExportJob(armor, charset);
257     }
258
259     QGpgME::RefreshKeysJob *refreshKeysJob() const Q_DECL_OVERRIDE
260     {
261         if (mProtocol != GpgME::CMS) { // fixme: add support for gpg, too
262             return 0;
263         }
264
265         // this operation is not supported by gpgme, so we have to call gpgsm ourselves:
266         return new QGpgME::QGpgMERefreshKeysJob();
267     }
268
269     QGpgME::DownloadJob *downloadJob(bool armor) const Q_DECL_OVERRIDE
270     {
271         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
272         if (!context) {
273             return 0;
274         }
275
276         context->setArmor(armor);
277         // this is the hackish interface for downloading from keyserers currently:
278         context->setKeyListMode(GpgME::Extern);
279         return new QGpgME::QGpgMEDownloadJob(context);
280     }
281
282     QGpgME::DeleteJob *deleteJob() const Q_DECL_OVERRIDE
283     {
284         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
285         if (!context) {
286             return 0;
287         }
288         return new QGpgME::QGpgMEDeleteJob(context);
289     }
290
291     QGpgME::SignEncryptJob *signEncryptJob(bool armor, bool textMode) const Q_DECL_OVERRIDE
292     {
293         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
294         if (!context) {
295             return 0;
296         }
297
298         context->setArmor(armor);
299         context->setTextMode(textMode);
300         return new QGpgME::QGpgMESignEncryptJob(context);
301     }
302
303     QGpgME::DecryptVerifyJob *decryptVerifyJob(bool textMode) const Q_DECL_OVERRIDE
304     {
305         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
306         if (!context) {
307             return 0;
308         }
309
310         context->setTextMode(textMode);
311         return new QGpgME::QGpgMEDecryptVerifyJob(context);
312     }
313
314     QGpgME::ChangeExpiryJob *changeExpiryJob() const Q_DECL_OVERRIDE
315     {
316         if (mProtocol != GpgME::OpenPGP) {
317             return 0;    // only supported by gpg
318         }
319
320         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
321         if (!context) {
322             return 0;
323         }
324         return new QGpgME::QGpgMEChangeExpiryJob(context);
325     }
326
327     QGpgME::ChangePasswdJob *changePasswdJob() const Q_DECL_OVERRIDE
328     {
329         if (!GpgME::hasFeature(GpgME::PasswdFeature, 0)) {
330             return 0;
331         }
332         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
333         if (!context) {
334             return 0;
335         }
336         return new QGpgME::QGpgMEChangePasswdJob(context);
337     }
338
339     QGpgME::SignKeyJob *signKeyJob() const Q_DECL_OVERRIDE
340     {
341         if (mProtocol != GpgME::OpenPGP) {
342             return 0;    // only supported by gpg
343         }
344
345         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
346         if (!context) {
347             return 0;
348         }
349         return new QGpgME::QGpgMESignKeyJob(context);
350     }
351
352     QGpgME::ChangeOwnerTrustJob *changeOwnerTrustJob() const Q_DECL_OVERRIDE
353     {
354         if (mProtocol != GpgME::OpenPGP) {
355             return 0;    // only supported by gpg
356         }
357
358         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
359         if (!context) {
360             return 0;
361         }
362         return new QGpgME::QGpgMEChangeOwnerTrustJob(context);
363     }
364
365     QGpgME::AddUserIDJob *addUserIDJob() const Q_DECL_OVERRIDE
366     {
367         if (mProtocol != GpgME::OpenPGP) {
368             return 0;    // only supported by gpg
369         }
370
371         GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
372         if (!context) {
373             return 0;
374         }
375         return new QGpgME::QGpgMEAddUserIDJob(context);
376     }
377
378 };
379
380 }
381
382 QGpgME::QGpgMEBackend::QGpgMEBackend()
383     : mCryptoConfig(0),
384       mOpenPGPProtocol(0),
385       mSMIMEProtocol(0)
386 {
387     GpgME::initializeLibrary();
388 }
389
390 QGpgME::QGpgMEBackend::~QGpgMEBackend()
391 {
392     delete mCryptoConfig; mCryptoConfig = 0;
393     delete mOpenPGPProtocol; mOpenPGPProtocol = 0;
394     delete mSMIMEProtocol; mSMIMEProtocol = 0;
395 }
396
397 QString QGpgME::QGpgMEBackend::name() const
398 {
399     return QStringLiteral("gpgme");
400 }
401
402 QString QGpgME::QGpgMEBackend::displayName() const
403 {
404     return QStringLiteral("GpgME");
405 }
406
407 QGpgME::CryptoConfig *QGpgME::QGpgMEBackend::config() const
408 {
409     if (!mCryptoConfig) {
410         if (GpgME::hasFeature(GpgME::GpgConfEngineFeature, 0)) {
411             mCryptoConfig = new QGpgMENewCryptoConfig;
412         }
413     }
414     return mCryptoConfig;
415 }
416
417 static bool check(GpgME::Protocol proto, QString *reason)
418 {
419     if (!GpgME::checkEngine(proto)) {
420         return true;
421     }
422     if (!reason) {
423         return false;
424     }
425     // error, check why:
426 #if 0
427 Port away from localised string or delete.
428     const GpgME::EngineInfo ei = GpgME::engineInfo(proto);
429     if (ei.isNull()) {
430         *reason = i18n("GPGME was compiled without support for %1.", proto == GpgME::CMS ? QLatin1String("S/MIME") : QLatin1String("OpenPGP"));
431     } else if (ei.fileName() && !ei.version()) {
432         *reason = i18n("Engine %1 is not installed properly.", QFile::decodeName(ei.fileName()));
433     } else if (ei.fileName() && ei.version() && ei.requiredVersion())
434         *reason = i18n("Engine %1 version %2 installed, "
435                        "but at least version %3 is required.",
436                        QFile::decodeName(ei.fileName()), QLatin1String(ei.version()), QLatin1String(ei.requiredVersion()));
437     else {
438         *reason = i18n("Unknown problem with engine for protocol %1.", proto == GpgME::CMS ? QLatin1String("S/MIME") : QLatin1String("OpenPGP"));
439     }
440 #endif
441     return false;
442 }
443
444 bool QGpgME::QGpgMEBackend::checkForOpenPGP(QString *reason) const
445 {
446     return check(GpgME::OpenPGP, reason);
447 }
448
449 bool QGpgME::QGpgMEBackend::checkForSMIME(QString *reason) const
450 {
451     return check(GpgME::CMS, reason);
452 }
453
454 bool QGpgME::QGpgMEBackend::checkForProtocol(const char *name, QString *reason) const
455 {
456     if (qstricmp(name, OpenPGP) == 0) {
457         return check(GpgME::OpenPGP, reason);
458     }
459     if (qstricmp(name, SMIME) == 0) {
460         return check(GpgME::CMS, reason);
461     }
462     if (reason) {
463         *reason = QStringLiteral("Unsupported protocol \"%1\"").arg(QLatin1String(name));
464     }
465     return false;
466 }
467
468 QGpgME::Protocol *QGpgME::QGpgMEBackend::openpgp() const
469 {
470     if (!mOpenPGPProtocol)
471         if (checkForOpenPGP()) {
472             mOpenPGPProtocol = new ::Protocol(GpgME::OpenPGP);
473         }
474     return mOpenPGPProtocol;
475 }
476
477 QGpgME::Protocol *QGpgME::QGpgMEBackend::smime() const
478 {
479     if (!mSMIMEProtocol)
480         if (checkForSMIME()) {
481             mSMIMEProtocol = new ::Protocol(GpgME::CMS);
482         }
483     return mSMIMEProtocol;
484 }
485
486 QGpgME::Protocol *QGpgME::QGpgMEBackend::protocol(const char *name) const
487 {
488     if (qstricmp(name, OpenPGP) == 0) {
489         return openpgp();
490     }
491     if (qstricmp(name, SMIME) == 0) {
492         return smime();
493     }
494     return 0;
495 }
496
497 bool QGpgME::QGpgMEBackend::supportsProtocol(const char *name) const
498 {
499     return qstricmp(name, OpenPGP) == 0 || qstricmp(name, SMIME) == 0;
500 }
501
502 const char *QGpgME::QGpgMEBackend::enumerateProtocols(int i) const
503 {
504     switch (i) {
505     case 0: return OpenPGP;
506     case 1: return SMIME;
507     default: return 0;
508     }
509 }
510
511 static QGpgME::QGpgMEBackend *gpgmeBackend;
512
513 QGpgME::Protocol *QGpgME::openpgp()
514 {
515     if (!gpgmeBackend) {
516         gpgmeBackend = new QGpgME::QGpgMEBackend();
517     }
518     return gpgmeBackend->openpgp();
519 }
520
521 QGpgME::Protocol *QGpgME::smime()
522 {
523     if (!gpgmeBackend) {
524         gpgmeBackend = new QGpgME::QGpgMEBackend();
525     }
526     return gpgmeBackend->smime();
527 }