w32: Better protect the IO-system's fd_table
[gpgme.git] / src / encrypt.c
1 /* encrypt.c - Encrypt 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 "gpgme.h"
30 #include "debug.h"
31 #include "context.h"
32 #include "ops.h"
33
34 \f
35 typedef struct
36 {
37   struct _gpgme_op_encrypt_result result;
38
39   /* The error code from a FAILURE status line or 0.  */
40   gpg_error_t failure_code;
41
42   /* The fingerprint from the last KEY_CONSIDERED status line.  */
43   char *kc_fpr;
44
45   /* The flags from the last KEY_CONSIDERED status line.  */
46   unsigned int kc_flags;
47
48   /* A pointer to the next pointer of the last invalid recipient in
49      the list.  This makes appending new invalid recipients painless
50      while preserving the order.  */
51   gpgme_invalid_key_t *lastp;
52 } *op_data_t;
53
54
55 static void
56 release_op_data (void *hook)
57 {
58   op_data_t opd = (op_data_t) hook;
59   gpgme_invalid_key_t invalid_recipient = opd->result.invalid_recipients;
60
61   while (invalid_recipient)
62     {
63       gpgme_invalid_key_t next = invalid_recipient->next;
64       if (invalid_recipient->fpr)
65         free (invalid_recipient->fpr);
66       free (invalid_recipient);
67       invalid_recipient = next;
68     }
69
70   free (opd->kc_fpr);
71 }
72
73
74 gpgme_encrypt_result_t
75 gpgme_op_encrypt_result (gpgme_ctx_t ctx)
76 {
77   void *hook;
78   op_data_t opd;
79   gpgme_error_t err;
80
81   TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_result", ctx);
82
83   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
84   opd = hook;
85
86   if (err || !opd)
87     {
88       TRACE_SUC0 ("result=(null)");
89       return NULL;
90     }
91
92   if (_gpgme_debug_trace ())
93     {
94       gpgme_invalid_key_t invkeys = opd->result.invalid_recipients;
95       int i = 0;
96
97       while (invkeys)
98         {
99           TRACE_LOG3 ("invalid_recipients[%i] = %s (%s)",
100                       i, invkeys->fpr ? invkeys->fpr : "(null)",
101                       gpg_strerror (invkeys->reason));
102           invkeys = invkeys->next;
103           i++;
104         }
105     }
106
107   TRACE_SUC1 ("result=%p", &opd->result);
108   return &opd->result;
109 }
110
111 \f
112 gpgme_error_t
113 _gpgme_encrypt_status_handler (void *priv, gpgme_status_code_t code,
114                                char *args)
115 {
116   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
117   gpgme_error_t err;
118   void *hook;
119   op_data_t opd;
120
121   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
122   opd = hook;
123   if (err)
124     return err;
125
126   switch (code)
127     {
128     case GPGME_STATUS_FAILURE:
129       opd->failure_code = _gpgme_parse_failure (args);
130       break;
131
132     case GPGME_STATUS_EOF:
133       if (opd->result.invalid_recipients)
134         return gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
135       if (opd->failure_code)
136         return opd->failure_code;
137       break;
138
139     case GPGME_STATUS_KEY_CONSIDERED:
140       /* This is emitted during gpg's key lookup to give information
141        * about the lookup results.  We store the last one so it can be
142        * used in connection with INV_RECP.  */
143       free (opd->kc_fpr);
144       opd->kc_fpr = NULL;
145       err = _gpgme_parse_key_considered (args, &opd->kc_fpr, &opd->kc_flags);
146       if (err)
147         return err;
148       break;
149
150     case GPGME_STATUS_INV_RECP:
151       err = _gpgme_parse_inv_recp (args, 0, opd->kc_fpr, opd->kc_flags,
152                                    opd->lastp);
153       if (err)
154         return err;
155
156       opd->lastp = &(*opd->lastp)->next;
157       free (opd->kc_fpr);
158       opd->kc_fpr = NULL;
159       break;
160
161     case GPGME_STATUS_NO_RECP:
162       /* Should not happen, because we require at least one recipient.  */
163       return gpg_error (GPG_ERR_GENERAL);
164
165     default:
166       break;
167     }
168   return 0;
169 }
170
171
172 static gpgme_error_t
173 encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args)
174 {
175   gpgme_error_t err;
176
177   err = _gpgme_progress_status_handler (priv, code, args);
178   if (!err)
179     err = _gpgme_passphrase_status_handler (priv, code, args);
180   return err;
181 }
182
183
184 static gpgme_error_t
185 encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
186 {
187   gpgme_error_t err;
188
189   err = _gpgme_progress_status_handler (priv, code, args);
190   if (!err)
191     err = _gpgme_encrypt_status_handler (priv, code, args);
192
193   return err;
194 }
195
196
197 gpgme_error_t
198 _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx)
199 {
200   gpgme_error_t err;
201   void *hook;
202   op_data_t opd;
203
204   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, sizeof (*opd),
205                                release_op_data);
206   opd = hook;
207   if (err)
208     return err;
209
210   opd->lastp = &opd->result.invalid_recipients;
211   return 0;
212 }
213
214
215 static gpgme_error_t
216 encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
217                gpgme_encrypt_flags_t flags,
218                gpgme_data_t plain, gpgme_data_t cipher)
219 {
220   gpgme_error_t err;
221   int symmetric = 0;
222
223   err = _gpgme_op_reset (ctx, synchronous);
224   if (err)
225     return err;
226
227   err = _gpgme_op_encrypt_init_result (ctx);
228   if (err)
229     return err;
230
231   symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC);
232
233   if (!plain)
234     return gpg_error (GPG_ERR_NO_DATA);
235   if (!cipher)
236     return gpg_error (GPG_ERR_INV_VALUE);
237   if (recp && ! *recp)
238     return gpg_error (GPG_ERR_INV_VALUE);
239
240   if (symmetric && ctx->passphrase_cb)
241     {
242       /* Symmetric encryption requires a passphrase.  */
243       err = _gpgme_engine_set_command_handler
244         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
245       if (err)
246         return err;
247     }
248
249   _gpgme_engine_set_status_handler (ctx->engine,
250                                     symmetric
251                                     ? encrypt_sym_status_handler
252                                     : encrypt_status_handler,
253                                     ctx);
254
255   return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher,
256                                    ctx->use_armor);
257 }
258
259
260 gpgme_error_t
261 gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
262                         gpgme_encrypt_flags_t flags,
263                         gpgme_data_t plain, gpgme_data_t cipher)
264 {
265   gpgme_error_t err;
266
267   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx,
268               "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
269
270   if (!ctx)
271     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
272
273   if (_gpgme_debug_trace () && recp)
274     {
275       int i = 0;
276
277       while (recp[i])
278         {
279           TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
280                       (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
281                       recp[i]->subkeys->fpr : "invalid");
282           i++;
283         }
284     }
285
286   err = encrypt_start (ctx, 0, recp, flags, plain, cipher);
287   return TRACE_ERR (err);
288 }
289
290
291 /* Encrypt plaintext PLAIN within CTX for the recipients RECP and
292    store the resulting ciphertext in CIPHER.  */
293 gpgme_error_t
294 gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
295                   gpgme_encrypt_flags_t flags,
296                   gpgme_data_t plain, gpgme_data_t cipher)
297 {
298   gpgme_error_t err;
299
300   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx,
301               "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
302
303   if (!ctx)
304     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
305
306   if (_gpgme_debug_trace () && recp)
307     {
308       int i = 0;
309
310       while (recp[i])
311         {
312           TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
313                       (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
314                       recp[i]->subkeys->fpr : "invalid");
315           i++;
316         }
317     }
318
319   err = encrypt_start (ctx, 1, recp, flags, plain, cipher);
320   if (!err)
321     err = _gpgme_wait_one (ctx);
322   return TRACE_ERR (err);
323 }