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