2005-10-01 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / keylist.c
1 /* keylist.c - Listing keys.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include "gpgme.h"
34 #include "util.h"
35 #include "context.h"
36 #include "ops.h"
37 #include "debug.h"
38
39 \f
40 struct key_queue_item_s
41 {
42   struct key_queue_item_s *next;
43   gpgme_key_t key;
44 };
45
46 typedef struct
47 {
48   struct _gpgme_op_keylist_result result;
49
50   gpgme_key_t tmp_key;
51   /* This points to the last uid in tmp_key.  */
52   gpgme_user_id_t tmp_uid;
53   /* Something new is available.  */
54   int key_cond;
55   struct key_queue_item_s *key_queue;
56 } *op_data_t;
57
58
59 static void
60 release_op_data (void *hook)
61 {
62   op_data_t opd = (op_data_t) hook;
63   struct key_queue_item_s *key = opd->key_queue;
64
65   if (opd->tmp_key)
66     gpgme_key_unref (opd->tmp_key);
67   /* opd->tmp_uid is actually part of opd->tmp_key, so we do not need
68      to release it here.  */
69
70   while (key)
71     {
72       struct key_queue_item_s *next = key->next;
73
74       gpgme_key_unref (key->key);
75       key = next;
76     }
77 }
78
79
80 gpgme_keylist_result_t
81 gpgme_op_keylist_result (gpgme_ctx_t ctx)
82 {
83   void *hook;
84   op_data_t opd;
85   gpgme_error_t err;
86
87   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
88   opd = hook;
89   if (err || !opd)
90     return NULL;
91
92   return &opd->result;
93 }
94
95 \f
96 static gpgme_error_t
97 keylist_status_handler (void *priv, gpgme_status_code_t code, char *args)
98 {
99   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
100   gpgme_error_t err;
101   void *hook;
102   op_data_t opd;
103
104   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
105   opd = hook;
106   if (err)
107     return err;
108
109   switch (code)
110     {
111     case GPGME_STATUS_TRUNCATED:
112       opd->result.truncated = 1;
113       break;
114
115     default:
116       break;
117     }
118   return 0;
119 }
120
121 \f
122 static void
123 set_subkey_trust_info (gpgme_subkey_t subkey, const char *src)
124 {
125   while (*src && !isdigit (*src))
126     {
127       switch (*src)
128         {
129         case 'e':
130           subkey->expired = 1;
131           break;
132
133         case 'r':
134           subkey->revoked = 1;
135           break;
136
137         case 'd':
138           /* Note that gpg 1.3 won't print that anymore but only uses
139              the capabilities field. */
140           subkey->disabled = 1;
141           break;
142
143         case 'i':
144           subkey->invalid = 1;
145           break;
146         }
147       src++;
148     }
149 }
150
151
152 static void
153 set_mainkey_trust_info (gpgme_key_t key, const char *src)
154 {
155   /* First set the trust info of the main key (the first subkey).  */
156   set_subkey_trust_info (key->subkeys, src);
157
158   /* Now set the summarized trust info.  */
159   while (*src && !isdigit (*src))
160     {
161       switch (*src)
162         {
163         case 'e':
164           key->expired = 1;
165           break;
166
167         case 'r':
168           key->revoked = 1;
169           break;
170
171         case 'd':
172           /* Note that gpg 1.3 won't print that anymore but only uses
173              the capabilities field. */
174           key->disabled = 1;
175           break;
176
177         case 'i':
178           key->invalid = 1;
179           break;
180         }
181       src++;
182     }
183 }
184
185
186 static void
187 set_userid_flags (gpgme_key_t key, const char *src)
188 {
189   gpgme_user_id_t uid = key->_last_uid;
190
191   assert (uid);
192   /* Look at letters and stop at the first digit.  */
193   while (*src && !isdigit (*src))
194     {
195       switch (*src)
196         {
197         case 'r':
198           uid->revoked = 1;
199           break;
200           
201         case 'i':
202           uid->invalid = 1;
203           break;
204
205         case 'n':
206           uid->validity = GPGME_VALIDITY_NEVER;
207           break;
208
209         case 'm':
210           uid->validity = GPGME_VALIDITY_MARGINAL;
211           break;
212
213         case 'f':
214           uid->validity = GPGME_VALIDITY_FULL;
215           break;
216
217         case 'u':
218           uid->validity = GPGME_VALIDITY_ULTIMATE;
219           break;
220         }
221       src++;
222     }
223 }
224
225
226 static void
227 set_subkey_capability (gpgme_subkey_t subkey, const char *src)
228 {
229   while (*src)
230     {
231       switch (*src)
232         {
233         case 'e':
234           subkey->can_encrypt = 1;
235           break;
236
237         case 's':
238           subkey->can_sign = 1;
239           break;
240
241         case 'c':
242           subkey->can_certify = 1;
243           break;
244
245         case 'a':
246           subkey->can_authenticate = 1;
247           break;
248
249         case 'q':
250           subkey->is_qualified = 1;
251           break;
252
253         case 'd':
254           subkey->disabled = 1;
255           break;
256         }
257       src++;
258     }
259 }
260
261
262 static void
263 set_mainkey_capability (gpgme_key_t key, const char *src)
264 {
265   /* First set the capabilities of the main key (the first subkey).  */
266   set_subkey_capability (key->subkeys, src);
267
268   while (*src)
269     {
270       switch (*src)
271         {
272         case 'd':
273         case 'D':
274           /* Note, that this flag is also set using the key validity
275              field for backward compatibility with gpg 1.2.  We use d
276              and D, so that a future gpg version will be able to
277              disable certain subkeys. Currently it is expected that
278              gpg sets this for the primary key. */
279           key->disabled = 1;
280           break;
281
282         case 'e':
283         case 'E':
284           key->can_encrypt = 1;
285           break;
286
287         case 's':
288         case 'S':
289           key->can_sign = 1;
290           break;
291
292         case 'c':
293         case 'C':
294           key->can_certify = 1;
295           break;
296
297         case 'a':
298         case 'A':
299           key->can_authenticate = 1;
300           break;
301
302         case 'q':
303         case 'Q':
304           key->is_qualified = 1;
305           break;
306         }
307       src++;
308     }
309 }
310
311
312 static void
313 set_ownertrust (gpgme_key_t key, const char *src)
314 {
315   /* Look at letters and stop at the first digit.  */
316   while (*src && !isdigit (*src))
317     {
318       switch (*src)
319         {
320         case 'n':
321           key->owner_trust = GPGME_VALIDITY_NEVER;
322           break;
323
324         case 'm':
325           key->owner_trust = GPGME_VALIDITY_MARGINAL;
326           break;
327
328         case 'f':
329           key->owner_trust = GPGME_VALIDITY_FULL;
330           break;
331
332         case 'u':
333           key->owner_trust = GPGME_VALIDITY_ULTIMATE;
334           break;
335
336         default:
337           key->owner_trust = GPGME_VALIDITY_UNKNOWN;
338           break;
339         }
340       src++;
341     }
342 }
343
344
345 /* We have read an entire key into tmp_key and should now finish it.
346    It is assumed that this releases tmp_key.  */
347 static void
348 finish_key (gpgme_ctx_t ctx, op_data_t opd)
349 {
350   gpgme_key_t key = opd->tmp_key;
351
352   opd->tmp_key = NULL;
353   opd->tmp_uid = NULL;
354
355   if (key)
356     _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
357 }
358
359
360 /* Note: We are allowed to modify LINE.  */
361 static gpgme_error_t
362 keylist_colon_handler (void *priv, char *line)
363 {
364   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
365   enum
366     {
367       RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
368       RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV
369     }
370   rectype = RT_NONE;
371 #define NR_FIELDS 13
372   char *field[NR_FIELDS];
373   int fields = 0;
374   void *hook;
375   op_data_t opd;
376   gpgme_error_t err;
377   gpgme_key_t key;
378   gpgme_subkey_t subkey = NULL;
379   gpgme_key_sig_t keysig = NULL;
380
381   DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n",
382           ctx, key, line ? line : "(null)");
383
384   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
385   opd = hook;
386   if (err)
387     return err;
388
389   key = opd->tmp_key;
390
391   if (!line)
392     {
393       /* End Of File.  */
394       finish_key (ctx, opd);
395       return 0;
396     }
397
398   while (line && fields < NR_FIELDS)
399     {
400       field[fields++] = line;
401       line = strchr (line, ':');
402       if (line)
403         *(line++) = '\0';
404     }
405
406   if (!strcmp (field[0], "sig"))
407     rectype = RT_SIG;
408   else if (!strcmp (field[0], "rev"))
409     rectype = RT_REV;
410   else if (!strcmp (field[0], "pub"))
411     rectype = RT_PUB;
412   else if (!strcmp (field[0], "sec"))
413     rectype = RT_SEC;
414   else if (!strcmp (field[0], "crt"))
415     rectype = RT_CRT;
416   else if (!strcmp (field[0], "crs"))
417     rectype = RT_CRS;
418   else if (!strcmp (field[0], "fpr") && key) 
419     rectype = RT_FPR;
420   else if (!strcmp (field[0], "uid") && key)
421     rectype = RT_UID;
422   else if (!strcmp (field[0], "sub") && key)
423     rectype = RT_SUB; 
424   else if (!strcmp (field[0], "ssb") && key)
425     rectype = RT_SSB;
426   else 
427     rectype = RT_NONE;
428
429   /* Only look at signatures immediately following a user ID.  For
430      this, clear the user ID pointer when encountering anything but a
431      signature.  */
432   if (rectype != RT_SIG && rectype != RT_REV)
433     opd->tmp_uid = NULL;
434
435   switch (rectype)
436     {
437     case RT_PUB:
438     case RT_SEC:
439     case RT_CRT:
440     case RT_CRS:
441       /* Start a new keyblock.  */
442       err = _gpgme_key_new (&key);
443       if (err)
444         return err;
445       key->keylist_mode = ctx->keylist_mode;
446       err = _gpgme_key_add_subkey (key, &subkey);
447       if (err)
448         {
449           gpgme_key_unref (key);
450           return err;
451         }
452
453       if (rectype == RT_SEC || rectype == RT_CRS)
454         key->secret = 1;
455       if (rectype == RT_CRT || rectype == RT_CRS)
456         key->protocol = GPGME_PROTOCOL_CMS;
457       finish_key (ctx, opd);
458       opd->tmp_key = key;
459
460       /* Field 2 has the trust info.  */
461       if (fields >= 2)
462         set_mainkey_trust_info (key, field[1]);
463
464       /* Field 3 has the key length.  */
465       if (fields >= 3)
466         {
467           int i = atoi (field[2]);
468           /* Ignore invalid values.  */
469           if (i > 1)
470             subkey->length = i; 
471         }
472
473       /* Field 4 has the public key algorithm.  */
474       if (fields >= 4)
475         {
476           int i = atoi (field[3]);
477           if (i >= 1 && i < 128)
478             subkey->pubkey_algo = i;
479         }
480
481       /* Field 5 has the long keyid.  */
482       if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
483         strcpy (subkey->_keyid, field[4]);
484
485       /* Field 6 has the timestamp (seconds).  */
486       if (fields >= 6)
487         subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
488
489       /* Field 7 has the expiration time (seconds).  */
490       if (fields >= 7)
491         subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
492
493       /* Field 8 has the X.509 serial number.  */
494       if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS))
495         {
496           key->issuer_serial = strdup (field[7]);
497           if (!key->issuer_serial)
498             return gpg_error_from_errno (errno);
499         }
500           
501       /* Field 9 has the ownertrust.  */
502       if (fields >= 9)
503         set_ownertrust (key, field[8]);
504
505       /* Field 10 is not used for gpg due to --fixed-list-mode option
506          but GPGSM stores the issuer name.  */
507       if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS))
508         if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0))
509           return gpg_error (GPG_ERR_ENOMEM);    /* FIXME */
510
511       /* Field 11 has the signature class.  */
512
513       /* Field 12 has the capabilities.  */
514       if (fields >= 12)
515         set_mainkey_capability (key, field[11]);
516       break;
517
518     case RT_SUB:
519     case RT_SSB:
520       /* Start a new subkey.  */
521       err = _gpgme_key_add_subkey (key, &subkey);
522       if (err)
523         return err;
524
525       if (rectype == RT_SSB)
526         subkey->secret = 1;
527
528       /* Field 2 has the trust info.  */
529       if (fields >= 2)
530         set_subkey_trust_info (subkey, field[1]);
531
532       /* Field 3 has the key length.  */
533       if (fields >= 3)
534         {
535           int i = atoi (field[2]);
536           /* Ignore invalid values.  */
537           if (i > 1)
538             subkey->length = i;
539         }
540
541       /* Field 4 has the public key algorithm.  */
542       if (fields >= 4)
543         {
544           int i = atoi (field[3]);
545           if (i >= 1 && i < 128)
546             subkey->pubkey_algo = i;
547         }
548
549       /* Field 5 has the long keyid.  */
550       if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
551         strcpy (subkey->_keyid, field[4]);
552
553       /* Field 6 has the timestamp (seconds).  */
554       if (fields >= 6)
555         subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
556
557       /* Field 7 has the expiration time (seconds).  */
558       if (fields >= 7)
559         subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
560
561       /* Field 8 is reserved (LID).  */
562       /* Field 9 has the ownertrust.  */
563       /* Field 10, the user ID, is n/a for a subkey.  */
564       
565       /* Field 11 has the signature class.  */
566
567       /* Field 12 has the capabilities.  */
568       if (fields >= 12)
569         set_subkey_capability (subkey, field[11]);
570       break;
571
572     case RT_UID:
573       /* Field 2 has the trust info, and field 10 has the user ID.  */
574       if (fields >= 10)
575         {
576           if (_gpgme_key_append_name (key, field[9]))
577             return gpg_error_from_errno (GPG_ERR_ENOMEM);       /* FIXME */
578           else
579             {
580               if (field[1])
581                 set_userid_flags (key, field[1]);
582               opd->tmp_uid = key->_last_uid;
583             }
584         }
585       break;
586
587     case RT_FPR:
588       /* Field 10 has the fingerprint (take only the first one).  */
589       if (fields >= 10 && !key->subkeys->fpr && field[9] && *field[9])
590         {
591           key->subkeys->fpr = strdup (field[9]);
592           if (!key->subkeys->fpr)
593             return gpg_error_from_errno (errno);
594         }
595
596       /* Field 13 has the gpgsm chain ID (take only the first one).  */
597       if (fields >= 13 && !key->chain_id && *field[12])
598         {
599           key->chain_id = strdup (field[12]);
600           if (!key->chain_id)
601             return gpg_error_from_errno (errno);
602         }
603       break;
604
605     case RT_SIG:
606     case RT_REV:
607       if (!opd->tmp_uid)
608         return 0;
609
610       /* Start a new (revoked) signature.  */
611       assert (opd->tmp_uid == key->_last_uid);
612       keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL);
613       if (!keysig)
614         return gpg_error (GPG_ERR_ENOMEM);      /* FIXME */
615
616       /* Field 2 has the calculated trust ('!', '-', '?', '%').  */
617       if (fields >= 2)
618         switch (field[1][0])
619           {
620           case '!':
621             keysig->status = gpg_error (GPG_ERR_NO_ERROR);
622             break;
623
624           case '-':
625             keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
626             break;
627
628           case '?':
629             keysig->status = gpg_error (GPG_ERR_NO_PUBKEY);
630             break;
631
632           case '%':
633             keysig->status = gpg_error (GPG_ERR_GENERAL);
634             break;
635
636           default:
637             keysig->status = gpg_error (GPG_ERR_NO_ERROR);
638             break;
639           }
640
641       /* Field 4 has the public key algorithm.  */
642       if (fields >= 4)
643         {
644           int i = atoi (field[3]);
645           if (i >= 1 && i < 128)
646             keysig->pubkey_algo = i;
647         }
648       
649       /* Field 5 has the long keyid.  */
650       if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1)
651         strcpy (keysig->_keyid, field[4]);
652       
653       /* Field 6 has the timestamp (seconds).  */
654       if (fields >= 6)
655         keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL);
656
657       /* Field 7 has the expiration time (seconds).  */
658       if (fields >= 7)
659         keysig->expires = _gpgme_parse_timestamp (field[6], NULL);
660
661       /* Field 11 has the signature class (eg, 0x30 means revoked).  */
662       if (fields >= 11)
663         if (field[10][0] && field[10][1])
664           {
665             int sig_class = _gpgme_hextobyte (field[10]);
666             if (sig_class >= 0)
667               {
668                 keysig->sig_class = sig_class;
669                 keysig->class = keysig->sig_class;
670                 if (sig_class == 0x30)
671                   keysig->revoked = 1;
672               }
673             if (field[10][2] == 'x')
674               keysig->exportable = 1;
675           }
676       break;
677
678     case RT_NONE:
679       /* Unknown record.  */
680       break;
681     }
682   return 0;
683 }
684
685
686 void
687 _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data)
688 {
689   gpgme_error_t err;
690   gpgme_ctx_t ctx = (gpgme_ctx_t) data;
691   gpgme_key_t key = (gpgme_key_t) type_data;
692   void *hook;
693   op_data_t opd;
694   struct key_queue_item_s *q, *q2;
695
696   assert (type == GPGME_EVENT_NEXT_KEY);
697
698   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
699   opd = hook;
700   if (err)
701     return;
702
703   q = malloc (sizeof *q);
704   if (!q)
705     {
706       gpgme_key_unref (key);
707       /* FIXME       return GPGME_Out_Of_Core; */
708       return;
709     }
710   q->key = key;
711   q->next = NULL;
712   /* FIXME: Use a tail pointer?  */
713   if (!(q2 = opd->key_queue))
714     opd->key_queue = q;
715   else
716     {
717       for (; q2->next; q2 = q2->next)
718         ;
719       q2->next = q;
720     }
721   opd->key_cond = 1;
722 }
723
724
725 /* Start a keylist operation within CTX, searching for keys which
726    match PATTERN.  If SECRET_ONLY is true, only secret keys are
727    returned.  */
728 gpgme_error_t
729 gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
730 {
731   gpgme_error_t err;
732   void *hook;
733   op_data_t opd;
734
735   err = _gpgme_op_reset (ctx, 2);
736   if (err)
737     return err;
738
739   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
740                                sizeof (*opd), release_op_data);
741   opd = hook;
742   if (err)
743     return err;
744
745   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
746
747   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
748                                               keylist_colon_handler, ctx);
749   if (err)
750     return err;
751
752   return _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
753                                    ctx->keylist_mode);
754 }
755
756
757 /* Start a keylist operation within CTX, searching for keys which
758    match PATTERN.  If SECRET_ONLY is true, only secret keys are
759    returned.  */
760 gpgme_error_t
761 gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
762                             int secret_only, int reserved)
763 {
764   gpgme_error_t err;
765   void *hook;
766   op_data_t opd;
767
768   err = _gpgme_op_reset (ctx, 2);
769   if (err)
770     return err;
771
772   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
773                                sizeof (*opd), release_op_data);
774   opd = hook;
775   if (err)
776     return err;
777
778   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
779   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
780                                               keylist_colon_handler, ctx);
781   if (err)
782     return err;
783
784   return _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
785                                        reserved, ctx->keylist_mode);
786 }
787
788
789 /* Return the next key from the keylist in R_KEY.  */
790 gpgme_error_t
791 gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
792 {
793   gpgme_error_t err;
794   struct key_queue_item_s *queue_item;
795   void *hook;
796   op_data_t opd;
797
798   if (!ctx || !r_key)
799     return gpg_error (GPG_ERR_INV_VALUE);
800   *r_key = NULL;
801   if (!ctx)
802     return gpg_error (GPG_ERR_INV_VALUE);
803
804   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
805   opd = hook;
806   if (err)
807     return err;
808
809   if (!opd->key_queue)
810     {
811       err = _gpgme_wait_on_condition (ctx, &opd->key_cond);
812       if (err)
813         return err;
814
815       if (!opd->key_cond)
816         return gpg_error (GPG_ERR_EOF);
817
818       opd->key_cond = 0; 
819       assert (opd->key_queue);
820     }
821   queue_item = opd->key_queue;
822   opd->key_queue = queue_item->next;
823   if (!opd->key_queue)
824     opd->key_cond = 0;
825   
826   *r_key = queue_item->key;
827   free (queue_item);
828   return 0;
829 }
830
831
832 /* Terminate a pending keylist operation within CTX.  */
833 gpgme_error_t
834 gpgme_op_keylist_end (gpgme_ctx_t ctx)
835 {
836   if (!ctx)
837     return gpg_error (GPG_ERR_INV_VALUE);
838
839   return 0;
840 }
841
842 \f
843 /* Get the key with the fingerprint FPR from the crypto backend.  If
844    SECRET is true, get the secret key.  */
845 gpgme_error_t
846 gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
847                int secret)
848 {
849   gpgme_ctx_t listctx;
850   gpgme_error_t err;
851   gpgme_key_t key;
852
853   if (!ctx || !r_key || !fpr)
854     return gpg_error (GPG_ERR_INV_VALUE);
855   
856   if (strlen (fpr) < 8) /* We have at least a key ID.  */
857     return gpg_error (GPG_ERR_INV_VALUE);
858
859   /* FIXME: We use our own context because we have to avoid the user's
860      I/O callback handlers.  */
861   err = gpgme_new (&listctx);
862   if (err)
863     return err;
864   gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
865   gpgme_set_keylist_mode (listctx, ctx->keylist_mode);
866   err = gpgme_op_keylist_start (listctx, fpr, secret);
867   if (!err)
868     err = gpgme_op_keylist_next (listctx, r_key);
869   if (!err)
870     {
871       err = gpgme_op_keylist_next (listctx, &key);
872       if (gpgme_err_code (err) == GPG_ERR_EOF)
873         err = gpg_error (GPG_ERR_NO_ERROR);
874       else
875         {
876           if (!err)
877             {
878               gpgme_key_unref (key);
879               err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
880             }
881           gpgme_key_unref (*r_key);
882         }
883     }
884   gpgme_release (listctx);
885   return err;
886 }