3ee49f7b6f137e76d3d4eef36daf312af92b2dd4
[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->validity = 0;
263     uid->name_part = NULL;
264     uid->email_part = NULL;
265     uid->comment_part = NULL;
266     d = uid->name;
267
268     while ( *s ) {
269         if ( *s != '\\' )
270             *d++ = *s++;
271         else if ( s[1] == '\\' ) {
272             s++;
273             *d++ = *s++; 
274         }
275         else if ( s[1] == 'n' ) {
276             s += 2;
277             *d++ = '\n'; 
278         }
279         else if ( s[1] == 'r' ) {
280             s += 2;
281             *d++ = '\r'; 
282         }
283         else if ( s[1] == 'v' ) {
284             s += 2;
285             *d++ = '\v'; 
286         }
287         else if ( s[1] == 'b' ) {
288             s += 2;
289             *d++ = '\b'; 
290         }
291         else if ( s[1] == '0' ) {
292             /* Hmmm: no way to express this */
293             s += 2;
294             *d++ = '\\';
295             *d++ = '\0'; 
296         }
297         else if ( s[1] == 'x' && my_isdigit (s[2]) && my_isdigit (s[3]) ) {
298             unsigned int val = (s[2]-'0')*16 + (s[3]-'0');
299             if ( !val ) {
300                 *d++ = '\\';
301                 *d++ = '\0'; 
302             }
303             else 
304                 *(byte*)d++ = val;
305             s += 3;
306         }
307         else { /* should not happen */
308             s++;
309             *d++ = '\\'; 
310             *d++ = *s++;
311         } 
312     }
313     *d++ = 0;
314     parse_user_id ( uid, d );
315
316     uid->next = key->uids;
317     key->uids = uid;
318     return 0;
319 }
320
321
322 static void
323 add_otag ( GpgmeData d, const char *tag )
324 {
325     _gpgme_data_append_string ( d, "    <" );
326     _gpgme_data_append_string ( d, tag );
327     _gpgme_data_append_string ( d, ">" );
328 }
329
330 static void
331 add_ctag ( GpgmeData d, const char *tag )
332 {
333     _gpgme_data_append_string ( d, "</" );
334     _gpgme_data_append_string ( d, tag );
335     _gpgme_data_append_string ( d, ">\n" );
336 }
337
338 static void
339 add_tag_and_string ( GpgmeData d, const char *tag, const char *string )
340 {
341     add_otag (d, tag);
342     _gpgme_data_append_string_for_xml ( d, string );
343     add_ctag (d, tag); 
344 }
345
346 static void
347 add_tag_and_uint ( GpgmeData d, const char *tag, unsigned int val )
348 {
349     char buf[30];
350     sprintf (buf, "%u", val );
351     add_tag_and_string ( d, tag, buf );
352 }
353
354 static void
355 add_tag_and_time ( GpgmeData d, const char *tag, time_t val )
356 {
357     char buf[30];
358
359     if (!val || val == (time_t)-1 )
360         return;
361     sprintf (buf, "%lu", (unsigned long)val );
362     add_tag_and_string ( d, tag, buf );
363 }
364
365 char *
366 gpgme_key_get_as_xml ( GpgmeKey key )
367 {
368     GpgmeData d;
369     struct user_id_s *u;
370     struct subkey_s *k;
371
372     if ( !key )
373         return NULL;
374     
375     if ( gpgme_data_new ( &d ) )
376         return NULL;
377     
378     _gpgme_data_append_string ( d, "<GnupgKeyblock>\n"
379                                    "  <mainkey>\n" );
380     if ( key->secret )
381         _gpgme_data_append_string ( d, "    <secret/>\n");
382     add_tag_and_string (d, "keyid", key->keys.keyid );   
383     if (key->keys.fingerprint)
384         add_tag_and_string (d, "fpr", key->keys.fingerprint );
385     add_tag_and_uint (d, "algo", key->keys.key_algo );
386     add_tag_and_uint (d, "len", key->keys.key_len );
387     add_tag_and_time (d, "created", key->keys.timestamp );
388     /*add_tag_and_time (d, "expires", key->expires );*/
389     _gpgme_data_append_string (d, "  </mainkey>\n");
390
391     /* Now the user IDs */
392     for ( u = key->uids; u; u = u->next ) {
393         _gpgme_data_append_string (d, "  <userid>\n");
394         add_tag_and_string ( d, "raw", u->name );
395         if ( *u->name_part )
396             add_tag_and_string ( d, "name", u->name_part );
397         if ( *u->email_part )
398             add_tag_and_string ( d, "email", u->email_part );
399         if ( *u->comment_part )
400             add_tag_and_string ( d, "comment", u->comment_part );
401         _gpgme_data_append_string (d, "  </userid>\n");
402     }
403     
404     for (k=key->keys.next; k; k = k->next ) {
405         _gpgme_data_append_string (d, "  <subkey>\n");
406         if ( k->secret )
407             _gpgme_data_append_string ( d, "    <secret/>\n");
408         add_tag_and_string (d, "keyid", k->keyid );   
409         if (k->fingerprint)
410             add_tag_and_string (d, "fpr", k->fingerprint );
411         add_tag_and_uint (d, "algo", k->key_algo );
412         add_tag_and_uint (d, "len", k->key_len );
413         add_tag_and_time (d, "created", k->timestamp );
414         _gpgme_data_append_string (d, "  </subkey>\n");
415     }
416     _gpgme_data_append_string ( d, "</GnupgKeyblock>\n" );
417
418     return _gpgme_data_release_and_return_string (d);
419 }
420
421
422 const char *
423 gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
424                             const void *reserved, int idx )
425 {
426     const char *val = NULL;
427     struct user_id_s *u;
428
429     if (!key)
430         return NULL;
431     if (reserved)
432         return NULL;
433     if (idx < 0)
434         return NULL;
435
436     switch (what) {
437       case GPGME_ATTR_KEYID:
438         val = key->keys.keyid;
439         break;
440       case GPGME_ATTR_FPR:
441         val = key->keys.fingerprint;
442         break;
443       case GPGME_ATTR_ALGO:    
444         val = pkalgo_to_string (key->keys.key_algo);
445         break;
446       case GPGME_ATTR_LEN:     
447       case GPGME_ATTR_CREATED: 
448       case GPGME_ATTR_EXPIRE:  
449         break; /* use another get function */
450       case GPGME_ATTR_OTRUST:  
451         val = "[fixme]";
452         break;
453       case GPGME_ATTR_USERID:  
454         for (u=key->uids; u && idx; u=u->next, idx-- )
455             ;
456         val = u? u->name : NULL;
457         break;
458       case GPGME_ATTR_NAME:   
459         for (u=key->uids; u && idx; u=u->next, idx-- )
460             ;
461         val = u? u->name_part : NULL;
462         break;
463       case GPGME_ATTR_EMAIL:
464         for (u=key->uids; u && idx; u=u->next, idx-- )
465             ;
466         val = u? u->email_part : NULL;
467         break;
468       case GPGME_ATTR_COMMENT:
469         for (u=key->uids; u && idx; u=u->next, idx-- )
470             ;
471         val = u? u->comment_part : NULL;
472         break;
473       case GPGME_ATTR_VALIDITY:
474         for (u=key->uids; u && idx; u=u->next, idx-- )
475             ;
476         if (u) {
477             switch (u->validity) {
478               case GPGME_VALIDITY_UNKNOWN:   val = "?"; break;
479               case GPGME_VALIDITY_UNDEFINED: val = "q"; break;
480               case GPGME_VALIDITY_NEVER:     val = "n"; break;
481               case GPGME_VALIDITY_MARGINAL:  val = "m"; break;
482               case GPGME_VALIDITY_FULL:      val = "f"; break;
483               case GPGME_VALIDITY_ULTIMATE:  val = "u"; break;
484             }
485         }
486         break;
487       case GPGME_ATTR_LEVEL:  /* not used here */
488       case GPGME_ATTR_TYPE:
489         break;
490       case GPGME_ATTR_IS_SECRET:
491         if (key->secret)
492             val = "1";
493         break;
494     }
495     return val;
496 }
497
498
499 unsigned long
500 gpgme_key_get_ulong_attr ( GpgmeKey key, GpgmeAttr what,
501                            const void *reserved, int idx )
502 {
503     unsigned long val = 0;
504     struct user_id_s *u;
505
506     if (!key)
507         return 0;
508     if (reserved)
509         return 0;
510     if (idx < 0)
511         return 0;
512
513     switch (what) {
514       case GPGME_ATTR_ALGO:    
515         val = (unsigned long)key->keys.key_algo;
516         break;
517       case GPGME_ATTR_LEN:     
518         val = (unsigned long)key->keys.key_len;
519         break;
520       case GPGME_ATTR_CREATED: 
521         val = key->keys.timestamp < 0? 0L:(unsigned long)key->keys.timestamp;
522         break;
523       case GPGME_ATTR_VALIDITY:
524         for (u=key->uids; u && idx; u=u->next, idx-- )
525             ;
526         if (u)
527             val = u->validity;
528         break;
529       case GPGME_ATTR_IS_SECRET:
530         val = !!key->secret;
531         break;
532       default:
533         break;
534     }
535     return val;
536 }
537
538