Rework status table to be less dynamically generated.
[gpgme.git] / src / decrypt.c
1 /* decrypt.c - Decrypt function.
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 <string.h>
27 #include <errno.h>
28
29 #include "debug.h"
30 #include "gpgme.h"
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34
35
36 \f
37 typedef struct
38 {
39   struct _gpgme_op_decrypt_result result;
40
41   int okay;
42   int failed;
43
44   /* A pointer to the next pointer of the last recipient in the list.
45      This makes appending new invalid signers painless while
46      preserving the order.  */
47   gpgme_recipient_t *last_recipient_p;
48 } *op_data_t;
49
50
51 static void
52 release_op_data (void *hook)
53 {
54   op_data_t opd = (op_data_t) hook;
55   gpgme_recipient_t recipient = opd->result.recipients;
56
57   if (opd->result.unsupported_algorithm)
58     free (opd->result.unsupported_algorithm);
59
60   if (opd->result.file_name)
61     free (opd->result.file_name);
62
63   while (recipient)
64     {
65       gpgme_recipient_t next = recipient->next;
66       free (recipient);
67       recipient = next;
68     }
69 }
70
71
72 gpgme_decrypt_result_t
73 gpgme_op_decrypt_result (gpgme_ctx_t ctx)
74 {
75   void *hook;
76   op_data_t opd;
77   gpgme_error_t err;
78
79   TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
80
81   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
82   opd = hook;
83   if (err || !opd)
84     {
85       TRACE_SUC0 ("result=(null)");
86       return NULL;
87     }
88
89   if (_gpgme_debug_trace ())
90     {
91       gpgme_recipient_t rcp;
92
93       if (opd->result.unsupported_algorithm)
94         {
95           TRACE_LOG1 ("result: unsupported_algorithm: %s",
96                       opd->result.unsupported_algorithm);
97         }
98       if (opd->result.wrong_key_usage)
99         {
100           TRACE_LOG ("result: wrong key usage");
101         }
102       rcp = opd->result.recipients;
103       while (rcp)
104         {
105           TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
106                       "status=%s", rcp->keyid, rcp->pubkey_algo,
107                       gpg_strerror (rcp->status));
108           rcp = rcp->next;
109         }
110       if (opd->result.file_name)
111         {
112           TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
113         }
114     }
115
116   TRACE_SUC1 ("result=%p", &opd->result);
117   return &opd->result;
118 }
119
120 \f
121 static gpgme_error_t
122 parse_enc_to (char *args, gpgme_recipient_t *recp)
123 {
124   gpgme_recipient_t rec;
125   char *tail;
126   int i;
127
128   rec = malloc (sizeof (*rec));
129   if (!rec)
130     return gpg_error_from_syserror ();
131
132   rec->next = NULL;
133   rec->keyid = rec->_keyid;
134   rec->status = 0;
135
136   for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
137     {
138       if (args[i] == '\0' || args[i] == ' ')
139         break;
140
141       rec->_keyid[i] = args[i];
142     }
143   rec->_keyid[i] = '\0';
144
145   args = &args[i];
146   if (*args != '\0' && *args != ' ')
147     {
148       free (rec);
149       return gpg_error (GPG_ERR_INV_ENGINE);
150     }
151
152   while (*args == ' ')
153     args++;
154
155   if (*args)
156     {
157       gpg_err_set_errno (0);
158       rec->pubkey_algo = strtol (args, &tail, 0);
159       if (errno || args == tail || *tail != ' ')
160         {
161           /* The crypto backend does not behave.  */
162           free (rec);
163           return gpg_error (GPG_ERR_INV_ENGINE);
164         }
165     }
166
167   /* FIXME: The key length is always 0 right now, so no need to parse
168      it.  */
169
170   *recp = rec;
171   return 0;
172 }
173
174
175 gpgme_error_t
176 _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
177                                char *args)
178 {
179   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
180   gpgme_error_t err;
181   void *hook;
182   op_data_t opd;
183
184   err = _gpgme_passphrase_status_handler (priv, code, args);
185   if (err)
186     return err;
187
188   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
189   opd = hook;
190   if (err)
191     return err;
192
193   switch (code)
194     {
195     case GPGME_STATUS_EOF:
196       /* FIXME: These error values should probably be attributed to
197          the underlying crypto engine (as error source).  */
198       if (opd->failed)
199         return gpg_error (GPG_ERR_DECRYPT_FAILED);
200       else if (!opd->okay)
201         return gpg_error (GPG_ERR_NO_DATA);
202       break;
203
204     case GPGME_STATUS_DECRYPTION_INFO:
205       /* Fixme: Provide a way to return the used symmetric algorithm. */
206       break;
207
208     case GPGME_STATUS_DECRYPTION_OKAY:
209       opd->okay = 1;
210       break;
211
212     case GPGME_STATUS_DECRYPTION_FAILED:
213       opd->failed = 1;
214       break;
215
216     case GPGME_STATUS_ERROR:
217       /* Note that this is an informational status code which should
218          not lead to an error return unless it is something not
219          related to the backend.  */
220       {
221         const char d_alg[] = "decrypt.algorithm";
222         const char k_alg[] = "decrypt.keyusage";
223
224         if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
225           {
226             args += sizeof (d_alg) - 1;
227             while (*args == ' ')
228               args++;
229
230             if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
231               {
232                 char *end;
233
234                 while (*args && *args != ' ')
235                   args++;
236                 while (*args == ' ')
237                   args++;
238
239                 end = strchr (args, ' ');
240                 if (end)
241                   *end = '\0';
242
243                 if (!(*args == '?' && *(args + 1) == '\0'))
244                   {
245                     opd->result.unsupported_algorithm = strdup (args);
246                     if (!opd->result.unsupported_algorithm)
247                       return gpg_error_from_syserror ();
248                   }
249               }
250           }
251         else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
252           {
253             args += sizeof (k_alg) - 1;
254             while (*args == ' ')
255               args++;
256
257             if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
258               opd->result.wrong_key_usage = 1;
259           }
260       }
261       break;
262
263     case GPGME_STATUS_ENC_TO:
264       err = parse_enc_to (args, opd->last_recipient_p);
265       if (err)
266         return err;
267
268       opd->last_recipient_p = &(*opd->last_recipient_p)->next;
269       break;
270
271     case GPGME_STATUS_NO_SECKEY:
272       {
273         gpgme_recipient_t rec = opd->result.recipients;
274
275         while (rec)
276           {
277             if (!strcmp (rec->keyid, args))
278               {
279                 rec->status = gpg_error (GPG_ERR_NO_SECKEY);
280                 break;
281               }
282             rec = rec->next;
283           }
284         /* FIXME: Is this ok?  */
285         if (!rec)
286           return gpg_error (GPG_ERR_INV_ENGINE);
287       }
288       break;
289
290     case GPGME_STATUS_PLAINTEXT:
291       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
292       if (err)
293         return err;
294
295     default:
296       break;
297     }
298
299   return 0;
300 }
301
302
303 static gpgme_error_t
304 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
305 {
306   gpgme_error_t err;
307
308   err = _gpgme_progress_status_handler (priv, code, args);
309   if (!err)
310     err = _gpgme_decrypt_status_handler (priv, code, args);
311   return err;
312 }
313
314
315 gpgme_error_t
316 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
317 {
318   gpgme_error_t err;
319   void *hook;
320   op_data_t opd;
321
322   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
323                                sizeof (*opd), release_op_data);
324   opd = hook;
325   if (err)
326     return err;
327
328   opd->last_recipient_p = &opd->result.recipients;
329   return 0;
330 }
331
332
333 static gpgme_error_t
334 decrypt_start (gpgme_ctx_t ctx, int synchronous,
335                       gpgme_data_t cipher, gpgme_data_t plain)
336 {
337   gpgme_error_t err;
338
339   err = _gpgme_op_reset (ctx, synchronous);
340   if (err)
341     return err;
342
343   err = _gpgme_op_decrypt_init_result (ctx);
344   if (err)
345     return err;
346
347   if (!cipher)
348     return gpg_error (GPG_ERR_NO_DATA);
349   if (!plain)
350     return gpg_error (GPG_ERR_INV_VALUE);
351
352   if (err)
353     return err;
354
355   if (ctx->passphrase_cb)
356     {
357       err = _gpgme_engine_set_command_handler
358         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
359       if (err)
360         return err;
361     }
362
363   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
364
365   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
366 }
367
368
369 gpgme_error_t
370 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
371                         gpgme_data_t plain)
372 {
373   gpgme_error_t err;
374
375   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
376               "cipher=%p, plain=%p", cipher, plain);
377
378   if (!ctx)
379     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
380   
381   err = decrypt_start (ctx, 0, cipher, plain);
382   return TRACE_ERR (err);
383 }
384
385
386 /* Decrypt ciphertext CIPHER within CTX and store the resulting
387    plaintext in PLAIN.  */
388 gpgme_error_t
389 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
390 {
391   gpgme_error_t err;
392
393   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx,
394               "cipher=%p, plain=%p", cipher, plain);
395
396   if (!ctx)
397     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
398   
399   err = decrypt_start (ctx, 1, cipher, plain);
400   if (!err)
401     err = _gpgme_wait_one (ctx);
402   return TRACE_ERR (err);
403 }