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