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