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