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