Release 0.2.1
[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         if (ctx->tmp_key)
48             finish_key (ctx);
49         break;
50
51       default:
52         /* ignore all other codes */
53         break;
54     }
55 }
56
57
58 static time_t
59 parse_timestamp ( char *p )
60 {
61     struct tm tm;
62     int i;
63     
64     if (!*p )
65         return 0;
66
67     if (strlen(p) < 10 || p[4] != '-' || p[7] != '-' )
68         return (time_t)-1;
69     p[4] = 0;
70     p[7] = 0;
71     p[10] = 0; /* just in case the time part follows */
72     memset (&tm, 0, sizeof tm);
73
74     i = atoi (p);
75     if ( i < 1900 )
76         return (time_t)-1;
77     tm.tm_year = i - 1900;
78
79     i = atoi (p+5);
80     if ( i < 1 || i > 12 )
81         return (time_t)-1;
82     tm.tm_mon = i-1;
83
84     i = atoi (p+8);
85     if ( i < 1 || i > 31 )
86         return (time_t)-1;
87     tm.tm_mday = i;
88
89     return mktime (&tm);
90 }
91
92
93 static void
94 set_mainkey_trust_info ( GpgmeKey key, const char *s )
95 {
96     /* look at letters and stop at the first digit */
97     for (; *s && !my_isdigit (*s); s++ ) {
98         switch (*s) {
99           case 'e': key->keys.flags.expired = 1; break;
100           case 'r': key->keys.flags.revoked = 1; break;
101           case 'd': key->keys.flags.disabled = 1; break;
102           case 'i': key->keys.flags.invalid = 1; break;
103         }
104     }
105 }
106
107
108 static void
109 set_userid_flags ( GpgmeKey key, const char *s )
110 {
111     /* look at letters and stop at the first digit */
112     for (; *s && !my_isdigit (*s); s++ ) {
113         switch (*s) {
114           case 'r': key->uids->revoked  = 1; break;
115           case 'i': key->uids->invalid  = 1; break;
116
117           case 'n': key->uids->validity = 1; break;
118           case 'm': key->uids->validity = 2; break;
119           case 'f': key->uids->validity = 3; break;
120           case 'u': key->uids->validity = 4; break;
121         }
122     }
123 }
124
125 static void
126 set_subkey_trust_info ( struct subkey_s *k, const char *s )
127 {
128     /* look at letters and stop at the first digit */
129     for (; *s && !my_isdigit (*s); s++ ) {
130         switch (*s) {
131           case 'e': k->flags.expired = 1; break;
132           case 'r': k->flags.revoked = 1; break;
133           case 'd': k->flags.disabled = 1; break;
134           case 'i': k->flags.invalid = 1; break;
135         }
136     }
137 }
138
139 static void
140 set_mainkey_capability ( GpgmeKey key, const char *s )
141 {
142     for (; *s ; s++ ) {
143         switch (*s) {
144           case 'e': key->keys.flags.can_encrypt = 1; break;
145           case 's': key->keys.flags.can_sign = 1; break;
146           case 'c': key->keys.flags.can_certify = 1; break;
147           case 'E': key->gloflags.can_encrypt = 1; break;
148           case 'S': key->gloflags.can_sign = 1; break;
149           case 'C': key->gloflags.can_certify = 1; break;
150         }
151     }
152 }
153
154 static void
155 set_subkey_capability ( struct subkey_s *k, const char *s )
156 {
157     for (; *s; s++ ) {
158         switch (*s) {
159           case 'e': k->flags.can_encrypt = 1; break;
160           case 's': k->flags.can_sign = 1; break;
161           case 'c': k->flags.can_certify = 1; break;
162         }
163     }
164 }
165
166
167
168 /* Note: we are allowed to modify line */
169 static void
170 keylist_colon_handler ( GpgmeCtx ctx, char *line )
171 {
172     char *p, *pend;
173     int field = 0;
174     enum {
175         RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC
176     } rectype = RT_NONE;
177     GpgmeKey key = ctx->tmp_key;
178     int i;
179     const char *trust_info = NULL;
180     struct subkey_s *sk = NULL;
181
182     if ( ctx->out_of_core )
183         return;
184     if (!line)
185         return; /* EOF */
186
187     for (p = line; p; p = pend) {
188         field++;
189         pend = strchr (p, ':');
190         if (pend) 
191             *pend++ = 0;
192
193         if ( field == 1 ) {
194             if ( !strcmp ( p, "sig" ) )
195                 rectype = RT_SIG;
196             else if ( !strcmp ( p, "uid" ) && key ) {
197                 rectype = RT_UID;
198                 key = ctx->tmp_key;
199             }
200             else if ( !strcmp (p, "sub") && key ) {
201                 /* start a new subkey */
202                 rectype = RT_SUB;
203                 if ( !(sk = _gpgme_key_add_subkey (key)) ) {
204                     ctx->out_of_core=1;
205                     return;
206                 }
207             }
208             else if ( !strcmp (p, "ssb") && key ) {
209                 /* start a new secret subkey */
210                 rectype = RT_SSB;
211                 if ( !(sk = _gpgme_key_add_secret_subkey (key)) ) {
212                     ctx->out_of_core=1;
213                     return;
214                 }
215             }
216             else if ( !strcmp (p, "pub") ) {
217                 /* start a new keyblock */
218                 if ( _gpgme_key_new ( &key ) ) {
219                     ctx->out_of_core=1; /* the only kind of error we can get*/
220                     return;
221                 }
222                 rectype = RT_PUB;
223                 if ( ctx->tmp_key )
224                     finish_key ( ctx );
225                 assert ( !ctx->tmp_key );
226                 ctx->tmp_key = key;
227             }
228             else if ( !strcmp (p, "sec") ) {
229                 /* start a new keyblock */
230                 if ( _gpgme_key_new_secret ( &key ) ) {
231                     ctx->out_of_core=1; /*the only kind of error we can get*/
232                     return;
233                 }
234                 rectype = RT_SEC;
235                 if ( ctx->tmp_key )
236                     finish_key ( ctx );
237                 assert ( !ctx->tmp_key );
238                 ctx->tmp_key = key;
239             }
240             else if ( !strcmp ( p, "fpr" ) && key ) 
241                 rectype = RT_FPR;
242             else 
243                 rectype = RT_NONE;
244             
245         }
246         else if ( rectype == RT_PUB || rectype == RT_SEC ) {
247             switch (field) {
248               case 2: /* trust info */
249                 trust_info = p; 
250                 set_mainkey_trust_info (key, trust_info);
251                 break;
252               case 3: /* key length */
253                 i = atoi (p); 
254                 if ( i > 1 ) /* ignore invalid values */
255                     key->keys.key_len = i; 
256                 break;
257               case 4: /* pubkey algo */
258                 i = atoi (p);
259                 if ( i > 1 && i < 128 )
260                     key->keys.key_algo = i;
261                 break;
262               case 5: /* long keyid */
263                 if ( strlen (p) == DIM(key->keys.keyid)-1 )
264                     strcpy (key->keys.keyid, p);
265                 break;
266               case 6: /* timestamp (1998-02-28) */
267                 key->keys.timestamp = parse_timestamp (p);
268                 break;
269               case 7: /* valid for n days */
270                 break;
271               case 8: /* reserved (LID) */
272                 break;
273               case 9: /* ownertrust */
274                 break;
275               case 10: /* not used due to --fixed-list-mode option */
276                 break;
277               case 11: /* signature class  */
278                 break;
279               case 12: /* capabilities */
280                 set_mainkey_capability (key, p );
281                 break;
282               case 13:
283                 pend = NULL;  /* we can stop here */
284                 break;
285             }
286         }
287         else if ( (rectype == RT_SUB || rectype== RT_SSB) && sk ) {
288             switch (field) {
289               case 2: /* trust info */
290                 set_subkey_trust_info ( sk, p);
291                 break;
292               case 3: /* key length */
293                 i = atoi (p); 
294                 if ( i > 1 ) /* ignore invalid values */
295                     sk->key_len = i; 
296                 break;
297               case 4: /* pubkey algo */
298                 i = atoi (p);
299                 if ( i > 1 && i < 128 )
300                     sk->key_algo = i;
301                 break;
302               case 5: /* long keyid */
303                 if ( strlen (p) == DIM(sk->keyid)-1 )
304                     strcpy (sk->keyid, p);
305                 break;
306               case 6: /* timestamp (1998-02-28) */
307                 sk->timestamp = parse_timestamp (p);
308                 break;
309               case 7: /* valid for n days */
310                 break;
311               case 8: /* reserved (LID) */
312                 break;
313               case 9: /* ownertrust */
314                 break;
315               case 10:/* user ID n/a for a subkey */
316                 break;
317               case 11:  /* signature class  */
318                 break;
319               case 12: /* capability */
320                 set_subkey_capability ( sk, p );
321                 break;
322               case 13:
323                 pend = NULL;  /* we can stop here */
324                 break;
325             }
326         }
327         else if ( rectype == RT_UID ) {
328             switch (field) {
329               case 2: /* trust info */
330                 trust_info = p;  /*save for later */
331                 break;
332               case 10: /* user ID */
333                 if ( _gpgme_key_append_name ( key, p) )
334                     ctx->out_of_core = 1;
335                 else {
336                     if (trust_info)
337                         set_userid_flags (key, trust_info);
338                 }
339                 pend = NULL;  /* we can stop here */
340                 break;
341             }
342         }
343         else if ( rectype == RT_FPR ) {
344             switch (field) {
345               case 10: /* fingerprint (take only the first one)*/
346                 if ( !key->keys.fingerprint && *p ) {
347                     key->keys.fingerprint = xtrystrdup (p);
348                     if ( !key->keys.fingerprint )
349                         ctx->out_of_core = 1;
350                 }
351                 pend = NULL; /* that is all we want */
352                 break;
353             }
354         }
355     }
356     
357 }
358
359
360 /*
361  * We have read an entire key into ctx->tmp_key and should now finish
362  * it.  It is assumed that this releases ctx->tmp_key.
363  */
364 static void
365 finish_key ( GpgmeCtx ctx )
366 {
367     GpgmeKey key = ctx->tmp_key;
368     struct key_queue_item_s *q, *q2;
369     
370     assert (key);
371     ctx->tmp_key = NULL;
372     
373     q = xtrymalloc ( sizeof *q );
374     if ( !q ) {
375         gpgme_key_release (key);
376         ctx->out_of_core = 1;
377         return;
378     }
379     q->key = key;
380     q->next = NULL;
381     /* fixme: lock queue. Use a tail pointer? */
382     if ( !(q2 = ctx->key_queue) )
383         ctx->key_queue = q;
384     else {
385         for ( ; q2->next; q2 = q2->next )
386             ;
387         q2->next = q;
388     }
389     ctx->key_cond = 1;
390     /* fixme: unlock queue */
391 }
392
393
394
395
396 /**
397  * gpgme_op_keylist_start:
398  * @c: context 
399  * @pattern: a GnuPg user ID or NULL for all
400  * @secret_only: List only keys where the secret part is available
401  * 
402  * Note that this function also cancels a pending key listing operaton..
403  * 
404  * Return value:  0 on success or an errorcode. 
405  **/
406 GpgmeError
407 gpgme_op_keylist_start ( GpgmeCtx c,  const char *pattern, int secret_only )
408 {
409     GpgmeError rc = 0;
410     int i;
411
412     if ( !c )
413         return mk_error (Invalid_Value);
414     c->pending = 1;
415
416     _gpgme_release_result (c);
417     c->out_of_core = 0;
418
419     if ( c->gpg ) {
420         _gpgme_gpg_release ( c->gpg ); 
421         c->gpg = NULL;
422     }
423     gpgme_key_release (c->tmp_key);
424     c->tmp_key = NULL;
425     /* Fixme: release key_queue */
426     
427     rc = _gpgme_gpg_new ( &c->gpg );
428     if (rc)
429         goto leave;
430
431     _gpgme_gpg_set_status_handler ( c->gpg, keylist_status_handler, c );
432
433     rc = _gpgme_gpg_set_colon_line_handler ( c->gpg,
434                                              keylist_colon_handler, c );
435     if (rc)
436         goto leave;
437
438     /* build the commandline */
439     for ( i=0; i < c->verbosity; i++ )
440         _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
441     _gpgme_gpg_add_arg ( c->gpg, "--with-colons" );
442     _gpgme_gpg_add_arg ( c->gpg, "--fixed-list-mode" );
443     _gpgme_gpg_add_arg ( c->gpg, "--with-fingerprint" );
444     if (c->keylist_mode == 1)
445         _gpgme_gpg_add_arg ( c->gpg, "--no-expensive-trust-checks" );
446     _gpgme_gpg_add_arg ( c->gpg, secret_only?
447                          "--list-secret-keys":"--list-keys" );
448     
449     /* Tell the gpg object about the data */
450     _gpgme_gpg_add_arg ( c->gpg, "--" );
451     if (pattern && *pattern)
452         _gpgme_gpg_add_arg ( c->gpg, pattern );
453
454     /* and kick off the process */
455     rc = _gpgme_gpg_spawn ( c->gpg, c );
456
457  leave:
458     if (rc) {
459         c->pending = 0; 
460         _gpgme_gpg_release ( c->gpg ); c->gpg = NULL;
461     }
462     return rc;
463 }
464
465
466 GpgmeError
467 gpgme_op_keylist_next ( GpgmeCtx c, GpgmeKey *r_key )
468 {
469     struct key_queue_item_s *q;
470
471     if (!r_key)
472         return mk_error (Invalid_Value);
473     *r_key = NULL;
474     if (!c)
475         return mk_error (Invalid_Value);
476     if ( !c->pending )
477         return mk_error (No_Request);
478     if ( c->out_of_core )
479         return mk_error (Out_Of_Core);
480
481     if ( !c->key_queue ) {
482         _gpgme_wait_on_condition (c, 1, &c->key_cond );
483         if ( c->out_of_core )
484             return mk_error (Out_Of_Core);
485         if ( !c->key_cond )
486             return mk_error (EOF);
487         c->key_cond = 0; 
488         assert ( c->key_queue );
489     }
490     q = c->key_queue;
491     c->key_queue = q->next;
492
493     *r_key = q->key;
494     xfree (q);
495     return 0;
496 }
497
498
499
500