2003-04-25 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / key.c
1 /* key.c - Key objects.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11  
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <ctype.h>
29
30 #include "util.h"
31 #include "ops.h"
32 #include "key.h"
33 #include "sema.h"
34
35
36 /* Protects all reference counters in keys.  All other accesses to a
37    key are either read only or happen before the key is entered into
38    the cache.  */
39 DEFINE_STATIC_LOCK (key_ref_lock);
40
41 \f
42 static const char *
43 pkalgo_to_string (int algo)
44 {
45   switch (algo)
46     {
47     case 1: 
48     case 2:
49     case 3:
50       return "RSA";
51
52     case 16:
53     case 20:
54       return "ElG";
55
56     case 17:
57       return "DSA";
58
59     default:
60       return "Unknown";
61     }
62 }
63
64
65 static const char *
66 otrust_to_string (int otrust)
67 {
68   switch (otrust)
69     {
70     case GPGME_VALIDITY_NEVER:
71       return "n";
72
73     case GPGME_VALIDITY_MARGINAL:
74       return "m";
75
76     case GPGME_VALIDITY_FULL:
77       return "f";
78
79     case GPGME_VALIDITY_ULTIMATE:
80       return "u";
81
82     default:
83       return "?";
84     }
85 }
86
87
88 static const char *
89 validity_to_string (int validity)
90 {
91   switch (validity)
92     {
93     case GPGME_VALIDITY_UNDEFINED:
94       return "q";
95
96     case GPGME_VALIDITY_NEVER:
97       return "n";
98
99     case GPGME_VALIDITY_MARGINAL:
100       return "m";
101
102     case GPGME_VALIDITY_FULL:
103       return "f";
104
105     case GPGME_VALIDITY_ULTIMATE:
106       return "u";
107
108     case GPGME_VALIDITY_UNKNOWN:
109     default:
110       return "?";
111     }
112 }
113
114
115 static GpgmeError
116 key_new (GpgmeKey *r_key, int secret)
117 {
118   GpgmeKey key;
119
120   *r_key = NULL;
121   key = calloc (1, sizeof *key);
122   if (!key)
123     return GPGME_Out_Of_Core;
124   key->ref_count = 1;
125   *r_key = key;
126   if (secret)
127     key->secret = 1;
128   return 0;
129 }
130
131
132 GpgmeError
133 _gpgme_key_new (GpgmeKey *r_key)
134 {
135   return key_new (r_key, 0);
136 }
137
138
139 GpgmeError
140 _gpgme_key_new_secret (GpgmeKey *r_key)
141 {
142   return key_new (r_key, 1);
143 }
144
145
146 /* Acquire a reference to KEY.  */
147 void
148 gpgme_key_ref (GpgmeKey key)
149 {
150   if (!key)
151     return;
152   LOCK (key_ref_lock);
153   key->ref_count++;
154   UNLOCK (key_ref_lock);
155 }
156
157 \f
158 static struct subkey_s *
159 add_subkey (GpgmeKey key, int secret)
160 {
161   struct subkey_s *k, *kk;
162
163   k = calloc (1, sizeof *k);
164   if (!k)
165     return NULL;
166
167   if (!(kk = key->keys.next))
168     key->keys.next = k;
169   else
170     {
171       while (kk->next)
172         kk = kk->next;
173       kk->next = k;
174     }
175   if (secret)
176     k->secret = 1;
177   return k;
178 }
179
180
181 struct subkey_s *
182 _gpgme_key_add_subkey (GpgmeKey key)
183 {
184   return add_subkey (key, 0);
185 }
186
187
188 struct subkey_s *
189 _gpgme_key_add_secret_subkey (GpgmeKey key)
190 {
191   return add_subkey (key, 1);
192 }
193
194 \f
195 static char *
196 set_user_id_part (char *tail, const char *buf, size_t len)
197 {
198   while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t')) 
199     len--;
200   for (; len; len--)
201     *tail++ = *buf++;
202   *tail++ = 0;
203   return tail;
204 }
205
206
207 static void
208 parse_user_id (const char *src, const char **name, const char **email,
209                     const char **comment, char *tail)
210 {
211   const char *start = NULL;
212   int in_name = 0;
213   int in_email = 0;
214   int in_comment = 0;
215
216   while (*src)
217     {
218       if (in_email)
219         {
220           if (*src == '<')
221             /* Not legal but anyway.  */
222             in_email++;
223           else if (*src == '>')
224             {
225               if (!--in_email && !*email)
226                 {
227                   *email = tail;
228                   tail = set_user_id_part (tail, start, src - start);
229                 }
230             }
231         }
232       else if (in_comment)
233         {
234           if (*src == '(')
235             in_comment++;
236           else if (*src == ')')
237             {
238               if (!--in_comment && !*comment)
239                 {
240                   *comment = tail;
241                   tail = set_user_id_part (tail, start, src - start);
242                 }
243             }
244         }
245       else if (*src == '<')
246         {
247           if (in_name)
248             {
249               if (!*name)
250                 {
251                   *name = tail;
252                   tail = set_user_id_part (tail, start, src - start);
253                 }
254               in_name = 0;
255             }
256           in_email = 1;
257           start = src + 1;
258         }
259       else if (*src == '(')
260         {
261           if (in_name)
262             {
263               if (!*name)
264                 {
265                   *name = tail;
266                   tail = set_user_id_part (tail, start, src - start);
267                 }
268               in_name = 0;
269             }
270           in_comment = 1;
271           start = src + 1;
272         }
273       else if (!in_name && *src != ' ' && *src != '\t')
274         {
275           in_name = 1;
276           start = src;
277         }    
278       src++;
279     }
280  
281   if (in_name)
282     {
283       if (!*name)
284         {
285           *name = tail;
286           tail = set_user_id_part (tail, start, src - start);
287         }
288     }
289  
290   /* Let unused parts point to an EOS.  */
291   tail--;
292   if (!*name)
293     *name = tail;
294   if (!*email)
295     *email = tail;
296   if (!*comment)
297     *comment = tail;
298 }
299
300
301 static void
302 parse_x509_user_id (const char *src, const char **name, const char **email,
303                     const char **comment, char *tail)
304 {
305   if (*src == '<' && src[strlen (src) - 1] == '>')
306     *email = src;
307   
308   /* Let unused parts point to an EOS.  */
309   tail--;
310   if (!*name)
311     *name = tail;
312   if (!*email)
313     *email = tail;
314   if (!*comment)
315     *comment = tail;
316 }
317
318 \f
319 struct certsig_s *
320 _gpgme_key_add_certsig (GpgmeKey key, char *src)
321 {
322   int src_len = src ? strlen (src) : 0;
323   struct user_id_s *uid;
324   struct certsig_s *certsig;
325
326   assert (key); /* XXX */
327
328   uid = key->last_uid;
329   assert (uid); /* XXX */
330
331   /* We can malloc a buffer of the same length, because the converted
332      string will never be larger. Actually we allocate it twice the
333      size, so that we are able to store the parsed stuff there too.  */
334   certsig = calloc (1, sizeof (*certsig) + 2 * src_len + 3);
335   if (!certsig)
336     return NULL;
337
338   if (src)
339     {
340       char *dst = certsig->name;
341       _gpgme_decode_c_string (src, &dst, src_len + 1);
342       dst += src_len + 1;
343       if (key->x509)
344         parse_x509_user_id (src, &certsig->name_part, &certsig->email_part,
345                             &certsig->comment_part, dst);
346       else
347         parse_user_id (src, &certsig->name_part, &certsig->email_part,
348                        &certsig->comment_part, dst);
349     }
350
351   if (!uid->certsigs)
352     uid->certsigs = certsig;
353   if (uid->last_certsig)
354     uid->last_certsig->next = certsig;
355   uid->last_certsig = certsig;
356
357   return certsig;
358 }
359
360 \f
361 /**
362  * gpgme_key_release:
363  * @key: Key Object or NULL
364  * 
365  * Release the key object. Note, that this function may not do an
366  * actual release if there are other shallow copies of the objects.
367  * You have to call this function for every newly created key object
368  * as well as for every gpgme_key_ref() done on the key object.
369  **/
370 void
371 gpgme_key_release (GpgmeKey key)
372 {
373   struct certsig_s *c, *c2;
374   struct user_id_s *u, *u2;
375   struct subkey_s *k, *k2;
376
377   if (!key)
378     return;
379
380   LOCK (key_ref_lock);
381   assert (key->ref_count);
382   if (--key->ref_count)
383     {
384       UNLOCK (key_ref_lock);
385       return;
386     }
387   UNLOCK (key_ref_lock);
388
389   free (key->keys.fingerprint);
390   for (k = key->keys.next; k; k = k2)
391     {
392       k2 = k->next;
393       free (k->fingerprint);
394       free (k);
395     }
396   for (u = key->uids; u; u = u2)
397     {
398       u2 = u->next;
399       for (c = u->certsigs; c; c = c2)
400         {
401           c2 = c->next;
402           free (c);
403         }
404       free (u);
405     }
406   free (key->issuer_serial);
407   free (key->issuer_name);
408   free (key->chain_id);
409   free (key);
410 }
411
412
413 /**
414  * gpgme_key_unref:
415  * @key: Key Object
416  * 
417  * This is an alias for gpgme_key_release().
418  **/
419 void
420 gpgme_key_unref (GpgmeKey key)
421 {
422   gpgme_key_release (key);
423 }
424
425 \f
426 /* Take a name from the --with-colon listing, remove certain escape
427    sequences sequences and put it into the list of UIDs.  */
428 GpgmeError
429 _gpgme_key_append_name (GpgmeKey key, const char *src)
430 {
431   struct user_id_s *uid;
432   char *dst;
433   int src_len = strlen (src);
434
435   assert (key);
436   /* We can malloc a buffer of the same length, because the converted
437      string will never be larger. Actually we allocate it twice the
438      size, so that we are able to store the parsed stuff there too.  */
439   uid = malloc (sizeof (*uid) + 2 * src_len + 3);
440   if (!uid)
441     return GPGME_Out_Of_Core;
442   memset (uid, 0, sizeof *uid);
443
444   dst = uid->name;
445   _gpgme_decode_c_string (src, &dst, src_len + 1);
446
447   dst += src_len + 1;
448   if (key->x509)
449     parse_x509_user_id (src, &uid->name_part, &uid->email_part,
450                         &uid->comment_part, dst);
451   else
452     parse_user_id (src, &uid->name_part, &uid->email_part,
453                    &uid->comment_part, dst);
454
455   if (!key->uids)
456     key->uids = uid;
457   if (key->last_uid)
458     key->last_uid->next = uid;
459   key->last_uid = uid;
460
461   return 0;
462 }
463
464
465 static void
466 add_otag (GpgmeData d, const char *tag)
467 {
468   _gpgme_data_append_string (d, "    <");
469   _gpgme_data_append_string (d, tag);
470   _gpgme_data_append_string (d, ">");
471 }
472
473
474 static void
475 add_ctag (GpgmeData d, const char *tag)
476 {
477   _gpgme_data_append_string (d, "</");
478   _gpgme_data_append_string (d, tag);
479   _gpgme_data_append_string (d, ">\n");
480 }
481
482
483 static void
484 add_tag_and_string (GpgmeData d, const char *tag, const char *string)
485 {
486   add_otag (d, tag);
487   _gpgme_data_append_string_for_xml (d, string);
488   add_ctag (d, tag); 
489 }
490
491
492 static void
493 add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
494 {
495   char buf[30];
496   sprintf (buf, "%u", val);
497   add_tag_and_string (d, tag, buf);
498 }
499
500
501 static void
502 add_tag_and_time (GpgmeData d, const char *tag, time_t val)
503 {
504   char buf[30];
505
506   if (!val || val == (time_t) - 1)
507     return;
508   sprintf (buf, "%lu", (unsigned long) val);
509   add_tag_and_string (d, tag, buf);
510 }
511
512
513 static void
514 one_certsig_as_xml (GpgmeData data, struct certsig_s *certsig)
515 {
516   _gpgme_data_append_string (data, "    <signature>\n");
517   if (certsig->flags.invalid)
518     _gpgme_data_append_string (data, "      <invalid/>\n");
519   if (certsig->flags.revoked)
520     _gpgme_data_append_string (data, "      <revoked/>\n");
521   if (certsig->flags.expired)
522     _gpgme_data_append_string (data, "      <expired/>\n");
523   add_tag_and_string (data, "keyid", certsig->keyid);
524   add_tag_and_uint (data, "algo", certsig->algo);
525   add_tag_and_time (data, "created", certsig->timestamp);
526   add_tag_and_time (data, "expire", certsig->expires_at);
527   if (*certsig->name)
528     add_tag_and_string (data, "raw", certsig->name);
529   if (*certsig->name_part)
530     add_tag_and_string (data, "name", certsig->name_part);
531   if (*certsig->email_part)
532     add_tag_and_string (data, "email", certsig->email_part);
533   if (*certsig->comment_part)
534     add_tag_and_string (data, "comment", certsig->comment_part);
535   _gpgme_data_append_string (data, "    </signature>\n");
536 }
537
538
539 static void
540 one_uid_as_xml (GpgmeData data, struct user_id_s *uid)
541 {
542   struct certsig_s *certsig;
543
544   _gpgme_data_append_string (data, "  <userid>\n");
545   if (uid->invalid)
546     _gpgme_data_append_string (data, "    <invalid/>\n");
547   if (uid->revoked)
548     _gpgme_data_append_string (data, "    <revoked/>\n");
549   add_tag_and_string (data, "raw", uid->name);
550   if (*uid->name_part)
551     add_tag_and_string (data, "name", uid->name_part);
552   if (*uid->email_part)
553     add_tag_and_string (data, "email", uid->email_part);
554   if (*uid->comment_part)
555     add_tag_and_string (data, "comment", uid->comment_part);
556
557   /* Now the signatures.  */
558   for (certsig = uid->certsigs; certsig; certsig = certsig->next)
559     one_certsig_as_xml (data, certsig);
560   _gpgme_data_append_string (data, "  </userid>\n");
561 }
562
563
564 /**
565  * gpgme_key_get_as_xml:
566  * @key: Key object
567  * 
568  * Return the key object as an XML string.  The classer has to free
569  * that string.
570  * 
571  * Return value:  An XML string or NULL in case of a memory problem or
572  *                a NULL passed as @key
573  **/
574 char *
575 gpgme_key_get_as_xml (GpgmeKey key)
576 {
577   GpgmeData d;
578   struct user_id_s *u;
579   struct subkey_s *k;
580   
581   if (!key)
582     return NULL;
583   
584   if (gpgme_data_new (&d))
585     return NULL;
586   
587   _gpgme_data_append_string (d, "<GnupgKeyblock>\n"
588                              "  <mainkey>\n");
589   if (key->keys.secret)
590     _gpgme_data_append_string (d, "    <secret/>\n");
591   if (key->keys.flags.invalid)
592     _gpgme_data_append_string (d, "    <invalid/>\n");
593   if (key->keys.flags.revoked)
594     _gpgme_data_append_string (d, "    <revoked/>\n");
595   if (key->keys.flags.expired)
596     _gpgme_data_append_string (d, "    <expired/>\n");
597   if (key->keys.flags.disabled)
598     _gpgme_data_append_string (d, "    <disabled/>\n");
599   add_tag_and_string (d, "keyid", key->keys.keyid);
600   if (key->keys.fingerprint)
601     add_tag_and_string (d, "fpr", key->keys.fingerprint);
602   add_tag_and_uint (d, "algo", key->keys.key_algo);
603   add_tag_and_uint (d, "len", key->keys.key_len);
604   add_tag_and_time (d, "created", key->keys.timestamp);
605   add_tag_and_time (d, "expire", key->keys.expires_at);
606   add_tag_and_string (d, "otrust", otrust_to_string (key->otrust));
607   if (key->issuer_serial)
608     add_tag_and_string (d, "serial", key->issuer_serial);
609   if (key->issuer_name)
610     add_tag_and_string (d, "issuer", key->issuer_name);
611   if (key->chain_id)
612     add_tag_and_string (d, "chainid", key->chain_id);
613   _gpgme_data_append_string (d, "  </mainkey>\n");
614
615   /* Now the user IDs.  */
616   for (u = key->uids; u; u = u->next)
617     one_uid_as_xml (d,u);
618   
619   /* And now the subkeys.  */
620   for (k = key->keys.next; k; k = k->next)
621     {
622       _gpgme_data_append_string (d, "  <subkey>\n");
623       if (k->secret)
624         _gpgme_data_append_string (d, "    <secret/>\n");
625       if (k->flags.invalid)
626         _gpgme_data_append_string (d, "    <invalid/>\n");
627       if (k->flags.revoked)
628         _gpgme_data_append_string (d, "    <revoked/>\n");
629       if (k->flags.expired)
630         _gpgme_data_append_string (d, "    <expired/>\n");
631       if (k->flags.disabled)
632         _gpgme_data_append_string (d, "    <disabled/>\n");
633       add_tag_and_string (d, "keyid", k->keyid);
634       if (k->fingerprint)
635         add_tag_and_string (d, "fpr", k->fingerprint);
636       add_tag_and_uint (d, "algo", k->key_algo);
637       add_tag_and_uint (d, "len", k->key_len);
638       add_tag_and_time (d, "created", k->timestamp);
639       add_tag_and_time (d, "expire", k->expires_at);
640       _gpgme_data_append_string (d, "  </subkey>\n");
641     }
642   _gpgme_data_append_string (d, "</GnupgKeyblock>\n");
643   
644   return _gpgme_data_release_and_return_string (d);
645 }
646
647
648 static const char *
649 capabilities_to_string (struct subkey_s *k)
650 {
651   static const char *const strings[8] =
652     {
653       "",
654       "c",
655       "s",
656       "sc",
657       "e",
658       "ec",
659       "es",
660       "esc"
661     };
662   return strings[(!!k->flags.can_encrypt << 2)
663                  | (!!k->flags.can_sign << 1)
664                  | (!!k->flags.can_certify)];
665 }
666
667
668 /**
669  * gpgme_key_get_string_attr:
670  * @key: Key Object
671  * @what: Attribute specifier
672  * @reserved: Must be 0
673  * @idx: Index counter
674  * 
675  * Return a attribute as specified by @what and @idx.  Note that not
676  * all attributes can be returned as a string, in which case NULL is
677  * returned.  @idx is used to iterate through attributes which do have
678  * more than one instance (e.g. user IDs or sub keys).
679  * 
680  * Return value: NULL or an const string which is only valid as long
681  * as the key object itself is valid.
682  **/
683 const char *
684 gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
685                            const void *reserved, int idx)
686 {
687   struct subkey_s *subkey;
688   struct user_id_s *uid;
689   int i;
690
691   if (!key || reserved || idx < 0)
692     return NULL;
693
694   /* Select IDXth subkey.  */
695   subkey = &key->keys;
696   for (i = 0; i < idx; i++)
697     {
698       subkey = subkey->next;
699       if (!subkey)
700         break;
701     }
702
703   /* Select the IDXth user ID.  */
704   uid = key->uids;
705   for (i = 0; i < idx; i++)
706     {
707       uid = uid->next;
708       if (!uid)
709         break;
710     }
711
712   switch (what)
713     {
714     case GPGME_ATTR_KEYID:
715       return subkey ? subkey->keyid : NULL;
716
717     case GPGME_ATTR_FPR:
718       return subkey ? subkey->fingerprint : NULL;
719
720     case GPGME_ATTR_ALGO:    
721       return subkey ? pkalgo_to_string (subkey->key_algo) : NULL;
722
723     case GPGME_ATTR_TYPE:
724       return key->x509 ? "X.509" : "PGP";
725
726     case GPGME_ATTR_OTRUST:
727       return otrust_to_string (key->otrust);
728
729     case GPGME_ATTR_USERID:  
730       return uid ? uid->name : NULL;
731
732     case GPGME_ATTR_NAME:   
733       return uid ? uid->name_part : NULL;
734
735     case GPGME_ATTR_EMAIL:
736       return uid ? uid->email_part : NULL;
737
738     case GPGME_ATTR_COMMENT:
739       return uid ? uid->comment_part : NULL;
740
741     case GPGME_ATTR_VALIDITY:
742       return uid ? validity_to_string (uid->validity) : NULL;
743
744     case GPGME_ATTR_KEY_CAPS:    
745       return subkey ? capabilities_to_string (subkey) : NULL;
746
747     case GPGME_ATTR_SERIAL:
748       return key->issuer_serial;
749
750     case GPGME_ATTR_ISSUER:
751       return idx ? NULL : key->issuer_name;
752
753     case GPGME_ATTR_CHAINID:
754       return  key->chain_id;
755
756     default:
757       return NULL;
758     }
759 }
760
761
762 /**
763  * gpgme_key_get_ulong_attr:
764  * @key: 
765  * @what: 
766  * @reserved: 
767  * @idx: 
768  * 
769  * Return a attribute as specified by @what and @idx.  Note that not
770  * all attributes can be returned as an integer, in which case 0 is
771  * returned.  @idx is used to iterate through attributes which do have
772  * more than one instance (e.g. user IDs or sub keys).
773  *
774  * See gpgme.h for a list of attributes.
775  * 
776  * Return value: 0 or the requested value.
777  **/
778 unsigned long
779 gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
780                           const void *reserved, int idx)
781 {
782   struct subkey_s *subkey;
783   struct user_id_s *uid;
784   int i;
785
786   if (!key || reserved || idx < 0)
787     return 0;
788
789   /* Select IDXth subkey.  */
790   subkey = &key->keys;
791   for (i = 0; i < idx; i++)
792     {
793       subkey = subkey->next;
794       if (!subkey)
795         break;
796     }
797
798   /* Select the IDXth user ID.  */
799   uid = key->uids;
800   for (i = 0; i < idx; i++)
801     {
802       uid = uid->next;
803       if (!uid)
804         break;
805     }
806
807   switch (what)
808     {
809     case GPGME_ATTR_ALGO:
810       return subkey ? (unsigned long) subkey->key_algo : 0;
811
812     case GPGME_ATTR_LEN:
813       return subkey ? (unsigned long) subkey->key_len : 0;
814
815     case GPGME_ATTR_TYPE:
816       return key->x509 ? 1 : 0;
817
818     case GPGME_ATTR_CREATED: 
819       return (subkey && subkey->timestamp >= 0)
820         ? (unsigned long) subkey->timestamp : 0;
821
822     case GPGME_ATTR_EXPIRE: 
823       return (subkey && subkey->expires_at >= 0)
824         ? (unsigned long) subkey->expires_at : 0;
825
826     case GPGME_ATTR_VALIDITY:
827       return uid ? uid->validity : 0;
828
829     case GPGME_ATTR_OTRUST:
830       return key->otrust;
831
832     case GPGME_ATTR_IS_SECRET:
833       return !!key->secret;
834
835     case GPGME_ATTR_KEY_REVOKED:
836       return subkey ? subkey->flags.revoked : 0;
837
838     case GPGME_ATTR_KEY_INVALID:
839       return subkey ? subkey->flags.invalid : 0;
840
841     case GPGME_ATTR_KEY_EXPIRED:
842       return subkey ? subkey->flags.expired : 0;
843
844     case GPGME_ATTR_KEY_DISABLED:
845       return subkey ? subkey->flags.disabled : 0;
846
847     case GPGME_ATTR_UID_REVOKED:
848       return uid ? uid->revoked : 0;
849
850     case GPGME_ATTR_UID_INVALID:
851       return uid ? uid->invalid : 0;
852
853     case GPGME_ATTR_CAN_ENCRYPT:
854       return key->gloflags.can_encrypt;
855
856     case GPGME_ATTR_CAN_SIGN:
857       return key->gloflags.can_sign;
858
859     case GPGME_ATTR_CAN_CERTIFY:
860       return key->gloflags.can_certify;
861
862     default:
863       return 0;
864     }
865 }
866
867
868 static struct certsig_s *
869 get_certsig (GpgmeKey key, int uid_idx, int idx)
870 {
871   struct user_id_s *uid;
872   struct certsig_s *certsig;
873
874   if (!key || uid_idx < 0 || idx < 0)
875     return NULL;
876
877   uid = key->uids;
878   while (uid && uid_idx > 0)
879     {
880       uid = uid->next;
881       uid_idx--;
882     }
883   if (!uid)
884     return NULL;
885
886   certsig = uid->certsigs;
887   while (certsig && idx > 0)
888     {
889       certsig = certsig->next;
890       idx--;
891     }
892   return certsig;
893 }
894
895
896 const char *
897 gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
898                                const void *reserved, int idx)
899 {
900   struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
901
902   if (!certsig || reserved)
903     return NULL;
904
905   switch (what)
906     {
907     case GPGME_ATTR_KEYID:
908       return certsig->keyid;
909
910     case GPGME_ATTR_ALGO:    
911       return pkalgo_to_string (certsig->algo);
912
913     case GPGME_ATTR_USERID:  
914       return certsig->name;
915
916     case GPGME_ATTR_NAME:   
917       return certsig->name_part;
918
919     case GPGME_ATTR_EMAIL:
920       return certsig->email_part;
921
922     case GPGME_ATTR_COMMENT:
923       return certsig->comment_part;
924    
925     default:
926       return NULL;
927     }
928 }
929
930
931 unsigned long
932 gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
933                               const void *reserved, int idx)
934 {
935   struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
936
937   if (!certsig || reserved)
938     return 0;
939
940   switch (what)
941     {
942     case GPGME_ATTR_ALGO:    
943       return (unsigned long) certsig->algo;
944
945     case GPGME_ATTR_CREATED: 
946       return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
947
948     case GPGME_ATTR_EXPIRE: 
949       return certsig->expires_at < 0 ? 0L : (unsigned long) certsig->expires_at;
950
951     case GPGME_ATTR_KEY_REVOKED:
952       return certsig->flags.revoked;
953
954     case GPGME_ATTR_KEY_INVALID:
955       return certsig->flags.invalid;
956
957     case GPGME_ATTR_KEY_EXPIRED:
958       return certsig->flags.expired;
959
960     case GPGME_ATTR_SIG_CLASS:
961       return certsig->sig_class;
962
963     case GPGME_ATTR_SIG_STATUS:
964       return certsig->sig_stat;
965
966     default:
967       return 0;
968     }
969 }