tests/
[gpgme.git] / gpgme / keylist.c
1 /* keylist.c -  key listing
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001, 2002 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <assert.h>
28
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "key.h"
33
34 #define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
35
36 struct keylist_result_s
37 {
38   int truncated;
39   GpgmeData xmlinfo;
40 };
41
42 static void finish_key ( GpgmeCtx ctx );
43
44
45 void
46 _gpgme_release_keylist_result (KeylistResult result)
47 {
48   if (!result)
49     return;
50   xfree (result);
51 }
52
53 /* Append some XML info.  args is currently ignore but we might want
54    to add more information in the future (like source of the
55    keylisting.  With args of NULL the XML structure is closed.  */
56 static void
57 append_xml_keylistinfo (GpgmeData *rdh, char *args)
58 {
59   GpgmeData dh;
60
61   if (!*rdh)
62     {
63       if (gpgme_data_new (rdh))
64         return; /* FIXME: We are ignoring out-of-core.  */
65       dh = *rdh;
66       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
67     }
68   else
69     {
70       dh = *rdh;
71       _gpgme_data_append_string (dh, "  </keylisting>\n");
72     }
73
74   if (!args)
75     {
76       /* Just close the XML containter.  */
77       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
78       return;
79     }
80
81   _gpgme_data_append_string (dh,
82                              "  <keylisting>\n"
83                              "    <truncated/>\n"
84                              );
85     
86 }
87
88
89
90 static void
91 keylist_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
92 {
93   if (ctx->error)
94     return;
95   test_and_allocate_result (ctx, keylist);
96
97   switch (code)
98     {
99     case STATUS_TRUNCATED:
100       ctx->result.keylist->truncated = 1;
101       break;
102
103     case STATUS_EOF:
104       finish_key (ctx);
105       if (ctx->result.keylist->truncated)
106         append_xml_keylistinfo (&ctx->result.keylist->xmlinfo, "1");
107       if (ctx->result.keylist->xmlinfo)
108         {
109           append_xml_keylistinfo (&ctx->result.keylist->xmlinfo, NULL);
110           _gpgme_set_op_info (ctx, ctx->result.keylist->xmlinfo);
111           ctx->result.keylist->xmlinfo = NULL;
112         }
113       break;
114
115     default:
116       /* Ignore all other codes.  */
117       break;
118     }
119 }
120
121
122 static time_t
123 parse_timestamp (char *p)
124 {
125   if (!*p)
126     return 0;
127
128   return (time_t)strtoul (p, NULL, 10);
129 }
130
131
132 static void
133 set_mainkey_trust_info (GpgmeKey key, const char *s)
134 {
135   /* Look at letters and stop at the first digit.  */
136   for (; *s && !my_isdigit (*s); s++)
137     {
138       switch (*s)
139         {
140         case 'e': key->keys.flags.expired = 1; break;
141         case 'r': key->keys.flags.revoked = 1; break;
142         case 'd': key->keys.flags.disabled = 1; break;
143         case 'i': key->keys.flags.invalid = 1; break;
144         }
145     }
146 }
147
148
149 static void
150 set_userid_flags (GpgmeKey key, const char *s)
151 {
152   /* Look at letters and stop at the first digit.  */
153   for (; *s && !my_isdigit (*s); s++)
154     {
155       switch (*s)
156         {
157         case 'r': key->uids->revoked  = 1; break;
158         case 'i': key->uids->invalid  = 1; break;
159
160         case 'n': key->uids->validity = GPGME_VALIDITY_NEVER; break;
161         case 'm': key->uids->validity = GPGME_VALIDITY_MARGINAL; break;
162         case 'f': key->uids->validity = GPGME_VALIDITY_FULL; break;
163         case 'u': key->uids->validity = GPGME_VALIDITY_ULTIMATE; break;
164         }
165     }
166 }
167
168
169 static void
170 set_subkey_trust_info (struct subkey_s *k, const char *s)
171 {
172   /* Look at letters and stop at the first digit.  */
173   for (; *s && !my_isdigit (*s); s++)
174     {
175       switch (*s)
176         {
177         case 'e': k->flags.expired = 1; break;
178         case 'r': k->flags.revoked = 1; break;
179         case 'd': k->flags.disabled = 1; break;
180         case 'i': k->flags.invalid = 1; break;
181         }
182     }
183 }
184
185
186 static void
187 set_mainkey_capability (GpgmeKey key, const char *s)
188 {
189   for (; *s ; s++)
190     {
191       switch (*s)
192         {
193         case 'e': key->keys.flags.can_encrypt = 1; break;
194         case 's': key->keys.flags.can_sign = 1; break;
195         case 'c': key->keys.flags.can_certify = 1; break;
196         case 'E': key->gloflags.can_encrypt = 1; break;
197         case 'S': key->gloflags.can_sign = 1; break;
198         case 'C': key->gloflags.can_certify = 1; break;
199         }
200     }
201 }
202
203
204 static void
205 set_subkey_capability ( struct subkey_s *k, const char *s)
206 {
207   for (; *s; s++)
208     {
209       switch (*s)
210         {
211         case 'e': k->flags.can_encrypt = 1; break;
212         case 's': k->flags.can_sign = 1; break;
213         case 'c': k->flags.can_certify = 1; break;
214         }
215     }
216 }
217
218 static void
219 set_ownertrust (GpgmeKey key, const char *s)
220 {
221   /* Look at letters and stop at the first digit.  */
222   for (; *s && !my_isdigit (*s); s++)
223     {
224       switch (*s)
225         {
226         case 'n': key->otrust = GPGME_VALIDITY_NEVER; break;
227         case 'm': key->otrust = GPGME_VALIDITY_MARGINAL; break;
228         case 'f': key->otrust = GPGME_VALIDITY_FULL; break;
229         case 'u': key->otrust = GPGME_VALIDITY_ULTIMATE; break;
230         default : key->otrust = GPGME_VALIDITY_UNKNOWN; break;
231         }
232     }
233 }
234
235
236 /* Note: We are allowed to modify LINE.  */
237 static void
238 keylist_colon_handler (GpgmeCtx ctx, char *line)
239 {
240   char *p, *pend;
241   int field = 0;
242   enum
243     {
244       RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC,
245       RT_CRT, RT_CRS
246     }
247   rectype = RT_NONE;
248   GpgmeKey key = ctx->tmp_key;
249   int i;
250   const char *trust_info = NULL;
251   struct subkey_s *sk = NULL;
252
253   if (ctx->error)
254     return;
255   if (!line)
256     {
257       /* EOF */
258       finish_key (ctx);
259       return; 
260     }
261   
262   for (p = line; p; p = pend)
263     {
264       field++;
265       pend = strchr (p, ':');
266       if (pend) 
267         *pend++ = 0;
268
269       if (field == 1)
270         {
271           if (!strcmp (p, "sig"))
272             rectype = RT_SIG;
273           else if (!strcmp (p, "uid") && key)
274             {
275               rectype = RT_UID;
276               key = ctx->tmp_key;
277             }
278           else if (!strcmp (p, "sub") && key)
279             {
280               /* Start a new subkey.  */
281               rectype = RT_SUB; 
282               if (!(sk = _gpgme_key_add_subkey (key)))
283                 {
284                   ctx->error = mk_error (Out_Of_Core);
285                   return;
286                 }
287             }
288           else if (!strcmp (p, "ssb") && key)
289             {
290               /* Start a new secret subkey.  */
291               rectype = RT_SSB;
292               if (!(sk = _gpgme_key_add_secret_subkey (key)))
293                 {
294                   ctx->error = mk_error (Out_Of_Core);
295                   return;
296                 }
297             }
298           else if (!strcmp (p, "pub"))
299             {
300               /* Start a new keyblock.  */
301               if (_gpgme_key_new (&key))
302                 {
303                   /* The only kind of error we can get.  */
304                   ctx->error = mk_error (Out_Of_Core);
305                   return;
306                 }
307               rectype = RT_PUB;
308               finish_key (ctx);
309               assert (!ctx->tmp_key);
310               ctx->tmp_key = key;
311             }
312           else if (!strcmp (p, "sec"))
313             {
314               /* Start a new keyblock,  */
315               if (_gpgme_key_new_secret (&key))
316                 {
317                   /* The only kind of error we can get.  */
318                   ctx->error = mk_error (Out_Of_Core);
319                   return;
320                 }
321               rectype = RT_SEC;
322               finish_key (ctx);
323               assert (!ctx->tmp_key);
324               ctx->tmp_key = key;
325             }
326           else if (!strcmp (p, "crt"))
327             {
328               /* Start a new certificate. */
329               if (_gpgme_key_new (&key))
330                 {
331                   /* The only kind of error we can get.  */
332                   ctx->error = mk_error (Out_Of_Core);
333                   return;
334                 }
335               key->x509 = 1;
336               rectype = RT_CRT;
337               finish_key (ctx);
338               assert (!ctx->tmp_key);
339               ctx->tmp_key = key;
340             }
341           else if (!strcmp (p, "crs"))
342             {
343               /* Start a new certificate.  */
344               if (_gpgme_key_new_secret (&key))
345                 {
346                   /* The only kind of error we can get.  */
347                   ctx->error = mk_error (Out_Of_Core);
348                   return;
349                 }
350               key->x509 = 1;
351               rectype = RT_CRS;
352               finish_key (ctx);
353               assert (!ctx->tmp_key);
354               ctx->tmp_key = key;
355             }
356           else if (!strcmp (p, "fpr") && key) 
357             rectype = RT_FPR;
358           else 
359             rectype = RT_NONE;
360         }
361       else if (rectype == RT_PUB || rectype == RT_SEC
362                || rectype == RT_CRT || rectype == RT_CRS)
363         {
364           switch (field)
365             {
366             case 2: /* trust info */
367               trust_info = p; 
368               set_mainkey_trust_info (key, trust_info);
369               break;
370             case 3: /* key length */
371               i = atoi (p); 
372               if (i > 1) /* ignore invalid values */
373                 key->keys.key_len = i; 
374                 break;
375             case 4: /* pubkey algo */
376               i = atoi (p);
377               if (i > 1 && i < 128)
378                 key->keys.key_algo = i;
379               break;
380               case 5: /* long keyid */
381                 if (strlen (p) == DIM(key->keys.keyid) - 1)
382                   strcpy (key->keys.keyid, p);
383                 break;
384             case 6: /* timestamp (seconds) */
385               key->keys.timestamp = parse_timestamp (p);
386               break;
387             case 7: /* expiration time (seconds) */
388               key->keys.expires_at = parse_timestamp (p);
389               break;
390             case 8: /* X.509 serial number */
391               if (rectype == RT_CRT)
392                 {
393                   key->issuer_serial = xtrystrdup (p);
394                   if (!key->issuer_serial)
395                     ctx->error = mk_error (Out_Of_Core);
396                 }
397               break;
398             case 9: /* ownertrust */
399               set_ownertrust (key, p);
400               break;
401             case 10: /* not used for gpg due to --fixed-list-mode option 
402                         but gpgsm stores the issuer name */
403               if (rectype == RT_CRT)
404                 {
405                   key->issuer_name = xtrystrdup (p);
406                   if (!key->issuer_name)
407                     ctx->error = mk_error (Out_Of_Core);
408                 }
409               break;
410             case 11: /* signature class  */
411               break;
412             case 12: /* capabilities */
413               set_mainkey_capability (key, p);
414               break;
415             case 13:
416               pend = NULL;  /* we can stop here */
417               break;
418             }
419           }
420         else if ((rectype == RT_SUB || rectype== RT_SSB) && sk)
421           {
422             switch (field)
423               {
424               case 2: /* trust info */
425                 set_subkey_trust_info (sk, p);
426                 break;
427               case 3: /* key length */
428                 i = atoi (p); 
429                 if (i > 1) /* ignore invalid values */
430                   sk->key_len = i; 
431                 break;
432               case 4: /* pubkey algo */
433                 i = atoi (p);
434                 if (i > 1 && i < 128)
435                   sk->key_algo = i;
436                 break;
437               case 5: /* long keyid */
438                 if (strlen (p) == DIM(sk->keyid) - 1)
439                   strcpy (sk->keyid, p);
440                 break;
441               case 6: /* timestamp (seconds) */
442                 sk->timestamp = parse_timestamp (p);
443                 break;
444               case 7: /* expiration time (seconds) */
445                 break;
446               case 8: /* reserved (LID) */
447                 break;
448               case 9: /* ownertrust */
449                 break;
450               case 10:/* user ID n/a for a subkey */
451                 break;
452               case 11:  /* signature class  */
453                 break;
454               case 12: /* capability */
455                 set_subkey_capability (sk, p);
456                 break;
457               case 13:
458                 pend = NULL;  /* we can stop here */
459                 break;
460             }
461         }
462       else if (rectype == RT_UID)
463         {
464           switch (field)
465             {
466             case 2: /* trust info */
467               trust_info = p;  /*save for later */
468               break;
469             case 10: /* user ID */
470               if (_gpgme_key_append_name (key, p))
471                 /* The only kind of error we can get*/
472                 ctx->error = mk_error (Out_Of_Core);
473               else
474                 {
475                   if (trust_info)
476                   set_userid_flags (key, trust_info);
477                 }
478               pend = NULL;  /* we can stop here */
479               break;
480             }
481         }
482       else if (rectype == RT_FPR)
483         {
484           switch (field)
485             {
486             case 10: /* fingerprint (take only the first one)*/
487               if (!key->keys.fingerprint && *p)
488                 {
489                   key->keys.fingerprint = xtrystrdup (p);
490                   if (!key->keys.fingerprint)
491                     ctx->error = mk_error (Out_Of_Core);
492                 }
493               break;
494             case 13: /* gpgsm chain ID (take only the first one)*/
495               if (!key->chain_id && *p)
496                 {
497                   key->chain_id = xtrystrdup (p);
498                   if (!key->chain_id)
499                     ctx->error = mk_error (Out_Of_Core);
500                 }
501               pend = NULL; /* that is all we want */
502               break;
503             }
504         }
505     }
506 }
507
508
509 /*
510  * We have read an entire key into ctx->tmp_key and should now finish
511  * it.  It is assumed that this releases ctx->tmp_key.
512  */
513 static void
514 finish_key (GpgmeCtx ctx)
515 {
516   GpgmeKey key = ctx->tmp_key;
517   struct key_queue_item_s *q, *q2;
518
519   if (key)
520     {
521       ctx->tmp_key = NULL;
522         
523       _gpgme_key_cache_add (key);
524         
525       q = xtrymalloc (sizeof *q);
526       if (!q)
527         {
528           gpgme_key_release (key);
529           ctx->error = mk_error (Out_Of_Core);
530           return;
531         }
532       q->key = key;
533       q->next = NULL;
534       /* FIXME: Lock queue.  Use a tail pointer?  */
535       if (!(q2 = ctx->key_queue))
536         ctx->key_queue = q;
537       else
538         {
539           for (; q2->next; q2 = q2->next)
540             ;
541           q2->next = q;
542         }
543       ctx->key_cond = 1;
544       /* FIXME: Unlock queue.  */
545     }
546 }
547
548
549 /**
550  * gpgme_op_keylist_start:
551  * @c: context 
552  * @pattern: a GnuPG user ID or NULL for all
553  * @secret_only: List only keys where the secret part is available
554  * 
555  * Note that this function also cancels a pending key listing
556  * operaton. To actually retrieve the key, use
557  * gpgme_op_keylist_next().
558  * 
559  * Return value:  0 on success or an errorcode. 
560  **/
561 GpgmeError
562 gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
563 {
564   GpgmeError err = 0;
565
566   err = _gpgme_op_reset (ctx, 0);
567   if (err)
568     goto leave;
569
570   gpgme_key_release (ctx->tmp_key);
571   ctx->tmp_key = NULL;
572   /* Fixme: Release key_queue.  */
573
574   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
575   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
576                                               keylist_colon_handler, ctx);
577   if (err)
578     goto leave;
579
580   /* We don't want to use the verbose mode as this will also print 
581      the key signatures which is in most cases not needed and furthermore we 
582      just ignore those lines - This should speed up things */
583   _gpgme_engine_set_verbosity (ctx->engine, 0);
584
585   err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
586                                   ctx->keylist_mode);
587
588   if (!err)     /* And kick off the process.  */
589     err = _gpgme_engine_start (ctx->engine, ctx);
590
591  leave:
592   if (err)
593     {
594       ctx->pending = 0; 
595       _gpgme_engine_release (ctx->engine);
596       ctx->engine = NULL;
597     }
598   return err;
599 }
600
601
602 /**
603  * gpgme_op_keylist_ext_start:
604  * @c: context 
605  * @pattern: a NULL terminated array of search patterns
606  * @secret_only: List only keys where the secret part is available
607  * @reserved: Should be 0.
608  * 
609  * Note that this function also cancels a pending key listing
610  * operaton. To actually retrieve the key, use
611  * gpgme_op_keylist_next().
612  * 
613  * Return value:  0 on success or an errorcode. 
614  **/
615 GpgmeError
616 gpgme_op_keylist_ext_start (GpgmeCtx ctx, const char *pattern[],
617                             int secret_only, int reserved)
618 {
619   GpgmeError err = 0;
620
621   err = _gpgme_op_reset (ctx, 0);
622   if (err)
623     goto leave;
624
625   gpgme_key_release (ctx->tmp_key);
626
627   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
628   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
629                                               keylist_colon_handler, ctx);
630   if (err)
631     goto leave;
632
633   /* We don't want to use the verbose mode as this will also print 
634      the key signatures which is in most cases not needed and furthermore we 
635      just ignore those lines - This should speed up things */
636   _gpgme_engine_set_verbosity (ctx->engine, 0);
637
638   err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
639                                       reserved, ctx->keylist_mode);
640
641   if (!err)     /* And kick off the process.  */
642     err = _gpgme_engine_start (ctx->engine, ctx);
643
644  leave:
645   if (err)
646     {
647       ctx->pending = 0; 
648       _gpgme_engine_release (ctx->engine);
649       ctx->engine = NULL;
650     }
651   return err;
652 }
653
654
655 /**
656  * gpgme_op_keylist_next:
657  * @c: Context
658  * @r_key: Returned key object
659  * 
660  * Return the next key from the key listing started with
661  * gpgme_op_keylist_start().  The caller must free the key using
662  * gpgme_key_release().  If the last key has already been returned the
663  * last time the function was called, %GPGME_EOF is returned and the
664  * operation is finished.
665  * 
666  * Return value: 0 on success, %GPGME_EOF or another error code.
667  **/
668 GpgmeError
669 gpgme_op_keylist_next (GpgmeCtx ctx, GpgmeKey *r_key)
670 {
671   struct key_queue_item_s *queue_item;
672
673   if (!r_key)
674     return mk_error (Invalid_Value);
675   *r_key = NULL;
676   if (!ctx)
677     return mk_error (Invalid_Value);
678   if (!ctx->pending)
679     return mk_error (No_Request);
680   if (ctx->error)
681     return ctx->error;
682
683   if (!ctx->key_queue)
684     {
685       _gpgme_wait_on_condition (ctx, 1, &ctx->key_cond);
686       if (ctx->error)
687         return ctx->error;
688       if (!ctx->key_cond)
689         {
690           ctx->pending = 0;
691           return mk_error (EOF);
692         }
693       ctx->key_cond = 0; 
694       assert (ctx->key_queue);
695     }
696   queue_item = ctx->key_queue;
697   ctx->key_queue = queue_item->next;
698   if (!ctx->key_queue)
699     ctx->key_cond = 0;
700   
701   *r_key = queue_item->key;
702   xfree (queue_item);
703   return 0;
704 }
705
706
707 /**
708  * gpgme_op_keylist_end:
709  * @c: Context
710  * 
711  * Ends the keylist operation and allows to use the context for some
712  * other operation next.
713  **/
714 GpgmeError
715 gpgme_op_keylist_end (GpgmeCtx ctx)
716 {
717   if (!ctx)
718     return mk_error (Invalid_Value);
719   if (!ctx->pending)
720     return mk_error (No_Request);
721   if (ctx->error)
722     return ctx->error;
723
724   ctx->pending = 0;
725   return 0;
726 }