Fix Windows port (spawn and assuan engine).
[gpgme.git] / src / import.c
1 /* import.c - Import a key.
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 <errno.h>
27 #include <string.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_import_result result;
39
40   /* A pointer to the next pointer of the last import status in the
41      list.  This makes appending new imports painless while preserving
42      the order.  */
43   gpgme_import_status_t *lastp;
44 } *op_data_t;
45
46
47 static void
48 release_op_data (void *hook)
49 {
50   op_data_t opd = (op_data_t) hook;
51   gpgme_import_status_t import = opd->result.imports;
52
53   while (import)
54     {
55       gpgme_import_status_t next = import->next;
56       free (import->fpr);
57       free (import);
58       import = next;
59     }
60 }
61
62
63 gpgme_import_result_t
64 gpgme_op_import_result (gpgme_ctx_t ctx)
65 {
66   void *hook;
67   op_data_t opd;
68   gpgme_error_t err;
69
70   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx);
71
72   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
73   opd = hook;
74   if (err || !opd)
75     {
76       TRACE_SUC0 ("result=(null)");
77       return NULL;
78     }
79
80   
81   if (_gpgme_debug_trace ())
82     {
83       gpgme_import_status_t impstat;
84       int i;
85
86       TRACE_LOG5 ("%i considered, %i no UID, %i imported, %i imported RSA, "
87                   "%i unchanged", opd->result.considered,
88                   opd->result.no_user_id, opd->result.imported,
89                   opd->result.imported_rsa, opd->result.unchanged);
90       TRACE_LOG4 ("%i new UIDs, %i new sub keys, %i new signatures, "
91                   "%i new revocations", opd->result.new_user_ids,
92                   opd->result.new_sub_keys, opd->result.new_signatures,
93                   opd->result.new_revocations);
94       TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged",
95                   opd->result.secret_read, opd->result.secret_imported,
96                   opd->result.secret_unchanged);
97       TRACE_LOG2 ("%i skipped new keys, %i not imported",
98                   opd->result.skipped_new_keys, opd->result.not_imported);
99
100       impstat = opd->result.imports;
101       i = 0;
102       while (impstat)
103         {
104           TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)",
105                       i, impstat->fpr, impstat->status, impstat->result);
106           impstat = impstat->next;
107           i++;
108         }
109     }
110
111   TRACE_SUC1 ("result=%p", &opd->result);
112   return &opd->result;
113 }
114
115 \f
116 static gpgme_error_t
117 parse_import (char *args, gpgme_import_status_t *import_status, int problem)
118 {
119   gpgme_import_status_t import;
120   char *tail;
121   long int nr;
122
123   import = malloc (sizeof (*import));
124   if (!import)
125     return gpg_error_from_syserror ();
126   import->next = NULL;
127
128   gpg_err_set_errno (0);
129   nr = strtol (args, &tail, 0);
130   if (errno || args == tail || *tail != ' ')
131     {
132       /* The crypto backend does not behave.  */
133       free (import);
134       return gpg_error (GPG_ERR_INV_ENGINE);
135     }
136   args = tail;
137
138   if (problem)
139     {
140       switch (nr)
141         {
142         case 0:
143         case 4:
144         default:
145           import->result = gpg_error (GPG_ERR_GENERAL);
146           break;
147
148         case 1:
149           import->result = gpg_error (GPG_ERR_BAD_CERT);
150           break;
151
152         case 2:
153           import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
154           break;
155
156         case 3:
157           import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
158           break;
159         }
160       import->status = 0;
161     }
162   else
163     {
164       import->result = gpg_error (GPG_ERR_NO_ERROR);
165       import->status = nr;
166     }
167
168   while (*args == ' ')
169     args++;
170   tail = strchr (args, ' ');
171   if (tail)
172     *tail = '\0';
173
174   import->fpr = strdup (args);
175   if (!import->fpr)
176     {
177       int saved_errno = errno;
178       free (import);
179       return gpg_error_from_errno (saved_errno);
180     }
181
182   *import_status = import;
183   return 0;
184 }
185
186
187
188 gpgme_error_t
189 parse_import_res (char *args, gpgme_import_result_t result)
190 {
191   char *tail;
192
193   gpg_err_set_errno (0);
194
195 #define PARSE_NEXT(x)                                   \
196   (x) = strtol (args, &tail, 0);                        \
197   if (errno || args == tail || *tail != ' ')            \
198     /* The crypto backend does not behave.  */          \
199     return gpg_error (GPG_ERR_INV_ENGINE);              \
200   args = tail;
201
202   PARSE_NEXT (result->considered);
203   PARSE_NEXT (result->no_user_id);
204   PARSE_NEXT (result->imported);
205   PARSE_NEXT (result->imported_rsa);
206   PARSE_NEXT (result->unchanged);
207   PARSE_NEXT (result->new_user_ids);
208   PARSE_NEXT (result->new_sub_keys);
209   PARSE_NEXT (result->new_signatures);
210   PARSE_NEXT (result->new_revocations);
211   PARSE_NEXT (result->secret_read);
212   PARSE_NEXT (result->secret_imported);
213   PARSE_NEXT (result->secret_unchanged);
214   PARSE_NEXT (result->skipped_new_keys);
215   PARSE_NEXT (result->not_imported);
216
217   return 0;
218 }
219
220
221 static gpgme_error_t
222 import_status_handler (void *priv, gpgme_status_code_t code, char *args)
223 {
224   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
225   gpgme_error_t err;
226   void *hook;
227   op_data_t opd;
228
229   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
230   opd = hook;
231   if (err)
232     return err;
233
234   switch (code)
235     {
236     case GPGME_STATUS_IMPORT_OK:
237     case GPGME_STATUS_IMPORT_PROBLEM:
238       err = parse_import (args, opd->lastp,
239                           code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
240       if (err)
241         return err;
242
243       opd->lastp = &(*opd->lastp)->next;
244       break;
245
246     case GPGME_STATUS_IMPORT_RES:
247       err = parse_import_res (args, &opd->result);
248       break;
249
250     default:
251       break;
252     }
253   return 0;
254 }
255
256
257 static gpgme_error_t
258 _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
259 {
260   gpgme_error_t err;
261   void *hook;
262   op_data_t opd;
263
264   err = _gpgme_op_reset (ctx, synchronous);
265   if (err)
266     return err;
267
268   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
269                                sizeof (*opd), release_op_data);
270   opd = hook;
271   if (err)
272     return err;
273   opd->lastp = &opd->result.imports;
274
275   if (!keydata)
276     return gpg_error (GPG_ERR_NO_DATA);
277
278   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
279
280   return _gpgme_engine_op_import (ctx->engine, keydata, NULL);
281 }
282
283
284 gpgme_error_t
285 gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
286 {
287   gpg_error_t err;
288
289   TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx,
290               "keydata=%p", keydata);
291
292   err = _gpgme_op_import_start (ctx, 0, keydata);
293   return TRACE_ERR (err);
294 }
295
296
297 /* Import the key in KEYDATA into the keyring.  */
298 gpgme_error_t
299 gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
300 {
301   gpgme_error_t err;
302
303   TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
304               "keydata=%p", keydata);
305
306   err = _gpgme_op_import_start (ctx, 1, keydata);
307   if (!err)
308     err = _gpgme_wait_one (ctx);
309   return TRACE_ERR (err);
310 }
311
312
313 \f
314 static gpgme_error_t
315 _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous, 
316                              gpgme_key_t *keys)
317 {
318   gpgme_error_t err;
319   void *hook;
320   op_data_t opd;
321   int idx, firstidx, nkeys;
322
323   err = _gpgme_op_reset (ctx, synchronous);
324   if (err)
325     return err;
326
327   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
328                                sizeof (*opd), release_op_data);
329   opd = hook;
330   if (err)
331     return err;
332   opd->lastp = &opd->result.imports;
333
334   if (!keys)
335     return gpg_error (GPG_ERR_NO_DATA);
336
337   for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
338     {
339       /* We only consider keys of the current protocol.  */
340       if (keys[idx]->protocol != ctx->protocol)
341         continue;
342       if (firstidx == -1)
343         firstidx = idx;
344       /* If a key has been found using a different key listing mode,
345          we bail out.  This makes the processing easier.  Fixme: To
346          allow a mix of keys we would need to sort them by key listing
347          mode and start two import operations one after the other.  */
348       if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
349         return gpg_error (GPG_ERR_CONFLICT);
350       nkeys++;
351     }
352   if (!nkeys)
353     return gpg_error (GPG_ERR_NO_DATA);
354
355   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
356
357   return _gpgme_engine_op_import (ctx->engine, NULL, keys);
358 }
359
360
361 /* Asynchronous version of gpgme_op_import_key.  */
362 gpgme_error_t
363 gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
364 {
365   gpg_error_t err;
366
367   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
368   if (_gpgme_debug_trace () && keys)
369     {
370       int i = 0;
371
372       while (keys[i])
373         {
374           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
375                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ? 
376                       keys[i]->subkeys->fpr : "invalid");
377           i++;
378         }
379     }
380
381   err = _gpgme_op_import_keys_start (ctx, 0, keys);
382   return TRACE_ERR (err);
383 }
384
385
386 /* Import the keys from the array KEYS into the keyring.  This
387    function allows to move a key from one engine to another as long as
388    they are compatible.  In particular it is used to actually import
389    keys retrieved from an external source (i.e. using
390    GPGME_KEYLIST_MODE_EXTERN).  It replaces the old workaround of
391    exporting and then importing a key as used to make an X.509 key
392    permanent.  This function automagically does the right thing.
393
394    KEYS is a NULL terminated array of gpgme key objects.  The result
395    is the usual import result structure.  Only keys matching the
396    current protocol are imported; other keys are ignored.  */
397 gpgme_error_t
398 gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
399 {
400   gpgme_error_t err;
401
402   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
403   if (_gpgme_debug_trace () && keys)
404     {
405       int i = 0;
406
407       while (keys[i])
408         {
409           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
410                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ? 
411                       keys[i]->subkeys->fpr : "invalid");
412           i++;
413         }
414     }
415
416   err = _gpgme_op_import_keys_start (ctx, 1, keys);
417   if (!err)
418     err = _gpgme_wait_one (ctx);
419   return TRACE_ERR (err);
420 }
421
422
423 /* Deprecated interface.  */
424 gpgme_error_t
425 gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
426 {
427   gpgme_error_t err = gpgme_op_import (ctx, keydata);
428   if (!err && nr)
429     {
430       gpgme_import_result_t result = gpgme_op_import_result (ctx);
431       *nr = result->considered;
432     }
433   return err;
434 }