doc/
[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 'd':
249         case 'D':
250           /* Note, that this flag is also set using the key validity
251              field for backward compatibility with gpg 1.2.  We use d
252              and D, so that a future gpg version will be able to
253              disable certain subkeys. Currently it is expected that
254              gpg sets this for the primary key. */
255           key->subkeys->disabled = 1;
256           break;
257
258         case 'E':
259           key->can_encrypt = 1;
260           break;
261
262         case 'S':
263           key->can_sign = 1;
264           break;
265
266         case 'C':
267           key->can_certify = 1;
268           break;
269         }
270       src++;
271     }
272 }
273
274
275 static void
276 set_subkey_capability (gpgme_subkey_t subkey, const char *src)
277 {
278   while (*src)
279     {
280       switch (*src)
281         {
282         case 'e':
283           subkey->can_encrypt = 1;
284           break;
285
286         case 's':
287           subkey->can_sign = 1;
288           break;
289
290         case 'c':
291           subkey->can_certify = 1;
292           break;
293         }
294       src++;
295     }
296 }
297
298 static void
299 set_ownertrust (gpgme_key_t key, const char *src)
300 {
301   /* Look at letters and stop at the first digit.  */
302   while (*src && !isdigit (*src))
303     {
304       switch (*src)
305         {
306         case 'n':
307           key->owner_trust = GPGME_VALIDITY_NEVER;
308           break;
309
310         case 'm':
311           key->owner_trust = GPGME_VALIDITY_MARGINAL;
312           break;
313
314         case 'f':
315           key->owner_trust = GPGME_VALIDITY_FULL;
316           break;
317
318         case 'u':
319           key->owner_trust = GPGME_VALIDITY_ULTIMATE;
320           break;
321
322         default:
323           key->owner_trust = GPGME_VALIDITY_UNKNOWN;
324           break;
325         }
326       src++;
327     }
328 }
329
330
331 /* We have read an entire key into tmp_key and should now finish it.
332    It is assumed that this releases tmp_key.  */
333 static void
334 finish_key (gpgme_ctx_t ctx, op_data_t opd)
335 {
336   gpgme_key_t key = opd->tmp_key;
337
338   opd->tmp_key = NULL;
339   opd->tmp_uid = NULL;
340
341   if (key)
342     _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
343 }
344
345
346 /* Note: We are allowed to modify LINE.  */
347 static gpgme_error_t
348 keylist_colon_handler (void *priv, char *line)
349 {
350   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
351   enum
352     {
353       RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
354       RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV
355     }
356   rectype = RT_NONE;
357 #define NR_FIELDS 13
358   char *field[NR_FIELDS];
359   int fields = 0;
360   void *hook;
361   op_data_t opd;
362   gpgme_error_t err;
363   gpgme_key_t key;
364   gpgme_subkey_t subkey = NULL;
365   gpgme_key_sig_t keysig = NULL;
366
367   DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n",
368           ctx, key, line ? line : "(null)");
369
370   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
371   opd = hook;
372   if (err)
373     return err;
374
375   key = opd->tmp_key;
376
377   if (!line)
378     {
379       /* End Of File.  */
380       finish_key (ctx, opd);
381       return 0;
382     }
383
384   while (line && fields < NR_FIELDS)
385     {
386       field[fields++] = line;
387       line = strchr (line, ':');
388       if (line)
389         *(line++) = '\0';
390     }
391
392   if (!strcmp (field[0], "sig"))
393     rectype = RT_SIG;
394   else if (!strcmp (field[0], "rev"))
395     rectype = RT_REV;
396   else if (!strcmp (field[0], "pub"))
397     rectype = RT_PUB;
398   else if (!strcmp (field[0], "sec"))
399     rectype = RT_SEC;
400   else if (!strcmp (field[0], "crt"))
401     rectype = RT_CRT;
402   else if (!strcmp (field[0], "crs"))
403     rectype = RT_CRS;
404   else if (!strcmp (field[0], "fpr") && key) 
405     rectype = RT_FPR;
406   else if (!strcmp (field[0], "uid") && key)
407     rectype = RT_UID;
408   else if (!strcmp (field[0], "sub") && key)
409     rectype = RT_SUB; 
410   else if (!strcmp (field[0], "ssb") && key)
411     rectype = RT_SSB;
412   else 
413     rectype = RT_NONE;
414
415   /* Only look at signatures immediately following a user ID.  For
416      this, clear the user ID pointer when encountering anything but a
417      signature.  */
418   if (rectype != RT_SIG && rectype != RT_REV)
419     opd->tmp_uid = NULL;
420
421   switch (rectype)
422     {
423     case RT_PUB:
424     case RT_SEC:
425     case RT_CRT:
426     case RT_CRS:
427       /* Start a new keyblock.  */
428       err = _gpgme_key_new (&key);
429       if (err)
430         return err;
431       err = _gpgme_key_add_subkey (key, &subkey);
432       if (err)
433         {
434           gpgme_key_unref (key);
435           return err;
436         }
437
438       if (rectype == RT_SEC || rectype == RT_CRS)
439         key->secret = 1;
440       if (rectype == RT_CRT || rectype == RT_CRS)
441         key->protocol = GPGME_PROTOCOL_CMS;
442       finish_key (ctx, opd);
443       opd->tmp_key = key;
444
445       /* Field 2 has the trust info.  */
446       if (fields >= 2)
447         set_mainkey_trust_info (key, field[1]);
448
449       /* Field 3 has the key length.  */
450       if (fields >= 3)
451         {
452           int i = atoi (field[2]);
453           /* Ignore invalid values.  */
454           if (i > 1)
455             subkey->length = i; 
456         }
457
458       /* Field 4 has the public key algorithm.  */
459       if (fields >= 4)
460         {
461           int i = atoi (field[3]);
462           if (i >= 1 && i < 128)
463             subkey->pubkey_algo = i;
464         }
465
466       /* Field 5 has the long keyid.  */
467       if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
468         strcpy (subkey->_keyid, field[4]);
469
470       /* Field 6 has the timestamp (seconds).  */
471       if (fields >= 6)
472         subkey->timestamp = parse_timestamp (field[5]);
473
474       /* Field 7 has the expiration time (seconds).  */
475       if (fields >= 7)
476         subkey->expires = parse_timestamp (field[6]);
477
478       /* Field 8 has the X.509 serial number.  */
479       if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS))
480         {
481           key->issuer_serial = strdup (field[7]);
482           if (!key->issuer_serial)
483             return gpg_error_from_errno (errno);
484         }
485           
486       /* Field 9 has the ownertrust.  */
487       if (fields >= 9)
488         set_ownertrust (key, field[8]);
489
490       /* Field 10 is not used for gpg due to --fixed-list-mode option
491          but GPGSM stores the issuer name.  */
492       if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS))
493         if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0))
494           return gpg_error (GPG_ERR_ENOMEM);    /* FIXME */
495
496       /* Field 11 has the signature class.  */
497
498       /* Field 12 has the capabilities.  */
499       if (fields >= 12)
500         set_mainkey_capability (key, field[11]);
501       break;
502
503     case RT_SUB:
504     case RT_SSB:
505       /* Start a new subkey.  */
506       err = _gpgme_key_add_subkey (key, &subkey);
507       if (err)
508         return err;
509
510       if (rectype == RT_SSB)
511         subkey->secret = 1;
512
513       /* Field 2 has the trust info.  */
514       if (fields >= 2)
515         set_subkey_trust_info (subkey, field[1]);
516
517       /* Field 3 has the key length.  */
518       if (fields >= 3)
519         {
520           int i = atoi (field[2]);
521           /* Ignore invalid values.  */
522           if (i > 1)
523             subkey->length = i;
524         }
525
526       /* Field 4 has the public key algorithm.  */
527       if (fields >= 4)
528         {
529           int i = atoi (field[3]);
530           if (i >= 1 && i < 128)
531             subkey->pubkey_algo = i;
532         }
533
534       /* Field 5 has the long keyid.  */
535       if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
536         strcpy (subkey->_keyid, field[4]);
537
538       /* Field 6 has the timestamp (seconds).  */
539       if (fields >= 6)
540         subkey->timestamp = parse_timestamp (field[5]);
541
542       /* Field 7 has the expiration time (seconds).  */
543       if (fields >= 7)
544         subkey->expires = parse_timestamp (field[6]);
545
546       /* Field 8 is reserved (LID).  */
547       /* Field 9 has the ownertrust.  */
548       /* Field 10, the user ID, is n/a for a subkey.  */
549       
550       /* Field 11 has the signature class.  */
551
552       /* Field 12 has the capabilities.  */
553       if (fields >= 12)
554         set_subkey_capability (subkey, field[11]);
555       break;
556
557     case RT_UID:
558       /* Field 2 has the trust info, and field 10 has the user ID.  */
559       if (fields >= 10)
560         {
561           if (_gpgme_key_append_name (key, field[9]))
562             return gpg_error_from_errno (GPG_ERR_ENOMEM);       /* FIXME */
563           else
564             {
565               if (field[1])
566                 set_userid_flags (key, field[1]);
567               opd->tmp_uid = key->_last_uid;
568             }
569         }
570       break;
571
572     case RT_FPR:
573       /* Field 10 has the fingerprint (take only the first one).  */
574       if (fields >= 10 && !key->subkeys->fpr && field[9] && *field[9])
575         {
576           key->subkeys->fpr = strdup (field[9]);
577           if (!key->subkeys->fpr)
578             return gpg_error_from_errno (errno);
579         }
580
581       /* Field 13 has the gpgsm chain ID (take only the first one).  */
582       if (fields >= 13 && !key->chain_id && *field[12])
583         {
584           key->chain_id = strdup (field[12]);
585           if (!key->chain_id)
586             return gpg_error_from_errno (errno);
587         }
588       break;
589
590     case RT_SIG:
591     case RT_REV:
592       if (!opd->tmp_uid)
593         return 0;
594
595       /* Start a new (revoked) signature.  */
596       assert (opd->tmp_uid == key->_last_uid);
597       keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL);
598       if (!keysig)
599         return gpg_error (GPG_ERR_ENOMEM);      /* FIXME */
600
601       /* Field 2 has the calculated trust ('!', '-', '?', '%').  */
602       if (fields >= 2)
603         switch (field[1][0])
604           {
605           case '!':
606             keysig->status = gpg_error (GPG_ERR_NO_ERROR);
607             break;
608
609           case '-':
610             keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
611             break;
612
613           case '?':
614             keysig->status = gpg_error (GPG_ERR_NO_PUBKEY);
615             break;
616
617           case '%':
618             keysig->status = gpg_error (GPG_ERR_GENERAL);
619             break;
620
621           default:
622             keysig->status = gpg_error (GPG_ERR_NO_ERROR);
623             break;
624           }
625
626       /* Field 4 has the public key algorithm.  */
627       if (fields >= 4)
628         {
629           int i = atoi (field[3]);
630           if (i >= 1 && i < 128)
631             keysig->pubkey_algo = i;
632         }
633       
634       /* Field 5 has the long keyid.  */
635       if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1)
636         strcpy (keysig->_keyid, field[4]);
637       
638       /* Field 6 has the timestamp (seconds).  */
639       if (fields >= 6)
640         keysig->timestamp = parse_timestamp (field[5]);
641
642       /* Field 7 has the expiration time (seconds).  */
643       if (fields >= 7)
644         keysig->expires = parse_timestamp (field[6]);
645
646       /* Field 11 has the signature class (eg, 0x30 means revoked).  */
647       if (fields >= 11)
648         if (field[10][0] && field[10][1])
649           {
650             int class = _gpgme_hextobyte (field[10]);
651             if (class >= 0)
652               {
653                 keysig->class = class;
654                 if (class == 0x30)
655                   keysig->revoked = 1;
656               }
657             if (field[10][2] == 'x')
658               keysig->exportable = 1;
659           }
660       break;
661
662     case RT_NONE:
663       /* Unknown record.  */
664       break;
665     }
666   return 0;
667 }
668
669
670 void
671 _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data)
672 {
673   gpgme_error_t err;
674   gpgme_ctx_t ctx = (gpgme_ctx_t) data;
675   gpgme_key_t key = (gpgme_key_t) type_data;
676   void *hook;
677   op_data_t opd;
678   struct key_queue_item_s *q, *q2;
679
680   assert (type == GPGME_EVENT_NEXT_KEY);
681
682   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
683   opd = hook;
684   if (err)
685     return;
686
687   q = malloc (sizeof *q);
688   if (!q)
689     {
690       gpgme_key_unref (key);
691       /* FIXME       return GPGME_Out_Of_Core; */
692       return;
693     }
694   q->key = key;
695   q->next = NULL;
696   /* FIXME: Use a tail pointer?  */
697   if (!(q2 = opd->key_queue))
698     opd->key_queue = q;
699   else
700     {
701       for (; q2->next; q2 = q2->next)
702         ;
703       q2->next = q;
704     }
705   opd->key_cond = 1;
706 }
707
708
709 /* Start a keylist operation within CTX, searching for keys which
710    match PATTERN.  If SECRET_ONLY is true, only secret keys are
711    returned.  */
712 gpgme_error_t
713 gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
714 {
715   gpgme_error_t err;
716   void *hook;
717   op_data_t opd;
718
719   err = _gpgme_op_reset (ctx, 2);
720   if (err)
721     return err;
722
723   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
724                                sizeof (*opd), release_op_data);
725   opd = hook;
726   if (err)
727     return err;
728
729   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
730
731   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
732                                               keylist_colon_handler, ctx);
733   if (err)
734     return err;
735
736   return _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
737                                    ctx->keylist_mode);
738 }
739
740
741 /* Start a keylist operation within CTX, searching for keys which
742    match PATTERN.  If SECRET_ONLY is true, only secret keys are
743    returned.  */
744 gpgme_error_t
745 gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
746                             int secret_only, int reserved)
747 {
748   gpgme_error_t err;
749   void *hook;
750   op_data_t opd;
751
752   err = _gpgme_op_reset (ctx, 2);
753   if (err)
754     return err;
755
756   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
757                                sizeof (*opd), release_op_data);
758   opd = hook;
759   if (err)
760     return err;
761
762   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
763   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
764                                               keylist_colon_handler, ctx);
765   if (err)
766     return err;
767
768   return _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
769                                        reserved, ctx->keylist_mode);
770 }
771
772
773 /* Return the next key from the keylist in R_KEY.  */
774 gpgme_error_t
775 gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
776 {
777   gpgme_error_t err;
778   struct key_queue_item_s *queue_item;
779   void *hook;
780   op_data_t opd;
781
782   if (!ctx || !r_key)
783     return gpg_error (GPG_ERR_INV_VALUE);
784   *r_key = NULL;
785   if (!ctx)
786     return gpg_error (GPG_ERR_INV_VALUE);
787
788   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
789   opd = hook;
790   if (err)
791     return err;
792
793   if (!opd->key_queue)
794     {
795       err = _gpgme_wait_on_condition (ctx, &opd->key_cond);
796       if (err)
797         return err;
798
799       if (!opd->key_cond)
800         return gpg_error (GPG_ERR_EOF);
801
802       opd->key_cond = 0; 
803       assert (opd->key_queue);
804     }
805   queue_item = opd->key_queue;
806   opd->key_queue = queue_item->next;
807   if (!opd->key_queue)
808     opd->key_cond = 0;
809   
810   *r_key = queue_item->key;
811   free (queue_item);
812   return 0;
813 }
814
815
816 /* Terminate a pending keylist operation within CTX.  */
817 gpgme_error_t
818 gpgme_op_keylist_end (gpgme_ctx_t ctx)
819 {
820   if (!ctx)
821     return gpg_error (GPG_ERR_INV_VALUE);
822
823   return 0;
824 }
825
826 \f
827 /* Get the key with the fingerprint FPR from the crypto backend.  If
828    SECRET is true, get the secret key.  */
829 gpgme_error_t
830 gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
831                int secret)
832 {
833   gpgme_ctx_t listctx;
834   gpgme_error_t err;
835
836   if (!ctx || !r_key)
837     return gpg_error (GPG_ERR_INV_VALUE);
838   
839   if (strlen (fpr) < 16)        /* We have at least a key ID.  */
840     return gpg_error (GPG_ERR_INV_VALUE);
841
842   /* FIXME: We use our own context because we have to avoid the user's
843      I/O callback handlers.  */
844   err = gpgme_new (&listctx);
845   if (err)
846     return err;
847   gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
848   gpgme_set_keylist_mode (listctx, ctx->keylist_mode);
849   err = gpgme_op_keylist_start (listctx, fpr, secret);
850   if (!err)
851     err = gpgme_op_keylist_next (listctx, r_key);
852   gpgme_release (listctx);
853   return err;
854 }