f599c232cb7fb423f22312248be1d9499e49acfc
[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   if (!ctx)
293     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
294
295   err = _gpgme_op_import_start (ctx, 0, keydata);
296   return TRACE_ERR (err);
297 }
298
299
300 /* Import the key in KEYDATA into the keyring.  */
301 gpgme_error_t
302 gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
303 {
304   gpgme_error_t err;
305
306   TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
307               "keydata=%p", keydata);
308
309   if (!ctx)
310     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
311
312   err = _gpgme_op_import_start (ctx, 1, keydata);
313   if (!err)
314     err = _gpgme_wait_one (ctx);
315   return TRACE_ERR (err);
316 }
317
318
319 \f
320 static gpgme_error_t
321 _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous,
322                              gpgme_key_t *keys)
323 {
324   gpgme_error_t err;
325   void *hook;
326   op_data_t opd;
327   int idx, firstidx, nkeys;
328
329   err = _gpgme_op_reset (ctx, synchronous);
330   if (err)
331     return err;
332
333   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
334                                sizeof (*opd), release_op_data);
335   opd = hook;
336   if (err)
337     return err;
338   opd->lastp = &opd->result.imports;
339
340   if (!keys)
341     return gpg_error (GPG_ERR_NO_DATA);
342
343   for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
344     {
345       /* We only consider keys of the current protocol.  */
346       if (keys[idx]->protocol != ctx->protocol)
347         continue;
348       if (firstidx == -1)
349         firstidx = idx;
350       /* If a key has been found using a different key listing mode,
351          we bail out.  This makes the processing easier.  Fixme: To
352          allow a mix of keys we would need to sort them by key listing
353          mode and start two import operations one after the other.  */
354       if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
355         return gpg_error (GPG_ERR_CONFLICT);
356       nkeys++;
357     }
358   if (!nkeys)
359     return gpg_error (GPG_ERR_NO_DATA);
360
361   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
362
363   return _gpgme_engine_op_import (ctx->engine, NULL, keys);
364 }
365
366
367 /* Asynchronous version of gpgme_op_import_key.  */
368 gpgme_error_t
369 gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
370 {
371   gpg_error_t err;
372
373   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
374
375   if (!ctx)
376     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
377
378   if (_gpgme_debug_trace () && keys)
379     {
380       int i = 0;
381
382       while (keys[i])
383         {
384           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
385                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
386                       keys[i]->subkeys->fpr : "invalid");
387           i++;
388         }
389     }
390
391   err = _gpgme_op_import_keys_start (ctx, 0, keys);
392   return TRACE_ERR (err);
393 }
394
395
396 /* Import the keys from the array KEYS into the keyring.  This
397    function allows to move a key from one engine to another as long as
398    they are compatible.  In particular it is used to actually import
399    keys retrieved from an external source (i.e. using
400    GPGME_KEYLIST_MODE_EXTERN).  It replaces the old workaround of
401    exporting and then importing a key as used to make an X.509 key
402    permanent.  This function automagically does the right thing.
403
404    KEYS is a NULL terminated array of gpgme key objects.  The result
405    is the usual import result structure.  Only keys matching the
406    current protocol are imported; other keys are ignored.  */
407 gpgme_error_t
408 gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
409 {
410   gpgme_error_t err;
411
412   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
413
414   if (!ctx)
415     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
416
417   if (_gpgme_debug_trace () && keys)
418     {
419       int i = 0;
420
421       while (keys[i])
422         {
423           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
424                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
425                       keys[i]->subkeys->fpr : "invalid");
426           i++;
427         }
428     }
429
430   err = _gpgme_op_import_keys_start (ctx, 1, keys);
431   if (!err)
432     err = _gpgme_wait_one (ctx);
433   return TRACE_ERR (err);
434 }
435
436
437 /* Deprecated interface.  */
438 gpgme_error_t
439 gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
440 {
441   gpgme_error_t err = gpgme_op_import (ctx, keydata);
442   if (!err && nr)
443     {
444       gpgme_import_result_t result = gpgme_op_import_result (ctx);
445       *nr = result->considered;
446     }
447   return err;
448 }