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