cpp: Add shorthand for key locate
[gpgme.git] / lang / cpp / src / key.cpp
1 /*
2   key.cpp - wraps a gpgme key
3   Copyright (C) 2003, 2005 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 <key.h>
28
29 #include "util.h"
30 #include "tofuinfo.h"
31 #include "context.h"
32
33 #include <gpgme.h>
34
35 #include <string.h>
36 #include <strings.h>
37 #include <istream>
38 #include <iterator>
39
40 const GpgME::Key::Null GpgME::Key::null;
41
42 namespace GpgME
43 {
44
45 Key::Key() : key() {}
46
47 Key::Key(const Null &) : key() {}
48
49 Key::Key(const shared_gpgme_key_t &k) : key(k) {}
50
51 Key::Key(gpgme_key_t k, bool ref)
52     : key(k
53           ? shared_gpgme_key_t(k, &gpgme_key_unref)
54           : shared_gpgme_key_t())
55 {
56     if (ref && impl()) {
57         gpgme_key_ref(impl());
58     }
59 }
60
61 UserID Key::userID(unsigned int index) const
62 {
63     return UserID(key, index);
64 }
65
66 Subkey Key::subkey(unsigned int index) const
67 {
68     return Subkey(key, index);
69 }
70
71 unsigned int Key::numUserIDs() const
72 {
73     if (!key) {
74         return 0;
75     }
76     unsigned int count = 0;
77     for (gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next) {
78         ++count;
79     }
80     return count;
81 }
82
83 unsigned int Key::numSubkeys() const
84 {
85     if (!key) {
86         return 0;
87     }
88     unsigned int count = 0;
89     for (gpgme_sub_key_t subkey = key->subkeys ; subkey ; subkey = subkey->next) {
90         ++count;
91     }
92     return count;
93 }
94
95 std::vector<UserID> Key::userIDs() const
96 {
97     if (!key) {
98         return std::vector<UserID>();
99     }
100
101     std::vector<UserID> v;
102     v.reserve(numUserIDs());
103     for (gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next) {
104         v.push_back(UserID(key, uid));
105     }
106     return v;
107 }
108
109 std::vector<Subkey> Key::subkeys() const
110 {
111     if (!key) {
112         return std::vector<Subkey>();
113     }
114
115     std::vector<Subkey> v;
116     v.reserve(numSubkeys());
117     for (gpgme_sub_key_t subkey = key->subkeys ; subkey ; subkey = subkey->next) {
118         v.push_back(Subkey(key, subkey));
119     }
120     return v;
121 }
122
123 Key::OwnerTrust Key::ownerTrust() const
124 {
125     if (!key) {
126         return Unknown;
127     }
128     switch (key->owner_trust) {
129     default:
130     case GPGME_VALIDITY_UNKNOWN:   return Unknown;
131     case GPGME_VALIDITY_UNDEFINED: return Undefined;
132     case GPGME_VALIDITY_NEVER:     return Never;
133     case GPGME_VALIDITY_MARGINAL:  return Marginal;
134     case GPGME_VALIDITY_FULL:     return Full;
135     case GPGME_VALIDITY_ULTIMATE: return Ultimate;
136     }
137 }
138 char Key::ownerTrustAsString() const
139 {
140     if (!key) {
141         return '?';
142     }
143     switch (key->owner_trust) {
144     default:
145     case GPGME_VALIDITY_UNKNOWN:   return '?';
146     case GPGME_VALIDITY_UNDEFINED: return 'q';
147     case GPGME_VALIDITY_NEVER:     return 'n';
148     case GPGME_VALIDITY_MARGINAL:  return 'm';
149     case GPGME_VALIDITY_FULL:     return 'f';
150     case GPGME_VALIDITY_ULTIMATE: return 'u';
151     }
152 }
153
154 Protocol Key::protocol() const
155 {
156     if (!key) {
157         return UnknownProtocol;
158     }
159     switch (key->protocol) {
160     case GPGME_PROTOCOL_CMS:     return CMS;
161     case GPGME_PROTOCOL_OpenPGP: return OpenPGP;
162     default:                     return UnknownProtocol;
163     }
164 }
165
166 const char *Key::protocolAsString() const
167 {
168     return key ? gpgme_get_protocol_name(key->protocol) : 0 ;
169 }
170
171 bool Key::isRevoked() const
172 {
173     return key && key->revoked;
174 }
175
176 bool Key::isExpired() const
177 {
178     return key && key->expired;
179 }
180
181 bool Key::isDisabled() const
182 {
183     return key && key->disabled;
184 }
185
186 bool Key::isInvalid() const
187 {
188     return key && key->invalid;
189 }
190
191 bool Key::hasSecret() const
192 {
193     return key && key->secret;
194 }
195
196 bool Key::isRoot() const
197 {
198     return key && key->subkeys && key->subkeys->fpr && key->chain_id &&
199            strcasecmp(key->subkeys->fpr, key->chain_id) == 0;
200 }
201
202 bool Key::canEncrypt() const
203 {
204     return key && key->can_encrypt;
205 }
206
207 bool Key::canSign() const
208 {
209 #ifndef GPGME_CAN_SIGN_ON_SECRET_OPENPGP_KEYLISTING_NOT_BROKEN
210     if (key && key->protocol == GPGME_PROTOCOL_OpenPGP) {
211         return true;
212     }
213 #endif
214     return canReallySign();
215 }
216
217 bool Key::canReallySign() const
218 {
219     return key && key->can_sign;
220 }
221
222 bool Key::canCertify() const
223 {
224     return key && key->can_certify;
225 }
226
227 bool Key::canAuthenticate() const
228 {
229     return key && key->can_authenticate;
230 }
231
232 bool Key::isQualified() const
233 {
234     return key && key->is_qualified;
235 }
236
237 bool Key::isDeVs() const
238 {
239     if (!key) {
240         return false;
241     }
242     if (!key->subkeys || !key->subkeys->is_de_vs) {
243         return false;
244     }
245     for (gpgme_sub_key_t subkey = key->subkeys ; subkey ; subkey = subkey->next) {
246         if (!subkey->is_de_vs) {
247             return false;
248         }
249     }
250     return true;
251 }
252
253 const char *Key::issuerSerial() const
254 {
255     return key ? key->issuer_serial : 0 ;
256 }
257 const char *Key::issuerName() const
258 {
259     return key ? key->issuer_name : 0 ;
260 }
261 const char *Key::chainID() const
262 {
263     return key ? key->chain_id : 0 ;
264 }
265
266 const char *Key::keyID() const
267 {
268     return key && key->subkeys ? key->subkeys->keyid : 0 ;
269 }
270
271 const char *Key::shortKeyID() const
272 {
273     if (!key || !key->subkeys || !key->subkeys->keyid) {
274         return 0;
275     }
276     const int len = strlen(key->subkeys->keyid);
277     if (len > 8) {
278         return key->subkeys->keyid + len - 8; // return the last 8 bytes (in hex notation)
279     } else {
280         return key->subkeys->keyid;
281     }
282 }
283
284 const char *Key::primaryFingerprint() const
285 {
286     if (!key) {
287         return nullptr;
288     }
289     if (key->fpr) {
290         /* Return what gpgme thinks is the primary fingerprint */
291         return key->fpr;
292     }
293     if (key->subkeys) {
294         /* Return the first subkeys fingerprint */
295         return key->subkeys->fpr;
296     }
297     return nullptr;
298 }
299
300 unsigned int Key::keyListMode() const
301 {
302     return key ? convert_from_gpgme_keylist_mode_t(key->keylist_mode) : 0 ;
303 }
304
305 const Key &Key::mergeWith(const Key &other)
306 {
307     // ### incomplete. Just merges has* and can*, nothing else atm
308     // ### detach also missing
309
310     if (!this->primaryFingerprint() ||
311             !other.primaryFingerprint() ||
312             strcasecmp(this->primaryFingerprint(), other.primaryFingerprint()) != 0) {
313         return *this; // only merge the Key object which describe the same key
314     }
315
316     const gpgme_key_t me = impl();
317     const gpgme_key_t him = other.impl();
318
319     if (!me || !him) {
320         return *this;
321     }
322
323     me->revoked          |= him->revoked;
324     me->expired          |= him->expired;
325     me->disabled         |= him->disabled;
326     me->invalid          |= him->invalid;
327     me->can_encrypt      |= him->can_encrypt;
328     me->can_sign         |= him->can_sign;
329     me->can_certify      |= him->can_certify;
330     me->secret           |= him->secret;
331     me->can_authenticate |= him->can_authenticate;
332     me->is_qualified     |= him->is_qualified;
333     me->keylist_mode     |= him->keylist_mode;
334
335     // make sure the gpgme_sub_key_t::is_cardkey flag isn't lost:
336     for (gpgme_sub_key_t mysk = me->subkeys ; mysk ; mysk = mysk->next) {
337         for (gpgme_sub_key_t hissk = him->subkeys ; hissk ; hissk = hissk->next) {
338             if (strcmp(mysk->fpr, hissk->fpr) == 0) {
339                 mysk->is_cardkey |= hissk->is_cardkey;
340                 break;
341             }
342         }
343     }
344
345     return *this;
346 }
347
348 void Key::update()
349 {
350     auto ctx = Context::createForProtocol(protocol());
351     if (!ctx) {
352         return;
353     }
354     ctx->setKeyListMode(KeyListMode::Local |
355                         KeyListMode::Signatures |
356                         KeyListMode::SignatureNotations |
357                         KeyListMode::Validate |
358                         KeyListMode::WithTofu);
359     Error err;
360     auto newKey = ctx->key(primaryFingerprint(), err, true);
361     // Not secret so we get the information from the pubring.
362     if (newKey.isNull())
363       {
364         newKey = ctx->key(primaryFingerprint(), err, false);
365       }
366     delete ctx;
367     if (err) {
368         return;
369     }
370     swap(newKey);
371     return;
372 }
373
374 // static
375 Key Key::locate(const char *mbox)
376 {
377     if (!mbox) {
378         return Key();
379     }
380
381     auto ctx = Context::createForProtocol(OpenPGP);
382     if (!ctx) {
383         return Key();
384     }
385
386     ctx->setKeyListMode (Extern | Local);
387
388     Error e = ctx->startKeyListing (mbox);
389     auto ret = ctx->nextKey (e);
390     delete ctx;
391
392     return ret;
393 }
394
395 //
396 //
397 // class Subkey
398 //
399 //
400
401 gpgme_sub_key_t find_subkey(const shared_gpgme_key_t &key, unsigned int idx)
402 {
403     if (key) {
404         for (gpgme_sub_key_t s = key->subkeys ; s ; s = s->next, --idx) {
405             if (idx == 0) {
406                 return s;
407             }
408         }
409     }
410     return 0;
411 }
412
413 gpgme_sub_key_t verify_subkey(const shared_gpgme_key_t &key, gpgme_sub_key_t subkey)
414 {
415     if (key) {
416         for (gpgme_sub_key_t s = key->subkeys ; s ; s = s->next) {
417             if (s == subkey) {
418                 return subkey;
419             }
420         }
421     }
422     return 0;
423 }
424
425 Subkey::Subkey() : key(), subkey(0) {}
426
427 Subkey::Subkey(const shared_gpgme_key_t &k, unsigned int idx)
428     : key(k), subkey(find_subkey(k, idx))
429 {
430
431 }
432
433 Subkey::Subkey(const shared_gpgme_key_t &k, gpgme_sub_key_t sk)
434     : key(k), subkey(verify_subkey(k, sk))
435 {
436
437 }
438
439 Key Subkey::parent() const
440 {
441     return Key(key);
442 }
443
444 const char *Subkey::keyID() const
445 {
446     return subkey ? subkey->keyid : 0 ;
447 }
448
449 const char *Subkey::fingerprint() const
450 {
451     return subkey ? subkey->fpr : 0 ;
452 }
453
454 Subkey::PubkeyAlgo Subkey::publicKeyAlgorithm() const
455 {
456     return subkey ? static_cast<PubkeyAlgo>(subkey->pubkey_algo) : AlgoUnknown;
457 }
458
459 const char *Subkey::publicKeyAlgorithmAsString() const
460 {
461     return gpgme_pubkey_algo_name(subkey ? subkey->pubkey_algo : (gpgme_pubkey_algo_t)0);
462 }
463
464 /* static */
465 const char *Subkey::publicKeyAlgorithmAsString(PubkeyAlgo algo)
466 {
467     if (algo == AlgoUnknown) {
468         return NULL;
469     }
470     return gpgme_pubkey_algo_name(static_cast<gpgme_pubkey_algo_t>(algo));
471 }
472
473 std::string Subkey::algoName() const
474 {
475     char *gpgmeStr;
476     if (subkey && (gpgmeStr = gpgme_pubkey_algo_string(subkey))) {
477         std::string ret = std::string(gpgmeStr);
478         gpgme_free(gpgmeStr);
479         return ret;
480     }
481     return std::string();
482 }
483
484 bool Subkey::canEncrypt() const
485 {
486     return subkey && subkey->can_encrypt;
487 }
488
489 bool Subkey::canSign() const
490 {
491     return subkey && subkey->can_sign;
492 }
493
494 bool Subkey::canCertify() const
495 {
496     return subkey && subkey->can_certify;
497 }
498
499 bool Subkey::canAuthenticate() const
500 {
501     return subkey && subkey->can_authenticate;
502 }
503
504 bool Subkey::isQualified() const
505 {
506     return subkey && subkey->is_qualified;
507 }
508
509 bool Subkey::isDeVs() const
510 {
511     return subkey && subkey->is_de_vs;
512 }
513
514 bool Subkey::isCardKey() const
515 {
516     return subkey && subkey->is_cardkey;
517 }
518
519 const char *Subkey::cardSerialNumber() const
520 {
521     return subkey ? subkey->card_number : nullptr;
522 }
523
524 const char *Subkey::keyGrip() const
525 {
526     return subkey ? subkey->keygrip : nullptr;
527 }
528
529 bool Subkey::isSecret() const
530 {
531     return subkey && subkey->secret;
532 }
533
534 unsigned int Subkey::length() const
535 {
536     return subkey ? subkey->length : 0 ;
537 }
538
539 time_t Subkey::creationTime() const
540 {
541     return static_cast<time_t>(subkey ? subkey->timestamp : 0);
542 }
543
544 time_t Subkey::expirationTime() const
545 {
546     return static_cast<time_t>(subkey ? subkey->expires : 0);
547 }
548
549 bool Subkey::neverExpires() const
550 {
551     return expirationTime() == time_t(0);
552 }
553
554 bool Subkey::isRevoked() const
555 {
556     return subkey && subkey->revoked;
557 }
558
559 bool Subkey::isInvalid() const
560 {
561     return subkey && subkey->invalid;
562 }
563
564 bool Subkey::isExpired() const
565 {
566     return subkey && subkey->expired;
567 }
568
569 bool Subkey::isDisabled() const
570 {
571     return subkey && subkey->disabled;
572 }
573
574 //
575 //
576 // class UserID
577 //
578 //
579
580 gpgme_user_id_t find_uid(const shared_gpgme_key_t &key, unsigned int idx)
581 {
582     if (key) {
583         for (gpgme_user_id_t u = key->uids ; u ; u = u->next, --idx) {
584             if (idx == 0) {
585                 return u;
586             }
587         }
588     }
589     return 0;
590 }
591
592 gpgme_user_id_t verify_uid(const shared_gpgme_key_t &key, gpgme_user_id_t uid)
593 {
594     if (key) {
595         for (gpgme_user_id_t u = key->uids ; u ; u = u->next) {
596             if (u == uid) {
597                 return uid;
598             }
599         }
600     }
601     return 0;
602 }
603
604 UserID::UserID() : key(), uid(0) {}
605
606 UserID::UserID(const shared_gpgme_key_t &k, gpgme_user_id_t u)
607     : key(k), uid(verify_uid(k, u))
608 {
609
610 }
611
612 UserID::UserID(const shared_gpgme_key_t &k, unsigned int idx)
613     : key(k), uid(find_uid(k, idx))
614 {
615
616 }
617
618 Key UserID::parent() const
619 {
620     return Key(key);
621 }
622
623 UserID::Signature UserID::signature(unsigned int index) const
624 {
625     return Signature(key, uid, index);
626 }
627
628 unsigned int UserID::numSignatures() const
629 {
630     if (!uid) {
631         return 0;
632     }
633     unsigned int count = 0;
634     for (gpgme_key_sig_t sig = uid->signatures ; sig ; sig = sig->next) {
635         ++count;
636     }
637     return count;
638 }
639
640 std::vector<UserID::Signature> UserID::signatures() const
641 {
642     if (!uid) {
643         return std::vector<Signature>();
644     }
645
646     std::vector<Signature> v;
647     v.reserve(numSignatures());
648     for (gpgme_key_sig_t sig = uid->signatures ; sig ; sig = sig->next) {
649         v.push_back(Signature(key, uid, sig));
650     }
651     return v;
652 }
653
654 const char *UserID::id() const
655 {
656     return uid ? uid->uid : 0 ;
657 }
658
659 const char *UserID::name() const
660 {
661     return uid ? uid->name : 0 ;
662 }
663
664 const char *UserID::email() const
665 {
666     return uid ? uid->email : 0 ;
667 }
668
669 const char *UserID::comment() const
670 {
671     return uid ? uid->comment : 0 ;
672 }
673
674 UserID::Validity UserID::validity() const
675 {
676     if (!uid) {
677         return Unknown;
678     }
679     switch (uid->validity) {
680     default:
681     case GPGME_VALIDITY_UNKNOWN:   return Unknown;
682     case GPGME_VALIDITY_UNDEFINED: return Undefined;
683     case GPGME_VALIDITY_NEVER:     return Never;
684     case GPGME_VALIDITY_MARGINAL:  return Marginal;
685     case GPGME_VALIDITY_FULL:      return Full;
686     case GPGME_VALIDITY_ULTIMATE:  return Ultimate;
687     }
688 }
689
690 char UserID::validityAsString() const
691 {
692     if (!uid) {
693         return '?';
694     }
695     switch (uid->validity) {
696     default:
697     case GPGME_VALIDITY_UNKNOWN:   return '?';
698     case GPGME_VALIDITY_UNDEFINED: return 'q';
699     case GPGME_VALIDITY_NEVER:     return 'n';
700     case GPGME_VALIDITY_MARGINAL:  return 'm';
701     case GPGME_VALIDITY_FULL:      return 'f';
702     case GPGME_VALIDITY_ULTIMATE:  return 'u';
703     }
704 }
705
706 bool UserID::isRevoked() const
707 {
708     return uid && uid->revoked;
709 }
710
711 bool UserID::isInvalid() const
712 {
713     return uid && uid->invalid;
714 }
715
716 TofuInfo UserID::tofuInfo() const
717 {
718     if (!uid) {
719         return TofuInfo();
720     }
721     return TofuInfo(uid->tofu);
722 }
723
724 //
725 //
726 // class Signature
727 //
728 //
729
730 gpgme_key_sig_t find_signature(gpgme_user_id_t uid, unsigned int idx)
731 {
732     if (uid) {
733         for (gpgme_key_sig_t s = uid->signatures ; s ; s = s->next, --idx) {
734             if (idx == 0) {
735                 return s;
736             }
737         }
738     }
739     return 0;
740 }
741
742 gpgme_key_sig_t verify_signature(gpgme_user_id_t uid, gpgme_key_sig_t sig)
743 {
744     if (uid) {
745         for (gpgme_key_sig_t s = uid->signatures ; s ; s = s->next) {
746             if (s == sig) {
747                 return sig;
748             }
749         }
750     }
751     return 0;
752 }
753
754 UserID::Signature::Signature() : key(), uid(0), sig(0) {}
755
756 UserID::Signature::Signature(const shared_gpgme_key_t &k, gpgme_user_id_t u, unsigned int idx)
757     : key(k), uid(verify_uid(k, u)), sig(find_signature(uid, idx))
758 {
759
760 }
761
762 UserID::Signature::Signature(const shared_gpgme_key_t &k, gpgme_user_id_t u, gpgme_key_sig_t s)
763     : key(k), uid(verify_uid(k, u)), sig(verify_signature(uid, s))
764 {
765
766 }
767
768 UserID UserID::Signature::parent() const
769 {
770     return UserID(key, uid);
771 }
772
773 const char *UserID::Signature::signerKeyID() const
774 {
775     return sig ? sig->keyid : 0 ;
776 }
777
778 const char *UserID::Signature::algorithmAsString() const
779 {
780     return gpgme_pubkey_algo_name(sig ? sig->pubkey_algo : (gpgme_pubkey_algo_t)0);
781 }
782
783 unsigned int UserID::Signature::algorithm() const
784 {
785     return sig ? sig->pubkey_algo : 0 ;
786 }
787
788 time_t UserID::Signature::creationTime() const
789 {
790     return static_cast<time_t>(sig ? sig->timestamp : 0);
791 }
792
793 time_t UserID::Signature::expirationTime() const
794 {
795     return static_cast<time_t>(sig ? sig->expires : 0);
796 }
797
798 bool UserID::Signature::neverExpires() const
799 {
800     return expirationTime() == time_t(0);
801 }
802
803 bool UserID::Signature::isRevokation() const
804 {
805     return sig && sig->revoked;
806 }
807
808 bool UserID::Signature::isInvalid() const
809 {
810     return sig && sig->invalid;
811 }
812
813 bool UserID::Signature::isExpired() const
814 {
815     return sig && sig->expired;
816 }
817
818 bool UserID::Signature::isExportable() const
819 {
820     return sig && sig->exportable;
821 }
822
823 const char *UserID::Signature::signerUserID() const
824 {
825     return sig ? sig->uid : 0 ;
826 }
827
828 const char *UserID::Signature::signerName() const
829 {
830     return sig ? sig->name : 0 ;
831 }
832
833 const char *UserID::Signature::signerEmail() const
834 {
835     return sig ? sig->email : 0 ;
836 }
837
838 const char *UserID::Signature::signerComment() const
839 {
840     return sig ? sig->comment : 0 ;
841 }
842
843 unsigned int UserID::Signature::certClass() const
844 {
845     return sig ? sig->sig_class : 0 ;
846 }
847
848 UserID::Signature::Status UserID::Signature::status() const
849 {
850     if (!sig) {
851         return GeneralError;
852     }
853
854     switch (gpgme_err_code(sig->status)) {
855     case GPG_ERR_NO_ERROR:      return NoError;
856     case GPG_ERR_SIG_EXPIRED:   return SigExpired;
857     case GPG_ERR_KEY_EXPIRED:   return KeyExpired;
858     case GPG_ERR_BAD_SIGNATURE: return BadSignature;
859     case GPG_ERR_NO_PUBKEY:     return NoPublicKey;
860     default:
861     case GPG_ERR_GENERAL:       return GeneralError;
862     }
863 }
864
865 std::string UserID::Signature::statusAsString() const
866 {
867     if (!sig) {
868         return std::string();
869     }
870     char buf[ 1024 ];
871     gpgme_strerror_r(sig->status, buf, sizeof buf);
872     buf[ sizeof buf - 1 ] = '\0';
873     return std::string(buf);
874 }
875
876 GpgME::Notation UserID::Signature::notation(unsigned int idx) const
877 {
878     if (!sig) {
879         return GpgME::Notation();
880     }
881     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
882         if (nota->name) {
883             if (idx-- == 0) {
884                 return GpgME::Notation(nota);
885             }
886         }
887     }
888     return GpgME::Notation();
889 }
890
891 unsigned int UserID::Signature::numNotations() const
892 {
893     if (!sig) {
894         return 0;
895     }
896     unsigned int count = 0;
897     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
898         if (nota->name) {
899             ++count; // others are policy URLs...
900         }
901     }
902     return count;
903 }
904
905 std::vector<Notation> UserID::Signature::notations() const
906 {
907     if (!sig) {
908         return std::vector<GpgME::Notation>();
909     }
910     std::vector<GpgME::Notation> v;
911     v.reserve(numNotations());
912     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
913         if (nota->name) {
914             v.push_back(GpgME::Notation(nota));
915         }
916     }
917     return v;
918 }
919
920 const char *UserID::Signature::policyURL() const
921 {
922     if (!sig) {
923         return 0;
924     }
925     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
926         if (!nota->name) {
927             return nota->value;
928         }
929     }
930     return 0;
931 }
932
933 std::string UserID::addrSpecFromString(const char *userid)
934 {
935     if (!userid) {
936         return std::string();
937     }
938     char *normalized = gpgme_addrspec_from_uid (userid);
939     if (normalized) {
940         std::string ret(normalized);
941         gpgme_free(normalized);
942         return ret;
943     }
944     return std::string();
945 }
946
947 std::string UserID::addrSpec() const
948 {
949     if (!uid || !uid->address) {
950         return std::string();
951     }
952
953     return uid->address;
954 }
955
956 Error UserID::revoke()
957 {
958     if (isNull()) {
959         return Error::fromCode(GPG_ERR_GENERAL);
960     }
961     auto ctx = Context::createForProtocol(parent().protocol());
962     if (!ctx) {
963         return Error::fromCode(GPG_ERR_INV_ENGINE);
964     }
965     Error ret = ctx->revUid(key, id());
966     delete ctx;
967     return ret;
968 }
969
970 Error Key::addUid(const char *uid)
971 {
972     if (isNull()) {
973         return Error::fromCode(GPG_ERR_GENERAL);
974     }
975     auto ctx = Context::createForProtocol(protocol());
976     if (!ctx) {
977         return Error::fromCode(GPG_ERR_INV_ENGINE);
978     }
979     Error ret = ctx->addUid(key, uid);
980     delete ctx;
981     return ret;
982 }
983
984 std::ostream &operator<<(std::ostream &os, const UserID &uid)
985 {
986     os << "GpgME::UserID(";
987     if (!uid.isNull()) {
988         os << "\n name:      " << protect(uid.name())
989            << "\n email:     " << protect(uid.email())
990            << "\n mbox:      " << uid.addrSpec()
991            << "\n comment:   " << protect(uid.comment())
992            << "\n validity:  " << uid.validityAsString()
993            << "\n revoked:   " << uid.isRevoked()
994            << "\n invalid:   " << uid.isInvalid()
995            << "\n numsigs:   " << uid.numSignatures()
996            << "\n tofuinfo:\n" << uid.tofuInfo();
997     }
998     return os << ')';
999 }
1000
1001 std::ostream &operator<<(std::ostream &os, const Key &key)
1002 {
1003     os << "GpgME::Key(";
1004     if (!key.isNull()) {
1005         os << "\n protocol:   " << protect(key.protocolAsString())
1006            << "\n ownertrust: " << key.ownerTrustAsString()
1007            << "\n issuer:     " << protect(key.issuerName())
1008            << "\n fingerprint:" << protect(key.primaryFingerprint())
1009            << "\n listmode:   " << key.keyListMode()
1010            << "\n canSign:    " << key.canReallySign()
1011            << "\n canEncrypt: " << key.canEncrypt()
1012            << "\n canCertify: " << key.canCertify()
1013            << "\n canAuth:    " << key.canAuthenticate()
1014            << "\n uids:\n";
1015         const std::vector<UserID> uids = key.userIDs();
1016         std::copy(uids.begin(), uids.end(),
1017                   std::ostream_iterator<UserID>(os, "\n"));
1018     }
1019     return os << ')';
1020 }
1021
1022 } // namespace GpgME