Adjusted for changes --fixed-list-mode in 1.0.4h
[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 <assert.h>
26
27 #include "util.h"
28 #include "ops.h"
29 #include "key.h"
30
31 #define ALLOC_CHUNK 1024
32 #define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
33
34
35 static const char *
36 pkalgo_to_string ( int algo )
37 {
38     switch (algo) {
39       case 1: 
40       case 2:
41       case 3: return "RSA";
42       case 16:
43       case 20: return "ElG";
44       case 17: return "DSA";
45       default: return "Unknown";
46     }
47 }
48
49
50
51
52 static GpgmeError
53 key_new ( GpgmeKey *r_key, int secret )
54 {
55     GpgmeKey key;
56
57     *r_key = NULL;
58     key = xtrycalloc ( 1, sizeof *key );
59     if (!key)
60         return mk_error (Out_Of_Core);
61     key->ref_count = 1;
62     *r_key = key;
63     if (secret)
64         key->secret = 1;
65     return 0;
66 }
67
68 GpgmeError
69 _gpgme_key_new ( GpgmeKey *r_key )
70 {
71     return key_new ( r_key, 0 );
72 }
73
74 GpgmeError
75 _gpgme_key_new_secret ( GpgmeKey *r_key )
76 {
77     return key_new ( r_key, 1 );
78 }
79
80 void
81 gpgme_key_ref ( GpgmeKey key )
82 {
83     return_if_fail (key);
84     key->ref_count++;
85 }
86
87
88 static struct subkey_s *
89 add_subkey (GpgmeKey key, int secret)
90 {
91     struct subkey_s *k, *kk;
92
93     k = xtrycalloc (1, sizeof *k);
94     if (!k)
95         return NULL;
96
97     if( !(kk=key->keys.next) )
98         key->keys.next = k;
99     else {
100         while ( kk->next )
101             kk = kk->next;
102         kk->next = k;
103     }
104     if (secret)
105         k->secret = 1;
106     return k;
107 }
108
109 struct subkey_s *
110 _gpgme_key_add_subkey (GpgmeKey key)
111 {
112     return add_subkey (key, 0);
113 }
114
115 struct subkey_s *
116 _gpgme_key_add_secret_subkey (GpgmeKey key)
117 {
118     return add_subkey (key, 1);
119 }
120
121 void
122 gpgme_key_release ( GpgmeKey key )
123 {
124     struct user_id_s *u, *u2;
125     struct subkey_s *k, *k2;
126
127     if (!key)
128         return;
129
130     assert (key->ref_count);
131     if ( --key->ref_count )
132         return;
133
134     xfree (key->keys.fingerprint);
135     for (k = key->keys.next; k; k = k2 ) {
136         k2 = k->next;
137         xfree (k->fingerprint);
138         xfree (k);
139     }
140     for (u = key->uids; u; u = u2 ) {
141         u2 = u->next;
142         xfree (u);
143     }
144     xfree (key);
145 }
146
147 void
148 gpgme_key_unref (GpgmeKey key)
149 {
150     gpgme_key_release (key);
151 }
152
153
154 static char *
155 set_user_id_part ( char *tail, const char *buf, size_t len )
156 {
157     while ( len && (buf[len-1] == ' ' || buf[len-1] == '\t') ) 
158         len--;
159     for ( ; len; len--)
160         *tail++ = *buf++;
161     *tail++ = 0;
162     return tail;
163 }
164
165
166 static void
167 parse_user_id ( struct user_id_s *uid, char *tail )
168 {
169     const char *s, *start=NULL;
170     int in_name = 0;
171     int in_email = 0;
172     int in_comment = 0;
173
174     for (s=uid->name; *s; s++ ) {
175         if ( in_email ) {
176             if ( *s == '<' )
177                 in_email++; /* not legal but anyway */
178             else if (*s== '>') {
179                 if ( !--in_email ) {
180                     if (!uid->email_part) {
181                         uid->email_part = tail;
182                         tail = set_user_id_part ( tail, start, s-start );
183                     }
184                 }
185             }
186         }
187         else if ( in_comment ) {
188             if ( *s == '(' )
189                 in_comment++;
190             else if (*s== ')') {
191                 if ( !--in_comment ) {
192                     if (!uid->comment_part) {
193                         uid->comment_part = tail;
194                         tail = set_user_id_part ( tail, start, s-start );
195                     }
196                 }
197             }
198         }
199         else if ( *s == '<' ) {
200             if ( in_name ) {
201                 if ( !uid->name_part ) {
202                     uid->name_part = tail;
203                     tail = set_user_id_part (tail, start, s-start );
204                 }
205                 in_name = 0;
206             }
207             in_email = 1;
208             start = s+1;
209         }
210         else if ( *s == '(' ) {
211             if ( in_name ) {
212                 if ( !uid->name_part ) {
213                     uid->name_part = tail;
214                     tail = set_user_id_part (tail, start, s-start );
215                 }
216                 in_name = 0;
217             }
218             in_comment = 1;
219             start = s+1;
220         }
221         else if ( !in_name && *s != ' ' && *s != '\t' ) {
222             in_name = 1;
223             start = s;
224         }    
225     }
226
227     if ( in_name ) {
228         if ( !uid->name_part ) {
229             uid->name_part = tail;
230             tail = set_user_id_part (tail, start, s-start );
231         }
232     }
233
234     /* let unused parts point to an EOS */ 
235     tail--;
236     if (!uid->name_part)
237         uid->name_part = tail;
238     if (!uid->email_part)
239         uid->email_part = tail;
240     if (!uid->comment_part)
241         uid->comment_part = tail;
242
243 }
244
245 /* 
246  * Take a name from the --with-colon listing, remove certain escape sequences
247  * sequences and put it into the list of UIDs
248  */
249 GpgmeError
250 _gpgme_key_append_name ( GpgmeKey key, const char *s )
251 {
252     struct user_id_s *uid;
253     char *d;
254
255     assert (key);
256     /* we can malloc a buffer of the same length, because the
257      * converted string will never be larger. Actually we allocate it
258      * twice the size, so that we are able to store the parsed stuff
259      * there too */
260     uid = xtrymalloc ( sizeof *uid + 2*strlen (s)+3 );
261     if ( !uid )
262         return mk_error (Out_Of_Core);
263     uid->revoked = 0;
264     uid->invalid = 0;
265     uid->validity = 0;
266     uid->name_part = NULL;
267     uid->email_part = NULL;
268     uid->comment_part = NULL;
269     d = uid->name;
270
271     while ( *s ) {
272         if ( *s != '\\' )
273             *d++ = *s++;
274         else if ( s[1] == '\\' ) {
275             s++;
276             *d++ = *s++; 
277         }
278         else if ( s[1] == 'n' ) {
279             s += 2;
280             *d++ = '\n'; 
281         }
282         else if ( s[1] == 'r' ) {
283             s += 2;
284             *d++ = '\r'; 
285         }
286         else if ( s[1] == 'v' ) {
287             s += 2;
288             *d++ = '\v'; 
289         }
290         else if ( s[1] == 'b' ) {
291             s += 2;
292             *d++ = '\b'; 
293         }
294         else if ( s[1] == '0' ) {
295             /* Hmmm: no way to express this */
296             s += 2;
297             *d++ = '\\';
298             *d++ = '\0'; 
299         }
300         else if ( s[1] == 'x' && my_isdigit (s[2]) && my_isdigit (s[3]) ) {
301             unsigned int val = (s[2]-'0')*16 + (s[3]-'0');
302             if ( !val ) {
303                 *d++ = '\\';
304                 *d++ = '\0'; 
305             }
306             else 
307                 *(byte*)d++ = val;
308             s += 3;
309         }
310         else { /* should not happen */
311             s++;
312             *d++ = '\\'; 
313             *d++ = *s++;
314         } 
315     }
316     *d++ = 0;
317     parse_user_id ( uid, d );
318
319     uid->next = key->uids;
320     key->uids = uid;
321     return 0;
322 }
323
324
325 static void
326 add_otag ( GpgmeData d, const char *tag )
327 {
328     _gpgme_data_append_string ( d, "    <" );
329     _gpgme_data_append_string ( d, tag );
330     _gpgme_data_append_string ( d, ">" );
331 }
332
333 static void
334 add_ctag ( GpgmeData d, const char *tag )
335 {
336     _gpgme_data_append_string ( d, "</" );
337     _gpgme_data_append_string ( d, tag );
338     _gpgme_data_append_string ( d, ">\n" );
339 }
340
341 static void
342 add_tag_and_string ( GpgmeData d, const char *tag, const char *string )
343 {
344     add_otag (d, tag);
345     _gpgme_data_append_string_for_xml ( d, string );
346     add_ctag (d, tag); 
347 }
348
349 static void
350 add_tag_and_uint ( GpgmeData d, const char *tag, unsigned int val )
351 {
352     char buf[30];
353     sprintf (buf, "%u", val );
354     add_tag_and_string ( d, tag, buf );
355 }
356
357 static void
358 add_tag_and_time ( GpgmeData d, const char *tag, time_t val )
359 {
360     char buf[30];
361
362     if (!val || val == (time_t)-1 )
363         return;
364     sprintf (buf, "%lu", (unsigned long)val );
365     add_tag_and_string ( d, tag, buf );
366 }
367
368 static void
369 one_uid_as_xml (GpgmeData d, struct user_id_s *u)
370 {
371     _gpgme_data_append_string (d, "  <userid>\n");
372     if ( u->invalid )
373         _gpgme_data_append_string ( d, "    <invalid/>\n");
374     if ( u->revoked )
375         _gpgme_data_append_string ( d, "    <revoked/>\n");
376     add_tag_and_string ( d, "raw", u->name );
377     if ( *u->name_part )
378         add_tag_and_string ( d, "name", u->name_part );
379     if ( *u->email_part )
380         add_tag_and_string ( d, "email", u->email_part );
381     if ( *u->comment_part )
382         add_tag_and_string ( d, "comment", u->comment_part );
383     _gpgme_data_append_string (d, "  </userid>\n");
384 }
385
386
387 char *
388 gpgme_key_get_as_xml ( GpgmeKey key )
389 {
390     GpgmeData d;
391     struct user_id_s *u;
392     struct subkey_s *k;
393
394     if ( !key )
395         return NULL;
396     
397     if ( gpgme_data_new ( &d ) )
398         return NULL;
399     
400     _gpgme_data_append_string ( d, "<GnupgKeyblock>\n"
401                                    "  <mainkey>\n" );
402     if ( key->keys.secret )
403         _gpgme_data_append_string ( d, "    <secret/>\n");
404     if ( key->keys.flags.invalid )
405         _gpgme_data_append_string ( d, "    <invalid/>\n");
406     if ( key->keys.flags.revoked )
407         _gpgme_data_append_string ( d, "    <revoked/>\n");
408     if ( key->keys.flags.expired )
409         _gpgme_data_append_string ( d, "    <expired/>\n");
410     if ( key->keys.flags.disabled )
411         _gpgme_data_append_string ( d, "    <disabled/>\n");
412     add_tag_and_string (d, "keyid", key->keys.keyid );   
413     if (key->keys.fingerprint)
414         add_tag_and_string (d, "fpr", key->keys.fingerprint );
415     add_tag_and_uint (d, "algo", key->keys.key_algo );
416     add_tag_and_uint (d, "len", key->keys.key_len );
417     add_tag_and_time (d, "created", key->keys.timestamp );
418     /*add_tag_and_time (d, "expires", key->expires );*/
419     _gpgme_data_append_string (d, "  </mainkey>\n");
420
421     /* Now the user IDs.  We are listing the last one firs becuase this is
422      * the primary one. */
423     for (u = key->uids; u && u->next; u = u->next )
424         ;
425     if (u) {
426         one_uid_as_xml (d,u);
427         for ( u = key->uids; u && u->next; u = u->next ) {
428             one_uid_as_xml (d,u);
429         }
430     }
431
432     /* and now the subkeys */
433     for (k=key->keys.next; k; k = k->next ) {
434         _gpgme_data_append_string (d, "  <subkey>\n");
435         if ( k->secret )
436             _gpgme_data_append_string ( d, "    <secret/>\n");
437         if ( k->flags.invalid )
438             _gpgme_data_append_string ( d, "    <invalid/>\n");
439         if ( k->flags.revoked )
440             _gpgme_data_append_string ( d, "    <revoked/>\n");
441         if ( k->flags.expired )
442             _gpgme_data_append_string ( d, "    <expired/>\n");
443         if ( k->flags.disabled )
444             _gpgme_data_append_string ( d, "    <disabled/>\n");
445         add_tag_and_string (d, "keyid", k->keyid );   
446         if (k->fingerprint)
447             add_tag_and_string (d, "fpr", k->fingerprint );
448         add_tag_and_uint (d, "algo", k->key_algo );
449         add_tag_and_uint (d, "len", k->key_len );
450         add_tag_and_time (d, "created", k->timestamp );
451         _gpgme_data_append_string (d, "  </subkey>\n");
452     }
453     _gpgme_data_append_string ( d, "</GnupgKeyblock>\n" );
454
455     return _gpgme_data_release_and_return_string (d);
456 }
457
458
459 static const char *
460 capabilities_to_string (struct subkey_s *k)
461 {
462     static char *strings[8] = {
463         "",
464         "c",
465         "s",
466         "sc",
467         "e",
468         "ec",
469         "es",
470         "esc"
471     };
472     return strings[  (!!k->flags.can_encrypt << 2)
473                    | (!!k->flags.can_sign    << 1)
474                    | (!!k->flags.can_certify     ) ];
475 }
476
477 const char *
478 gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
479                             const void *reserved, int idx )
480 {
481     const char *val = NULL;
482     struct subkey_s *k;
483     struct user_id_s *u;
484
485     if (!key)
486         return NULL;
487     if (reserved)
488         return NULL;
489     if (idx < 0)
490         return NULL;
491
492     switch (what) {
493       case GPGME_ATTR_KEYID:
494         for (k=&key->keys; k && idx; k=k->next, idx-- )
495             ;
496         if (k) 
497             val = k->keyid;
498         break;
499       case GPGME_ATTR_FPR:
500         for (k=&key->keys; k && idx; k=k->next, idx-- )
501             ;
502         if (k) 
503             val = k->fingerprint;
504         break;
505       case GPGME_ATTR_ALGO:    
506         for (k=&key->keys; k && idx; k=k->next, idx-- )
507             ;
508         if (k) 
509             val = pkalgo_to_string (k->key_algo);
510         break;
511       case GPGME_ATTR_LEN:     
512       case GPGME_ATTR_CREATED: 
513       case GPGME_ATTR_EXPIRE:  
514         break; /* use another get function */
515       case GPGME_ATTR_OTRUST:  
516         val = "[fixme]";
517         break;
518       case GPGME_ATTR_USERID:  
519         for (u=key->uids; u && idx; u=u->next, idx-- )
520             ;
521         val = u? u->name : NULL;
522         break;
523       case GPGME_ATTR_NAME:   
524         for (u=key->uids; u && idx; u=u->next, idx-- )
525             ;
526         val = u? u->name_part : NULL;
527         break;
528       case GPGME_ATTR_EMAIL:
529         for (u=key->uids; u && idx; u=u->next, idx-- )
530             ;
531         val = u? u->email_part : NULL;
532         break;
533       case GPGME_ATTR_COMMENT:
534         for (u=key->uids; u && idx; u=u->next, idx-- )
535             ;
536         val = u? u->comment_part : NULL;
537         break;
538       case GPGME_ATTR_VALIDITY:
539         for (u=key->uids; u && idx; u=u->next, idx-- )
540             ;
541         if (u) {
542             switch (u->validity) {
543               case GPGME_VALIDITY_UNKNOWN:   val = "?"; break;
544               case GPGME_VALIDITY_UNDEFINED: val = "q"; break;
545               case GPGME_VALIDITY_NEVER:     val = "n"; break;
546               case GPGME_VALIDITY_MARGINAL:  val = "m"; break;
547               case GPGME_VALIDITY_FULL:      val = "f"; break;
548               case GPGME_VALIDITY_ULTIMATE:  val = "u"; break;
549             }
550         }
551         break;
552       case GPGME_ATTR_LEVEL:  /* not used here */
553       case GPGME_ATTR_TYPE:
554       case GPGME_ATTR_KEY_REVOKED:
555       case GPGME_ATTR_KEY_INVALID:
556       case GPGME_ATTR_KEY_EXPIRED:
557       case GPGME_ATTR_KEY_DISABLED:
558       case GPGME_ATTR_UID_REVOKED:
559       case GPGME_ATTR_UID_INVALID:
560       case GPGME_ATTR_CAN_ENCRYPT:
561       case GPGME_ATTR_CAN_SIGN:
562       case GPGME_ATTR_CAN_CERTIFY:
563         break;
564       case GPGME_ATTR_IS_SECRET:
565         if (key->secret)
566             val = "1";
567         break;
568       case GPGME_ATTR_KEY_CAPS:    
569         for (k=&key->keys; k && idx; k=k->next, idx-- )
570             ;
571         if (k) 
572             val = capabilities_to_string (k);
573         break;
574     }
575     return val;
576 }
577
578
579 unsigned long
580 gpgme_key_get_ulong_attr ( GpgmeKey key, GpgmeAttr what,
581                            const void *reserved, int idx )
582 {
583     unsigned long val = 0;
584     struct subkey_s *k;
585     struct user_id_s *u;
586
587     if (!key)
588         return 0;
589     if (reserved)
590         return 0;
591     if (idx < 0)
592         return 0;
593
594     switch (what) {
595       case GPGME_ATTR_ALGO:    
596         for (k=&key->keys; k && idx; k=k->next, idx-- )
597             ;
598         if (k) 
599             val = (unsigned long)k->key_algo;
600         break;
601       case GPGME_ATTR_LEN:     
602         for (k=&key->keys; k && idx; k=k->next, idx-- )
603             ;
604         if (k) 
605             val = (unsigned long)k->key_len;
606         break;
607       case GPGME_ATTR_CREATED: 
608         for (k=&key->keys; k && idx; k=k->next, idx-- )
609             ;
610         if (k) 
611             val = k->timestamp < 0? 0L:(unsigned long)k->timestamp;
612         break;
613       case GPGME_ATTR_VALIDITY:
614         for (u=key->uids; u && idx; u=u->next, idx-- )
615             ;
616         if (u)
617             val = u->validity;
618         break;
619       case GPGME_ATTR_IS_SECRET:
620         val = !!key->secret;
621         break;
622       case GPGME_ATTR_KEY_REVOKED:
623         for (k=&key->keys; k && idx; k=k->next, idx-- )
624             ;
625         if (k) 
626             val = k->flags.revoked;
627         break;
628       case GPGME_ATTR_KEY_INVALID:
629         for (k=&key->keys; k && idx; k=k->next, idx-- )
630             ;
631         if (k) 
632             val = k->flags.invalid;
633         break;
634       case GPGME_ATTR_KEY_EXPIRED:
635         for (k=&key->keys; k && idx; k=k->next, idx-- )
636             ;
637         if (k) 
638             val = k->flags.expired;
639         break;
640       case GPGME_ATTR_KEY_DISABLED:
641         for (k=&key->keys; k && idx; k=k->next, idx-- )
642             ;
643         if (k) 
644             val = k->flags.disabled;
645         break;
646       case GPGME_ATTR_UID_REVOKED:
647         for (u=key->uids; u && idx; u=u->next, idx-- )
648             ;
649         if (u)
650             val = u->revoked;
651         break;
652       case GPGME_ATTR_UID_INVALID:
653         for (u=key->uids; u && idx; u=u->next, idx-- )
654             ;
655         if (u)
656             val = u->invalid;
657         break;
658       case GPGME_ATTR_CAN_ENCRYPT:
659         val = key->gloflags.can_certify;
660         break;
661       case GPGME_ATTR_CAN_SIGN:
662         val = key->gloflags.can_sign;
663         break;
664       case GPGME_ATTR_CAN_CERTIFY:
665         val = key->gloflags.can_encrypt;
666         break;
667       default:
668         break;
669     }
670     return val;
671 }
672
673