2003-01-19 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / encrypt.c
1 /* encrypt.c -  encrypt functions
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002 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 <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "wait.h"
33
34 #define SKIP_TOKEN_OR_RETURN(a) do { \
35     while (*(a) && *(a) != ' ') (a)++; \
36     while (*(a) == ' ') (a)++; \
37     if (!*(a)) \
38         return; /* oops */ \
39 } while (0)
40
41 struct encrypt_result_s
42 {
43   int no_valid_recipients;
44   int invalid_recipients;
45   GpgmeData xmlinfo;
46 };
47
48 void
49 _gpgme_release_encrypt_result (EncryptResult result)
50 {
51   if (!result)
52     return;
53   gpgme_data_release (result->xmlinfo);
54   free (result);
55 }
56
57 /* 
58  * Parse the args and save the information 
59  * in an XML structure.
60  * With args of NULL the xml structure is closed.
61  */
62 static void
63 append_xml_encinfo (GpgmeData *rdh, char *args)
64 {
65   GpgmeData dh;
66   char helpbuf[100];
67
68   if (!*rdh)
69     {
70       if (gpgme_data_new (rdh))
71         return; /* FIXME: We are ignoring out-of-core.  */
72       dh = *rdh;
73       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
74     }
75   else
76     {
77       dh = *rdh;
78       _gpgme_data_append_string (dh, "  </encryption>\n");
79     }
80
81   if (!args)
82     {
83       /* Just close the XML containter.  */
84       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
85       return;
86     }
87
88   _gpgme_data_append_string (dh, "  <encryption>\n"
89                              "    <error>\n"
90                              "      <invalidRecipient/>\n");
91     
92   sprintf (helpbuf, "      <reason>%d</reason>\n", atoi (args));
93   _gpgme_data_append_string (dh, helpbuf);
94   SKIP_TOKEN_OR_RETURN (args);
95
96   _gpgme_data_append_string (dh, "      <name>");
97   _gpgme_data_append_percentstring_for_xml (dh, args);
98   _gpgme_data_append_string (dh, "</name>\n"
99                              "    </error>\n");
100 }
101
102
103 static void
104 status_handler_finish (GpgmeCtx ctx)
105 {
106   if (ctx->result.encrypt->xmlinfo)
107     {
108       append_xml_encinfo (&ctx->result.encrypt->xmlinfo, NULL);
109       _gpgme_set_op_info (ctx, ctx->result.encrypt->xmlinfo);
110       ctx->result.encrypt->xmlinfo = NULL;
111     }
112   if (ctx->error)
113     ; /* already set by kludge in engine-gpgsm */
114   else if (ctx->result.encrypt->no_valid_recipients) 
115     ctx->error = mk_error (No_Recipients);
116   else if (ctx->result.encrypt->invalid_recipients) 
117     ctx->error = mk_error (Invalid_Recipients);
118 }
119
120 void
121 _gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
122 {
123   if (ctx->error)
124     {
125       if (ctx->result.encrypt) /* check that we have allocated it. */
126         status_handler_finish (ctx);
127       return;
128     }
129   test_and_allocate_result (ctx, encrypt);
130
131   switch (code)
132     {
133     case GPGME_STATUS_EOF:
134       status_handler_finish (ctx);
135       break;
136
137     case GPGME_STATUS_INV_RECP:
138       ctx->result.encrypt->invalid_recipients++;
139       append_xml_encinfo (&ctx->result.encrypt->xmlinfo, args);
140       break;
141
142     case GPGME_STATUS_NO_RECP:
143       ctx->result.encrypt->no_valid_recipients = 1;
144       break;
145
146     default:
147       break;
148     }
149 }
150
151
152 void
153 _gpgme_encrypt_sym_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
154 {
155   _gpgme_passphrase_status_handler (ctx, code, args);
156 }
157
158
159 static GpgmeError
160 _gpgme_op_encrypt_start (GpgmeCtx ctx, int synchronous,
161                          GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph)
162 {
163   GpgmeError err = 0;
164   int symmetric = 0;
165
166   /* Do some checks.  */
167   if (!recp)
168     symmetric = 1;
169   else if (!gpgme_recipients_count (recp))
170     {
171       err = mk_error (No_Recipients);
172       goto leave;
173     }
174
175   err = _gpgme_op_reset (ctx, synchronous);
176   if (err)
177     goto leave;
178
179   if (symmetric)
180     {
181       err = _gpgme_passphrase_start (ctx);
182       if (err)
183         goto leave;
184     }
185
186   _gpgme_engine_set_status_handler (ctx->engine,
187                                     symmetric
188                                     ? _gpgme_encrypt_sym_status_handler
189                                     : _gpgme_encrypt_status_handler,
190                                     ctx);
191   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
192
193   /* Check the supplied data */
194   if (!plain)
195     {
196       err = mk_error (No_Data);
197       goto leave;
198     }
199   if (!ciph)
200     {
201       err = mk_error (Invalid_Value);
202       goto leave;
203     }
204
205   err = _gpgme_engine_op_encrypt (ctx->engine, recp, plain, ciph, ctx->use_armor);
206
207  leave:
208   if (err)
209     {
210       ctx->pending = 0; 
211       _gpgme_engine_release (ctx->engine);
212       ctx->engine = NULL;
213     }
214   return err;
215 }
216
217
218 GpgmeError
219 gpgme_op_encrypt_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain,
220                         GpgmeData ciph)
221 {
222   return _gpgme_op_encrypt_start (ctx, 0, recp, plain, ciph);
223 }
224
225
226 /**
227  * gpgme_op_encrypt:
228  * @c: The context
229  * @recp: A set of recipients 
230  * @in: plaintext input
231  * @out: ciphertext output
232  * 
233  * This function encrypts @in to @out for all recipients from
234  * @recp.  Other parameters are take from the context @c.
235  * The function does wait for the result.
236  * 
237  * Return value:  0 on success or an errorcode. 
238  **/
239 GpgmeError
240 gpgme_op_encrypt (GpgmeCtx ctx, GpgmeRecipients recp,
241                   GpgmeData plain, GpgmeData cipher)
242 {
243   int err = _gpgme_op_encrypt_start (ctx, 1, recp, plain, cipher);
244   if (!err)
245     err = _gpgme_wait_one (ctx);
246   return err;
247 }