2010-06-09 Marcus Brinkmann <marcus@g10code.de>
[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
34 \f
35 typedef struct
36 {
37   struct _gpgme_op_genkey_result result;
38
39   /* The key parameters passed to the crypto engine.  */
40   gpgme_data_t key_parameter;
41 } *op_data_t;
42
43
44 static void
45 release_op_data (void *hook)
46 {
47   op_data_t opd = (op_data_t) hook;
48   
49   if (opd->result.fpr)
50     free (opd->result.fpr);
51   if (opd->key_parameter)
52     gpgme_data_release (opd->key_parameter);
53 }
54
55
56 gpgme_genkey_result_t
57 gpgme_op_genkey_result (gpgme_ctx_t ctx)
58 {
59   void *hook;
60   op_data_t opd;
61   gpgme_error_t err;
62
63   TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
64
65   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
66   opd = hook;
67   if (err || !opd)
68     {
69       TRACE_SUC0 ("result=(null)");
70       return NULL;
71     }
72
73   TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
74               opd->result.primary ? "primary" : "no primary",
75               opd->result.sub ? "sub" : "no sub");
76
77   TRACE_SUC1 ("result=%p", &opd->result);
78   return &opd->result;
79 }
80
81 \f
82 static gpgme_error_t
83 genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
84 {
85   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
86   gpgme_error_t err;
87   void *hook;
88   op_data_t opd;
89
90   /* Pipe the status code through the progress status handler.  */
91   err = _gpgme_progress_status_handler (ctx, code, args);
92   if (err)
93     return err;
94
95   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
96   opd = hook;
97   if (err)
98     return err;
99
100   switch (code)
101     {
102     case GPGME_STATUS_KEY_CREATED:
103       if (args && *args)
104         {
105           if (*args == 'B' || *args == 'P')
106             opd->result.primary = 1;
107           if (*args == 'B' || *args == 'S')
108             opd->result.sub = 1;
109           if (args[1] == ' ')
110             {
111               if (opd->result.fpr)
112                 free (opd->result.fpr);
113               opd->result.fpr = strdup (&args[2]);
114               if (!opd->result.fpr)
115                 return gpg_error_from_errno (errno);
116             }
117         }
118       break;
119
120     case GPGME_STATUS_EOF:
121       /* FIXME: Should return some more useful error value.  */
122       if (!opd->result.primary && !opd->result.sub)
123         return gpg_error (GPG_ERR_GENERAL);
124       break;
125
126     default:
127       break;
128     }
129   return 0;
130 }
131
132
133 static gpgme_error_t
134 get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
135 {
136   const char *content;
137   const char *attrib;
138   const char *endtag;
139
140   /* Extract the key parameter from the XML structure.  */
141   parms = strstr (parms, "<GnupgKeyParms ");
142   if (!parms)
143     return gpg_error (GPG_ERR_INV_VALUE);
144
145   content = strchr (parms, '>');
146   if (!content)
147     return gpg_error (GPG_ERR_INV_VALUE);
148   content++;
149
150   attrib = strstr (parms, "format=\"internal\"");
151   if (!attrib || attrib >= content)
152     return gpg_error (GPG_ERR_INV_VALUE);
153
154   endtag = strstr (content, "</GnupgKeyParms>");
155   /* FIXME: Check that there are no control statements inside.  */
156   while (content[0] == '\n'
157          || (content[0] == '\r' && content[1] == '\n'))
158     content++;
159
160   return gpgme_data_new_from_mem (key_parameter, content,
161                                   endtag - content, 1);
162 }
163
164
165 static gpgme_error_t
166 genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
167               gpgme_data_t pubkey, gpgme_data_t seckey)
168 {
169   gpgme_error_t err;
170   void *hook;
171   op_data_t opd;
172   err = _gpgme_op_reset (ctx, synchronous);
173   if (err)
174     return err;
175   
176   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
177                                sizeof (*opd), release_op_data);
178   opd = hook;
179   if (err)
180     return err;
181
182   err = get_key_parameter (parms, &opd->key_parameter);
183   if (err)
184     return err;
185
186   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
187
188   return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
189                                   ctx->use_armor, pubkey, seckey);
190 }
191
192
193 /* Generate a new keypair and add it to the keyring.  PUBKEY and
194    SECKEY should be null for now.  PARMS specifies what keys should be
195    generated.  */
196 gpgme_error_t
197 gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
198                        gpgme_data_t pubkey, gpgme_data_t seckey)
199 {
200   gpgme_error_t err;
201
202   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
203               "pubkey=%p, seckey=%p", pubkey, seckey);
204   TRACE_LOGBUF (parms, strlen (parms));
205   err = genkey_start (ctx, 0, parms, pubkey, seckey);
206   return TRACE_ERR (err);
207 }
208
209
210 /* Generate a new keypair and add it to the keyring.  PUBKEY and
211    SECKEY should be null for now.  PARMS specifies what keys should be
212    generated.  */
213 gpgme_error_t
214 gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
215                  gpgme_data_t seckey)
216 {
217   gpgme_error_t err;
218
219   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
220               "pubkey=%p, seckey=%p", pubkey, seckey);
221   TRACE_LOGBUF (parms, strlen (parms));
222
223   err = genkey_start (ctx, 1, parms, pubkey, seckey);
224   if (!err)
225     err = _gpgme_wait_one (ctx);
226   return TRACE_ERR (err);
227 }