Added more doc entries and prepared for 0.2.3
[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) { /* EOF */
160         if (ctx->tmp_key)
161             finish_key (ctx);
162         return; 
163     }
164
165     for (p = line; p; p = pend) {
166         field++;
167         pend = strchr (p, ':');
168         if (pend) 
169             *pend++ = 0;
170
171         if ( field == 1 ) {
172             if ( !strcmp ( p, "sig" ) )
173                 rectype = RT_SIG;
174             else if ( !strcmp ( p, "uid" ) && key ) {
175                 rectype = RT_UID;
176                 key = ctx->tmp_key;
177             }
178             else if ( !strcmp (p, "sub") && key ) {
179                 /* start a new subkey */
180                 rectype = RT_SUB;
181                 if ( !(sk = _gpgme_key_add_subkey (key)) ) {
182                     ctx->out_of_core=1;
183                     return;
184                 }
185             }
186             else if ( !strcmp (p, "ssb") && key ) {
187                 /* start a new secret subkey */
188                 rectype = RT_SSB;
189                 if ( !(sk = _gpgme_key_add_secret_subkey (key)) ) {
190                     ctx->out_of_core=1;
191                     return;
192                 }
193             }
194             else if ( !strcmp (p, "pub") ) {
195                 /* start a new keyblock */
196                 if ( _gpgme_key_new ( &key ) ) {
197                     ctx->out_of_core=1; /* the only kind of error we can get*/
198                     return;
199                 }
200                 rectype = RT_PUB;
201                 if ( ctx->tmp_key )
202                     finish_key ( ctx );
203                 assert ( !ctx->tmp_key );
204                 ctx->tmp_key = key;
205             }
206             else if ( !strcmp (p, "sec") ) {
207                 /* start a new keyblock */
208                 if ( _gpgme_key_new_secret ( &key ) ) {
209                     ctx->out_of_core=1; /*the only kind of error we can get*/
210                     return;
211                 }
212                 rectype = RT_SEC;
213                 if ( ctx->tmp_key )
214                     finish_key ( ctx );
215                 assert ( !ctx->tmp_key );
216                 ctx->tmp_key = key;
217             }
218             else if ( !strcmp ( p, "fpr" ) && key ) 
219                 rectype = RT_FPR;
220             else 
221                 rectype = RT_NONE;
222             
223         }
224         else if ( rectype == RT_PUB || rectype == RT_SEC ) {
225             switch (field) {
226               case 2: /* trust info */
227                 trust_info = p; 
228                 set_mainkey_trust_info (key, trust_info);
229                 break;
230               case 3: /* key length */
231                 i = atoi (p); 
232                 if ( i > 1 ) /* ignore invalid values */
233                     key->keys.key_len = i; 
234                 break;
235               case 4: /* pubkey algo */
236                 i = atoi (p);
237                 if ( i > 1 && i < 128 )
238                     key->keys.key_algo = i;
239                 break;
240               case 5: /* long keyid */
241                 if ( strlen (p) == DIM(key->keys.keyid)-1 )
242                     strcpy (key->keys.keyid, p);
243                 break;
244               case 6: /* timestamp (seconds) */
245                 key->keys.timestamp = parse_timestamp (p);
246                 break;
247               case 7: /* valid for n days */
248                 break;
249               case 8: /* reserved (LID) */
250                 break;
251               case 9: /* ownertrust */
252                 break;
253               case 10: /* not used due to --fixed-list-mode option */
254                 break;
255               case 11: /* signature class  */
256                 break;
257               case 12: /* capabilities */
258                 set_mainkey_capability (key, p );
259                 break;
260               case 13:
261                 pend = NULL;  /* we can stop here */
262                 break;
263             }
264         }
265         else if ( (rectype == RT_SUB || rectype== RT_SSB) && sk ) {
266             switch (field) {
267               case 2: /* trust info */
268                 set_subkey_trust_info ( sk, p);
269                 break;
270               case 3: /* key length */
271                 i = atoi (p); 
272                 if ( i > 1 ) /* ignore invalid values */
273                     sk->key_len = i; 
274                 break;
275               case 4: /* pubkey algo */
276                 i = atoi (p);
277                 if ( i > 1 && i < 128 )
278                     sk->key_algo = i;
279                 break;
280               case 5: /* long keyid */
281                 if ( strlen (p) == DIM(sk->keyid)-1 )
282                     strcpy (sk->keyid, p);
283                 break;
284               case 6: /* timestamp (seconds) */
285                 sk->timestamp = parse_timestamp (p);
286                 break;
287               case 7: /* valid for n days */
288                 break;
289               case 8: /* reserved (LID) */
290                 break;
291               case 9: /* ownertrust */
292                 break;
293               case 10:/* user ID n/a for a subkey */
294                 break;
295               case 11:  /* signature class  */
296                 break;
297               case 12: /* capability */
298                 set_subkey_capability ( sk, p );
299                 break;
300               case 13:
301                 pend = NULL;  /* we can stop here */
302                 break;
303             }
304         }
305         else if ( rectype == RT_UID ) {
306             switch (field) {
307               case 2: /* trust info */
308                 trust_info = p;  /*save for later */
309                 break;
310               case 10: /* user ID */
311                 if ( _gpgme_key_append_name ( key, p) )
312                     ctx->out_of_core = 1;
313                 else {
314                     if (trust_info)
315                         set_userid_flags (key, trust_info);
316                 }
317                 pend = NULL;  /* we can stop here */
318                 break;
319             }
320         }
321         else if ( rectype == RT_FPR ) {
322             switch (field) {
323               case 10: /* fingerprint (take only the first one)*/
324                 if ( !key->keys.fingerprint && *p ) {
325                     key->keys.fingerprint = xtrystrdup (p);
326                     if ( !key->keys.fingerprint )
327                         ctx->out_of_core = 1;
328                 }
329                 pend = NULL; /* that is all we want */
330                 break;
331             }
332         }
333     }
334     
335 }
336
337
338 /*
339  * We have read an entire key into ctx->tmp_key and should now finish
340  * it.  It is assumed that this releases ctx->tmp_key.
341  */
342 static void
343 finish_key ( GpgmeCtx ctx )
344 {
345     GpgmeKey key = ctx->tmp_key;
346     struct key_queue_item_s *q, *q2;
347
348     assert (key);
349     ctx->tmp_key = NULL;
350
351     _gpgme_key_cache_add (key);
352
353     q = xtrymalloc ( sizeof *q );
354     if ( !q ) {
355         gpgme_key_release (key);
356         ctx->out_of_core = 1;
357         return;
358     }
359     q->key = key;
360     q->next = NULL;
361     /* fixme: lock queue. Use a tail pointer? */
362     if ( !(q2 = ctx->key_queue) )
363         ctx->key_queue = q;
364     else {
365         for ( ; q2->next; q2 = q2->next )
366             ;
367         q2->next = q;
368     }
369     ctx->key_cond = 1;
370     /* fixme: unlock queue */
371 }
372
373
374
375
376 /**
377  * gpgme_op_keylist_start:
378  * @c: context 
379  * @pattern: a GnuPG user ID or NULL for all
380  * @secret_only: List only keys where the secret part is available
381  * 
382  * Note that this function also cancels a pending key listing
383  * operaton. To actually retrieve the key, use
384  * gpgme_op_keylist_next().
385  * 
386  * Return value:  0 on success or an errorcode. 
387  **/
388 GpgmeError
389 gpgme_op_keylist_start ( GpgmeCtx c,  const char *pattern, int secret_only )
390 {
391     GpgmeError rc = 0;
392     int i;
393
394     if ( !c )
395         return mk_error (Invalid_Value);
396     c->pending = 1;
397
398     _gpgme_release_result (c);
399     c->out_of_core = 0;
400
401     if ( c->gpg ) {
402         _gpgme_gpg_release ( c->gpg ); 
403         c->gpg = NULL;
404     }
405     gpgme_key_release (c->tmp_key);
406     c->tmp_key = NULL;
407     /* Fixme: release key_queue */
408     
409     rc = _gpgme_gpg_new ( &c->gpg );
410     if (rc)
411         goto leave;
412
413     _gpgme_gpg_set_status_handler ( c->gpg, keylist_status_handler, c );
414
415     rc = _gpgme_gpg_set_colon_line_handler ( c->gpg,
416                                              keylist_colon_handler, c );
417     if (rc)
418         goto leave;
419
420     /* build the commandline */
421     for ( i=0; i < c->verbosity; i++ )
422         _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
423     _gpgme_gpg_add_arg ( c->gpg, "--with-colons" );
424     _gpgme_gpg_add_arg ( c->gpg, "--fixed-list-mode" );
425     _gpgme_gpg_add_arg ( c->gpg, "--with-fingerprint" );
426     if (c->keylist_mode == 1)
427         _gpgme_gpg_add_arg ( c->gpg, "--no-expensive-trust-checks" );
428     _gpgme_gpg_add_arg ( c->gpg, secret_only?
429                          "--list-secret-keys":"--list-keys" );
430     
431     /* Tell the gpg object about the data */
432     _gpgme_gpg_add_arg ( c->gpg, "--" );
433     if (pattern && *pattern)
434         _gpgme_gpg_add_arg ( c->gpg, pattern );
435
436     /* and kick off the process */
437     rc = _gpgme_gpg_spawn ( c->gpg, c );
438
439  leave:
440     if (rc) {
441         c->pending = 0; 
442         _gpgme_gpg_release ( c->gpg ); c->gpg = NULL;
443     }
444     return rc;
445 }
446
447
448 /**
449  * gpgme_op_keylist_next:
450  * @c: Context
451  * @r_key: Returned key object
452  * 
453  * Return the next key from the key listing started with
454  * gpgme_op_keylist_start().  The caller must free the key using 
455  * gpgme_key_release().
456  * 
457  * Return value: 0 on success, %GPGME_EOF or anoter error code.
458  **/
459 GpgmeError
460 gpgme_op_keylist_next ( GpgmeCtx c, GpgmeKey *r_key )
461 {
462     struct key_queue_item_s *q;
463
464     if (!r_key)
465         return mk_error (Invalid_Value);
466     *r_key = NULL;
467     if (!c)
468         return mk_error (Invalid_Value);
469     if ( !c->pending )
470         return mk_error (No_Request);
471     if ( c->out_of_core )
472         return mk_error (Out_Of_Core);
473
474     if ( !c->key_queue ) {
475         _gpgme_wait_on_condition (c, 1, &c->key_cond );
476         if ( c->out_of_core )
477             return mk_error (Out_Of_Core);
478         if ( !c->key_cond )
479             return mk_error (EOF);
480         c->key_cond = 0; 
481         assert ( c->key_queue );
482     }
483     q = c->key_queue;
484     c->key_queue = q->next;
485
486     *r_key = q->key;
487     xfree (q);
488     return 0;
489 }
490
491
492
493