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