1e7d5b12116267871d66ddc328b6b0d47354dbaf
[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 user_id_s *u;
257     
258     assert (key);
259     ctx->tmp_key = NULL;
260     
261     fprintf (stderr, "finish_key: keyid=`%s'\n", key->keyid );
262     if ( key->fingerprint )
263         fprintf (stderr, "finish_key:  fpr=`%s'\n", key->fingerprint );
264     for (u=key->uids; u; u = u->next ) 
265         fprintf (stderr, "finish_key:  uid=`%s'\n", u->name );
266         
267
268     /* fixme: call the callback or do something else with the key */
269     
270     _gpgme_key_release (key);
271 }
272
273
274
275
276 GpgmeError
277 gpgme_keylist_start ( GpgmeCtx c,  const char *pattern, int secret_only )
278 {
279     GpgmeError rc = 0;
280     int i;
281
282     fail_on_pending_request( c );
283     c->pending = 1;
284
285     _gpgme_release_result (c);
286     c->out_of_core = 0;
287
288     if ( c->gpg ) {
289         _gpgme_gpg_release_object ( c->gpg ); 
290         c->gpg = NULL;
291     }
292     _gpgme_key_release (c->tmp_key);
293     c->tmp_key = NULL;
294     
295     rc = _gpgme_gpg_new_object ( &c->gpg );
296     if (rc)
297         goto leave;
298
299     _gpgme_gpg_set_status_handler ( c->gpg, keylist_status_handler, c );
300
301     rc = _gpgme_gpg_set_colon_line_handler ( c->gpg,
302                                              keylist_colon_handler, c );
303     if (rc)
304         goto leave;
305
306     /* build the commandline */
307     for ( i=0; i < c->verbosity; i++ )
308         _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
309     _gpgme_gpg_add_arg ( c->gpg, "--with-colons" );
310     _gpgme_gpg_add_arg ( c->gpg, "--with-fingerprint" );
311     _gpgme_gpg_add_arg ( c->gpg, secret_only?
312                          "--list-secret-keys":"--list-keys" );
313     
314     /* Tell the gpg object about the data */
315     _gpgme_gpg_add_arg ( c->gpg, "--" );
316     if (pattern && *pattern)
317         _gpgme_gpg_add_arg ( c->gpg, pattern );
318
319     /* and kick off the process */
320     rc = _gpgme_gpg_spawn ( c->gpg, c );
321
322  leave:
323     if (rc) {
324         c->pending = 0; 
325         _gpgme_gpg_release_object ( c->gpg ); c->gpg = NULL;
326     }
327     return rc;
328 }
329
330
331
332
333
334