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