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