typo fix in comment.
[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
8  * it 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,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include "util.h"
29 #include "context.h"
30 #include "ops.h"
31 #include "wait.h"
32
33 #define SKIP_TOKEN_OR_RETURN(a) do { \
34     while (*(a) && *(a) != ' ') (a)++; \
35     while (*(a) == ' ') (a)++; \
36     if (!*(a)) \
37         return; /* oops */ \
38 } while (0)
39
40 struct encrypt_result_s
41 {
42   int no_valid_recipients;
43   int invalid_recipients;
44   GpgmeData xmlinfo;
45 };
46
47 void
48 _gpgme_release_encrypt_result (EncryptResult result)
49 {
50   if (!result)
51     return;
52   gpgme_data_release (result->xmlinfo);
53   xfree (result);
54 }
55
56 /* 
57  * Parse the args and save the information 
58  * in an XML structure.
59  * With args of NULL the xml structure is closed.
60  */
61 static void
62 append_xml_encinfo (GpgmeData *rdh, char *args)
63 {
64   GpgmeData dh;
65   char helpbuf[100];
66
67   if (!*rdh)
68     {
69       if (gpgme_data_new (rdh))
70         return; /* FIXME: We are ignoring out-of-core.  */
71       dh = *rdh;
72       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
73     }
74   else
75     {
76       dh = *rdh;
77       _gpgme_data_append_string (dh, "  </encryption>\n");
78     }
79
80   if (!args)
81     {
82       /* Just close the XML containter.  */
83       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
84       return;
85     }
86
87   _gpgme_data_append_string (dh, "  <encryption>\n"
88                              "    <error>\n"
89                              "      <invalidRecipient/>\n");
90     
91   sprintf (helpbuf, "      <reason>%d</reason>\n", atoi (args));
92   _gpgme_data_append_string (dh, helpbuf);
93   SKIP_TOKEN_OR_RETURN (args);
94
95   _gpgme_data_append_string (dh, "      <name>");
96   _gpgme_data_append_percentstring_for_xml (dh, args);
97   _gpgme_data_append_string (dh, "</name>\n"
98                              "    </error>\n");
99 }
100
101
102 void
103 _gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
104 {
105   if (ctx->error)
106     return;
107   test_and_allocate_result (ctx, encrypt);
108
109   switch (code)
110     {
111     case STATUS_EOF:
112       if (ctx->result.encrypt->xmlinfo)
113         {
114           append_xml_encinfo (&ctx->result.encrypt->xmlinfo, NULL);
115           _gpgme_set_op_info (ctx, ctx->result.encrypt->xmlinfo);
116           ctx->result.encrypt->xmlinfo = NULL;
117         }
118       if (ctx->result.encrypt->no_valid_recipients) 
119         ctx->error = mk_error (No_Recipients);
120       else if (ctx->result.encrypt->invalid_recipients) 
121         ctx->error = mk_error (Invalid_Recipients);
122       break;
123
124     case STATUS_INV_RECP:
125       ctx->result.encrypt->invalid_recipients++;
126       append_xml_encinfo (&ctx->result.encrypt->xmlinfo, args);
127       break;
128
129     case STATUS_NO_RECP:
130       ctx->result.encrypt->no_valid_recipients = 1;
131       break;
132
133     default:
134       break;
135     }
136 }
137
138
139 void
140 _gpgme_encrypt_sym_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
141 {
142   _gpgme_passphrase_status_handler (ctx, code, args);
143 }
144
145
146 static GpgmeError
147 _gpgme_op_encrypt_start (GpgmeCtx ctx, int synchronous,
148                          GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph)
149 {
150   GpgmeError err = 0;
151   int symmetric = 0;
152
153   /* Do some checks.  */
154   if (!recp)
155     symmetric = 1;
156   else if (!gpgme_recipients_count (recp))
157     {
158       err = mk_error (No_Recipients);
159       goto leave;
160     }
161
162   err = _gpgme_op_reset (ctx, synchronous);
163   if (err)
164     goto leave;
165
166   if (symmetric)
167     {
168       err = _gpgme_passphrase_start (ctx);
169       if (err)
170         goto leave;
171     }
172
173   _gpgme_engine_set_status_handler (ctx->engine,
174                                     symmetric
175                                     ? _gpgme_encrypt_sym_status_handler
176                                     : _gpgme_encrypt_status_handler,
177                                     ctx);
178   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
179
180   /* Check the supplied data */
181   if (gpgme_data_get_type (plain) == GPGME_DATA_TYPE_NONE)
182     {
183       err = mk_error (No_Data);
184       goto leave;
185     }
186   _gpgme_data_set_mode (plain, GPGME_DATA_MODE_OUT);
187   if (!ciph || gpgme_data_get_type (ciph) != GPGME_DATA_TYPE_NONE)
188     {
189       err = mk_error (Invalid_Value);
190       goto leave;
191     }
192   _gpgme_data_set_mode (ciph, GPGME_DATA_MODE_IN);
193
194   err = _gpgme_engine_op_encrypt (ctx->engine, recp, plain, ciph, ctx->use_armor);
195
196
197   if (!err)     /* And kick off the process.  */
198     err = _gpgme_engine_start (ctx->engine, ctx);
199
200  leave:
201   if (err)
202     {
203       ctx->pending = 0; 
204       _gpgme_engine_release (ctx->engine);
205       ctx->engine = NULL;
206     }
207   return err;
208 }
209
210
211 GpgmeError
212 gpgme_op_encrypt_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain,
213                         GpgmeData ciph)
214 {
215   return _gpgme_op_encrypt_start (ctx, 0, recp, plain, ciph);
216 }
217
218
219 /**
220  * gpgme_op_encrypt:
221  * @c: The context
222  * @recp: A set of recipients 
223  * @in: plaintext input
224  * @out: ciphertext output
225  * 
226  * This function encrypts @in to @out for all recipients from
227  * @recp.  Other parameters are take from the context @c.
228  * The function does wait for the result.
229  * 
230  * Return value:  0 on success or an errorcode. 
231  **/
232 GpgmeError
233 gpgme_op_encrypt (GpgmeCtx ctx, GpgmeRecipients recp,
234                   GpgmeData plain, GpgmeData cipher)
235 {
236   int err = _gpgme_op_encrypt_start (ctx, 1, recp, plain, cipher);
237   if (!err)
238     {
239       err = _gpgme_wait_one (ctx);
240       /* Old gpg versions don't return status info for invalid
241          recipients, so we simply check whether we got any output at
242          all, and if not we assume that we don't have valid
243          recipients.  */
244       if (!ctx->error && gpgme_data_get_type (cipher) == GPGME_DATA_TYPE_NONE)
245         ctx->error = mk_error (No_Recipients);
246       err = ctx->error;
247     }
248   return err;
249 }