2004-06-23 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / import.c
1 /* import.c - Import a key.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 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 General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (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    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #include "gpgme.h"
29 #include "context.h"
30 #include "ops.h"
31
32 \f
33 typedef struct
34 {
35   struct _gpgme_op_import_result result;
36
37   /* A pointer to the next pointer of the last import status in the
38      list.  This makes appending new imports painless while preserving
39      the order.  */
40   gpgme_import_status_t *lastp;
41 } *op_data_t;
42
43
44 static void
45 release_op_data (void *hook)
46 {
47   op_data_t opd = (op_data_t) hook;
48   gpgme_import_status_t import = opd->result.imports;
49
50   while (import)
51     {
52       gpgme_import_status_t next = import->next;
53       free (import->fpr);
54       free (import);
55       import = next;
56     }
57 }
58
59
60 gpgme_import_result_t
61 gpgme_op_import_result (gpgme_ctx_t ctx)
62 {
63   void *hook;
64   op_data_t opd;
65   gpgme_error_t err;
66
67   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
68   opd = hook;
69   if (err || !opd)
70     return NULL;
71
72   return &opd->result;
73 }
74
75 \f
76 static gpgme_error_t
77 parse_import (char *args, gpgme_import_status_t *import_status, int problem)
78 {
79   gpgme_import_status_t import;
80   char *tail;
81   long int nr;
82
83   import = malloc (sizeof (*import));
84   if (!import)
85     return gpg_error_from_errno (errno);
86   import->next = NULL;
87
88   errno = 0;
89   nr = strtol (args, &tail, 0);
90   if (errno || args == tail || *tail != ' ')
91     {
92       /* The crypto backend does not behave.  */
93       free (import);
94       return gpg_error (GPG_ERR_INV_ENGINE);
95     }
96   args = tail;
97
98   if (problem)
99     {
100       switch (nr)
101         {
102         case 0:
103         case 4:
104         default:
105           import->result = gpg_error (GPG_ERR_GENERAL);
106           break;
107
108         case 1:
109           import->result = gpg_error (GPG_ERR_BAD_CERT);
110           break;
111
112         case 2:
113           import->result = gpg_error (GPG_ERR_MISSING_CERT);
114           break;
115
116         case 3:
117           import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
118           break;
119         }
120       import->status = 0;
121     }
122   else
123     {
124       import->result = gpg_error (GPG_ERR_NO_ERROR);
125       import->status = nr;
126     }
127
128   while (*args == ' ')
129     args++;
130   tail = strchr (args, ' ');
131   if (tail)
132     *tail = '\0';
133
134   import->fpr = strdup (args);
135   if (!import->fpr)
136     {
137       int saved_errno = errno;
138       free (import);
139       return gpg_error_from_errno (saved_errno);
140     }
141
142   *import_status = import;
143   return 0;
144 }
145
146
147
148 gpgme_error_t
149 parse_import_res (char *args, gpgme_import_result_t result)
150 {
151   char *tail;
152
153   errno = 0;
154
155 #define PARSE_NEXT(x)                                   \
156   (x) = strtol (args, &tail, 0);                        \
157   if (errno || args == tail || *tail != ' ')            \
158     /* The crypto backend does not behave.  */          \
159     return gpg_error (GPG_ERR_INV_ENGINE);              \
160   args = tail;
161
162   PARSE_NEXT (result->considered);
163   PARSE_NEXT (result->no_user_id);
164   PARSE_NEXT (result->imported);
165   PARSE_NEXT (result->imported_rsa);
166   PARSE_NEXT (result->unchanged);
167   PARSE_NEXT (result->new_user_ids);
168   PARSE_NEXT (result->new_sub_keys);
169   PARSE_NEXT (result->new_signatures);
170   PARSE_NEXT (result->new_revocations);
171   PARSE_NEXT (result->secret_read);
172   PARSE_NEXT (result->secret_imported);
173   PARSE_NEXT (result->secret_unchanged);
174   PARSE_NEXT (result->skipped_new_keys);
175   PARSE_NEXT (result->not_imported);
176
177   return 0;
178 }
179
180
181 static gpgme_error_t
182 import_status_handler (void *priv, gpgme_status_code_t code, char *args)
183 {
184   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
185   gpgme_error_t err;
186   void *hook;
187   op_data_t opd;
188
189   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
190   opd = hook;
191   if (err)
192     return err;
193
194   switch (code)
195     {
196     case GPGME_STATUS_IMPORT_OK:
197     case GPGME_STATUS_IMPORT_PROBLEM:
198       err = parse_import (args, opd->lastp,
199                           code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
200       if (err)
201         return err;
202
203       opd->lastp = &(*opd->lastp)->next;
204       break;
205
206     case GPGME_STATUS_IMPORT_RES:
207       err = parse_import_res (args, &opd->result);
208       break;
209
210     default:
211       break;
212     }
213   return 0;
214 }
215
216
217 static gpgme_error_t
218 _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
219 {
220   gpgme_error_t err;
221   void *hook;
222   op_data_t opd;
223
224   err = _gpgme_op_reset (ctx, synchronous);
225   if (err)
226     return err;
227
228   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
229                                sizeof (*opd), release_op_data);
230   opd = hook;
231   if (err)
232     return err;
233   opd->lastp = &opd->result.imports;
234
235   if (!keydata)
236     return gpg_error (GPG_ERR_NO_DATA);
237
238   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
239
240   return _gpgme_engine_op_import (ctx->engine, keydata);
241 }
242
243
244 gpgme_error_t
245 gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
246 {
247   return _gpgme_op_import_start (ctx, 0, keydata);
248 }
249
250
251 /* Import the key in KEYDATA into the keyring.  */
252 gpgme_error_t
253 gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
254 {
255   gpgme_error_t err = _gpgme_op_import_start (ctx, 1, keydata);
256   if (!err)
257     err = _gpgme_wait_one (ctx);
258   return err;
259 }
260
261
262 gpgme_error_t
263 gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
264 {
265   gpgme_error_t err = gpgme_op_import (ctx, keydata);
266   if (!err && nr)
267     {
268       gpgme_import_result_t result = gpgme_op_import_result (ctx);
269       *nr = result->considered;
270     }
271   return err;
272 }