2004-09-30 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / 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 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 <string.h>
26 #include <errno.h>
27
28 #include "gpgme.h"
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32
33 \f
34 typedef struct
35 {
36   struct _gpgme_op_decrypt_result result;
37
38   int okay;
39   int failed;
40 } *op_data_t;
41
42
43 static void
44 release_op_data (void *hook)
45 {
46   op_data_t opd = (op_data_t) hook;
47
48   if (opd->result.unsupported_algorithm)
49     free (opd->result.unsupported_algorithm);
50 }
51
52
53 gpgme_decrypt_result_t
54 gpgme_op_decrypt_result (gpgme_ctx_t ctx)
55 {
56   void *hook;
57   op_data_t opd;
58   gpgme_error_t err;
59
60   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
61   opd = hook;
62   if (err || !opd)
63     return NULL;
64
65   return &opd->result;
66 }
67
68 \f
69 gpgme_error_t
70 _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
71                                char *args)
72 {
73   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
74   gpgme_error_t err;
75   void *hook;
76   op_data_t opd;
77
78   err = _gpgme_passphrase_status_handler (priv, code, args);
79   if (err)
80     return err;
81
82   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
83   opd = hook;
84   if (err)
85     return err;
86
87   switch (code)
88     {
89     case GPGME_STATUS_EOF:
90       /* FIXME: These error values should probably be attributed to
91          the underlying crypto engine (as error source).  */
92       if (opd->failed)
93         return gpg_error (GPG_ERR_DECRYPT_FAILED);
94       else if (!opd->okay)
95         return gpg_error (GPG_ERR_NO_DATA);
96       break;
97
98     case GPGME_STATUS_DECRYPTION_OKAY:
99       opd->okay = 1;
100       break;
101
102     case GPGME_STATUS_DECRYPTION_FAILED:
103       opd->failed = 1;
104       break;
105
106     case GPGME_STATUS_ERROR:
107       /* Note that this is an informational status code which should
108          not lead to an error return unless it is something not
109          related to the backend.  */
110       {
111         const char d_alg[] = "decrypt.algorithm";
112         const char u_alg[] = "Unsupported_Algorithm";
113         const char k_alg[] = "decrypt.keyusage";
114
115         if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
116           {
117             args += sizeof (d_alg) - 1;
118             while (*args == ' ')
119               args++;
120
121             if (!strncmp (args, u_alg, sizeof (u_alg) - 1))
122               {
123                 char *end;
124
125                 args += sizeof (u_alg) - 1;
126                 while (*args == ' ')
127                   args++;
128
129                 end = strchr (args, ' ');
130                 if (end)
131                   *end = '\0';
132
133                 if (!(*args == '?' && *(args + 1) == '\0'))
134                   {
135                     opd->result.unsupported_algorithm = strdup (args);
136                     if (!opd->result.unsupported_algorithm)
137                       return gpg_error_from_errno (errno);
138                   }
139               }
140           }
141         else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
142           {
143             args += sizeof (k_alg) - 1;
144             while (*args == ' ')
145               args++;
146
147             err = _gpgme_map_gnupg_error (args);
148             if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
149               opd->result.wrong_key_usage = 1;
150           }
151       }
152       break;
153         
154     default:
155       break;
156     }
157
158   return 0;
159 }
160
161
162 static gpgme_error_t
163 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
164 {
165   gpgme_error_t err;
166
167   err = _gpgme_progress_status_handler (priv, code, args);
168   if (!err)
169     err = _gpgme_decrypt_status_handler (priv, code, args);
170   return err;
171 }
172
173
174 gpgme_error_t
175 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
176 {
177   void *hook;
178   op_data_t opd;
179
180   return _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
181                                 sizeof (*opd), release_op_data);
182 }
183
184
185 static gpgme_error_t
186 decrypt_start (gpgme_ctx_t ctx, int synchronous,
187                       gpgme_data_t cipher, gpgme_data_t plain)
188 {
189   gpgme_error_t err;
190
191   err = _gpgme_op_reset (ctx, synchronous);
192   if (err)
193     return err;
194
195   err = _gpgme_op_decrypt_init_result (ctx);
196   if (err)
197     return err;
198
199   if (!cipher)
200     return gpg_error (GPG_ERR_NO_DATA);
201   if (!plain)
202     return gpg_error (GPG_ERR_INV_VALUE);
203
204   if (err)
205     return err;
206
207   if (ctx->passphrase_cb)
208     {
209       err = _gpgme_engine_set_command_handler
210         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
211       if (err)
212         return err;
213     }
214
215   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
216
217   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
218 }
219
220
221 gpgme_error_t
222 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
223                         gpgme_data_t plain)
224 {
225   return decrypt_start (ctx, 0, cipher, plain);
226 }
227
228
229 /* Decrypt ciphertext CIPHER within CTX and store the resulting
230    plaintext in PLAIN.  */
231 gpgme_error_t
232 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
233 {
234   gpgme_error_t err = decrypt_start (ctx, 1, cipher, plain);
235   if (!err)
236     err = _gpgme_wait_one (ctx);
237   return err;
238 }