core: Extend gpgme_user_id_t with 'address'.
[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   if (!recp)
232     symmetric = 1;
233
234   if (!plain)
235     return gpg_error (GPG_ERR_NO_DATA);
236   if (!cipher)
237     return gpg_error (GPG_ERR_INV_VALUE);
238   if (recp && ! *recp)
239     return gpg_error (GPG_ERR_INV_VALUE);
240
241   if (symmetric && ctx->passphrase_cb)
242     {
243       /* Symmetric encryption requires a passphrase.  */
244       err = _gpgme_engine_set_command_handler
245         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
246       if (err)
247         return err;
248     }
249
250   _gpgme_engine_set_status_handler (ctx->engine,
251                                     symmetric
252                                     ? encrypt_sym_status_handler
253                                     : encrypt_status_handler,
254                                     ctx);
255
256   return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher,
257                                    ctx->use_armor);
258 }
259
260
261 gpgme_error_t
262 gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
263                         gpgme_encrypt_flags_t flags,
264                         gpgme_data_t plain, gpgme_data_t cipher)
265 {
266   gpgme_error_t err;
267
268   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx,
269               "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
270
271   if (!ctx)
272     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
273
274   if (_gpgme_debug_trace () && recp)
275     {
276       int i = 0;
277
278       while (recp[i])
279         {
280           TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
281                       (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
282                       recp[i]->subkeys->fpr : "invalid");
283           i++;
284         }
285     }
286
287   err = encrypt_start (ctx, 0, recp, flags, plain, cipher);
288   return TRACE_ERR (err);
289 }
290
291
292 /* Encrypt plaintext PLAIN within CTX for the recipients RECP and
293    store the resulting ciphertext in CIPHER.  */
294 gpgme_error_t
295 gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
296                   gpgme_encrypt_flags_t flags,
297                   gpgme_data_t plain, gpgme_data_t cipher)
298 {
299   gpgme_error_t err;
300
301   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx,
302               "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
303
304   if (!ctx)
305     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
306
307   if (_gpgme_debug_trace () && recp)
308     {
309       int i = 0;
310
311       while (recp[i])
312         {
313           TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
314                       (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
315                       recp[i]->subkeys->fpr : "invalid");
316           i++;
317         }
318     }
319
320   err = encrypt_start (ctx, 1, recp, flags, plain, cipher);
321   if (!err)
322     err = _gpgme_wait_one (ctx);
323   return TRACE_ERR (err);
324 }