w32: Improve locating gpgconf on 64 bit systems.
[gpgme.git] / src / genkey.c
1 /* genkey.c - Key generation.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 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, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "gpgme.h"
30 #include "debug.h"
31 #include "context.h"
32 #include "ops.h"
33 #include "util.h"
34
35 \f
36 typedef struct
37 {
38   struct _gpgme_op_genkey_result result;
39
40   /* The error code from a FAILURE status line or 0.  */
41   gpg_error_t failure_code;
42
43   /* The key parameters passed to the crypto engine.  */
44   gpgme_data_t key_parameter;
45 } *op_data_t;
46
47
48 static void
49 release_op_data (void *hook)
50 {
51   op_data_t opd = (op_data_t) hook;
52
53   if (opd->result.fpr)
54     free (opd->result.fpr);
55   if (opd->key_parameter)
56     gpgme_data_release (opd->key_parameter);
57 }
58
59
60 gpgme_genkey_result_t
61 gpgme_op_genkey_result (gpgme_ctx_t ctx)
62 {
63   void *hook;
64   op_data_t opd;
65   gpgme_error_t err;
66
67   TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
68
69   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
70   opd = hook;
71   if (err || !opd)
72     {
73       TRACE_SUC0 ("result=(null)");
74       return NULL;
75     }
76
77   TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
78               opd->result.primary ? "primary" : "no primary",
79               opd->result.sub ? "sub" : "no sub");
80
81   TRACE_SUC1 ("result=%p", &opd->result);
82   return &opd->result;
83 }
84
85 \f
86 static gpgme_error_t
87 genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
88 {
89   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
90   gpgme_error_t err;
91   void *hook;
92   op_data_t opd;
93
94   /* Pipe the status code through the progress status handler.  */
95   err = _gpgme_progress_status_handler (ctx, code, args);
96   if (err)
97     return err;
98
99   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
100   opd = hook;
101   if (err)
102     return err;
103
104   switch (code)
105     {
106     case GPGME_STATUS_KEY_CREATED:
107       if (args && *args)
108         {
109           if (*args == 'B' || *args == 'P')
110             opd->result.primary = 1;
111           if (*args == 'B' || *args == 'S')
112             opd->result.sub = 1;
113           if (args[1] == ' ')
114             {
115               if (opd->result.fpr)
116                 free (opd->result.fpr);
117               opd->result.fpr = strdup (&args[2]);
118               if (!opd->result.fpr)
119                 return gpg_error_from_syserror ();
120             }
121         }
122       break;
123
124     case GPGME_STATUS_FAILURE:
125       opd->failure_code = _gpgme_parse_failure (args);
126       break;
127
128     case GPGME_STATUS_EOF:
129       /* FIXME: Should return some more useful error value.  */
130       if (!opd->result.primary && !opd->result.sub)
131         return gpg_error (GPG_ERR_GENERAL);
132       else if (opd->failure_code)
133         return opd->failure_code;
134       break;
135
136     case GPGME_STATUS_INQUIRE_MAXLEN:
137       if (ctx->status_cb)
138         {
139           err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
140           if (err)
141             return err;
142         }
143       break;
144
145     default:
146       break;
147     }
148   return 0;
149 }
150
151
152 static gpgme_error_t
153 get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
154 {
155   const char *content;
156   const char *attrib;
157   const char *endtag;
158
159   /* Extract the key parameter from the XML structure.  */
160   parms = strstr (parms, "<GnupgKeyParms ");
161   if (!parms)
162     return gpg_error (GPG_ERR_INV_VALUE);
163
164   content = strchr (parms, '>');
165   if (!content)
166     return gpg_error (GPG_ERR_INV_VALUE);
167   content++;
168
169   attrib = strstr (parms, "format=\"internal\"");
170   if (!attrib || attrib >= content)
171     return gpg_error (GPG_ERR_INV_VALUE);
172
173   endtag = strstr (content, "</GnupgKeyParms>");
174   /* FIXME: Check that there are no control statements inside.  */
175   while (content[0] == '\n'
176          || (content[0] == '\r' && content[1] == '\n'))
177     content++;
178
179   return gpgme_data_new_from_mem (key_parameter, content,
180                                   endtag - content, 1);
181 }
182
183
184 static gpgme_error_t
185 genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
186               gpgme_data_t pubkey, gpgme_data_t seckey)
187 {
188   gpgme_error_t err;
189   void *hook;
190   op_data_t opd;
191   err = _gpgme_op_reset (ctx, synchronous);
192   if (err)
193     return err;
194
195   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
196                                sizeof (*opd), release_op_data);
197   opd = hook;
198   if (err)
199     return err;
200
201   err = get_key_parameter (parms, &opd->key_parameter);
202   if (err)
203     return err;
204
205   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
206
207   if (ctx->passphrase_cb)
208     {
209       err = _gpgme_engine_set_command_handler
210         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
211       if (err)
212         return err;
213     }
214
215   return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
216                                   ctx->use_armor, pubkey, seckey);
217 }
218
219
220 /* Generate a new keypair and add it to the keyring.  PUBKEY and
221    SECKEY should be null for now.  PARMS specifies what keys should be
222    generated.  */
223 gpgme_error_t
224 gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
225                        gpgme_data_t pubkey, gpgme_data_t seckey)
226 {
227   gpgme_error_t err;
228
229   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
230               "pubkey=%p, seckey=%p", pubkey, seckey);
231   TRACE_LOGBUF (parms, strlen (parms));
232
233   if (!ctx)
234     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
235
236   err = genkey_start (ctx, 0, parms, pubkey, seckey);
237   return TRACE_ERR (err);
238 }
239
240
241 /* Generate a new keypair and add it to the keyring.  PUBKEY and
242    SECKEY should be null for now.  PARMS specifies what keys should be
243    generated.  */
244 gpgme_error_t
245 gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
246                  gpgme_data_t seckey)
247 {
248   gpgme_error_t err;
249
250   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
251               "pubkey=%p, seckey=%p", pubkey, seckey);
252   TRACE_LOGBUF (parms, strlen (parms));
253
254   if (!ctx)
255     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
256
257   err = genkey_start (ctx, 1, parms, pubkey, seckey);
258   if (!err)
259     err = _gpgme_wait_one (ctx);
260   return TRACE_ERR (err);
261 }