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