core,cpp: New key flag 'is_de_vs'.
[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     return key && key->subkeys && key->subkeys->is_de_vs;
240 }
241
242 const char *Key::issuerSerial() const
243 {
244     return key ? key->issuer_serial : 0 ;
245 }
246 const char *Key::issuerName() const
247 {
248     return key ? key->issuer_name : 0 ;
249 }
250 const char *Key::chainID() const
251 {
252     return key ? key->chain_id : 0 ;
253 }
254
255 const char *Key::keyID() const
256 {
257     return key && key->subkeys ? key->subkeys->keyid : 0 ;
258 }
259
260 const char *Key::shortKeyID() const
261 {
262     if (!key || !key->subkeys || !key->subkeys->keyid) {
263         return 0;
264     }
265     const int len = strlen(key->subkeys->keyid);
266     if (len > 8) {
267         return key->subkeys->keyid + len - 8; // return the last 8 bytes (in hex notation)
268     } else {
269         return key->subkeys->keyid;
270     }
271 }
272
273 const char *Key::primaryFingerprint() const
274 {
275     if (!key) {
276         return nullptr;
277     }
278     if (key->fpr) {
279         /* Return what gpgme thinks is the primary fingerprint */
280         return key->fpr;
281     }
282     if (key->subkeys) {
283         /* Return the first subkeys fingerprint */
284         return key->subkeys->fpr;
285     }
286     return nullptr;
287 }
288
289 unsigned int Key::keyListMode() const
290 {
291     return key ? convert_from_gpgme_keylist_mode_t(key->keylist_mode) : 0 ;
292 }
293
294 const Key &Key::mergeWith(const Key &other)
295 {
296     // ### incomplete. Just merges has* and can*, nothing else atm
297     // ### detach also missing
298
299     if (!this->primaryFingerprint() ||
300             !other.primaryFingerprint() ||
301             strcasecmp(this->primaryFingerprint(), other.primaryFingerprint()) != 0) {
302         return *this; // only merge the Key object which describe the same key
303     }
304
305     const gpgme_key_t me = impl();
306     const gpgme_key_t him = other.impl();
307
308     if (!me || !him) {
309         return *this;
310     }
311
312     me->revoked          |= him->revoked;
313     me->expired          |= him->expired;
314     me->disabled         |= him->disabled;
315     me->invalid          |= him->invalid;
316     me->can_encrypt      |= him->can_encrypt;
317     me->can_sign         |= him->can_sign;
318     me->can_certify      |= him->can_certify;
319     me->secret           |= him->secret;
320     me->can_authenticate |= him->can_authenticate;
321     me->is_qualified     |= him->is_qualified;
322     me->keylist_mode     |= him->keylist_mode;
323
324     // make sure the gpgme_sub_key_t::is_cardkey flag isn't lost:
325     for (gpgme_sub_key_t mysk = me->subkeys ; mysk ; mysk = mysk->next) {
326         for (gpgme_sub_key_t hissk = him->subkeys ; hissk ; hissk = hissk->next) {
327             if (strcmp(mysk->fpr, hissk->fpr) == 0) {
328                 mysk->is_cardkey |= hissk->is_cardkey;
329                 break;
330             }
331         }
332     }
333
334     return *this;
335 }
336
337 void Key::update()
338 {
339     auto ctx = Context::createForProtocol(protocol());
340     if (!ctx) {
341         return;
342     }
343     ctx->setKeyListMode(KeyListMode::Local |
344                         KeyListMode::Signatures |
345                         KeyListMode::SignatureNotations |
346                         KeyListMode::Validate |
347                         KeyListMode::WithTofu);
348     Error err;
349     auto newKey = ctx->key(primaryFingerprint(), err, true);
350     // Not secret so we get the information from the pubring.
351     if (newKey.isNull())
352       {
353         newKey = ctx->key(primaryFingerprint(), err, false);
354       }
355     delete ctx;
356     if (err) {
357         return;
358     }
359     swap(newKey);
360     return;
361 }
362
363 //
364 //
365 // class Subkey
366 //
367 //
368
369 gpgme_sub_key_t find_subkey(const shared_gpgme_key_t &key, unsigned int idx)
370 {
371     if (key) {
372         for (gpgme_sub_key_t s = key->subkeys ; s ; s = s->next, --idx) {
373             if (idx == 0) {
374                 return s;
375             }
376         }
377     }
378     return 0;
379 }
380
381 gpgme_sub_key_t verify_subkey(const shared_gpgme_key_t &key, gpgme_sub_key_t subkey)
382 {
383     if (key) {
384         for (gpgme_sub_key_t s = key->subkeys ; s ; s = s->next) {
385             if (s == subkey) {
386                 return subkey;
387             }
388         }
389     }
390     return 0;
391 }
392
393 Subkey::Subkey() : key(), subkey(0) {}
394
395 Subkey::Subkey(const shared_gpgme_key_t &k, unsigned int idx)
396     : key(k), subkey(find_subkey(k, idx))
397 {
398
399 }
400
401 Subkey::Subkey(const shared_gpgme_key_t &k, gpgme_sub_key_t sk)
402     : key(k), subkey(verify_subkey(k, sk))
403 {
404
405 }
406
407 Key Subkey::parent() const
408 {
409     return Key(key);
410 }
411
412 const char *Subkey::keyID() const
413 {
414     return subkey ? subkey->keyid : 0 ;
415 }
416
417 const char *Subkey::fingerprint() const
418 {
419     return subkey ? subkey->fpr : 0 ;
420 }
421
422 Subkey::PubkeyAlgo Subkey::publicKeyAlgorithm() const
423 {
424     return subkey ? static_cast<PubkeyAlgo>(subkey->pubkey_algo) : AlgoUnknown;
425 }
426
427 const char *Subkey::publicKeyAlgorithmAsString() const
428 {
429     return gpgme_pubkey_algo_name(subkey ? subkey->pubkey_algo : (gpgme_pubkey_algo_t)0);
430 }
431
432 /* static */
433 const char *Subkey::publicKeyAlgorithmAsString(PubkeyAlgo algo)
434 {
435     if (algo == AlgoUnknown) {
436         return NULL;
437     }
438     return gpgme_pubkey_algo_name(static_cast<gpgme_pubkey_algo_t>(algo));
439 }
440
441 std::string Subkey::algoName() const
442 {
443     char *gpgmeStr;
444     if (subkey && (gpgmeStr = gpgme_pubkey_algo_string(subkey))) {
445         std::string ret = std::string(gpgmeStr);
446         gpgme_free(gpgmeStr);
447         return ret;
448     }
449     return std::string();
450 }
451
452 bool Subkey::canEncrypt() const
453 {
454     return subkey && subkey->can_encrypt;
455 }
456
457 bool Subkey::canSign() const
458 {
459     return subkey && subkey->can_sign;
460 }
461
462 bool Subkey::canCertify() const
463 {
464     return subkey && subkey->can_certify;
465 }
466
467 bool Subkey::canAuthenticate() const
468 {
469     return subkey && subkey->can_authenticate;
470 }
471
472 bool Subkey::isQualified() const
473 {
474     return subkey && subkey->is_qualified;
475 }
476
477 bool Subkey::isDeVs() const
478 {
479     return subkey && subkey->is_de_vs;
480 }
481
482 bool Subkey::isCardKey() const
483 {
484     return subkey && subkey->is_cardkey;
485 }
486
487 const char *Subkey::cardSerialNumber() const
488 {
489     return subkey ? subkey->card_number : nullptr;
490 }
491
492 const char *Subkey::keyGrip() const
493 {
494     return subkey ? subkey->keygrip : nullptr;
495 }
496
497 bool Subkey::isSecret() const
498 {
499     return subkey && subkey->secret;
500 }
501
502 unsigned int Subkey::length() const
503 {
504     return subkey ? subkey->length : 0 ;
505 }
506
507 time_t Subkey::creationTime() const
508 {
509     return static_cast<time_t>(subkey ? subkey->timestamp : 0);
510 }
511
512 time_t Subkey::expirationTime() const
513 {
514     return static_cast<time_t>(subkey ? subkey->expires : 0);
515 }
516
517 bool Subkey::neverExpires() const
518 {
519     return expirationTime() == time_t(0);
520 }
521
522 bool Subkey::isRevoked() const
523 {
524     return subkey && subkey->revoked;
525 }
526
527 bool Subkey::isInvalid() const
528 {
529     return subkey && subkey->invalid;
530 }
531
532 bool Subkey::isExpired() const
533 {
534     return subkey && subkey->expired;
535 }
536
537 bool Subkey::isDisabled() const
538 {
539     return subkey && subkey->disabled;
540 }
541
542 //
543 //
544 // class UserID
545 //
546 //
547
548 gpgme_user_id_t find_uid(const shared_gpgme_key_t &key, unsigned int idx)
549 {
550     if (key) {
551         for (gpgme_user_id_t u = key->uids ; u ; u = u->next, --idx) {
552             if (idx == 0) {
553                 return u;
554             }
555         }
556     }
557     return 0;
558 }
559
560 gpgme_user_id_t verify_uid(const shared_gpgme_key_t &key, gpgme_user_id_t uid)
561 {
562     if (key) {
563         for (gpgme_user_id_t u = key->uids ; u ; u = u->next) {
564             if (u == uid) {
565                 return uid;
566             }
567         }
568     }
569     return 0;
570 }
571
572 UserID::UserID() : key(), uid(0) {}
573
574 UserID::UserID(const shared_gpgme_key_t &k, gpgme_user_id_t u)
575     : key(k), uid(verify_uid(k, u))
576 {
577
578 }
579
580 UserID::UserID(const shared_gpgme_key_t &k, unsigned int idx)
581     : key(k), uid(find_uid(k, idx))
582 {
583
584 }
585
586 Key UserID::parent() const
587 {
588     return Key(key);
589 }
590
591 UserID::Signature UserID::signature(unsigned int index) const
592 {
593     return Signature(key, uid, index);
594 }
595
596 unsigned int UserID::numSignatures() const
597 {
598     if (!uid) {
599         return 0;
600     }
601     unsigned int count = 0;
602     for (gpgme_key_sig_t sig = uid->signatures ; sig ; sig = sig->next) {
603         ++count;
604     }
605     return count;
606 }
607
608 std::vector<UserID::Signature> UserID::signatures() const
609 {
610     if (!uid) {
611         return std::vector<Signature>();
612     }
613
614     std::vector<Signature> v;
615     v.reserve(numSignatures());
616     for (gpgme_key_sig_t sig = uid->signatures ; sig ; sig = sig->next) {
617         v.push_back(Signature(key, uid, sig));
618     }
619     return v;
620 }
621
622 const char *UserID::id() const
623 {
624     return uid ? uid->uid : 0 ;
625 }
626
627 const char *UserID::name() const
628 {
629     return uid ? uid->name : 0 ;
630 }
631
632 const char *UserID::email() const
633 {
634     return uid ? uid->email : 0 ;
635 }
636
637 const char *UserID::comment() const
638 {
639     return uid ? uid->comment : 0 ;
640 }
641
642 UserID::Validity UserID::validity() const
643 {
644     if (!uid) {
645         return Unknown;
646     }
647     switch (uid->validity) {
648     default:
649     case GPGME_VALIDITY_UNKNOWN:   return Unknown;
650     case GPGME_VALIDITY_UNDEFINED: return Undefined;
651     case GPGME_VALIDITY_NEVER:     return Never;
652     case GPGME_VALIDITY_MARGINAL:  return Marginal;
653     case GPGME_VALIDITY_FULL:      return Full;
654     case GPGME_VALIDITY_ULTIMATE:  return Ultimate;
655     }
656 }
657
658 char UserID::validityAsString() const
659 {
660     if (!uid) {
661         return '?';
662     }
663     switch (uid->validity) {
664     default:
665     case GPGME_VALIDITY_UNKNOWN:   return '?';
666     case GPGME_VALIDITY_UNDEFINED: return 'q';
667     case GPGME_VALIDITY_NEVER:     return 'n';
668     case GPGME_VALIDITY_MARGINAL:  return 'm';
669     case GPGME_VALIDITY_FULL:      return 'f';
670     case GPGME_VALIDITY_ULTIMATE:  return 'u';
671     }
672 }
673
674 bool UserID::isRevoked() const
675 {
676     return uid && uid->revoked;
677 }
678
679 bool UserID::isInvalid() const
680 {
681     return uid && uid->invalid;
682 }
683
684 TofuInfo UserID::tofuInfo() const
685 {
686     if (!uid) {
687         return TofuInfo();
688     }
689     return TofuInfo(uid->tofu);
690 }
691
692 //
693 //
694 // class Signature
695 //
696 //
697
698 gpgme_key_sig_t find_signature(gpgme_user_id_t uid, unsigned int idx)
699 {
700     if (uid) {
701         for (gpgme_key_sig_t s = uid->signatures ; s ; s = s->next, --idx) {
702             if (idx == 0) {
703                 return s;
704             }
705         }
706     }
707     return 0;
708 }
709
710 gpgme_key_sig_t verify_signature(gpgme_user_id_t uid, gpgme_key_sig_t sig)
711 {
712     if (uid) {
713         for (gpgme_key_sig_t s = uid->signatures ; s ; s = s->next) {
714             if (s == sig) {
715                 return sig;
716             }
717         }
718     }
719     return 0;
720 }
721
722 UserID::Signature::Signature() : key(), uid(0), sig(0) {}
723
724 UserID::Signature::Signature(const shared_gpgme_key_t &k, gpgme_user_id_t u, unsigned int idx)
725     : key(k), uid(verify_uid(k, u)), sig(find_signature(uid, idx))
726 {
727
728 }
729
730 UserID::Signature::Signature(const shared_gpgme_key_t &k, gpgme_user_id_t u, gpgme_key_sig_t s)
731     : key(k), uid(verify_uid(k, u)), sig(verify_signature(uid, s))
732 {
733
734 }
735
736 UserID UserID::Signature::parent() const
737 {
738     return UserID(key, uid);
739 }
740
741 const char *UserID::Signature::signerKeyID() const
742 {
743     return sig ? sig->keyid : 0 ;
744 }
745
746 const char *UserID::Signature::algorithmAsString() const
747 {
748     return gpgme_pubkey_algo_name(sig ? sig->pubkey_algo : (gpgme_pubkey_algo_t)0);
749 }
750
751 unsigned int UserID::Signature::algorithm() const
752 {
753     return sig ? sig->pubkey_algo : 0 ;
754 }
755
756 time_t UserID::Signature::creationTime() const
757 {
758     return static_cast<time_t>(sig ? sig->timestamp : 0);
759 }
760
761 time_t UserID::Signature::expirationTime() const
762 {
763     return static_cast<time_t>(sig ? sig->expires : 0);
764 }
765
766 bool UserID::Signature::neverExpires() const
767 {
768     return expirationTime() == time_t(0);
769 }
770
771 bool UserID::Signature::isRevokation() const
772 {
773     return sig && sig->revoked;
774 }
775
776 bool UserID::Signature::isInvalid() const
777 {
778     return sig && sig->invalid;
779 }
780
781 bool UserID::Signature::isExpired() const
782 {
783     return sig && sig->expired;
784 }
785
786 bool UserID::Signature::isExportable() const
787 {
788     return sig && sig->exportable;
789 }
790
791 const char *UserID::Signature::signerUserID() const
792 {
793     return sig ? sig->uid : 0 ;
794 }
795
796 const char *UserID::Signature::signerName() const
797 {
798     return sig ? sig->name : 0 ;
799 }
800
801 const char *UserID::Signature::signerEmail() const
802 {
803     return sig ? sig->email : 0 ;
804 }
805
806 const char *UserID::Signature::signerComment() const
807 {
808     return sig ? sig->comment : 0 ;
809 }
810
811 unsigned int UserID::Signature::certClass() const
812 {
813     return sig ? sig->sig_class : 0 ;
814 }
815
816 UserID::Signature::Status UserID::Signature::status() const
817 {
818     if (!sig) {
819         return GeneralError;
820     }
821
822     switch (gpgme_err_code(sig->status)) {
823     case GPG_ERR_NO_ERROR:      return NoError;
824     case GPG_ERR_SIG_EXPIRED:   return SigExpired;
825     case GPG_ERR_KEY_EXPIRED:   return KeyExpired;
826     case GPG_ERR_BAD_SIGNATURE: return BadSignature;
827     case GPG_ERR_NO_PUBKEY:     return NoPublicKey;
828     default:
829     case GPG_ERR_GENERAL:       return GeneralError;
830     }
831 }
832
833 std::string UserID::Signature::statusAsString() const
834 {
835     if (!sig) {
836         return std::string();
837     }
838     char buf[ 1024 ];
839     gpgme_strerror_r(sig->status, buf, sizeof buf);
840     buf[ sizeof buf - 1 ] = '\0';
841     return std::string(buf);
842 }
843
844 GpgME::Notation UserID::Signature::notation(unsigned int idx) const
845 {
846     if (!sig) {
847         return GpgME::Notation();
848     }
849     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
850         if (nota->name) {
851             if (idx-- == 0) {
852                 return GpgME::Notation(nota);
853             }
854         }
855     }
856     return GpgME::Notation();
857 }
858
859 unsigned int UserID::Signature::numNotations() const
860 {
861     if (!sig) {
862         return 0;
863     }
864     unsigned int count = 0;
865     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
866         if (nota->name) {
867             ++count; // others are policy URLs...
868         }
869     }
870     return count;
871 }
872
873 std::vector<Notation> UserID::Signature::notations() const
874 {
875     if (!sig) {
876         return std::vector<GpgME::Notation>();
877     }
878     std::vector<GpgME::Notation> v;
879     v.reserve(numNotations());
880     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
881         if (nota->name) {
882             v.push_back(GpgME::Notation(nota));
883         }
884     }
885     return v;
886 }
887
888 const char *UserID::Signature::policyURL() const
889 {
890     if (!sig) {
891         return 0;
892     }
893     for (gpgme_sig_notation_t nota = sig->notations ; nota ; nota = nota->next) {
894         if (!nota->name) {
895             return nota->value;
896         }
897     }
898     return 0;
899 }
900
901 std::string UserID::addrSpecFromString(const char *userid)
902 {
903     if (!userid) {
904         return std::string();
905     }
906     char *normalized = gpgme_addrspec_from_uid (userid);
907     if (normalized) {
908         std::string ret(normalized);
909         gpgme_free(normalized);
910         return ret;
911     }
912     return std::string();
913 }
914
915 std::string UserID::addrSpec() const
916 {
917     if (!uid || !uid->address) {
918         return std::string();
919     }
920
921     return uid->address;
922 }
923
924 Error UserID::revoke()
925 {
926     if (isNull()) {
927         return Error::fromCode(GPG_ERR_GENERAL);
928     }
929     auto ctx = Context::createForProtocol(parent().protocol());
930     if (!ctx) {
931         return Error::fromCode(GPG_ERR_INV_ENGINE);
932     }
933     Error ret = ctx->revUid(key, id());
934     delete ctx;
935     return ret;
936 }
937
938 Error Key::addUid(const char *uid)
939 {
940     if (isNull()) {
941         return Error::fromCode(GPG_ERR_GENERAL);
942     }
943     auto ctx = Context::createForProtocol(protocol());
944     if (!ctx) {
945         return Error::fromCode(GPG_ERR_INV_ENGINE);
946     }
947     Error ret = ctx->addUid(key, uid);
948     delete ctx;
949     return ret;
950 }
951
952 std::ostream &operator<<(std::ostream &os, const UserID &uid)
953 {
954     os << "GpgME::UserID(";
955     if (!uid.isNull()) {
956         os << "\n name:      " << protect(uid.name())
957            << "\n email:     " << protect(uid.email())
958            << "\n mbox:      " << uid.addrSpec()
959            << "\n comment:   " << protect(uid.comment())
960            << "\n validity:  " << uid.validityAsString()
961            << "\n revoked:   " << uid.isRevoked()
962            << "\n invalid:   " << uid.isInvalid()
963            << "\n numsigs:   " << uid.numSignatures()
964            << "\n tofuinfo:\n" << uid.tofuInfo();
965     }
966     return os << ')';
967 }
968
969 std::ostream &operator<<(std::ostream &os, const Key &key)
970 {
971     os << "GpgME::Key(";
972     if (!key.isNull()) {
973         os << "\n protocol:   " << protect(key.protocolAsString())
974            << "\n ownertrust: " << key.ownerTrustAsString()
975            << "\n issuer:     " << protect(key.issuerName())
976            << "\n fingerprint:" << protect(key.primaryFingerprint())
977            << "\n listmode:   " << key.keyListMode()
978            << "\n canSign:    " << key.canReallySign()
979            << "\n canEncrypt: " << key.canEncrypt()
980            << "\n canCertify: " << key.canCertify()
981            << "\n canAuth:    " << key.canAuthenticate()
982            << "\n uids:\n";
983         const std::vector<UserID> uids = key.userIDs();
984         std::copy(uids.begin(), uids.end(),
985                   std::ostream_iterator<UserID>(os, "\n"));
986     }
987     return os << ')';
988 }
989
990 } // namespace GpgME