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