Add convenience function to get key from sig
[gpgme.git] / lang / cpp / src / verificationresult.cpp
1 /*
2   verificationresult.cpp - wraps a gpgme verify result
3   Copyright (C) 2004 Klarälvdalens Datakonsult AB
4
5   This file is part of GPGME++.
6
7   GPGME++ is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public
9   License as published by the Free Software Foundation; either
10   version 2 of the License, or (at your option) any later version.
11
12   GPGME++ is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU Library General Public License for more details.
16
17   You should have received a copy of the GNU Library General Public License
18   along with GPGME++; see the file COPYING.LIB.  If not, write to the
19   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20   Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24  #include "config.h"
25 #endif
26
27 #include <verificationresult.h>
28 #include <notation.h>
29 #include "result_p.h"
30 #include "util.h"
31 #include "key.h"
32 #include "context.h"
33
34 #include <gpgme.h>
35
36 #include <istream>
37 #include <algorithm>
38 #include <iterator>
39 #include <string>
40 #include <cstring>
41 #include <cstdlib>
42
43 #include <string.h>
44
45 class GpgME::VerificationResult::Private
46 {
47 public:
48     explicit Private(const gpgme_verify_result_t r)
49     {
50         if (!r) {
51             return;
52         }
53         if (r->file_name) {
54             file_name = r->file_name;
55         }
56         // copy recursively, using compiler-generated copy ctor.
57         // We just need to handle the pointers in the structs:
58         for (gpgme_signature_t is = r->signatures ; is ; is = is->next) {
59             gpgme_signature_t scopy = new _gpgme_signature(*is);
60             if (is->fpr) {
61                 scopy->fpr = strdup(is->fpr);
62             }
63 // PENDING(marc) why does this crash on Windows in strdup()?
64 # ifndef _WIN32
65             if (is->pka_address) {
66                 scopy->pka_address = strdup(is->pka_address);
67             }
68 # else
69             scopy->pka_address = 0;
70 # endif
71             scopy->next = 0;
72             sigs.push_back(scopy);
73             // copy keys
74             if (scopy->key) {
75                 keys.push_back(Key(scopy->key, true));
76             } else {
77                 keys.push_back(Key());
78             }
79             // copy notations:
80             nota.push_back(std::vector<Nota>());
81             purls.push_back(0);
82             for (gpgme_sig_notation_t in = is->notations ; in ; in = in->next) {
83                 if (!in->name) {
84                     if (in->value) {
85                         purls.back() = strdup(in->value);   // policy url
86                     }
87                     continue;
88                 }
89                 Nota n = { 0, 0, in->flags };
90                 n.name = strdup(in->name);
91                 if (in->value) {
92                     n.value = strdup(in->value);
93                 }
94                 nota.back().push_back(n);
95             }
96         }
97     }
98     ~Private()
99     {
100         for (std::vector<gpgme_signature_t>::iterator it = sigs.begin() ; it != sigs.end() ; ++it) {
101             std::free((*it)->fpr);
102             std::free((*it)->pka_address);
103             delete *it; *it = 0;
104         }
105         for (std::vector< std::vector<Nota> >::iterator it = nota.begin() ; it != nota.end() ; ++it) {
106             for (std::vector<Nota>::iterator jt = it->begin() ; jt != it->end() ; ++jt) {
107                 std::free(jt->name);  jt->name = 0;
108                 std::free(jt->value); jt->value = 0;
109             }
110         }
111         std::for_each(purls.begin(), purls.end(), &std::free);
112     }
113
114     struct Nota {
115         char *name;
116         char *value;
117         gpgme_sig_notation_flags_t flags;
118     };
119
120     std::vector<gpgme_signature_t> sigs;
121     std::vector< std::vector<Nota> > nota;
122     std::vector<GpgME::Key> keys;
123     std::vector<char *> purls;
124     std::string file_name;
125     Protocol proto;
126 };
127
128 GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, int error)
129     : GpgME::Result(error), d()
130 {
131     init(ctx);
132 }
133
134 GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, const Error &error)
135     : GpgME::Result(error), d()
136 {
137     init(ctx);
138 }
139
140 void GpgME::VerificationResult::init(gpgme_ctx_t ctx)
141 {
142     if (!ctx) {
143         return;
144     }
145     gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
146     if (!res) {
147         return;
148     }
149     d.reset(new Private(res));
150     gpgme_protocol_t proto = gpgme_get_protocol(ctx);
151     d->proto = proto == GPGME_PROTOCOL_OpenPGP ? OpenPGP :
152                proto == GPGME_PROTOCOL_CMS ? CMS :
153                UnknownProtocol;
154 }
155
156 make_standard_stuff(VerificationResult)
157
158 const char *GpgME::VerificationResult::fileName() const
159 {
160     return d ? d->file_name.c_str() : 0 ;
161 }
162
163 unsigned int GpgME::VerificationResult::numSignatures() const
164 {
165     return d ? d->sigs.size() : 0 ;
166 }
167
168 GpgME::Signature GpgME::VerificationResult::signature(unsigned int idx) const
169 {
170     return Signature(d, idx);
171 }
172
173 std::vector<GpgME::Signature> GpgME::VerificationResult::signatures() const
174 {
175     if (!d) {
176         return std::vector<Signature>();
177     }
178     std::vector<Signature> result;
179     result.reserve(d->sigs.size());
180     for (unsigned int i = 0 ; i < d->sigs.size() ; ++i) {
181         result.push_back(Signature(d, i));
182     }
183     return result;
184 }
185
186 GpgME::Signature::Signature(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int i)
187     : d(parent), idx(i)
188 {
189 }
190
191 GpgME::Signature::Signature() : d(), idx(0) {}
192
193 bool GpgME::Signature::isNull() const
194 {
195     return !d || idx >= d->sigs.size() ;
196 }
197
198 GpgME::Signature::Summary GpgME::Signature::summary() const
199 {
200     if (isNull()) {
201         return None;
202     }
203     gpgme_sigsum_t sigsum = d->sigs[idx]->summary;
204     unsigned int result = 0;
205     if (sigsum & GPGME_SIGSUM_VALID) {
206         result |= Valid;
207     }
208     if (sigsum & GPGME_SIGSUM_GREEN) {
209         result |= Green;
210     }
211     if (sigsum & GPGME_SIGSUM_RED) {
212         result |= Red;
213     }
214     if (sigsum & GPGME_SIGSUM_KEY_REVOKED) {
215         result |= KeyRevoked;
216     }
217     if (sigsum & GPGME_SIGSUM_KEY_EXPIRED) {
218         result |= KeyExpired;
219     }
220     if (sigsum & GPGME_SIGSUM_SIG_EXPIRED) {
221         result |= SigExpired;
222     }
223     if (sigsum & GPGME_SIGSUM_KEY_MISSING) {
224         result |= KeyMissing;
225     }
226     if (sigsum & GPGME_SIGSUM_CRL_MISSING) {
227         result |= CrlMissing;
228     }
229     if (sigsum & GPGME_SIGSUM_CRL_TOO_OLD) {
230         result |= CrlTooOld;
231     }
232     if (sigsum & GPGME_SIGSUM_BAD_POLICY) {
233         result |= BadPolicy;
234     }
235     if (sigsum & GPGME_SIGSUM_SYS_ERROR) {
236         result |= SysError;
237     }
238     if (sigsum & GPGME_SIGSUM_TOFU_CONFLICT) {
239         result |= TofuConflict;
240     }
241     return static_cast<Summary>(result);
242 }
243
244 const char *GpgME::Signature::fingerprint() const
245 {
246     return isNull() ? 0 : d->sigs[idx]->fpr ;
247 }
248
249 GpgME::Error GpgME::Signature::status() const
250 {
251     return Error(isNull() ? 0 : d->sigs[idx]->status);
252 }
253
254 time_t GpgME::Signature::creationTime() const
255 {
256     return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->timestamp);
257 }
258
259 time_t GpgME::Signature::expirationTime() const
260 {
261     return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->exp_timestamp);
262 }
263
264 bool GpgME::Signature::neverExpires() const
265 {
266     return expirationTime() == (time_t)0;
267 }
268
269 bool GpgME::Signature::isWrongKeyUsage() const
270 {
271     return !isNull() && d->sigs[idx]->wrong_key_usage;
272 }
273
274 bool GpgME::Signature::isVerifiedUsingChainModel() const
275 {
276     return !isNull() && d->sigs[idx]->chain_model;
277 }
278
279 GpgME::Signature::PKAStatus GpgME::Signature::pkaStatus() const
280 {
281     if (!isNull()) {
282         return static_cast<PKAStatus>(d->sigs[idx]->pka_trust);
283     }
284     return UnknownPKAStatus;
285 }
286
287 const char *GpgME::Signature::pkaAddress() const
288 {
289     if (!isNull()) {
290         return d->sigs[idx]->pka_address;
291     }
292     return 0;
293 }
294
295 GpgME::Signature::Validity GpgME::Signature::validity() const
296 {
297     if (isNull()) {
298         return Unknown;
299     }
300     switch (d->sigs[idx]->validity) {
301     default:
302     case GPGME_VALIDITY_UNKNOWN:   return Unknown;
303     case GPGME_VALIDITY_UNDEFINED: return Undefined;
304     case GPGME_VALIDITY_NEVER:     return Never;
305     case GPGME_VALIDITY_MARGINAL:  return Marginal;
306     case GPGME_VALIDITY_FULL:      return Full;
307     case GPGME_VALIDITY_ULTIMATE:  return Ultimate;
308     }
309 }
310
311 char GpgME::Signature::validityAsString() const
312 {
313     if (isNull()) {
314         return '?';
315     }
316     switch (d->sigs[idx]->validity) {
317     default:
318     case GPGME_VALIDITY_UNKNOWN:   return '?';
319     case GPGME_VALIDITY_UNDEFINED: return 'q';
320     case GPGME_VALIDITY_NEVER:     return 'n';
321     case GPGME_VALIDITY_MARGINAL:  return 'm';
322     case GPGME_VALIDITY_FULL:      return 'f';
323     case GPGME_VALIDITY_ULTIMATE:  return 'u';
324     }
325 }
326
327 GpgME::Error GpgME::Signature::nonValidityReason() const
328 {
329     return Error(isNull() ? 0 : d->sigs[idx]->validity_reason);
330 }
331
332 unsigned int GpgME::Signature::publicKeyAlgorithm() const
333 {
334     if (!isNull()) {
335         return d->sigs[idx]->pubkey_algo;
336     }
337     return 0;
338 }
339
340 const char *GpgME::Signature::publicKeyAlgorithmAsString() const
341 {
342     if (!isNull()) {
343         return gpgme_pubkey_algo_name(d->sigs[idx]->pubkey_algo);
344     }
345     return 0;
346 }
347
348 unsigned int GpgME::Signature::hashAlgorithm() const
349 {
350     if (!isNull()) {
351         return d->sigs[idx]->hash_algo;
352     }
353     return 0;
354 }
355
356 const char *GpgME::Signature::hashAlgorithmAsString() const
357 {
358     if (!isNull()) {
359         return gpgme_hash_algo_name(d->sigs[idx]->hash_algo);
360     }
361     return 0;
362 }
363
364 const char *GpgME::Signature::policyURL() const
365 {
366     return isNull() ? 0 : d->purls[idx] ;
367 }
368
369 GpgME::Notation GpgME::Signature::notation(unsigned int nidx) const
370 {
371     return GpgME::Notation(d, idx, nidx);
372 }
373
374 std::vector<GpgME::Notation> GpgME::Signature::notations() const
375 {
376     if (isNull()) {
377         return std::vector<GpgME::Notation>();
378     }
379     std::vector<GpgME::Notation> result;
380     result.reserve(d->nota[idx].size());
381     for (unsigned int i = 0 ; i < d->nota[idx].size() ; ++i) {
382         result.push_back(GpgME::Notation(d, idx, i));
383     }
384     return result;
385 }
386
387 GpgME::Key GpgME::Signature::key() const
388 {
389     if (isNull()) {
390         return Key();
391     }
392     return d->keys[idx];
393 }
394
395 GpgME::Key GpgME::Signature::key(bool search, bool update) const
396 {
397     if (isNull()) {
398         return Key();
399     }
400
401     GpgME::Key ret = key();
402     if (ret.isNull() && search) {
403         auto ctx = Context::createForProtocol (d->proto);
404         if (ctx) {
405             ctx->setKeyListMode(KeyListMode::Local |
406                         KeyListMode::Signatures |
407                         KeyListMode::SignatureNotations |
408                         KeyListMode::Validate |
409                         KeyListMode::WithTofu);
410             Error e;
411             ret = d->keys[idx] = ctx->key(fingerprint(), e, false);
412             delete ctx;
413         }
414     }
415     if (update) {
416         ret.update();
417     }
418     return ret;
419 }
420
421 class GpgME::Notation::Private
422 {
423 public:
424     Private() : d(), sidx(0), nidx(0), nota(0) {}
425     Private(const std::shared_ptr<VerificationResult::Private> &priv, unsigned int sindex, unsigned int nindex)
426         : d(priv), sidx(sindex), nidx(nindex), nota(0)
427     {
428
429     }
430     Private(gpgme_sig_notation_t n)
431         : d(), sidx(0), nidx(0), nota(n ? new _gpgme_sig_notation(*n) : 0)
432     {
433         if (nota && nota->name) {
434             nota->name = strdup(nota->name);
435         }
436         if (nota && nota->value) {
437             nota->value = strdup(nota->value);
438         }
439     }
440     Private(const Private &other)
441         : d(other.d), sidx(other.sidx), nidx(other.nidx), nota(other.nota)
442     {
443         if (nota) {
444             nota->name = strdup(nota->name);
445             nota->value = strdup(nota->value);
446         }
447     }
448     ~Private()
449     {
450         if (nota) {
451             std::free(nota->name);  nota->name = 0;
452             std::free(nota->value); nota->value = 0;
453             delete nota;
454         }
455     }
456
457     std::shared_ptr<VerificationResult::Private> d;
458     unsigned int sidx, nidx;
459     gpgme_sig_notation_t nota;
460 };
461
462 GpgME::Notation::Notation(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int sindex, unsigned int nindex)
463     : d(new Private(parent, sindex, nindex))
464 {
465
466 }
467
468 GpgME::Notation::Notation(gpgme_sig_notation_t nota)
469     : d(new Private(nota))
470 {
471
472 }
473
474 GpgME::Notation::Notation() : d() {}
475
476 bool GpgME::Notation::isNull() const
477 {
478     if (!d) {
479         return true;
480     }
481     if (d->d) {
482         return d->sidx >= d->d->nota.size() || d->nidx >= d->d->nota[d->sidx].size() ;
483     }
484     return !d->nota;
485 }
486
487 const char *GpgME::Notation::name() const
488 {
489     return
490         isNull() ? 0 :
491         d->d ? d->d->nota[d->sidx][d->nidx].name :
492         d->nota ? d->nota->name : 0 ;
493 }
494
495 const char *GpgME::Notation::value() const
496 {
497     return
498         isNull() ? 0 :
499         d->d ? d->d->nota[d->sidx][d->nidx].value :
500         d->nota ? d->nota->value : 0 ;
501 }
502
503 GpgME::Notation::Flags GpgME::Notation::flags() const
504 {
505     return
506         convert_from_gpgme_sig_notation_flags_t(
507             isNull() ? 0 :
508             d->d ? d->d->nota[d->sidx][d->nidx].flags :
509             d->nota ? d->nota->flags : 0);
510 }
511
512 bool GpgME::Notation::isHumanReadable() const
513 {
514     return flags() & HumanReadable;
515 }
516
517 bool GpgME::Notation::isCritical() const
518 {
519     return flags() & Critical;
520 }
521
522 std::ostream &GpgME::operator<<(std::ostream &os, const VerificationResult &result)
523 {
524     os << "GpgME::VerificationResult(";
525     if (!result.isNull()) {
526         os << "\n error:      " << result.error()
527            << "\n fileName:   " << protect(result.fileName())
528            << "\n signatures:\n";
529         const std::vector<Signature> sigs = result.signatures();
530         std::copy(sigs.begin(), sigs.end(),
531                   std::ostream_iterator<Signature>(os, "\n"));
532     }
533     return os << ')';
534 }
535
536 std::ostream &GpgME::operator<<(std::ostream &os, Signature::PKAStatus pkaStatus)
537 {
538 #define OUTPUT( x ) if ( !(pkaStatus & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
539     os << "GpgME::Signature::PKAStatus(";
540     OUTPUT(UnknownPKAStatus);
541     OUTPUT(PKAVerificationFailed);
542     OUTPUT(PKAVerificationSucceeded);
543 #undef OUTPUT
544     return os << ')';
545 }
546
547 std::ostream &GpgME::operator<<(std::ostream &os, Signature::Summary summary)
548 {
549 #define OUTPUT( x ) if ( !(summary & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
550     os << "GpgME::Signature::Summary(";
551     OUTPUT(Valid);
552     OUTPUT(Green);
553     OUTPUT(Red);
554     OUTPUT(KeyRevoked);
555     OUTPUT(KeyExpired);
556     OUTPUT(SigExpired);
557     OUTPUT(KeyMissing);
558     OUTPUT(CrlMissing);
559     OUTPUT(CrlTooOld);
560     OUTPUT(BadPolicy);
561     OUTPUT(SysError);
562     OUTPUT(TofuConflict);
563 #undef OUTPUT
564     return os << ')';
565 }
566
567 std::ostream &GpgME::operator<<(std::ostream &os, const Signature &sig)
568 {
569     os << "GpgME::Signature(";
570     if (!sig.isNull()) {
571         os << "\n Summary:                   " << sig.summary()
572            << "\n Fingerprint:               " << protect(sig.fingerprint())
573            << "\n Status:                    " << sig.status()
574            << "\n creationTime:              " << sig.creationTime()
575            << "\n expirationTime:            " << sig.expirationTime()
576            << "\n isWrongKeyUsage:           " << sig.isWrongKeyUsage()
577            << "\n isVerifiedUsingChainModel: " << sig.isVerifiedUsingChainModel()
578            << "\n pkaStatus:                 " << sig.pkaStatus()
579            << "\n pkaAddress:                " << protect(sig.pkaAddress())
580            << "\n validity:                  " << sig.validityAsString()
581            << "\n nonValidityReason:         " << sig.nonValidityReason()
582            << "\n publicKeyAlgorithm:        " << protect(sig.publicKeyAlgorithmAsString())
583            << "\n hashAlgorithm:             " << protect(sig.hashAlgorithmAsString())
584            << "\n policyURL:                 " << protect(sig.policyURL())
585            << "\n notations:\n";
586         const std::vector<Notation> nota = sig.notations();
587         std::copy(nota.begin(), nota.end(),
588                   std::ostream_iterator<Notation>(os, "\n"));
589     }
590     return os << ')';
591 }
592
593 std::ostream &GpgME::operator<<(std::ostream &os, Notation::Flags flags)
594 {
595     os << "GpgME::Notation::Flags(";
596 #define OUTPUT( x ) if ( !(flags & (GpgME::Notation:: x)) ) {} else do { os << #x " "; } while(0)
597     OUTPUT(HumanReadable);
598     OUTPUT(Critical);
599 #undef OUTPUT
600     return os << ')';
601 }
602
603 std::ostream &GpgME::operator<<(std::ostream &os, const Notation &nota)
604 {
605     os << "GpgME::Signature::Notation(";
606     if (!nota.isNull()) {
607         os << "\n name:  " << protect(nota.name())
608            << "\n value: " << protect(nota.value())
609            << "\n flags: " << nota.flags()
610            << '\n';
611     }
612     return os << ")";
613 }