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