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