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