2001-12-05 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / keylist.c
1 /* keylist.c -  key listing
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <assert.h>
28
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "key.h"
33
34 #define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
35
36 static void finish_key ( GpgmeCtx ctx );
37
38
39 static void
40 keylist_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
41 {
42     if ( ctx->out_of_core )
43         return;
44
45     switch (code) {
46       case STATUS_EOF:
47         finish_key (ctx);
48         break;
49
50       default:
51         /* ignore all other codes */
52         break;
53     }
54 }
55
56
57 static time_t
58 parse_timestamp ( char *p )
59 {
60     if (!*p )
61         return 0;
62
63     return (time_t)strtoul (p, NULL, 10);
64 }
65
66
67 static void
68 set_mainkey_trust_info ( GpgmeKey key, const char *s )
69 {
70     /* look at letters and stop at the first digit */
71     for (; *s && !my_isdigit (*s); s++ ) {
72         switch (*s) {
73           case 'e': key->keys.flags.expired = 1; break;
74           case 'r': key->keys.flags.revoked = 1; break;
75           case 'd': key->keys.flags.disabled = 1; break;
76           case 'i': key->keys.flags.invalid = 1; break;
77         }
78     }
79 }
80
81
82 static void
83 set_userid_flags ( GpgmeKey key, const char *s )
84 {
85     /* look at letters and stop at the first digit */
86     for (; *s && !my_isdigit (*s); s++ ) {
87         switch (*s) {
88           case 'r': key->uids->revoked  = 1; break;
89           case 'i': key->uids->invalid  = 1; break;
90
91           case 'n': key->uids->validity = GPGME_VALIDITY_NEVER; break;
92           case 'm': key->uids->validity = GPGME_VALIDITY_MARGINAL; break;
93           case 'f': key->uids->validity = GPGME_VALIDITY_FULL; break;
94           case 'u': key->uids->validity = GPGME_VALIDITY_ULTIMATE; break;
95         }
96     }
97 }
98
99 static void
100 set_subkey_trust_info ( struct subkey_s *k, const char *s )
101 {
102     /* look at letters and stop at the first digit */
103     for (; *s && !my_isdigit (*s); s++ ) {
104         switch (*s) {
105           case 'e': k->flags.expired = 1; break;
106           case 'r': k->flags.revoked = 1; break;
107           case 'd': k->flags.disabled = 1; break;
108           case 'i': k->flags.invalid = 1; break;
109         }
110     }
111 }
112
113 static void
114 set_mainkey_capability ( GpgmeKey key, const char *s )
115 {
116     for (; *s ; s++ ) {
117         switch (*s) {
118           case 'e': key->keys.flags.can_encrypt = 1; break;
119           case 's': key->keys.flags.can_sign = 1; break;
120           case 'c': key->keys.flags.can_certify = 1; break;
121           case 'E': key->gloflags.can_encrypt = 1; break;
122           case 'S': key->gloflags.can_sign = 1; break;
123           case 'C': key->gloflags.can_certify = 1; break;
124         }
125     }
126 }
127
128 static void
129 set_subkey_capability ( struct subkey_s *k, const char *s )
130 {
131     for (; *s; s++ ) {
132         switch (*s) {
133           case 'e': k->flags.can_encrypt = 1; break;
134           case 's': k->flags.can_sign = 1; break;
135           case 'c': k->flags.can_certify = 1; break;
136         }
137     }
138 }
139
140
141
142 /* Note: we are allowed to modify line */
143 static void
144 keylist_colon_handler ( GpgmeCtx ctx, char *line )
145 {
146     char *p, *pend;
147     int field = 0;
148     enum {
149         RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC
150     } rectype = RT_NONE;
151     GpgmeKey key = ctx->tmp_key;
152     int i;
153     const char *trust_info = NULL;
154     struct subkey_s *sk = NULL;
155
156     if ( ctx->out_of_core )
157         return;
158     if (!line) { /* EOF */
159         finish_key (ctx);
160         return; 
161     }
162
163     for (p = line; p; p = pend) {
164         field++;
165         pend = strchr (p, ':');
166         if (pend) 
167             *pend++ = 0;
168
169         if ( field == 1 ) {
170             if ( !strcmp ( p, "sig" ) )
171                 rectype = RT_SIG;
172             else if ( !strcmp ( p, "uid" ) && key ) {
173                 rectype = RT_UID;
174                 key = ctx->tmp_key;
175             }
176             else if ( !strcmp (p, "sub") && key ) {
177                 /* start a new subkey */
178                 rectype = RT_SUB;
179                 if ( !(sk = _gpgme_key_add_subkey (key)) ) {
180                     ctx->out_of_core=1;
181                     return;
182                 }
183             }
184             else if ( !strcmp (p, "ssb") && key ) {
185                 /* start a new secret subkey */
186                 rectype = RT_SSB;
187                 if ( !(sk = _gpgme_key_add_secret_subkey (key)) ) {
188                     ctx->out_of_core=1;
189                     return;
190                 }
191             }
192             else if ( !strcmp (p, "pub") ) {
193                 /* start a new keyblock */
194                 if ( _gpgme_key_new ( &key ) ) {
195                     ctx->out_of_core=1; /* the only kind of error we can get*/
196                     return;
197                 }
198                 rectype = RT_PUB;
199                 finish_key ( ctx );
200                 assert ( !ctx->tmp_key );
201                 ctx->tmp_key = key;
202             }
203             else if ( !strcmp (p, "sec") ) {
204                 /* start a new keyblock */
205                 if ( _gpgme_key_new_secret ( &key ) ) {
206                     ctx->out_of_core=1; /*the only kind of error we can get*/
207                     return;
208                 }
209                 rectype = RT_SEC;
210                 finish_key ( ctx );
211                 assert ( !ctx->tmp_key );
212                 ctx->tmp_key = key;
213             }
214             else if ( !strcmp ( p, "fpr" ) && key ) 
215                 rectype = RT_FPR;
216             else 
217                 rectype = RT_NONE;
218             
219         }
220         else if ( rectype == RT_PUB || rectype == RT_SEC ) {
221             switch (field) {
222               case 2: /* trust info */
223                 trust_info = p; 
224                 set_mainkey_trust_info (key, trust_info);
225                 break;
226               case 3: /* key length */
227                 i = atoi (p); 
228                 if ( i > 1 ) /* ignore invalid values */
229                     key->keys.key_len = i; 
230                 break;
231               case 4: /* pubkey algo */
232                 i = atoi (p);
233                 if ( i > 1 && i < 128 )
234                     key->keys.key_algo = i;
235                 break;
236               case 5: /* long keyid */
237                 if ( strlen (p) == DIM(key->keys.keyid)-1 )
238                     strcpy (key->keys.keyid, p);
239                 break;
240               case 6: /* timestamp (seconds) */
241                 key->keys.timestamp = parse_timestamp (p);
242                 break;
243               case 7: /* valid for n days */
244                 break;
245               case 8: /* reserved (LID) */
246                 break;
247               case 9: /* ownertrust */
248                 break;
249               case 10: /* not used due to --fixed-list-mode option */
250                 break;
251               case 11: /* signature class  */
252                 break;
253               case 12: /* capabilities */
254                 set_mainkey_capability (key, p );
255                 break;
256               case 13:
257                 pend = NULL;  /* we can stop here */
258                 break;
259             }
260         }
261         else if ( (rectype == RT_SUB || rectype== RT_SSB) && sk ) {
262             switch (field) {
263               case 2: /* trust info */
264                 set_subkey_trust_info ( sk, p);
265                 break;
266               case 3: /* key length */
267                 i = atoi (p); 
268                 if ( i > 1 ) /* ignore invalid values */
269                     sk->key_len = i; 
270                 break;
271               case 4: /* pubkey algo */
272                 i = atoi (p);
273                 if ( i > 1 && i < 128 )
274                     sk->key_algo = i;
275                 break;
276               case 5: /* long keyid */
277                 if ( strlen (p) == DIM(sk->keyid)-1 )
278                     strcpy (sk->keyid, p);
279                 break;
280               case 6: /* timestamp (seconds) */
281                 sk->timestamp = parse_timestamp (p);
282                 break;
283               case 7: /* valid for n days */
284                 break;
285               case 8: /* reserved (LID) */
286                 break;
287               case 9: /* ownertrust */
288                 break;
289               case 10:/* user ID n/a for a subkey */
290                 break;
291               case 11:  /* signature class  */
292                 break;
293               case 12: /* capability */
294                 set_subkey_capability ( sk, p );
295                 break;
296               case 13:
297                 pend = NULL;  /* we can stop here */
298                 break;
299             }
300         }
301         else if ( rectype == RT_UID ) {
302             switch (field) {
303               case 2: /* trust info */
304                 trust_info = p;  /*save for later */
305                 break;
306               case 10: /* user ID */
307                 if ( _gpgme_key_append_name ( key, p) )
308                     ctx->out_of_core = 1;
309                 else {
310                     if (trust_info)
311                         set_userid_flags (key, trust_info);
312                 }
313                 pend = NULL;  /* we can stop here */
314                 break;
315             }
316         }
317         else if ( rectype == RT_FPR ) {
318             switch (field) {
319               case 10: /* fingerprint (take only the first one)*/
320                 if ( !key->keys.fingerprint && *p ) {
321                     key->keys.fingerprint = xtrystrdup (p);
322                     if ( !key->keys.fingerprint )
323                         ctx->out_of_core = 1;
324                 }
325                 pend = NULL; /* that is all we want */
326                 break;
327             }
328         }
329     }
330     
331 }
332
333
334 /*
335  * We have read an entire key into ctx->tmp_key and should now finish
336  * it.  It is assumed that this releases ctx->tmp_key.
337  */
338 static void
339 finish_key ( GpgmeCtx ctx )
340 {
341     GpgmeKey key = ctx->tmp_key;
342     struct key_queue_item_s *q, *q2;
343
344     if (key) {
345         ctx->tmp_key = NULL;
346         
347         _gpgme_key_cache_add (key);
348         
349         q = xtrymalloc ( sizeof *q );
350         if ( !q ) {
351             gpgme_key_release (key);
352             ctx->out_of_core = 1;
353             return;
354         }
355         q->key = key;
356         q->next = NULL;
357         /* fixme: lock queue. Use a tail pointer? */
358         if ( !(q2 = ctx->key_queue) )
359             ctx->key_queue = q;
360         else {
361             for ( ; q2->next; q2 = q2->next )
362                 ;
363             q2->next = q;
364         }
365         ctx->key_cond = 1;
366         /* fixme: unlock queue */
367     }
368 }
369
370 /**
371  * gpgme_op_keylist_start:
372  * @c: context 
373  * @pattern: a GnuPG user ID or NULL for all
374  * @secret_only: List only keys where the secret part is available
375  * 
376  * Note that this function also cancels a pending key listing
377  * operaton. To actually retrieve the key, use
378  * gpgme_op_keylist_next().
379  * 
380  * Return value:  0 on success or an errorcode. 
381  **/
382 GpgmeError
383 gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
384 {
385   GpgmeError err = 0;
386
387   if (!ctx)
388     return mk_error (Invalid_Value);
389   ctx->pending = 1;
390
391   _gpgme_release_result (ctx);
392   ctx->out_of_core = 0;
393
394   if (ctx->engine)
395     {
396       _gpgme_engine_release (ctx->engine); 
397       ctx->engine = NULL;
398     }
399   gpgme_key_release (ctx->tmp_key);
400   ctx->tmp_key = NULL;
401   /* Fixme: Release key_queue.  */
402     
403   err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
404                            : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
405   if (err)
406     goto leave;
407
408   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
409   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
410                                               keylist_colon_handler, ctx);
411   if (err)
412     goto leave;
413   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
414
415   err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, ctx->keylist_mode);
416
417   if (!err)     /* And kick off the process.  */
418     err = _gpgme_engine_start (ctx->engine, ctx);
419
420  leave:
421   if (err)
422     {
423       ctx->pending = 0; 
424       _gpgme_engine_release (ctx->engine);
425       ctx->engine = NULL;
426     }
427   return err;
428 }
429
430 /**
431  * gpgme_op_keylist_next:
432  * @c: Context
433  * @r_key: Returned key object
434  * 
435  * Return the next key from the key listing started with
436  * gpgme_op_keylist_start().  The caller must free the key using
437  * gpgme_key_release().  If the last key has already been returned the
438  * last time the function was called, %GPGME_EOF is returned and the
439  * operation is finished.
440  * 
441  * Return value: 0 on success, %GPGME_EOF or another error code.
442  **/
443 GpgmeError
444 gpgme_op_keylist_next (GpgmeCtx ctx, GpgmeKey *r_key)
445 {
446   struct key_queue_item_s *queue_item;
447
448   if (!r_key)
449     return mk_error (Invalid_Value);
450   *r_key = NULL;
451   if (!ctx)
452     return mk_error (Invalid_Value);
453   if (!ctx->pending )
454     return mk_error (No_Request);
455   if (ctx->out_of_core)
456     return mk_error (Out_Of_Core);
457
458   if (!ctx->key_queue)
459     {
460       _gpgme_wait_on_condition (ctx, 1, &ctx->key_cond);
461       if (ctx->out_of_core)
462         return mk_error (Out_Of_Core);
463       if (!ctx->key_cond)
464         {
465           ctx->pending = 0;
466           return mk_error (EOF);
467         }
468       ctx->key_cond = 0; 
469         assert (ctx->key_queue);
470     }
471   queue_item = ctx->key_queue;
472   ctx->key_queue = queue_item->next;
473   if (!ctx->key_queue)
474     ctx->key_cond = 0;
475   
476   *r_key = queue_item->key;
477   xfree (queue_item);
478   return 0;
479 }