2004-05-21 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             gpgme_error_t err;
144
145             args += sizeof (k_alg) - 1;
146             while (*args == ' ')
147               args++;
148
149             err = _gpgme_map_gnupg_error (args);
150             if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
151               opd->result.wrong_key_usage = 1;
152           }
153       }
154       break;
155         
156     default:
157       break;
158     }
159
160   return 0;
161 }
162
163
164 static gpgme_error_t
165 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
166 {
167   gpgme_error_t err;
168
169   err = _gpgme_progress_status_handler (priv, code, args);
170   if (!err)
171     err = _gpgme_decrypt_status_handler (priv, code, args);
172   return err;
173 }
174
175
176 gpgme_error_t
177 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
178 {
179   void *hook;
180   op_data_t opd;
181
182   return _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
183                                 sizeof (*opd), release_op_data);
184 }
185
186
187 static gpgme_error_t
188 decrypt_start (gpgme_ctx_t ctx, int synchronous,
189                       gpgme_data_t cipher, gpgme_data_t plain)
190 {
191   gpgme_error_t err;
192
193   err = _gpgme_op_reset (ctx, synchronous);
194   if (err)
195     return err;
196
197   err = _gpgme_op_decrypt_init_result (ctx);
198   if (err)
199     return err;
200
201   if (!cipher)
202     return gpg_error (GPG_ERR_NO_DATA);
203   if (!plain)
204     return gpg_error (GPG_ERR_INV_VALUE);
205
206   if (err)
207     return err;
208
209   if (ctx->passphrase_cb)
210     {
211       err = _gpgme_engine_set_command_handler
212         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
213       if (err)
214         return err;
215     }
216
217   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
218
219   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
220 }
221
222
223 gpgme_error_t
224 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
225                         gpgme_data_t plain)
226 {
227   return decrypt_start (ctx, 0, cipher, plain);
228 }
229
230
231 /* Decrypt ciphertext CIPHER within CTX and store the resulting
232    plaintext in PLAIN.  */
233 gpgme_error_t
234 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
235 {
236   gpgme_error_t err = decrypt_start (ctx, 1, cipher, plain);
237   if (!err)
238     err = _gpgme_wait_one (ctx);
239   return err;
240 }