2004-09-30 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / encrypt.c
1 /* encrypt.c - Encrypt function.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 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 "context.h"
30 #include "ops.h"
31
32 \f
33 typedef struct
34 {
35   struct _gpgme_op_encrypt_result result;
36
37   /* A pointer to the next pointer of the last invalid recipient in
38      the list.  This makes appending new invalid recipients painless
39      while preserving the order.  */
40   gpgme_invalid_key_t *lastp;
41 } *op_data_t;
42
43
44 static void
45 release_op_data (void *hook)
46 {
47   op_data_t opd = (op_data_t) hook;
48   gpgme_invalid_key_t invalid_recipient = opd->result.invalid_recipients;
49
50   while (invalid_recipient)
51     {
52       gpgme_invalid_key_t next = invalid_recipient->next;
53       if (invalid_recipient->fpr)
54         free (invalid_recipient->fpr);
55       invalid_recipient = next;
56     }
57 }
58
59
60 gpgme_encrypt_result_t
61 gpgme_op_encrypt_result (gpgme_ctx_t ctx)
62 {
63   void *hook;
64   op_data_t opd;
65   gpgme_error_t err;
66
67   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
68   opd = hook;
69
70   if (err || !opd)
71     return NULL;
72
73   return &opd->result;
74 }
75
76 \f
77 gpgme_error_t
78 _gpgme_encrypt_status_handler (void *priv, gpgme_status_code_t code,
79                                char *args)
80 {
81   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
82   gpgme_error_t err;
83   void *hook;
84   op_data_t opd;
85
86   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
87   opd = hook;
88   if (err)
89     return err;
90
91   switch (code)
92     {
93     case GPGME_STATUS_EOF:
94       if (opd->result.invalid_recipients)
95         return gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
96       break;
97
98     case GPGME_STATUS_INV_RECP:
99       err = _gpgme_parse_inv_recp (args, opd->lastp);
100       if (err)
101         return err;
102
103       opd->lastp = &(*opd->lastp)->next;
104       break;
105
106     case GPGME_STATUS_NO_RECP:
107       /* Should not happen, because we require at least one recipient.  */
108       return gpg_error (GPG_ERR_GENERAL);
109
110     default:
111       break;
112     }
113   return 0;
114 }
115
116
117 static gpgme_error_t
118 encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args)
119 {
120   gpgme_error_t err;
121
122   err = _gpgme_progress_status_handler (priv, code, args);
123   if (!err)
124     err = _gpgme_passphrase_status_handler (priv, code, args);
125   return err;
126 }
127
128
129 static gpgme_error_t
130 encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
131 {
132   return _gpgme_progress_status_handler (priv, code, args)
133     || _gpgme_encrypt_status_handler (priv, code, args);
134 }
135
136
137 gpgme_error_t
138 _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx)
139 {
140   gpgme_error_t err;
141   void *hook;
142   op_data_t opd;
143
144   err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, sizeof (*opd),
145                                release_op_data);
146   opd = hook;
147   if (err)
148     return err;
149
150   opd->lastp = &opd->result.invalid_recipients;
151   return 0;
152 }
153
154
155 static gpgme_error_t
156 encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
157                gpgme_encrypt_flags_t flags,
158                gpgme_data_t plain, gpgme_data_t cipher)
159 {
160   gpgme_error_t err;
161   int symmetric = 0;
162
163   err = _gpgme_op_reset (ctx, synchronous);
164   if (err)
165     return err;
166
167   err = _gpgme_op_encrypt_init_result (ctx);
168   if (err)
169     return err;
170
171   if (!recp)
172     symmetric = 1;
173
174   if (!plain)
175     return gpg_error (GPG_ERR_NO_DATA);
176   if (!cipher)
177     return gpg_error (GPG_ERR_INV_VALUE);
178   if (recp && ! *recp)
179     return gpg_error (GPG_ERR_INV_VALUE);
180
181   if (symmetric && ctx->passphrase_cb)
182     {
183       /* Symmetric encryption requires a passphrase.  */
184       err = _gpgme_engine_set_command_handler
185         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
186       if (err)
187         return err;
188     }
189
190   _gpgme_engine_set_status_handler (ctx->engine,
191                                     symmetric
192                                     ? encrypt_sym_status_handler
193                                     : encrypt_status_handler,
194                                     ctx);
195
196   return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher,
197                                    ctx->use_armor);
198 }
199
200
201 gpgme_error_t
202 gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
203                         gpgme_encrypt_flags_t flags,
204                         gpgme_data_t plain, gpgme_data_t cipher)
205 {
206   return encrypt_start (ctx, 0, recp, flags, plain, cipher);
207 }
208
209
210 /* Encrypt plaintext PLAIN within CTX for the recipients RECP and
211    store the resulting ciphertext in CIPHER.  */
212 gpgme_error_t
213 gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
214                   gpgme_encrypt_flags_t flags,
215                   gpgme_data_t plain, gpgme_data_t cipher)
216 {
217   gpgme_error_t err = encrypt_start (ctx, 1, recp, flags, plain, cipher);
218   if (!err)
219     err = _gpgme_wait_one (ctx);
220   return err;
221 }