doc/
[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
32 #define SKIP_TOKEN_OR_RETURN(a) do { \
33     while (*(a) && *(a) != ' ') (a)++; \
34     while (*(a) == ' ') (a)++; \
35     if (!*(a)) \
36         return; /* oops */ \
37 } while (0)
38
39 struct encrypt_result_s
40 {
41   int no_valid_recipients;
42   int invalid_recipients;
43   GpgmeData xmlinfo;
44 };
45
46 void
47 _gpgme_release_encrypt_result (EncryptResult result)
48 {
49   if (!result)
50     return;
51   gpgme_data_release (result->xmlinfo);
52   xfree (result);
53 }
54
55 /* 
56  * Parse the args and save the information 
57  * in an XML structure.
58  * With args of NULL the xml structure is closed.
59  */
60 static void
61 append_xml_encinfo (GpgmeData *rdh, char *args)
62 {
63   GpgmeData dh;
64   char helpbuf[100];
65
66   if (!*rdh)
67     {
68       if (gpgme_data_new (rdh))
69         return; /* FIXME: We are ignoring out-of-core.  */
70       dh = *rdh;
71       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
72     }
73   else
74     {
75       dh = *rdh;
76       _gpgme_data_append_string (dh, "  </encryption>\n");
77     }
78
79   if (!args)
80     {
81       /* Just close the XML containter.  */
82       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
83       return;
84     }
85
86   _gpgme_data_append_string (dh, "  <encryption>\n"
87                              "    <error>\n"
88                              "      <invalidRecipient/>\n");
89     
90   sprintf (helpbuf, "      <reason>%d</reason>\n", atoi (args));
91   _gpgme_data_append_string (dh, helpbuf);
92   SKIP_TOKEN_OR_RETURN (args);
93
94   _gpgme_data_append_string (dh, "      <name>");
95   _gpgme_data_append_percentstring_for_xml (dh, args);
96   _gpgme_data_append_string (dh, "</name>\n"
97                              "    </error>\n");
98 }
99
100
101 void
102 _gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
103 {
104   if (ctx->error)
105     return;
106   test_and_allocate_result (ctx, encrypt);
107
108   switch (code)
109     {
110     case STATUS_EOF:
111       if (ctx->result.encrypt->xmlinfo)
112         {
113           append_xml_encinfo (&ctx->result.encrypt->xmlinfo, NULL);
114           _gpgme_set_op_info (ctx, ctx->result.encrypt->xmlinfo);
115           ctx->result.encrypt->xmlinfo = NULL;
116         }
117       if (ctx->result.encrypt->no_valid_recipients) 
118         ctx->error = mk_error (No_Recipients);
119       else if (ctx->result.encrypt->invalid_recipients) 
120         ctx->error = mk_error (Invalid_Recipients);
121       break;
122
123     case STATUS_INV_RECP:
124       ctx->result.encrypt->invalid_recipients++;
125       append_xml_encinfo (&ctx->result.encrypt->xmlinfo, args);
126       break;
127
128     case STATUS_NO_RECP:
129       ctx->result.encrypt->no_valid_recipients = 1;
130       break;
131
132     default:
133       break;
134     }
135 }
136
137
138 void
139 _gpgme_encrypt_sym_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
140 {
141   _gpgme_passphrase_status_handler (ctx, code, args);
142 }
143
144
145 GpgmeError
146 gpgme_op_encrypt_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain,
147                         GpgmeData ciph)
148 {
149   int err = 0;
150   int symmetric = 0;
151
152   fail_on_pending_request (ctx);
153   ctx->pending = 1;
154
155   _gpgme_release_result (ctx);
156
157   /* Do some checks.  */
158   if (!recp)
159     symmetric = 1;
160   else if (!gpgme_recipients_count (recp))
161     {
162       err = mk_error (No_Recipients);
163       goto leave;
164     }
165
166   /* Create an engine object.  */
167   _gpgme_engine_release (ctx->engine);
168   ctx->engine = NULL;
169   err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
170                            : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
171   if (err)
172     goto leave;
173
174   if (symmetric)
175     {
176       err = _gpgme_passphrase_start (ctx);
177       if (err)
178         goto leave;
179     }
180
181   _gpgme_engine_set_status_handler (ctx->engine,
182                                     symmetric
183                                     ? _gpgme_encrypt_sym_status_handler
184                                     : _gpgme_encrypt_status_handler,
185                                     ctx);
186   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
187
188   /* Check the supplied data */
189   if (gpgme_data_get_type (plain) == GPGME_DATA_TYPE_NONE)
190     {
191       err = mk_error (No_Data);
192       goto leave;
193     }
194   _gpgme_data_set_mode (plain, GPGME_DATA_MODE_OUT);
195   if (!ciph || gpgme_data_get_type (ciph) != GPGME_DATA_TYPE_NONE)
196     {
197       err = mk_error (Invalid_Value);
198       goto leave;
199     }
200   _gpgme_data_set_mode (ciph, GPGME_DATA_MODE_IN);
201
202   err = _gpgme_engine_op_encrypt (ctx->engine, recp, plain, ciph, ctx->use_armor);
203
204
205   if (!err)     /* And kick off the process.  */
206     err = _gpgme_engine_start (ctx->engine, ctx);
207
208  leave:
209   if (err)
210     {
211       ctx->pending = 0; 
212       _gpgme_engine_release (ctx->engine);
213       ctx->engine = NULL;
214     }
215   return err;
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, recp, plain, cipher);
237   if (!err)
238     {
239       gpgme_wait (ctx, &err, 1);
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 }