Add progress CB and subkey listing
[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 GpgmeError
35 _gpgme_key_new( GpgmeKey *r_key )
36 {
37     GpgmeKey key;
38
39     *r_key = NULL;
40     key = xtrycalloc ( 1, sizeof *key );
41     if (!key)
42         return mk_error (Out_Of_Core);
43
44     *r_key = key;
45     return 0;
46 }
47
48
49 struct subkey_s *
50 _gpgme_key_add_subkey (GpgmeKey key)
51 {
52     struct subkey_s *k, *kk;
53
54     k = xtrycalloc (1, sizeof *k);
55     if (!k)
56         return NULL;
57
58     if( !(kk=key->keys.next) )
59         key->keys.next = k;
60     else {
61         while ( kk->next )
62             kk = kk->next;
63         kk->next = k;
64     }
65     return k;
66 }
67
68
69 void
70 _gpgme_key_release ( GpgmeKey key )
71 {
72     struct user_id_s *u, *u2;
73     struct subkey_s *k, *k2;
74
75     if (!key)
76         return;
77
78     xfree (key->keys.fingerprint);
79     for (k = key->keys.next; k; k = k2 ) {
80         k2 = k->next;
81         xfree (k->fingerprint);
82         xfree (k);
83     }
84     for (u = key->uids; u; u = u2 ) {
85         u2 = u->next;
86         xfree (u);
87     }
88     xfree (key);
89 }
90
91 /* 
92  * Take a name from the --with-colon listing, remove certain escape sequences
93  * sequences and put it into the list of UIDs
94  */
95 GpgmeError
96 _gpgme_key_append_name ( GpgmeKey key, const char *s )
97 {
98     struct user_id_s *uid;
99     char *d;
100
101     assert (key);
102     /* we can malloc a buffer of the same length, because the converted
103      * string will never be larger */
104     uid = xtrymalloc ( sizeof *uid + strlen (s) );
105     if ( !uid )
106         return mk_error (Out_Of_Core);
107     uid->validity = 0;
108     d = uid->name;
109
110     while ( *s ) {
111         if ( *s != '\\' )
112             *d++ = *s++;
113         else if ( s[1] == '\\' ) {
114             s++;
115             *d++ = *s++; 
116         }
117         else if ( s[1] == 'n' ) {
118             s += 2;
119             *d++ = '\n'; 
120         }
121         else if ( s[1] == 'r' ) {
122             s += 2;
123             *d++ = '\r'; 
124         }
125         else if ( s[1] == 'v' ) {
126             s += 2;
127             *d++ = '\v'; 
128         }
129         else if ( s[1] == 'b' ) {
130             s += 2;
131             *d++ = '\b'; 
132         }
133         else if ( s[1] == '0' ) {
134             /* Hmmm: no way to express this */
135             s += 2;
136             *d++ = '\\';
137             *d++ = '\0'; 
138         }
139         else if ( s[1] == 'x' && my_isdigit (s[2]) && my_isdigit (s[3]) ) {
140             unsigned int val = (s[2]-'0')*16 + (s[3]-'0');
141             if ( !val ) {
142                 *d++ = '\\';
143                 *d++ = '\0'; 
144             }
145             else 
146                 *(byte*)d++ = val;
147             s += 3;
148         }
149         else { /* should not happen */
150             s++;
151             *d++ = '\\'; 
152             *d++ = *s++;
153         } 
154     }
155
156     uid->next = key->uids;
157     key->uids = uid;
158     return 0;
159 }
160
161
162 static void
163 add_otag ( GpgmeData d, const char *tag )
164 {
165     _gpgme_data_append_string ( d, "    <" );
166     _gpgme_data_append_string ( d, tag );
167     _gpgme_data_append_string ( d, ">" );
168 }
169
170 static void
171 add_ctag ( GpgmeData d, const char *tag )
172 {
173     _gpgme_data_append_string ( d, "</" );
174     _gpgme_data_append_string ( d, tag );
175     _gpgme_data_append_string ( d, ">\n" );
176 }
177
178 static void
179 add_tag_and_string ( GpgmeData d, const char *tag, const char *string )
180 {
181     add_otag (d, tag);
182     _gpgme_data_append_string_for_xml ( d, string );
183     add_ctag (d, tag); 
184 }
185
186 static void
187 add_user_id_name ( GpgmeData d, const char *buf, size_t len )
188 {
189     while ( len && (buf[len-1] == ' ' || buf[len-1] == '\t') ) 
190         len--;
191     if (len) {
192         add_otag (d, "name" );
193         _gpgme_data_append_for_xml ( d, buf, len );
194         add_ctag (d, "name");
195     }
196 }
197
198
199 static void
200 add_user_id ( GpgmeData d, const char *string )
201 {
202     const char *s, *start=NULL;
203     int in_name = 0;
204     int in_email = 0;
205     int in_comment = 0;
206
207     for (s=string; *s; s++ ) {
208         if ( in_email ) {
209             if ( *s == '<' )
210                 in_email++; /* not legal but anyway */
211             else if (*s== '>') {
212                 if ( !--in_email ) {
213                     _gpgme_data_append_for_xml ( d, start, s-start );
214                     add_ctag (d, "email");
215                 }
216             }
217         }
218         else if ( in_comment ) {
219             if ( *s == '(' )
220                 in_comment++;
221             else if (*s== ')') {
222                 if ( !--in_comment ) {
223                     _gpgme_data_append_for_xml ( d, start, s-start );
224                     add_ctag (d, "comment");
225                 }
226             }
227         }
228         else if ( *s == '<' ) {
229             if ( in_name ) {
230                 add_user_id_name (d, start, s-start );
231                 in_name = 0;
232             }
233             in_email = 1;
234             add_otag ( d, "email" );
235             start = s+1;
236         }
237         else if ( *s == '(' ) {
238             if ( in_name ) {
239                 add_user_id_name (d, start, s-start );
240                 in_name = 0;
241             }
242             in_comment = 1;
243             add_otag ( d, "comment" );
244             start = s+1;
245         }
246         else if ( !in_name && *s != ' ' && *s != '\t' ) {
247             in_name = 1;
248             start = s;
249         }    
250     }
251
252     if ( in_name ) 
253         add_user_id_name (d, start, s-start );
254 }
255
256 static void
257 add_tag_and_uint ( GpgmeData d, const char *tag, unsigned int val )
258 {
259     char buf[30];
260     sprintf (buf, "%u", val );
261     add_tag_and_string ( d, tag, buf );
262 }
263
264 static void
265 add_tag_and_time ( GpgmeData d, const char *tag, time_t val )
266 {
267     char buf[30];
268
269     if (!val || val == (time_t)-1 )
270         return;
271     sprintf (buf, "%lu", (unsigned long)val );
272     add_tag_and_string ( d, tag, buf );
273 }
274
275 char *
276 gpgme_key_get_as_xml ( GpgmeKey key )
277 {
278     GpgmeData d;
279     struct user_id_s *u;
280     struct subkey_s *k;
281
282     if ( !key )
283         return NULL;
284     
285     if ( gpgme_data_new ( &d ) )
286         return NULL;
287     
288     _gpgme_data_append_string ( d, "<GnupgKeyblock>\n"
289                                    "  <mainkey>\n" );
290     add_tag_and_string (d, "keyid", key->keys.keyid );   
291     if (key->keys.fingerprint)
292         add_tag_and_string (d, "fpr", key->keys.fingerprint );
293     add_tag_and_uint (d, "algo", key->keys.key_algo );
294     add_tag_and_uint (d, "len", key->keys.key_len );
295     add_tag_and_time (d, "created", key->keys.timestamp );
296     /*add_tag_and_time (d, "expires", key->expires );*/
297     _gpgme_data_append_string (d, "  </mainkey>\n");
298
299     /* Now the user IDs */
300     for ( u = key->uids; u; u = u->next ) {
301         _gpgme_data_append_string (d, "  <userid>\n");
302         add_tag_and_string ( d, "raw", u->name );
303         add_user_id ( d, u->name );
304         _gpgme_data_append_string (d, "  </userid>\n");
305     }
306     
307     for (k=key->keys.next; k; k = k->next ) {
308         _gpgme_data_append_string (d, "  <subkey>\n");
309         add_tag_and_string (d, "keyid", k->keyid );   
310         if (k->fingerprint)
311             add_tag_and_string (d, "fpr", k->fingerprint );
312         add_tag_and_uint (d, "algo", k->key_algo );
313         add_tag_and_uint (d, "len", k->key_len );
314         add_tag_and_time (d, "created", k->timestamp );
315         _gpgme_data_append_string (d, "  </subkey>\n");
316     }
317     _gpgme_data_append_string ( d, "</GnupgKeyblock>\n" );
318
319     return _gpgme_data_release_and_return_string (d);
320 }
321
322
323
324
325