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