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