doc/
[gpgme.git] / gpgme / encrypt.c
1 /* encrypt.c -  encrypt functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 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_recipients;
42   GpgmeData xmlinfo;
43 };
44
45 void
46 _gpgme_release_encrypt_result (EncryptResult result)
47 {
48   if (!result)
49     return;
50   gpgme_data_release (result->xmlinfo);
51   xfree (result);
52 }
53
54 /* 
55  * Parse the args and save the information 
56  * in an XML structure.
57  * With args of NULL the xml structure is closed.
58  */
59 static void
60 append_xml_encinfo (GpgmeData *rdh, char *args)
61 {
62   GpgmeData dh;
63   char helpbuf[100];
64
65   if (!*rdh)
66     {
67       if (gpgme_data_new (rdh))
68         return; /* FIXME: We are ignoring out-of-core.  */
69       dh = *rdh;
70       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
71     }
72   else
73     {
74       dh = *rdh;
75       _gpgme_data_append_string (dh, "  </encryption>\n");
76     }
77
78   if (!args)
79     {
80       /* Just close the XML containter.  */
81       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
82       return;
83     }
84
85   _gpgme_data_append_string (dh, "  <encryption>\n"
86                              "    <error>\n"
87                              "      <invalidRecipient/>\n");
88     
89   sprintf (helpbuf, "      <reason>%d</reason>\n", atoi (args));
90   _gpgme_data_append_string (dh, helpbuf);
91   SKIP_TOKEN_OR_RETURN (args);
92
93   _gpgme_data_append_string (dh, "      <name>");
94   _gpgme_data_append_percentstring_for_xml (dh, args);
95   _gpgme_data_append_string (dh, "</name>\n"
96                              "    </error>\n");
97 }
98
99
100 static void
101 encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
102 {
103   if (ctx->out_of_core)
104     return;
105   if (!ctx->result.encrypt)
106     {
107       ctx->result.encrypt = xtrycalloc (1, sizeof *ctx->result.encrypt);
108       if (!ctx->result.encrypt)
109         {
110           ctx->out_of_core = 1;
111           return;
112         }
113     }
114
115   switch (code)
116     {
117     case STATUS_EOF:
118       if (ctx->result.encrypt->xmlinfo)
119         {
120           append_xml_encinfo (&ctx->result.encrypt->xmlinfo, NULL);
121           _gpgme_set_op_info (ctx, ctx->result.encrypt->xmlinfo);
122           ctx->result.encrypt->xmlinfo = NULL;
123         }
124       break;
125
126     case STATUS_INV_RECP:
127       append_xml_encinfo (&ctx->result.encrypt->xmlinfo, args);
128       break;
129
130     case STATUS_NO_RECP:
131       ctx->result.encrypt->no_recipients = 1; /* i.e. no usable ones */
132       break;
133
134     default:
135       break;
136     }
137 }
138
139
140 GpgmeError
141 gpgme_op_encrypt_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain,
142                         GpgmeData ciph)
143 {
144   int err = 0;
145
146   fail_on_pending_request (ctx);
147   ctx->pending = 1;
148
149   _gpgme_release_result (ctx);
150   ctx->out_of_core = 0;
151
152   /* Do some checks.  */
153   if (!gpgme_recipients_count (recp))
154     {
155       /* Fixme: In this case we should do symmentric encryption.  */
156       err = mk_error (No_Recipients);
157       goto leave;
158     }
159
160   /* Create an engine object.  */
161   _gpgme_engine_release (ctx->engine);
162   ctx->engine = NULL;
163   err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
164                            : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
165   if (err)
166     goto leave;
167
168   _gpgme_engine_set_status_handler (ctx->engine, encrypt_status_handler, ctx);
169   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
170
171   /* Check the supplied data */
172   if (gpgme_data_get_type (plain) == GPGME_DATA_TYPE_NONE)
173     {
174       err = mk_error (No_Data);
175       goto leave;
176     }
177   _gpgme_data_set_mode (plain, GPGME_DATA_MODE_OUT);
178   if (!ciph || gpgme_data_get_type (ciph) != GPGME_DATA_TYPE_NONE)
179     {
180       err = mk_error (Invalid_Value);
181       goto leave;
182     }
183   _gpgme_data_set_mode (ciph, GPGME_DATA_MODE_IN);
184
185   err = _gpgme_engine_op_encrypt (ctx->engine, recp, plain, ciph, ctx->use_armor);
186
187
188   if (!err)     /* And kick off the process.  */
189     err = _gpgme_engine_start (ctx->engine, ctx);
190
191  leave:
192   if (err)
193     {
194       ctx->pending = 0; 
195       _gpgme_engine_release (ctx->engine);
196       ctx->engine = NULL;
197     }
198   return err;
199 }
200
201
202 /**
203  * gpgme_op_encrypt:
204  * @c: The context
205  * @recp: A set of recipients 
206  * @in: plaintext input
207  * @out: ciphertext output
208  * 
209  * This function encrypts @in to @out for all recipients from
210  * @recp.  Other parameters are take from the context @c.
211  * The function does wait for the result.
212  * 
213  * Return value:  0 on success or an errorcode. 
214  **/
215 GpgmeError
216 gpgme_op_encrypt (GpgmeCtx ctx, GpgmeRecipients recp,
217                   GpgmeData plain, GpgmeData cipher)
218 {
219   int err = gpgme_op_encrypt_start (ctx, recp, plain, cipher);
220   if (!err)
221     {
222       gpgme_wait (ctx, 1);
223       if (!ctx->result.encrypt)
224         err = mk_error (General_Error);
225       else if (ctx->out_of_core)
226         err = mk_error (Out_Of_Core);
227       else
228         {
229           if (ctx->result.encrypt->no_recipients) 
230             err = mk_error (No_Recipients);
231         }
232       /* Old gpg versions don't return status info for invalid
233          recipients, so we simply check whether we got any output at
234          all, and if not we assume that we don't have valid
235          recipients.  */
236       if (!err && gpgme_data_get_type (cipher) == GPGME_DATA_TYPE_NONE)
237         err = mk_error (No_Recipients);
238     }
239   return err;
240 }
241
242
243
244
245
246
247