/[winpt]/trunk/Src/wptKeyCache.cpp
ViewVC logotype

Contents of /trunk/Src/wptKeyCache.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 133 - (show annotations)
Mon Jan 9 09:15:29 2006 UTC (19 years, 1 month ago) by twoaday
File size: 18177 byte(s)
A lot of minor bug fixes.
New icons.

For a complete history, see the ChangeLog entries.


1 /* wptKeyCache.cpp- Caching for the pub- and the secring
2 * Copyright (C) 2001-2006 Timo Schulz
3 *
4 * This file is part of MyGPGME.
5 *
6 * MyGPGME is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * MyGPGME is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <windows.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <malloc.h>
29 #include <ctype.h>
30 #include <assert.h>
31 #include <gpgme.h>
32
33 #include "wptKeyCache.h"
34 #include "openpgp.h"
35 #include "wptNLS.h"
36 #include "wptErrors.h"
37 #include "wptW32API.h"
38 #include "wptGPG.h"
39
40
41 /* Attribute list which holds the image data. */
42 struct attr_list_s {
43 struct attr_list_s *next;
44 char *fpr; /* fingerprint of the key */
45 unsigned char *d; /* actual JPEG data. */
46 unsigned long octets; /* length of the data. */
47 unsigned int flags; /* status of the attribute. */
48 };
49 typedef struct attr_list_s *attr_list_t;
50
51
52 void
53 free_attr_list (attr_list_t ctx)
54 {
55 attr_list_t n;
56 while (ctx) {
57 n = ctx->next;
58 free (ctx->fpr);
59 free (ctx->d);
60 ctx = n;
61 }
62 }
63
64 /* Parse the attribute list in @fp and store it into @ctx.
65 Return value: number of parsed items. */
66 int
67 parse_attr_list (FILE *fp, const BYTE *data, DWORD datlen, attr_list_t *ctx)
68 {
69 attr_list_t c, t;
70 char buf[512], *p, *buffer;
71 int pos, n=0;
72
73 *ctx = NULL;
74 while (fgets (buf, 511, fp)) {
75 if (strstr (buf, "\r\n"))
76 buf[strlen (buf)-2]=0;
77 if (strstr (buf, "\n"))
78 buf[strlen (buf)-1]=0;
79 if (strlen (buf) < 2 || !strstr (buf, "ATTRIBUTE"))
80 continue;
81 buffer = buf+9+10;
82 pos = 0;
83 c = (attr_list_t)calloc (1, sizeof *c);
84 p = strtok (buffer, " ");
85 while (p != NULL) {
86 switch (pos) {
87 case 0:
88 c->fpr = strdup (p);
89 break;
90
91 case 1:
92 c->octets = strtoul (p, NULL, 10);
93 break;
94
95 case 7:
96 c->flags = strtoul (p, NULL, 10);
97 break;
98
99 default:
100 break;
101 }
102 pos++;
103 p = strtok (NULL, " ");
104 }
105 /*printf ("id=%s octets=%d flags=%d\n", c->fpr, c->octets, c->flags);*/
106 if (!*ctx)
107 *ctx = c;
108 else {
109 for (t = *ctx; t->next; t=t->next)
110 ;
111 t->next = c;
112 }
113 c->d = (unsigned char*)malloc (c->octets);
114 memcpy (c->d, data, c->octets);
115 data += c->octets;
116 datlen -= c->octets;
117 n++;
118 }
119 /*assert (datlen == 0); */
120 return n;
121 }
122
123
124 static int
125 parse_attr_data (const char *keyid, attr_list_t *list)
126 {
127 gpgme_error_t err;
128 FILE *tmp;
129 char *status;
130 BYTE *data;
131 DWORD ndata;
132
133 err = gpg_get_photoid_data (keyid, &status, &data, &ndata);
134 if (err)
135 return err;
136
137 if (ndata > 0) {
138 tmp = tmpfile ();
139 fwrite (status, 1, strlen (status), tmp);
140 fflush (tmp);
141 rewind (tmp);
142
143 ndata = parse_attr_list (tmp, data, ndata, list);
144 fclose (tmp);
145 }
146 else
147 *list = NULL;
148
149 safe_free (status);
150 safe_free (data);
151 return ndata;
152 }
153
154
155 /* Parse the secret keyring and retrieve some additional information
156 for each key which was found. */
157 static void
158 parse_secring (gpg_keycache_t cache, const char *kid, const char *secring)
159 {
160 PACKET *pkt;
161 PKT_secret_key *sk;
162 gpg_iobuf_t inp;
163 gpgme_error_t err;
164 gpgme_key_t key;
165 struct keycache_s *c=NULL;
166 char keyid[16+1];
167
168 inp = gpg_iobuf_open (secring);
169 if (!inp)
170 return;
171
172 gpg_iobuf_ioctl (inp, 3, 1, NULL);
173 pkt = (PACKET*)calloc (1, sizeof *pkt);
174 gpg_init_packet (pkt);
175 while (gpg_parse_packet (inp, pkt) != -1) {
176 if (pkt->pkttype == PKT_SECRET_KEY) {
177 sk = pkt->pkt.secret_key;
178 /* XXX: key IDs of card public keys are wrong! */
179 _snprintf (keyid, sizeof (keyid)-1, "%08lX",
180 gpg_keyid_from_sk (sk, NULL));
181 if (kid && strcmp (kid, keyid) != 0)
182 goto next;
183 err = gpg_keycache_find_key2 (cache, keyid, 0, &key, &c);
184 if (err)
185 goto next;
186 c->gloflags.is_protected = sk->is_protected;
187 c->gloflags.divert_to_card = sk->protect.s2k.mode==1002? 1 : 0;
188 if (c->pubpart != NULL) {
189 c->pubpart->gloflags.is_protected = sk->is_protected;
190 c->pubpart->gloflags.divert_to_card = sk->protect.s2k.mode==1002? 1 : 0;
191 }
192 }
193 next:
194 gpg_free_packet (pkt);
195 gpg_init_packet (pkt);
196 }
197 safe_free (pkt);
198 gpg_iobuf_close (inp);
199 }
200
201
202 /* Update the photo image of a single key with the fingerprint
203 @fpr. The @dat struct contains the new item data. */
204 static gpgme_error_t
205 keycache_update_photo (gpg_keycache_t ctx, const char *fpr, attr_list_t dat)
206 {
207 struct keycache_s *fnd = NULL;
208 gpgme_key_t key;
209
210 gpg_keycache_find_key2 (ctx, fpr, 0, &key, &fnd);
211 if (!fnd)
212 return gpg_error (GPG_ERR_NOT_FOUND);
213 safe_free (fnd->attrib.d);
214 fnd->attrib.flags = dat->flags;
215 fnd->attrib.len = dat->octets;
216 fnd->attrib.d = (unsigned char*)malloc (dat->octets);
217 memcpy (fnd->attrib.d, dat->d, dat->octets);
218 return 0;
219 }
220
221
222 /* Update all photo images in the cache. */
223 static gpgme_error_t
224 keycache_update_photos (gpg_keycache_t ctx)
225 {
226 attr_list_t list=NULL, n;
227 DWORD ndata;
228
229 ndata = parse_attr_data (NULL, &list);
230 if (ndata < 1) {
231 free_attr_list (list);
232 return 0;
233 }
234
235 for (n=list; n; n=n->next)
236 keycache_update_photo (ctx, n->fpr, n);
237 free_attr_list (list);
238 return 0;
239 }
240
241
242 /* Merge the information from the keyrings into the key cache structure. */
243 gpgme_error_t
244 keycache_prepare2 (gpg_keycache_t ctx, const char *kid,
245 const char *pubring, const char *secring)
246 {
247 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
248 gpgme_key_t key = NULL;
249 gpg_iobuf_t inp;
250 PACKET *pkt;
251 struct keycache_s *c;
252 const byte *sym_prefs;
253 char keyid[16+1];
254 int key_seen = 0;
255 size_t nsym =0;
256
257 if (secring) {
258 parse_secring (ctx, kid, secring);
259 if (!pubring)
260 return 0;
261 }
262 inp = gpg_iobuf_open (pubring);
263 if (!inp)
264 return gpg_error (GPG_ERR_KEYRING_OPEN);
265 gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */
266
267 pkt = (PACKET*)calloc (1, sizeof * pkt);
268 gpg_init_packet (pkt);
269 while (gpg_parse_packet (inp, pkt) != -1) {
270 if (pkt->pkttype == PKT_PUBLIC_KEY) {
271 strcpy (keyid, "");
272 key_seen = 1;
273 }
274
275 if (pkt->pkttype == PKT_SIGNATURE &&
276 pkt->pkt.signature->sig_class == 0x1F) {
277 if (pkt->pkt.signature->numrevkeys == 0)
278 goto next;
279 _snprintf (keyid, sizeof (keyid) -1, "%08X",
280 pkt->pkt.signature->keyid[1]);
281 if (kid && strcmp (kid, keyid) != 0)
282 goto next;
283 err = gpg_keycache_find_key2 (ctx, keyid, 0, &key, &c);
284 if (err)
285 goto next;
286 c->gloflags.has_desig_rev = 1;
287 }
288 if (pkt->pkttype == PKT_SIGNATURE && key_seen == 1 ) {
289 sym_prefs = gpg_parse_sig_subpkt (pkt->pkt.signature->hashed,
290 SIGSUBPKT_PREF_SYM, &nsym);
291 if (!sym_prefs)
292 goto next;
293 _snprintf (keyid, sizeof (keyid) - 1, "%08X",
294 pkt->pkt.signature->keyid[1]);
295 if (kid && strcmp (kid, keyid) != 0)
296 goto next;
297 err = gpg_keycache_find_key2 (ctx, keyid, 0, &key, &c);
298 if (err)
299 goto next;
300 else if (nsym > 0) {
301 c->sym_prefs = (unsigned char*)calloc (1, nsym+1);
302 if (!c->sym_prefs)
303 return gpg_error (GPG_ERR_ENOMEM);
304 memcpy (c->sym_prefs, sym_prefs, nsym);
305 }
306 }
307 next:
308 gpg_free_packet (pkt);
309 gpg_init_packet(pkt);
310 }
311
312 safe_free (pkt);
313 gpg_iobuf_close (inp);
314 return err;
315 }
316
317
318 gpgme_error_t
319 gpg_keycache_prepare (gpg_keycache_t ctx, const char *pubr, const char *secr)
320 {
321 return keycache_prepare2 (ctx, NULL, pubr, secr);
322 }
323
324 gpgme_error_t
325 gpg_keycache_prepare_single (gpg_keycache_t ctx, const char *keyid,
326 const char *pubr, const char *secr)
327 {
328 if (!strncmp (keyid, "0x", 2))
329 keyid += 2;
330 return keycache_prepare2 (ctx, keyid, pubr, secr);
331 }
332
333
334 /* Create new keycache object and return it in @r_ctx.
335 Return value: 0 on success. */
336 gpgme_error_t
337 gpg_keycache_new (gpg_keycache_t *r_ctx)
338 {
339 gpg_keycache_t ctx;
340
341 if (!r_ctx)
342 return gpg_error (GPG_ERR_INV_ARG);
343 ctx = (gpg_keycache_t)calloc (1, sizeof *ctx);
344 if (!ctx)
345 return gpg_error (GPG_ERR_ENOMEM);
346 ctx->secret = 0;
347 ctx->pos = 0;
348 *r_ctx = ctx;
349 return 0;
350 }
351
352
353 /* Release keycache object @ctx. */
354 void
355 gpg_keycache_release (gpg_keycache_t ctx)
356 {
357 struct keycache_s *c, *c2;
358
359 if (!ctx)
360 return;
361
362 for (c = ctx->item; c; c = c2) {
363 c2 = c->next;
364 gpgme_key_release (c->key);
365 c->key = NULL;
366 safe_free (c->sym_prefs);
367 safe_free (c->attrib.d);
368 safe_free (c->card_type);
369 free (c);
370 }
371 safe_free (ctx);
372 }
373
374
375 /* Set (progress) callback for the given keycache object.
376 @ctx the keycache
377 @cb the callback function
378 @cb_value1 opaque value which is passed to the callback.
379 @cb_value2 see @cb_value1. */
380 void
381 gpg_keycache_set_cb (gpg_keycache_t ctx,
382 void (*cb)(void *, const char *, int, int, int),
383 void * cb_value1, int cb_value2)
384 {
385 if (!ctx)
386 return;
387 ctx->cb = cb;
388 ctx->cb_value = cb_value1;
389 ctx->cb_value2 = cb_value2;
390 }
391
392
393 /* Add @key to the cache @ctx. @opaque return the key cache context as a void*.
394 Return value: 0 on success. */
395 gpgme_error_t
396 gpg_keycache_add_key (gpg_keycache_t ctx, gpgme_key_t key, void **opaque)
397 {
398 struct keycache_s *c, *n1;
399
400 if (!ctx)
401 return gpg_error (GPG_ERR_INV_ARG);
402
403 c = (struct keycache_s*)calloc (1, sizeof *c);
404 if (!c)
405 return gpg_error (GPG_ERR_ENOMEM);
406 c->gloflags.is_protected = 1; /*default: assume protection. */
407 c->key = key;
408 if (!ctx->item)
409 ctx->item = c;
410 else {
411 for (n1 = ctx->item; n1 && n1->next; n1 = n1->next)
412 ;
413 n1->next = c;
414 }
415 if (opaque)
416 *opaque = c;
417 return 0;
418 }
419
420
421
422 #define has_keyid_len(pattern) (\
423 strlen (pattern) == 8 || strlen (pattern) == 10 || \
424 strlen (pattern) == 16 || strlen (pattern) == 18)
425
426
427 gpgme_error_t
428 gpg_keycache_find_key2 (gpg_keycache_t ctx, const char *pattern, int flags,
429 gpgme_key_t *r_key, struct keycache_s **r_item)
430 {
431 struct keycache_s *c;
432 gpgme_subkey_t s;
433 gpgme_user_id_t u;
434 gpgme_key_t key;
435 const char *kid;
436
437 if (!ctx || !r_key)
438 return gpg_error (GPG_ERR_INV_ARG);
439
440 if (strstr (pattern, "0x"))
441 pattern += 2;
442
443 /* Sometimes a subkey has no valid fpr. As a kludge we convert v4
444 fingerprints into the 64-bit keyid. */
445 if (strlen (pattern) == 40 && isxdigit (*pattern))
446 pattern += 32;
447
448 /* XXX: this code is very slow, revamp it and use hash tables whenever
449 it is possible. */
450 for (c = ctx->item; c; c = c->next) {
451 key = c->key;
452 assert (key->_refs >= 1);
453 for (s = key->subkeys; s; s = s->next) {
454 for (u = key->uids; u; u = u->next) {
455 if (u->name && stristr (u->name, pattern)) {
456 if (r_item)
457 *r_item = c;
458 *r_key = flags? c->pubpart->key : c->key;
459 return 0;
460 }
461 }
462 if (has_keyid_len (pattern))
463 kid = s->keyid;
464 else
465 kid = s->fpr;
466 if (kid && stristr (kid, pattern)) {
467 if (r_item)
468 *r_item = c;
469 *r_key = flags? c->pubpart->key : c->key;
470 return 0;
471 }
472 }
473 }
474 *r_key = NULL;
475 return gpg_error (GPG_ERR_INTERNAL);
476 }
477
478
479 gpgme_error_t
480 gpg_keycache_find_key (gpg_keycache_t ctx, const char *pattern,
481 int flags, gpgme_key_t *r_key)
482 {
483 return gpg_keycache_find_key2 (ctx, pattern, flags, r_key, NULL);
484 }
485
486
487 /* Reload a photo image of a single key with the keyid @keyid.
488 Return value: 0 on success. */
489 static gpgme_error_t
490 keycache_reload_photo (gpg_keycache_t ctx, const char *keyid)
491 {
492 attr_list_t list;
493
494 if (parse_attr_data (keyid, &list) < 1) {
495 free_attr_list (list);
496 return 0;
497 }
498 keycache_update_photo (ctx, list->fpr, list);
499 free_attr_list (list);
500 return 0;
501 }
502
503
504 gpgme_error_t
505 gpg_keycache_update_key (gpg_keycache_t ctx, int is_sec,
506 void *opaque, const char *keyid)
507 {
508 struct keycache_s *c = NULL, *c_new=NULL;
509 gpgme_key_t key=NULL, fndkey=NULL;
510 gpgme_error_t err;
511 gpgme_ctx_t gctx;
512 gpg_keycache_t pub = (gpg_keycache_t)opaque;
513
514 err = gpgme_new (&gctx);
515 if (err)
516 return err;
517 err = gpgme_get_key (gctx, keyid, &key, is_sec);
518 gpgme_release (gctx);
519 if (err)
520 return err;
521 err = gpg_keycache_find_key2 (ctx, keyid, 0, &fndkey, &c);
522 if (!err && c != NULL) {
523 log_debug ("keycache update: keyid=%s %p\r\n", keyid, pub);
524 gpgme_key_release (fndkey);
525 c->key = key;
526 c->flags = 0;
527 if (is_sec && pub != NULL &&
528 !gpg_keycache_find_key (pub, keyid, 0, &fndkey)) {
529 log_debug ("keycache update: set public part %p\r\n", fndkey);
530 c->pubpart->key = fndkey;
531 }
532 /* XXX: this is also called for keys without a photo-id. */
533 keycache_reload_photo (ctx, keyid);
534 }
535 else {
536 log_debug ("keycache add: sync public part\r\n");
537 if (is_sec)
538 gpg_keycache_find_key2 (pub, keyid, 0, &fndkey, &c_new);
539 gpg_keycache_add_key (ctx, key, (void **)&c);
540 if (c != NULL && is_sec) {
541 log_debug ("keycache add: keyid=%s %p %p\r\n", keyid, c, fndkey);
542 c->pubpart = c_new;
543 if (c_new != NULL) {
544 c->pubpart->key = fndkey;
545 c->gloflags.is_protected = c_new->gloflags.is_protected;
546 c->gloflags.divert_to_card = c_new->gloflags.divert_to_card;
547 }
548 }
549 }
550 return 0;
551 }
552
553
554 /* Delete a key from the cache @ctx with the pattern @pattern.
555 Return value: 0 on success. */
556 gpgme_error_t
557 gpg_keycache_delete_key (gpg_keycache_t ctx, const char *pattern)
558 {
559 struct keycache_s *itm = NULL, *c;
560 gpgme_key_t key;
561 gpgme_error_t rc;
562
563 if (!ctx)
564 return gpg_error (GPG_ERR_INV_ARG);
565 rc = gpg_keycache_find_key2 (ctx, pattern, 0, &key, &itm);
566 if (rc)
567 return rc;
568
569 c = ctx->item;
570 if (c->next == NULL) {
571 gpgme_key_release (itm->key);
572 if (itm)
573 free (itm);
574 ctx->item = NULL;
575 }
576 else {
577 while (c && c->next != itm)
578 c = c->next;
579 c->next = c->next->next;
580 gpgme_key_release (itm->key);
581 if (itm)
582 free (itm);
583 }
584 return 0;
585 }
586
587
588 /* Initialize the given cache @ctx. If @pattern is NULL, the entire keyring
589 will be added to the cache. @secret is 1 if the source is the secret keyring.
590 Return value: 0 on success. */
591 gpgme_error_t
592 gpg_keycache_init (gpg_keycache_t ctx, const char *pattern, int secret)
593 {
594 gpgme_error_t err;
595 gpgme_ctx_t c;
596 gpgme_key_t key;
597 int count = 0;
598
599 if (!ctx)
600 return gpg_error (GPG_ERR_INV_ARG);
601
602 err = gpgme_new (&c);
603 if (err)
604 return err;
605
606 gpgme_set_keylist_mode (c, GPGME_KEYLIST_MODE_SIGS);
607 err = gpgme_op_keylist_start (c, pattern, secret);
608 while(!err) {
609 err = gpgme_op_keylist_next (c, &key);
610 if (!err)
611 err = gpg_keycache_add_key (ctx, key, NULL);
612 if (ctx->cb)
613 ctx->cb (ctx->cb_value, _("Load GPG Keyrings..."), 0,
614 count++, ctx->cb_value2);
615 }
616 if (gpgme_err_code (err) == GPG_ERR_EOF)
617 err = gpg_error (GPG_ERR_NO_ERROR);
618 keycache_update_photos (ctx);
619 /* XXX: make sure the progress dialog is closed. */
620 gpgme_op_keylist_end (c);
621 gpgme_release (c);
622 return err;
623 }
624
625
626 /* XXX: kludge to see if the key is stored on a card. */
627 static int
628 key_divert_to_card (gpgme_key_t key)
629 {
630 gpgme_subkey_t k;
631 int n=0, n_alg=0, can_auth = 0;
632
633 for (k = key->subkeys; k; k = k->next) {
634 n++;
635 if (k->pubkey_algo == GPGME_PK_RSA && k->length == 1024)
636 n_alg++;
637 if (k->can_authenticate)
638 can_auth++;
639 }
640 if (n == 3 && n_alg == 3 && can_auth == 1)
641 return 1;
642 return 0;
643 }
644
645
646 static unsigned char*
647 copy_uid_prefs (const unsigned char *prefs)
648 {
649 unsigned char *p;
650 size_t pos=0;
651
652 while (prefs[pos] != 0)
653 pos++;
654 p = (unsigned char*)calloc (1, pos+1);
655 if (!p)
656 abort ();
657 memcpy (p, prefs, pos);
658 return p;
659 }
660
661
662 gpgme_error_t
663 gpg_keycache_sync (gpg_keycache_t pub, gpg_keycache_t sec)
664 {
665 struct keycache_s *c, *c_sec;
666 gpgme_key_t key;
667
668 if (!pub || !sec)
669 return gpg_error (GPG_ERR_INV_ARG);
670
671 for (c=sec->item; c; c=c->next) {
672 if (!gpg_keycache_find_key2 (pub, c->key->subkeys->keyid, 0, &key, &c_sec)) {
673 c_sec->gloflags.is_protected = c->gloflags.is_protected;
674 c_sec->gloflags.divert_to_card = c->gloflags.divert_to_card;
675 if (!c->gloflags.divert_to_card)
676 c->gloflags.divert_to_card = key_divert_to_card (key);
677 c->sym_prefs = copy_uid_prefs (c_sec->sym_prefs);
678 c->pubpart = c_sec;
679 c->pubpart->key = key;
680 }
681 }
682 return 0;
683 }
684
685
686 /* Rewind the given cache @ctx to the begin. */
687 void
688 gpg_keycache_rewind (gpg_keycache_t ctx)
689 {
690 if (ctx)
691 ctx->pos = 0;
692 }
693
694
695
696 /* Return the number of elements in the cache @ctx. */
697 int
698 gpg_keycache_get_size (gpg_keycache_t ctx)
699 {
700 struct keycache_s *c;
701 int count = 0;
702
703 if (!ctx)
704 return 0;
705 for (c = ctx->item; c; c = c->next)
706 count++;
707 return count;
708 }
709
710
711 static gpgme_error_t
712 keycache_next_key (gpg_keycache_t ctx, int flags,
713 struct keycache_s **c, gpgme_key_t *r_key)
714 {
715 if (!ctx || !r_key)
716 return gpg_error (GPG_ERR_INV_ARG);
717
718 if (!ctx->pos)
719 ctx->tmp = ctx->item;
720
721 if (!ctx->tmp || !ctx->tmp->key) {
722 ctx->pos = 0;
723 *r_key = NULL;
724 return gpg_error (GPG_ERR_EOF);
725 }
726
727 *r_key = flags? ctx->tmp->pubpart->key : ctx->tmp->key;
728 *c = ctx->tmp = ctx->tmp->next;
729 ctx->pos++;
730
731 return 0;
732 }
733
734
735 /* Return the next key from the cache @ctx. The key will be returned
736 in @r_key. @flags can contain additional flags.
737 Return value: 0 on success. */
738 gpgme_error_t
739 gpg_keycache_next_key (gpg_keycache_t ctx, int flags, gpgme_key_t *r_key)
740 {
741 struct keycache_s *c=NULL;
742 gpgme_error_t err = 0;
743
744 err = keycache_next_key (ctx, flags, &c, r_key);
745 return err;
746 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26