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