Line data Source code
1 : /* genkey.c - Key generation.
2 : * Copyright (C) 2000 Werner Koch (dd9jn)
3 : * Copyright (C) 2001, 2002, 2003, 2004, 2016 g10 Code GmbH
4 : *
5 : * This file is part of GPGME.
6 : *
7 : * GPGME is free software; you can redistribute it and/or modify it
8 : * under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * GPGME is distributed in the hope that it will be useful, but
13 : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : * Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public
18 : * License along with this program; if not, see <https://www.gnu.org/licenses/>.
19 : */
20 :
21 : #if HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 : #include <stdlib.h>
25 : #include <string.h>
26 : #include <errno.h>
27 :
28 : #include "gpgme.h"
29 : #include "debug.h"
30 : #include "context.h"
31 : #include "ops.h"
32 : #include "util.h"
33 :
34 :
35 : typedef struct
36 : {
37 : struct _gpgme_op_genkey_result result;
38 :
39 : /* The error code from a FAILURE status line or 0. */
40 : gpg_error_t failure_code;
41 :
42 : /* The error code from certain ERROR status lines or 0. */
43 : gpg_error_t error_code;
44 :
45 : /* Flag to indicate that a UID is to be added. */
46 : gpg_error_t uidmode;
47 :
48 : /* The key parameters passed to the crypto engine. */
49 : gpgme_data_t key_parameter;
50 : } *op_data_t;
51 :
52 :
53 : static void
54 106 : release_op_data (void *hook)
55 : {
56 106 : op_data_t opd = (op_data_t) hook;
57 :
58 106 : if (opd->result.fpr)
59 76 : free (opd->result.fpr);
60 106 : if (opd->key_parameter)
61 4 : gpgme_data_release (opd->key_parameter);
62 106 : }
63 :
64 :
65 : gpgme_genkey_result_t
66 72 : gpgme_op_genkey_result (gpgme_ctx_t ctx)
67 : {
68 : void *hook;
69 : op_data_t opd;
70 : gpgme_error_t err;
71 :
72 72 : TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
73 :
74 72 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
75 72 : opd = hook;
76 72 : if (err || !opd)
77 : {
78 0 : TRACE_SUC0 ("result=(null)");
79 0 : return NULL;
80 : }
81 :
82 72 : TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
83 : opd->result.primary ? "primary" : "no primary",
84 : opd->result.sub ? "sub" : "no sub");
85 :
86 72 : TRACE_SUC1 ("result=%p", &opd->result);
87 72 : return &opd->result;
88 : }
89 :
90 :
91 :
92 : /* Parse an error status line. Return the error location and the
93 : error code. The function may modify ARGS. */
94 : static char *
95 6 : parse_error (char *args, gpg_error_t *r_err)
96 : {
97 6 : char *where = strchr (args, ' ');
98 : char *which;
99 :
100 6 : if (where)
101 : {
102 6 : *where = '\0';
103 6 : which = where + 1;
104 :
105 6 : where = strchr (which, ' ');
106 6 : if (where)
107 0 : *where = '\0';
108 :
109 6 : where = args;
110 : }
111 : else
112 : {
113 0 : *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
114 0 : return NULL;
115 : }
116 :
117 6 : *r_err = atoi (which);
118 :
119 6 : return where;
120 : }
121 :
122 :
123 : static gpgme_error_t
124 1922 : genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
125 : {
126 1922 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
127 : gpgme_error_t err;
128 : void *hook;
129 : op_data_t opd;
130 : char *loc;
131 :
132 : /* Pipe the status code through the progress status handler. */
133 1922 : err = _gpgme_progress_status_handler (ctx, code, args);
134 1922 : if (err)
135 0 : return err;
136 :
137 1922 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
138 1922 : opd = hook;
139 1922 : if (err)
140 0 : return err;
141 :
142 1922 : switch (code)
143 : {
144 : case GPGME_STATUS_KEY_CREATED:
145 78 : if (args && *args)
146 : {
147 78 : if (*args == 'B' || *args == 'P')
148 : {
149 52 : opd->result.primary = 1;
150 52 : opd->result.uid = 1;
151 : }
152 78 : if (*args == 'B' || *args == 'S')
153 38 : opd->result.sub = 1;
154 78 : if (args[1] == ' ')
155 : {
156 78 : if (opd->result.fpr)
157 0 : free (opd->result.fpr);
158 78 : opd->result.fpr = strdup (&args[2]);
159 78 : if (!opd->result.fpr)
160 0 : return gpg_error_from_syserror ();
161 : }
162 : }
163 78 : break;
164 :
165 : case GPGME_STATUS_ERROR:
166 6 : loc = parse_error (args, &err);
167 6 : if (!loc)
168 0 : return err;
169 6 : if (!opd->error_code)
170 6 : opd->error_code = err;
171 6 : break;
172 :
173 : case GPGME_STATUS_FAILURE:
174 0 : opd->failure_code = _gpgme_parse_failure (args);
175 0 : break;
176 :
177 : case GPGME_STATUS_EOF:
178 108 : if (opd->error_code)
179 6 : return opd->error_code;
180 102 : else if (!opd->uidmode && !opd->result.primary && !opd->result.sub)
181 0 : return gpg_error (GPG_ERR_GENERAL);
182 102 : else if (opd->failure_code)
183 0 : return opd->failure_code;
184 102 : else if (opd->uidmode == 1)
185 19 : opd->result.uid = 1; /* We have no status line, thus this hack. */
186 102 : break;
187 :
188 : case GPGME_STATUS_INQUIRE_MAXLEN:
189 5 : if (ctx->status_cb && !ctx->full_status)
190 : {
191 0 : err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
192 0 : if (err)
193 0 : return err;
194 : }
195 5 : break;
196 :
197 : default:
198 1725 : break;
199 : }
200 1916 : return 0;
201 : }
202 :
203 :
204 : static gpgme_error_t
205 6 : get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
206 : {
207 : const char *content;
208 : const char *attrib;
209 : const char *endtag;
210 :
211 : /* Extract the key parameter from the XML structure. */
212 6 : parms = strstr (parms, "<GnupgKeyParms ");
213 6 : if (!parms)
214 0 : return gpg_error (GPG_ERR_INV_VALUE);
215 :
216 6 : content = strchr (parms, '>');
217 6 : if (!content)
218 0 : return gpg_error (GPG_ERR_INV_VALUE);
219 6 : content++;
220 :
221 6 : attrib = strstr (parms, "format=\"internal\"");
222 6 : if (!attrib || attrib >= content)
223 0 : return gpg_error (GPG_ERR_INV_VALUE);
224 :
225 6 : endtag = strstr (content, "</GnupgKeyParms>");
226 6 : if (!endtag)
227 0 : endtag = content + strlen (content);
228 :
229 : /* FIXME: Check that there are no control statements inside. */
230 18 : while (content < endtag
231 12 : && (content[0] == '\n'
232 6 : || (content[0] == '\r' && content[1] == '\n')))
233 6 : content++;
234 :
235 6 : return gpgme_data_new_from_mem (key_parameter, content,
236 6 : endtag - content, 1);
237 : }
238 :
239 :
240 : static gpgme_error_t
241 6 : genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
242 : gpgme_data_t pubkey, gpgme_data_t seckey)
243 : {
244 : gpgme_error_t err;
245 : void *hook;
246 : op_data_t opd;
247 6 : err = _gpgme_op_reset (ctx, synchronous);
248 6 : if (err)
249 0 : return err;
250 :
251 6 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
252 : sizeof (*opd), release_op_data);
253 6 : opd = hook;
254 6 : if (err)
255 0 : return err;
256 :
257 6 : err = get_key_parameter (parms, &opd->key_parameter);
258 6 : if (err)
259 0 : return err;
260 :
261 6 : _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
262 :
263 6 : if (ctx->passphrase_cb)
264 : {
265 0 : err = _gpgme_engine_set_command_handler
266 : (ctx->engine, _gpgme_passphrase_command_handler, ctx);
267 0 : if (err)
268 0 : return err;
269 : }
270 :
271 6 : return _gpgme_engine_op_genkey (ctx->engine,
272 : NULL, NULL, 0, 0, NULL, 0,
273 : opd->key_parameter,
274 6 : ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
275 : pubkey, seckey);
276 : }
277 :
278 :
279 : /* Generate a new keypair and add it to the keyring. PUBKEY and
280 : SECKEY should be null for now. PARMS specifies what keys should be
281 : generated. */
282 : gpgme_error_t
283 0 : gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
284 : gpgme_data_t pubkey, gpgme_data_t seckey)
285 : {
286 : gpgme_error_t err;
287 :
288 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
289 : "pubkey=%p, seckey=%p", pubkey, seckey);
290 0 : TRACE_LOGBUF (parms, parms? strlen (parms):0);
291 :
292 0 : if (!ctx || parms)
293 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
294 :
295 0 : err = genkey_start (ctx, 0, parms, pubkey, seckey);
296 0 : return TRACE_ERR (err);
297 : }
298 :
299 :
300 : /* Generate a new keypair and add it to the keyring. PUBKEY and
301 : SECKEY should be null for now. PARMS specifies what keys should be
302 : generated. */
303 : gpgme_error_t
304 6 : gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
305 : gpgme_data_t seckey)
306 : {
307 : gpgme_error_t err;
308 :
309 6 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
310 : "pubkey=%p, seckey=%p", pubkey, seckey);
311 6 : TRACE_LOGBUF (parms, parms? strlen (parms):0);
312 :
313 6 : if (!ctx)
314 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
315 :
316 6 : err = genkey_start (ctx, 1, parms, pubkey, seckey);
317 6 : if (!err)
318 6 : err = _gpgme_wait_one (ctx);
319 6 : return TRACE_ERR (err);
320 : }
321 :
322 :
323 :
324 : static gpgme_error_t
325 48 : createkey_start (gpgme_ctx_t ctx, int synchronous,
326 : const char *userid, const char *algo,
327 : unsigned long reserved, unsigned long expires,
328 : gpgme_key_t anchorkey, unsigned int flags)
329 : {
330 : gpgme_error_t err;
331 : void *hook;
332 : op_data_t opd;
333 :
334 48 : err = _gpgme_op_reset (ctx, synchronous);
335 48 : if (err)
336 0 : return err;
337 :
338 48 : if (reserved || anchorkey || !userid)
339 0 : return gpg_error (GPG_ERR_INV_ARG);
340 :
341 48 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
342 : sizeof (*opd), release_op_data);
343 48 : opd = hook;
344 48 : if (err)
345 0 : return err;
346 :
347 48 : _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
348 :
349 48 : if (ctx->passphrase_cb)
350 : {
351 2 : err = _gpgme_engine_set_command_handler
352 : (ctx->engine, _gpgme_passphrase_command_handler, ctx);
353 2 : if (err)
354 0 : return err;
355 : }
356 :
357 48 : return _gpgme_engine_op_genkey (ctx->engine,
358 : userid, algo, reserved, expires,
359 : anchorkey, flags,
360 : NULL,
361 48 : ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
362 : NULL, NULL);
363 :
364 : }
365 :
366 :
367 : gpgme_error_t
368 0 : gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo,
369 : unsigned long reserved, unsigned long expires,
370 : gpgme_key_t anchorkey, unsigned int flags)
371 : {
372 : gpgme_error_t err;
373 :
374 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx,
375 : "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
376 :
377 0 : if (!ctx)
378 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
379 :
380 0 : err = createkey_start (ctx, 0,
381 : userid, algo, reserved, expires, anchorkey, flags);
382 0 : return TRACE_ERR (err);
383 : }
384 :
385 :
386 : gpgme_error_t
387 48 : gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
388 : unsigned long reserved, unsigned long expires,
389 : gpgme_key_t anchorkey, unsigned int flags)
390 : {
391 : gpgme_error_t err;
392 :
393 48 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx,
394 : "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
395 :
396 48 : if (!ctx)
397 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
398 :
399 48 : err = createkey_start (ctx, 1,
400 : userid, algo, reserved, expires, anchorkey, flags);
401 48 : if (!err)
402 48 : err = _gpgme_wait_one (ctx);
403 48 : return TRACE_ERR (err);
404 : }
405 :
406 :
407 :
408 : static gpgme_error_t
409 26 : createsubkey_start (gpgme_ctx_t ctx, int synchronous,
410 : gpgme_key_t key,
411 : const char *algo,
412 : unsigned long reserved, unsigned long expires,
413 : unsigned int flags)
414 : {
415 : gpgme_error_t err;
416 : void *hook;
417 : op_data_t opd;
418 :
419 26 : if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
420 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
421 :
422 26 : err = _gpgme_op_reset (ctx, synchronous);
423 26 : if (err)
424 0 : return err;
425 :
426 26 : if (reserved || !key)
427 0 : return gpg_error (GPG_ERR_INV_ARG);
428 :
429 26 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
430 : sizeof (*opd), release_op_data);
431 26 : opd = hook;
432 26 : if (err)
433 0 : return err;
434 :
435 26 : _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
436 :
437 26 : if (ctx->passphrase_cb)
438 : {
439 2 : err = _gpgme_engine_set_command_handler
440 : (ctx->engine, _gpgme_passphrase_command_handler, ctx);
441 2 : if (err)
442 0 : return err;
443 : }
444 :
445 26 : return _gpgme_engine_op_genkey (ctx->engine,
446 : NULL, algo, reserved, expires,
447 : key, flags,
448 : NULL,
449 26 : ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
450 : NULL, NULL);
451 :
452 : }
453 :
454 :
455 : /* Add a subkey to an existing KEY. */
456 : gpgme_error_t
457 0 : gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
458 : unsigned long reserved, unsigned long expires,
459 : unsigned int flags)
460 : {
461 : gpgme_error_t err;
462 :
463 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx,
464 : "key=%p, algo='%s' flags=0x%x", key, algo, flags);
465 :
466 0 : if (!ctx)
467 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
468 :
469 0 : err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags);
470 0 : return TRACE_ERR (err);
471 : }
472 :
473 :
474 : gpgme_error_t
475 26 : gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
476 : unsigned long reserved, unsigned long expires,
477 : unsigned int flags)
478 : {
479 : gpgme_error_t err;
480 :
481 26 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey", ctx,
482 : "key=%p, algo='%s' flags=0x%x", key, algo, flags);
483 :
484 26 : if (!ctx)
485 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
486 :
487 26 : err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags);
488 26 : if (!err)
489 26 : err = _gpgme_wait_one (ctx);
490 26 : return TRACE_ERR (err);
491 : }
492 :
493 :
494 :
495 : static gpgme_error_t
496 28 : addrevuid_start (gpgme_ctx_t ctx, int synchronous, int extraflags,
497 : gpgme_key_t key, const char *userid, unsigned int flags)
498 : {
499 : gpgme_error_t err;
500 : void *hook;
501 : op_data_t opd;
502 :
503 28 : if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
504 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
505 :
506 28 : if (!key || !userid)
507 0 : return gpg_error (GPG_ERR_INV_ARG);
508 :
509 28 : err = _gpgme_op_reset (ctx, synchronous);
510 28 : if (err)
511 0 : return err;
512 :
513 28 : err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
514 : sizeof (*opd), release_op_data);
515 28 : opd = hook;
516 28 : if (err)
517 0 : return err;
518 :
519 28 : opd->uidmode = extraflags? 2 : 1;
520 :
521 28 : _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
522 :
523 28 : if (ctx->passphrase_cb)
524 : {
525 1 : err = _gpgme_engine_set_command_handler
526 : (ctx->engine, _gpgme_passphrase_command_handler, ctx);
527 1 : if (err)
528 0 : return err;
529 : }
530 :
531 28 : return _gpgme_engine_op_genkey (ctx->engine,
532 : userid, NULL, 0, 0,
533 : key, flags,
534 : NULL,
535 : extraflags,
536 : NULL, NULL);
537 :
538 : }
539 :
540 :
541 : /* Add USERID to an existing KEY. */
542 : gpgme_error_t
543 0 : gpgme_op_adduid_start (gpgme_ctx_t ctx,
544 : gpgme_key_t key, const char *userid, unsigned int flags)
545 : {
546 : gpgme_error_t err;
547 :
548 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid_start", ctx,
549 : "uid='%s' flags=0x%x", userid, flags);
550 :
551 0 : if (!ctx)
552 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
553 :
554 0 : err = addrevuid_start (ctx, 0, 0, key, userid, flags);
555 0 : return TRACE_ERR (err);
556 : }
557 :
558 :
559 : gpgme_error_t
560 21 : gpgme_op_adduid (gpgme_ctx_t ctx,
561 : gpgme_key_t key, const char *userid, unsigned int flags)
562 : {
563 : gpgme_error_t err;
564 :
565 21 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid", ctx,
566 : "uid='%s' flags=0x%x", userid, flags);
567 :
568 21 : if (!ctx)
569 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
570 :
571 21 : err = addrevuid_start (ctx, 1, 0, key, userid, flags);
572 21 : if (!err)
573 21 : err = _gpgme_wait_one (ctx);
574 21 : return TRACE_ERR (err);
575 : }
576 :
577 :
578 : /* Revoke USERID from KEY. */
579 : gpgme_error_t
580 0 : gpgme_op_revuid_start (gpgme_ctx_t ctx,
581 : gpgme_key_t key, const char *userid, unsigned int flags)
582 : {
583 : gpgme_error_t err;
584 :
585 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid_start", ctx,
586 : "uid='%s' flags=0x%x", userid, flags);
587 :
588 0 : if (!ctx)
589 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
590 :
591 0 : err = addrevuid_start (ctx, 0, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
592 0 : return TRACE_ERR (err);
593 : }
594 :
595 :
596 : gpgme_error_t
597 7 : gpgme_op_revuid (gpgme_ctx_t ctx,
598 : gpgme_key_t key, const char *userid, unsigned int flags)
599 : {
600 : gpgme_error_t err;
601 :
602 7 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid", ctx,
603 : "uid='%s' flags=0x%x", userid, flags);
604 :
605 7 : if (!ctx)
606 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
607 :
608 7 : err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
609 7 : if (!err)
610 7 : err = _gpgme_wait_one (ctx);
611 7 : return TRACE_ERR (err);
612 : }
613 :
614 :
615 : /* Set a flag on the USERID of KEY. The only supported flag right now
616 : * is "primary" to mark the primary key. */
617 : static gpg_error_t
618 0 : set_uid_flag (gpgme_ctx_t ctx, int synchronous,
619 : gpgme_key_t key, const char *userid,
620 : const char *name, const char *value)
621 : {
622 : gpgme_error_t err;
623 :
624 0 : TRACE_BEG4 (DEBUG_CTX, "gpgme_op_set_uid_flag", ctx,
625 : "%d uid='%s' '%s'='%s'", synchronous, userid, name, value);
626 :
627 0 : if (!ctx || !name || !key || !userid)
628 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
629 :
630 0 : if (!strcmp (name, "primary"))
631 : {
632 0 : if (value)
633 0 : err = gpg_error (GPG_ERR_INV_ARG);
634 : else
635 0 : err = addrevuid_start (ctx, synchronous,
636 : GENKEY_EXTRAFLAG_SETPRIMARY, key, userid, 0);
637 : }
638 : else
639 0 : return err = gpg_error (GPG_ERR_UNKNOWN_NAME);
640 :
641 0 : if (synchronous && !err)
642 0 : err = _gpgme_wait_one (ctx);
643 0 : return TRACE_ERR (err);
644 : }
645 :
646 :
647 : /* See set_uid_flag. */
648 : gpgme_error_t
649 0 : gpgme_op_set_uid_flag_start (gpgme_ctx_t ctx,
650 : gpgme_key_t key, const char *userid,
651 : const char *name, const char *value)
652 : {
653 0 : return set_uid_flag (ctx, 0, key, userid, name, value);
654 : }
655 :
656 :
657 : /* See set_uid_flag. This is the synchronous variant. */
658 : gpgme_error_t
659 0 : gpgme_op_set_uid_flag (gpgme_ctx_t ctx,
660 : gpgme_key_t key, const char *userid,
661 : const char *name, const char *value)
662 : {
663 0 : return set_uid_flag (ctx, 1, key, userid, name, value);
664 : }
|