f00f8dffc403b6dcaac218c665a9dd92ec294bb8
[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 GpgmeError
52 _gpgme_key_new( GpgmeKey *r_key )
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
61     *r_key = key;
62     return 0;
63 }
64
65
66 struct subkey_s *
67 _gpgme_key_add_subkey (GpgmeKey key)
68 {
69     struct subkey_s *k, *kk;
70
71     k = xtrycalloc (1, sizeof *k);
72     if (!k)
73         return NULL;
74
75     if( !(kk=key->keys.next) )
76         key->keys.next = k;
77     else {
78         while ( kk->next )
79             kk = kk->next;
80         kk->next = k;
81     }
82     return k;
83 }
84
85
86 void
87 _gpgme_key_release ( GpgmeKey key )
88 {
89     struct user_id_s *u, *u2;
90     struct subkey_s *k, *k2;
91
92     if (!key)
93         return;
94
95     xfree (key->keys.fingerprint);
96     for (k = key->keys.next; k; k = k2 ) {
97         k2 = k->next;
98         xfree (k->fingerprint);
99         xfree (k);
100     }
101     for (u = key->uids; u; u = u2 ) {
102         u2 = u->next;
103         xfree (u);
104     }
105     xfree (key);
106 }
107
108 static char *
109 set_user_id_part ( char *tail, const char *buf, size_t len )
110 {
111     while ( len && (buf[len-1] == ' ' || buf[len-1] == '\t') ) 
112         len--;
113     for ( ; len; len--)
114         *tail++ = *buf++;
115     *tail++ = 0;
116     return tail;
117 }
118
119
120 static void
121 parse_user_id ( struct user_id_s *uid, char *tail )
122 {
123     const char *s, *start=NULL;
124     int in_name = 0;
125     int in_email = 0;
126     int in_comment = 0;
127
128     for (s=uid->name; *s; s++ ) {
129         if ( in_email ) {
130             if ( *s == '<' )
131                 in_email++; /* not legal but anyway */
132             else if (*s== '>') {
133                 if ( !--in_email ) {
134                     if (!uid->email_part) {
135                         uid->email_part = tail;
136                         tail = set_user_id_part ( tail, start, s-start );
137                     }
138                 }
139             }
140         }
141         else if ( in_comment ) {
142             if ( *s == '(' )
143                 in_comment++;
144             else if (*s== ')') {
145                 if ( !--in_comment ) {
146                     if (!uid->comment_part) {
147                         uid->comment_part = tail;
148                         tail = set_user_id_part ( tail, start, s-start );
149                     }
150                 }
151             }
152         }
153         else if ( *s == '<' ) {
154             if ( in_name ) {
155                 if ( !uid->name_part ) {
156                     uid->name_part = tail;
157                     tail = set_user_id_part (tail, start, s-start );
158                 }
159                 in_name = 0;
160             }
161             in_email = 1;
162             start = s+1;
163         }
164         else if ( *s == '(' ) {
165             if ( in_name ) {
166                 if ( !uid->name_part ) {
167                     uid->name_part = tail;
168                     tail = set_user_id_part (tail, start, s-start );
169                 }
170                 in_name = 0;
171             }
172             in_comment = 1;
173             start = s+1;
174         }
175         else if ( !in_name && *s != ' ' && *s != '\t' ) {
176             in_name = 1;
177             start = s;
178         }    
179     }
180
181     if ( in_name ) {
182         if ( !uid->name_part ) {
183             uid->name_part = tail;
184             tail = set_user_id_part (tail, start, s-start );
185         }
186     }
187
188     /* let unused parts point to an EOS */ 
189     tail--;
190     if (!uid->name_part)
191         uid->name_part = tail;
192     if (!uid->email_part)
193         uid->email_part = tail;
194     if (!uid->comment_part)
195         uid->comment_part = tail;
196
197 }
198
199 /* 
200  * Take a name from the --with-colon listing, remove certain escape sequences
201  * sequences and put it into the list of UIDs
202  */
203 GpgmeError
204 _gpgme_key_append_name ( GpgmeKey key, const char *s )
205 {
206     struct user_id_s *uid;
207     char *d;
208
209     assert (key);
210     /* we can malloc a buffer of the same length, because the
211      * converted string will never be larger. Actually we allocate it
212      * twice the size, so that we are able to store the parsed stuff
213      * there too */
214     uid = xtrymalloc ( sizeof *uid + 2*strlen (s)+3 );
215     if ( !uid )
216         return mk_error (Out_Of_Core);
217     uid->validity = 0;
218     uid->name_part = NULL;
219     uid->email_part = NULL;
220     uid->comment_part = NULL;
221     d = uid->name;
222
223     while ( *s ) {
224         if ( *s != '\\' )
225             *d++ = *s++;
226         else if ( s[1] == '\\' ) {
227             s++;
228             *d++ = *s++; 
229         }
230         else if ( s[1] == 'n' ) {
231             s += 2;
232             *d++ = '\n'; 
233         }
234         else if ( s[1] == 'r' ) {
235             s += 2;
236             *d++ = '\r'; 
237         }
238         else if ( s[1] == 'v' ) {
239             s += 2;
240             *d++ = '\v'; 
241         }
242         else if ( s[1] == 'b' ) {
243             s += 2;
244             *d++ = '\b'; 
245         }
246         else if ( s[1] == '0' ) {
247             /* Hmmm: no way to express this */
248             s += 2;
249             *d++ = '\\';
250             *d++ = '\0'; 
251         }
252         else if ( s[1] == 'x' && my_isdigit (s[2]) && my_isdigit (s[3]) ) {
253             unsigned int val = (s[2]-'0')*16 + (s[3]-'0');
254             if ( !val ) {
255                 *d++ = '\\';
256                 *d++ = '\0'; 
257             }
258             else 
259                 *(byte*)d++ = val;
260             s += 3;
261         }
262         else { /* should not happen */
263             s++;
264             *d++ = '\\'; 
265             *d++ = *s++;
266         } 
267     }
268     *d++ = 0;
269     parse_user_id ( uid, d );
270
271     uid->next = key->uids;
272     key->uids = uid;
273     return 0;
274 }
275
276
277 static void
278 add_otag ( GpgmeData d, const char *tag )
279 {
280     _gpgme_data_append_string ( d, "    <" );
281     _gpgme_data_append_string ( d, tag );
282     _gpgme_data_append_string ( d, ">" );
283 }
284
285 static void
286 add_ctag ( GpgmeData d, const char *tag )
287 {
288     _gpgme_data_append_string ( d, "</" );
289     _gpgme_data_append_string ( d, tag );
290     _gpgme_data_append_string ( d, ">\n" );
291 }
292
293 static void
294 add_tag_and_string ( GpgmeData d, const char *tag, const char *string )
295 {
296     add_otag (d, tag);
297     _gpgme_data_append_string_for_xml ( d, string );
298     add_ctag (d, tag); 
299 }
300
301 static void
302 add_tag_and_uint ( GpgmeData d, const char *tag, unsigned int val )
303 {
304     char buf[30];
305     sprintf (buf, "%u", val );
306     add_tag_and_string ( d, tag, buf );
307 }
308
309 static void
310 add_tag_and_time ( GpgmeData d, const char *tag, time_t val )
311 {
312     char buf[30];
313
314     if (!val || val == (time_t)-1 )
315         return;
316     sprintf (buf, "%lu", (unsigned long)val );
317     add_tag_and_string ( d, tag, buf );
318 }
319
320 char *
321 gpgme_key_get_as_xml ( GpgmeKey key )
322 {
323     GpgmeData d;
324     struct user_id_s *u;
325     struct subkey_s *k;
326
327     if ( !key )
328         return NULL;
329     
330     if ( gpgme_data_new ( &d ) )
331         return NULL;
332     
333     _gpgme_data_append_string ( d, "<GnupgKeyblock>\n"
334                                    "  <mainkey>\n" );
335     add_tag_and_string (d, "keyid", key->keys.keyid );   
336     if (key->keys.fingerprint)
337         add_tag_and_string (d, "fpr", key->keys.fingerprint );
338     add_tag_and_uint (d, "algo", key->keys.key_algo );
339     add_tag_and_uint (d, "len", key->keys.key_len );
340     add_tag_and_time (d, "created", key->keys.timestamp );
341     /*add_tag_and_time (d, "expires", key->expires );*/
342     _gpgme_data_append_string (d, "  </mainkey>\n");
343
344     /* Now the user IDs */
345     for ( u = key->uids; u; u = u->next ) {
346         _gpgme_data_append_string (d, "  <userid>\n");
347         add_tag_and_string ( d, "raw", u->name );
348         if ( *u->name_part )
349             add_tag_and_string ( d, "name", u->name_part );
350         if ( *u->email_part )
351             add_tag_and_string ( d, "email", u->email_part );
352         if ( *u->comment_part )
353             add_tag_and_string ( d, "comment", u->comment_part );
354         _gpgme_data_append_string (d, "  </userid>\n");
355     }
356     
357     for (k=key->keys.next; k; k = k->next ) {
358         _gpgme_data_append_string (d, "  <subkey>\n");
359         add_tag_and_string (d, "keyid", k->keyid );   
360         if (k->fingerprint)
361             add_tag_and_string (d, "fpr", k->fingerprint );
362         add_tag_and_uint (d, "algo", k->key_algo );
363         add_tag_and_uint (d, "len", k->key_len );
364         add_tag_and_time (d, "created", k->timestamp );
365         _gpgme_data_append_string (d, "  </subkey>\n");
366     }
367     _gpgme_data_append_string ( d, "</GnupgKeyblock>\n" );
368
369     return _gpgme_data_release_and_return_string (d);
370 }
371
372
373 const char *
374 gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
375                             const void *reserved, int idx )
376 {
377     const char *val = NULL;
378     struct user_id_s *u;
379
380     if (!key)
381         return NULL;
382     if (reserved)
383         return NULL;
384     if (idx < 0)
385         return NULL;
386
387     switch (what) {
388       case GPGME_ATTR_KEYID:
389         val = key->keys.keyid;
390         break;
391       case GPGME_ATTR_FPR:
392         val = key->keys.fingerprint;
393         break;
394       case GPGME_ATTR_ALGO:    
395         val = pkalgo_to_string (key->keys.key_algo);
396         break;
397       case GPGME_ATTR_LEN:     
398       case GPGME_ATTR_CREATED: 
399       case GPGME_ATTR_EXPIRE:  
400         break; /* use another get function */
401       case GPGME_ATTR_OTRUST:  
402         val = "[fixme]";
403         break;
404       case GPGME_ATTR_USERID:  
405         for (u=key->uids; u && idx; u=u->next, idx-- )
406             ;
407         val = u? u->name : NULL;
408         break;
409       case GPGME_ATTR_NAME:   
410         for (u=key->uids; u && idx; u=u->next, idx-- )
411             ;
412         val = u? u->name_part : NULL;
413         break;
414       case GPGME_ATTR_EMAIL:
415         for (u=key->uids; u && idx; u=u->next, idx-- )
416             ;
417         val = u? u->email_part : NULL;
418         break;
419       case GPGME_ATTR_COMMENT:
420         for (u=key->uids; u && idx; u=u->next, idx-- )
421             ;
422         val = u? u->comment_part : NULL;
423         break;
424       case GPGME_ATTR_VALIDITY:
425         val = "[foxme]";
426         break;
427       case GPGME_ATTR_LEVEL:  /* not used here */
428       case GPGME_ATTR_TYPE:
429         break;
430     }
431     return val;
432 }
433
434
435 unsigned long
436 gpgme_key_get_ulong_attr ( GpgmeKey key, GpgmeAttr what,
437                            const void *reserved, int idx )
438 {
439     unsigned long val = 0;
440
441     if (!key)
442         return 0;
443     if (reserved)
444         return 0;
445     if (idx < 0)
446         return 0;
447
448     switch (what) {
449       case GPGME_ATTR_ALGO:    
450         val = (unsigned long)key->keys.key_algo;
451         break;
452       case GPGME_ATTR_LEN:     
453         val = (unsigned long)key->keys.key_len;
454         break;
455       case GPGME_ATTR_CREATED: 
456         val = key->keys.timestamp < 0? 0L:(unsigned long)key->keys.timestamp;
457         break;
458       default:
459         break;
460     }
461     return val;
462 }
463
464