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