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
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34 #include "key.h"
35 #include "debug.h"
36
37 \f
38 struct keylist_result
39 {
40   int truncated;
41   GpgmeData xmlinfo;
42 };
43 typedef struct keylist_result *KeylistResult;
44
45 static void
46 release_keylist_result (void *hook)
47 {
48   KeylistResult result = (KeylistResult) hook;
49
50   if (result->xmlinfo)
51     gpgme_data_release (result->xmlinfo);
52 }
53
54
55 /* Append some XML info.  args is currently ignore but we might want
56    to add more information in the future (like source of the
57    keylisting.  With args of NULL the XML structure is closed.  */
58 static void
59 append_xml_keylistinfo (GpgmeData *rdh, char *args)
60 {
61   GpgmeData dh;
62
63   if (!*rdh)
64     {
65       if (gpgme_data_new (rdh))
66         return; /* FIXME: We are ignoring out-of-core.  */
67       dh = *rdh;
68       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
69     }
70   else
71     {
72       dh = *rdh;
73       _gpgme_data_append_string (dh, "  </keylisting>\n");
74     }
75
76   if (!args)
77     {
78       /* Just close the XML containter.  */
79       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
80       return;
81     }
82
83   _gpgme_data_append_string (dh, "  <keylisting>\n    <truncated/>\n");
84     
85 }
86
87
88 static GpgmeError
89 keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
90 {
91   GpgmeError err;
92   KeylistResult result;
93
94   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, (void **) &result,
95                                sizeof (*result), release_keylist_result);
96   if (err)
97     return err;
98
99   switch (code)
100     {
101     case GPGME_STATUS_TRUNCATED:
102       result->truncated = 1;
103       break;
104
105     case GPGME_STATUS_EOF:
106       if (result->truncated)
107         append_xml_keylistinfo (&result->xmlinfo, "1");
108       if (result->xmlinfo)
109         {
110           append_xml_keylistinfo (&result->xmlinfo, NULL);
111           _gpgme_set_op_info (ctx, result->xmlinfo);
112           result->xmlinfo = NULL;
113         }
114       break;
115
116     default:
117       break;
118     }
119   return 0;
120 }
121
122 \f
123 static time_t
124 parse_timestamp (char *timestamp)
125 {
126   if (!*timestamp)
127     return 0;
128
129   return (time_t) strtoul (timestamp, NULL, 10);
130 }
131
132
133 static void
134 set_mainkey_trust_info (GpgmeKey key, const char *src)
135 {
136   /* Look at letters and stop at the first digit.  */
137   while (*src && !isdigit (*src))
138     {
139       switch (*src)
140         {
141         case 'e':
142           key->keys.flags.expired = 1;
143           break;
144
145         case 'r':
146           key->keys.flags.revoked = 1;
147           break;
148
149         case 'd':
150           /* Note that gpg 1.3 won't print that anymore but only uses
151              the capabilities field. */
152           key->keys.flags.disabled = 1;
153           break;
154
155         case 'i':
156           key->keys.flags.invalid = 1;
157           break;
158         }
159       src++;
160     }
161 }
162
163
164 static void
165 set_userid_flags (GpgmeKey key, const char *src)
166 {
167   struct user_id_s *uid = key->last_uid;
168
169   assert (uid);
170   /* Look at letters and stop at the first digit.  */
171   while (*src && !isdigit (*src))
172     {
173       switch (*src)
174         {
175         case 'r':
176           uid->revoked = 1;
177           break;
178           
179         case 'i':
180           uid->invalid = 1;
181           break;
182
183         case 'n':
184           uid->validity = GPGME_VALIDITY_NEVER;
185           break;
186
187         case 'm':
188           uid->validity = GPGME_VALIDITY_MARGINAL;
189           break;
190
191         case 'f':
192           uid->validity = GPGME_VALIDITY_FULL;
193           break;
194
195         case 'u':
196           uid->validity = GPGME_VALIDITY_ULTIMATE;
197           break;
198         }
199       src++;
200     }
201 }
202
203
204 static void
205 set_subkey_trust_info (struct subkey_s *subkey, const char *src)
206 {
207   /* Look at letters and stop at the first digit.  */
208   while (*src && !isdigit (*src))
209     {
210       switch (*src)
211         {
212         case 'e':
213           subkey->flags.expired = 1;
214           break;
215
216         case 'r':
217           subkey->flags.revoked = 1;
218           break;
219
220         case 'd':
221           subkey->flags.disabled = 1;
222           break;
223
224         case 'i':
225           subkey->flags.invalid = 1;
226           break;
227         }
228       src++;
229     }
230 }
231
232
233 static void
234 set_mainkey_capability (GpgmeKey key, const char *src)
235 {
236   while (*src)
237     {
238       switch (*src)
239         {
240         case 'e':
241           key->keys.flags.can_encrypt = 1;
242           break;
243
244         case 's':
245           key->keys.flags.can_sign = 1;
246           break;
247
248         case 'c':
249           key->keys.flags.can_certify = 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->keys.flags.disabled = 1;
260           break;
261
262         case 'E':
263           key->gloflags.can_encrypt = 1;
264           break;
265
266         case 'S':
267           key->gloflags.can_sign = 1;
268           break;
269
270         case 'C':
271           key->gloflags.can_certify = 1;
272           break;
273         }
274       src++;
275     }
276 }
277
278
279 static void
280 set_subkey_capability (struct subkey_s *subkey, const char *src)
281 {
282   while (*src)
283     {
284       switch (*src)
285         {
286         case 'e':
287           subkey->flags.can_encrypt = 1;
288           break;
289
290         case 's':
291           subkey->flags.can_sign = 1;
292           break;
293
294         case 'c':
295           subkey->flags.can_certify = 1;
296           break;
297         }
298       src++;
299     }
300 }
301
302 static void
303 set_ownertrust (GpgmeKey key, const char *src)
304 {
305   /* Look at letters and stop at the first digit.  */
306   while (*src && !isdigit (*src))
307     {
308       switch (*src)
309         {
310         case 'n':
311           key->otrust = GPGME_VALIDITY_NEVER;
312           break;
313
314         case 'm':
315           key->otrust = GPGME_VALIDITY_MARGINAL;
316           break;
317
318         case 'f':
319           key->otrust = GPGME_VALIDITY_FULL;
320           break;
321
322         case 'u':
323           key->otrust = GPGME_VALIDITY_ULTIMATE;
324           break;
325
326         default:
327           key->otrust = GPGME_VALIDITY_UNKNOWN;
328           break;
329         }
330       src++;
331     }
332 }
333
334
335 /* We have read an entire key into ctx->tmp_key and should now finish
336    it.  It is assumed that this releases ctx->tmp_key.  */
337 static void
338 finish_key (GpgmeCtx ctx)
339 {
340   GpgmeKey key = ctx->tmp_key;
341
342   ctx->tmp_key = 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 GpgmeError
351 keylist_colon_handler (GpgmeCtx ctx, char *line)
352 {
353   enum
354     {
355       RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC,
356       RT_CRT, RT_CRS, RT_REV
357     }
358   rectype = RT_NONE;
359 #define NR_FIELDS 13
360   char *field[NR_FIELDS];
361   int fields = 0;
362   GpgmeKey key = ctx->tmp_key;
363   struct subkey_s *subkey = NULL;
364   struct certsig_s *certsig = NULL;
365
366   DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n",
367           ctx, key, line ? line : "(null)");
368
369   if (!line)
370     {
371       /* End Of File.  */
372       finish_key (ctx);
373       return 0;
374     }
375
376   while (line && fields < NR_FIELDS)
377     {
378       field[fields++] = line;
379       line = strchr (line, ':');
380       if (line)
381         *(line++) = '\0';
382     }
383
384   if (!strcmp (field[0], "sig"))
385     rectype = RT_SIG;
386   else if (!strcmp (field[0], "rev"))
387     rectype = RT_REV;
388   else if (!strcmp (field[0], "uid") && key)
389     rectype = RT_UID;
390   else if (!strcmp (field[0], "sub") && key)
391     {
392       /* Start a new subkey.  */
393       rectype = RT_SUB; 
394       if (!(subkey = _gpgme_key_add_subkey (key)))
395         return GPGME_Out_Of_Core;
396     }
397   else if (!strcmp (field[0], "ssb") && key)
398     {
399       /* Start a new secret subkey.  */
400       rectype = RT_SSB;
401       if (!(subkey = _gpgme_key_add_secret_subkey (key)))
402         return GPGME_Out_Of_Core;
403     }
404   else if (!strcmp (field[0], "pub"))
405     {
406       /* Start a new keyblock.  */
407       if (_gpgme_key_new (&key))
408         /* The only kind of error we can get.  */
409         return GPGME_Out_Of_Core;
410       rectype = RT_PUB;
411       finish_key (ctx);
412       assert (!ctx->tmp_key);
413       ctx->tmp_key = key;
414     }
415   else if (!strcmp (field[0], "sec"))
416     {
417       /* Start a new keyblock,  */
418       if (_gpgme_key_new_secret (&key))
419         return GPGME_Out_Of_Core;
420       rectype = RT_SEC;
421       finish_key (ctx);
422       assert (!ctx->tmp_key);
423       ctx->tmp_key = key;
424     }
425   else if (!strcmp (field[0], "crt"))
426     {
427       /* Start a new certificate.  */
428       if (_gpgme_key_new (&key))
429         return GPGME_Out_Of_Core;
430       key->x509 = 1;
431       rectype = RT_CRT;
432       finish_key (ctx);
433       assert (!ctx->tmp_key);
434       ctx->tmp_key = key;
435     }
436   else if (!strcmp (field[0], "crs"))
437     {
438       /* Start a new certificate.  */
439       if (_gpgme_key_new_secret (&key))
440         return GPGME_Out_Of_Core;
441       key->x509 = 1;
442       rectype = RT_CRS;
443       finish_key (ctx);
444       assert (!ctx->tmp_key);
445       ctx->tmp_key = key;
446     }
447   else if (!strcmp (field[0], "fpr") && key) 
448     rectype = RT_FPR;
449   else 
450     rectype = RT_NONE;
451
452   /* Only look at signatures immediately following a user ID.  For
453      this, clear the user ID pointer when encountering anything but a
454      signature.  */
455   if (rectype != RT_SIG && rectype != RT_REV)
456     ctx->tmp_uid = NULL;
457
458   switch (rectype)
459     {
460     case RT_CRT:
461     case RT_CRS:
462       /* Field 8 has the X.509 serial number.  */
463       if (fields >= 8)
464         {
465           key->issuer_serial = strdup (field[7]);
466           if (!key->issuer_serial)
467             return GPGME_Out_Of_Core;
468         }
469
470       /* Field 10 is not used for gpg due to --fixed-list-mode option
471          but GPGSM stores the issuer name.  */
472       if (fields >= 10 && _gpgme_decode_c_string (field[9],
473                                                   &key->issuer_name, 0))
474         return GPGME_Out_Of_Core;
475       /* Fall through!  */
476
477     case RT_PUB:
478     case RT_SEC:
479       /* Field 2 has the trust info.  */
480       if (fields >= 2)
481         set_mainkey_trust_info (key, field[1]);
482
483       /* Field 3 has the key length.  */
484       if (fields >= 3)
485         {
486           int i = atoi (field[2]);
487           /* Ignore invalid values.  */
488           if (i > 1)
489             key->keys.key_len = i; 
490         }
491
492       /* Field 4 has the public key algorithm.  */
493       if (fields >= 4)
494         {
495           int i = atoi (field[3]);
496           if (i >= 1 && i < 128)
497             key->keys.key_algo = i;
498         }
499
500       /* Field 5 has the long keyid.  */
501       if (fields >= 5 && strlen (field[4]) == DIM(key->keys.keyid) - 1)
502         strcpy (key->keys.keyid, field[4]);
503
504       /* Field 6 has the timestamp (seconds).  */
505       if (fields >= 6)
506         key->keys.timestamp = parse_timestamp (field[5]);
507
508       /* Field 7 has the expiration time (seconds).  */
509       if (fields >= 7)
510         key->keys.expires_at = parse_timestamp (field[6]);
511
512       /* Field 9 has the ownertrust.  */
513       if (fields >= 9)
514         set_ownertrust (key, field[8]);
515
516       /* Field 11 has the signature class.  */
517
518       /* Field 12 has the capabilities.  */
519       if (fields >= 12)
520         set_mainkey_capability (key, field[11]);
521       break;
522
523     case RT_SUB:
524     case RT_SSB:
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->key_len = 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->key_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_at = 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 GPGME_Out_Of_Core;
575           else
576             {
577               if (field[1])
578                 set_userid_flags (key, field[1]);
579               ctx->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->keys.fingerprint && field[9] && *field[9])
587         {
588           key->keys.fingerprint = strdup (field[9]);
589           if (!key->keys.fingerprint)
590             return GPGME_Out_Of_Core;
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 GPGME_Out_Of_Core;
599         }
600       break;
601
602     case RT_SIG:
603     case RT_REV:
604       if (!ctx->tmp_uid)
605         return 0;
606
607       /* Start a new (revoked) signature.  */
608       assert (ctx->tmp_uid == key->last_uid);
609       certsig = _gpgme_key_add_certsig (key, (fields >= 10) ? field[9] : NULL);
610       if (!certsig)
611         return GPGME_Out_Of_Core;
612
613       /* Field 2 has the calculated trust ('!', '-', '?', '%').  */
614       if (fields >= 2)
615         switch (field[1][0])
616           {
617           case '!':
618             certsig->sig_stat = GPGME_SIG_STAT_GOOD;
619             break;
620
621           case '-':
622             certsig->sig_stat = GPGME_SIG_STAT_BAD;
623             break;
624
625           case '?':
626             certsig->sig_stat = GPGME_SIG_STAT_NOKEY;
627             break;
628
629           case '%':
630             certsig->sig_stat = GPGME_SIG_STAT_ERROR;
631             break;
632
633           default:
634             certsig->sig_stat = GPGME_SIG_STAT_NONE;
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             certsig->algo = i;
644         }
645       
646       /* Field 5 has the long keyid.  */
647       if (fields >= 5 && strlen (field[4]) == DIM(certsig->keyid) - 1)
648         strcpy (certsig->keyid, field[4]);
649       
650       /* Field 6 has the timestamp (seconds).  */
651       if (fields >= 6)
652         certsig->timestamp = parse_timestamp (field[5]);
653
654       /* Field 7 has the expiration time (seconds).  */
655       if (fields >= 7)
656         certsig->expires_at = 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                 certsig->sig_class = class;
666                 if (class == 0x30)
667                   certsig->flags.revoked = 1;
668               }
669             if (field[10][2] == 'x')
670               certsig->flags.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, GpgmeEventIO type, void *type_data)
684 {
685   GpgmeCtx ctx = (GpgmeCtx) data;
686   GpgmeKey key = (GpgmeKey) type_data;
687   struct key_queue_item_s *q, *q2;
688
689   assert (type == GPGME_EVENT_NEXT_KEY);
690
691   _gpgme_key_cache_add (key);
692
693   q = malloc (sizeof *q);
694   if (!q)
695     {
696       gpgme_key_release (key);
697       /* FIXME       return GPGME_Out_Of_Core; */
698       return;
699     }
700   q->key = key;
701   q->next = NULL;
702   /* FIXME: Lock queue.  Use a tail pointer?  */
703   if (!(q2 = ctx->key_queue))
704     ctx->key_queue = q;
705   else
706     {
707       for (; q2->next; q2 = q2->next)
708         ;
709       q2->next = q;
710     }
711   ctx->key_cond = 1;
712   /* FIXME: Unlock queue.  */
713 }
714
715
716 /**
717  * gpgme_op_keylist_start:
718  * @c: context 
719  * @pattern: a GnuPG user ID or NULL for all
720  * @secret_only: List only keys where the secret part is available
721  * 
722  * Note that this function also cancels a pending key listing
723  * operaton. To actually retrieve the key, use
724  * gpgme_op_keylist_next().
725  * 
726  * Return value:  0 on success or an errorcode. 
727  **/
728 GpgmeError
729 gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
730 {
731   GpgmeError err = 0;
732
733   err = _gpgme_op_reset (ctx, 2);
734   if (err)
735     goto leave;
736
737   gpgme_key_release (ctx->tmp_key);
738   ctx->tmp_key = NULL;
739   /* Fixme: Release key_queue.  */
740
741   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
742   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
743                                               keylist_colon_handler, ctx);
744   if (err)
745     goto leave;
746
747   /* We don't want to use the verbose mode as this will also print the
748      key signatures which is in most cases not needed and furthermore
749      we just ignore those lines - This should speed up things.  */
750   _gpgme_engine_set_verbosity (ctx->engine, 0);
751
752   err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
753                                   ctx->keylist_mode);
754
755  leave:
756   if (err)
757     {
758       _gpgme_engine_release (ctx->engine);
759       ctx->engine = NULL;
760     }
761   return err;
762 }
763
764
765 /**
766  * gpgme_op_keylist_ext_start:
767  * @c: context 
768  * @pattern: a NULL terminated array of search patterns
769  * @secret_only: List only keys where the secret part is available
770  * @reserved: Should be 0.
771  * 
772  * Note that this function also cancels a pending key listing
773  * operaton. To actually retrieve the key, use
774  * gpgme_op_keylist_next().
775  * 
776  * Return value:  0 on success or an errorcode. 
777  **/
778 GpgmeError
779 gpgme_op_keylist_ext_start (GpgmeCtx ctx, const char *pattern[],
780                             int secret_only, int reserved)
781 {
782   GpgmeError err = 0;
783
784   err = _gpgme_op_reset (ctx, 2);
785   if (err)
786     goto leave;
787
788   gpgme_key_release (ctx->tmp_key);
789   ctx->tmp_key = NULL;
790
791   _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
792   err = _gpgme_engine_set_colon_line_handler (ctx->engine,
793                                               keylist_colon_handler, ctx);
794   if (err)
795     goto leave;
796
797   /* We don't want to use the verbose mode as this will also print the
798      key signatures which is in most cases not needed and furthermore
799      we just ignore those lines - This should speed up things.  */
800   _gpgme_engine_set_verbosity (ctx->engine, 0);
801
802   err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
803                                       reserved, ctx->keylist_mode);
804
805  leave:
806   if (err)
807     {
808       _gpgme_engine_release (ctx->engine);
809       ctx->engine = NULL;
810     }
811   return err;
812 }
813
814
815 /**
816  * gpgme_op_keylist_next:
817  * @c: Context
818  * @r_key: Returned key object
819  * 
820  * Return the next key from the key listing started with
821  * gpgme_op_keylist_start().  The caller must free the key using
822  * gpgme_key_release().  If the last key has already been returned the
823  * last time the function was called, %GPGME_EOF is returned and the
824  * operation is finished.
825  * 
826  * Return value: 0 on success, %GPGME_EOF or another error code.
827  **/
828 GpgmeError
829 gpgme_op_keylist_next (GpgmeCtx ctx, GpgmeKey *r_key)
830 {
831   struct key_queue_item_s *queue_item;
832
833   if (!r_key)
834     return GPGME_Invalid_Value;
835   *r_key = NULL;
836   if (!ctx)
837     return GPGME_Invalid_Value;
838
839   if (!ctx->key_queue)
840     {
841       GpgmeError err = _gpgme_wait_on_condition (ctx, &ctx->key_cond);
842       if (err)
843         return err;
844       if (!ctx->key_cond)
845         return GPGME_EOF;
846       ctx->key_cond = 0; 
847       assert (ctx->key_queue);
848     }
849   queue_item = ctx->key_queue;
850   ctx->key_queue = queue_item->next;
851   if (!ctx->key_queue)
852     ctx->key_cond = 0;
853   
854   *r_key = queue_item->key;
855   free (queue_item);
856   return 0;
857 }
858
859
860 /**
861  * gpgme_op_keylist_end:
862  * @c: Context
863  * 
864  * Ends the keylist operation and allows to use the context for some
865  * other operation next.
866  **/
867 GpgmeError
868 gpgme_op_keylist_end (GpgmeCtx ctx)
869 {
870   if (!ctx)
871     return GPGME_Invalid_Value;
872
873   return 0;
874 }