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