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