enable decryption of ascii-armored gpg messages
[claws.git] / src / pgptext.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001 Jens Jahnke <jan0sch@gmx.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #if USE_GPGME
25
26 #include "defs.h"
27
28 #include <glib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <locale.h>
32 #include <ctype.h>
33
34 #include <gpgme.h>
35
36 #include "intl.h"
37 #include "procmime.h"
38 #include "procheader.h"
39 #include "base64.h"
40 #include "uuencode.h"
41 #include "unmime.h"
42 #include "codeconv.h"
43 #include "utils.h"
44 #include "prefs_common.h"
45 #include "passphrase.h"
46 #include "select-keys.h"
47 #include "sigstatus.h"
48 #include "rfc2015.h"
49
50 #include "pgptext.h"
51
52 #define DIM(v)     (sizeof(v)/sizeof((v)[0]))
53
54 static char *content_names[] = {
55     "Content-Type",
56     "Content-Disposition",
57     "Content-Transfer-Encoding",
58     NULL
59 };
60
61 static char *mime_version_name[] = {
62     "Mime-Version",
63     NULL
64 };
65
66
67 struct passphrase_cb_info_s {
68     GpgmeCtx c;
69     int did_it;
70 };
71
72 static const char *
73 passphrase_cb (void *opaque, const char *desc, void *r_hd)
74 {
75     struct passphrase_cb_info_s *info = opaque;
76     GpgmeCtx ctx = info ? info->c : NULL;
77     const char *pass;
78
79     if (!desc) {
80         /* FIXME: cleanup by looking at *r_hd */
81         return NULL;
82     }
83
84     gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
85     g_message ("%% requesting passphrase for `%s': ", desc );
86     pass = gpgmegtk_passphrase_mbox (desc);
87     if (!pass) {
88         g_message ("%% cancel passphrase entry");
89         gpgme_cancel (ctx);
90     }
91     else
92         g_message ("%% sending passphrase");
93
94     return pass;
95 }
96
97 static GpgmeData
98 pgptext_decrypt (MimeInfo *partinfo, FILE *fp)
99 {
100     GpgmeCtx ctx = NULL;
101     GpgmeError err;
102     GpgmeData cipher = NULL, plain = NULL;
103     struct passphrase_cb_info_s info;
104
105     memset (&info, 0, sizeof info);
106
107     err = gpgme_new (&ctx);
108     if (err) {
109         g_message ("gpgme_new failed: %s", gpgme_strerror (err));
110         goto leave;
111     }
112
113     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
114                                         partinfo->fpos, partinfo->size);
115     if (err) {
116         g_message ("gpgme_data_new_from_filepart failed: %s",
117                    gpgme_strerror (err));
118         goto leave;
119     }
120
121     err = gpgme_data_new (&plain);
122     if (err) {
123         g_message ("gpgme_new failed: %s", gpgme_strerror (err));
124         goto leave;
125     }
126
127     if (!getenv("GPG_AGENT_INFO")) {
128         info.c = ctx;
129         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
130     } 
131
132     err = gpgme_op_decrypt (ctx, cipher, plain);
133
134 leave:
135     gpgme_data_release (cipher);
136     if (err) {
137         g_warning ("** decryption failed: %s", gpgme_strerror (err));
138         gpgme_data_release (plain);
139         plain = NULL;
140     }
141     else
142         g_message ("** decryption succeeded");
143
144     gpgme_release (ctx);
145     return plain;
146 }
147
148 static int
149 headerp(char *p, char **names)
150 {
151     int i, c;
152     char *p2;
153
154     p2 = strchr(p, ':');
155     if(!p2 || p == p2) {
156         return 0;
157     }
158     if(p2[-1] == ' ' || p2[-1] == '\t') {
159         return 0;
160     }
161
162     if(!names[0])
163         return 1;  
164
165     c = *p2;
166     *p2 = 0;
167     for(i = 0 ; names[i] != NULL; i++) {
168         if(!name_cmp (names[i], p))
169             break;
170     }
171     *p2 = c;
172
173     return names[i] != NULL;
174 }
175
176 MimeInfo * pgptext_find_signature (MimeInfo *mimeinfo)
177 {
178 }
179
180 gboolean pgptext_has_signature (MimeInfo *mimeinfo)
181 {
182         /*
183          * check for the following strings:
184          *   -----BEGIN PGP SIGNED MESSAGE-----
185          *   ----- ????
186          *   -----BEGIN PGP SIGNATURE-----
187          *   -----END PGP SIGNATURE-----
188          */
189         
190         return 0;
191 }
192
193 void pgptext_check_signature (MimeInfo *mimeinfo, FILE *fp)
194 {
195 }
196
197 int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
198 {
199         FILE *fp;
200         gchar *file, *tmpchk;
201         gchar buf[BUFFSIZE];
202         gboolean has_begin_pgp_msg = FALSE;
203         gboolean has_end_pgp_msg = FALSE;
204         gchar *check_begin_pgp_msg = "-----BEGIN PGP MESSAGE-----\n";
205         gchar *check_end_pgp_msg = "-----END PGP MESSAGE-----\n";
206         
207         g_return_if_fail(msginfo != NULL);
208         
209         if (!mimeinfo)
210                 return 0;
211         
212         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
213         mimeinfo = procmime_scan_mime_header(fp);
214         fclose(fp);
215         if (!mimeinfo) return;
216
217         file = procmsg_get_message_file_path(msginfo);
218         g_return_if_fail(file != NULL);
219
220         if (mimeinfo->mime_type != MIME_TEXT) {
221                 if ((fp = fopen(file, "r")) == NULL) {
222                         FILE_OP_ERROR(file, "fopen");
223                         return;
224                 }
225                 /* skip headers */
226                 if (mimeinfo->mime_type == MIME_MULTIPART) {
227                         if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
228                         perror("fseek");
229                         while (fgets(buf, sizeof(buf), fp) != NULL)
230                                 if (buf[0] == '\r' || buf[0] == '\n') break;
231                 }
232                 /* now check for a pgptext encrypted message */
233                 while (fgets(buf, sizeof(buf), fp) != NULL) {
234                         tmpchk = g_strnfill(sizeof(buf), '\n');
235                         memmove(tmpchk, &buf, sizeof(buf));
236                         
237                         if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
238                                 has_begin_pgp_msg = TRUE;
239                         if (strstr(tmpchk, check_end_pgp_msg) != NULL)
240                                 has_end_pgp_msg = TRUE;
241                         
242                         g_free(tmpchk);
243                 }
244                 fclose(fp);
245         } else {
246                 if ((fp = fopen(file, "r")) == NULL) {
247                         FILE_OP_ERROR(file, "fopen");
248                         return;
249                 }
250                 /* skip headers */
251                 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
252                 perror("fseek");
253                 while (fgets(buf, sizeof(buf), fp) != NULL)
254                         if (buf[0] == '\r' || buf[0] == '\n') break;
255                 
256                 /* now check for a pgptext encrypted message */
257                 while (fgets(buf, sizeof(buf), fp) != NULL) {
258                         tmpchk = g_strnfill(sizeof(buf), '\n');
259                         memmove(tmpchk, &buf, sizeof(buf));
260                         
261                         if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
262                                 has_begin_pgp_msg = TRUE;
263                         if (strstr(tmpchk, check_end_pgp_msg) != NULL)
264                                 has_end_pgp_msg = TRUE;
265                         
266                         g_free(tmpchk);
267                 }
268                 fclose(fp);
269         }
270         
271         g_free(file);   
272         
273         /* do we have a proper message? */
274         if (has_begin_pgp_msg && has_end_pgp_msg)
275                 return 1;
276         else
277                 return 0;
278 }
279
280 void pgptext_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
281 {
282     static int id;
283     MimeInfo *partinfo;
284     int n, found;
285     int ver_okay=0;
286     char *fname;
287     GpgmeData plain;
288     FILE *dstfp;
289     size_t nread;
290     char buf[BUFFSIZE];
291     GpgmeError err;
292
293     g_return_if_fail (mimeinfo->mime_type == MIME_TEXT);
294
295     g_message ("** text/plain with pgptext encountered");
296
297     partinfo = procmime_scan_message(msginfo);
298                 
299     /* skip headers */
300     if (fseek(fp, partinfo->fpos, SEEK_SET) < 0)
301         perror("fseek");
302     while (fgets(buf, sizeof(buf), fp) != NULL) {
303                                 partinfo->fpos = partinfo->fpos + strlen(buf);
304         if (buf[0] == '\r' || buf[0] == '\n') break;
305                 }
306     /* get size */
307     while (fgets(buf, sizeof(buf), fp) != NULL)
308                                 partinfo->size = partinfo->size + strlen(buf);
309                 
310     plain = pgptext_decrypt (partinfo, fp);
311     if (!plain) {
312         msginfo->decryption_failed = 1;
313         return;
314     }
315     
316     fname = g_strdup_printf("%s%cplaintext.%08x",
317                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
318
319     if ((dstfp = fopen(fname, "w")) == NULL) {
320         FILE_OP_ERROR(fname, "fopen");
321         g_free(fname);
322         msginfo->decryption_failed = 1;
323         return;
324     }
325
326     /* write the orginal header to the new file */
327     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
328         perror("fseek");
329
330     while (fgets(buf, sizeof(buf), fp)) {
331         if (headerp (buf, content_names))
332             continue;
333         if (buf[0] == '\r' || buf[0] == '\n')
334             break;
335         fputs (buf, dstfp);
336     }
337                 
338     err = gpgme_data_rewind (plain);
339     if (err)
340         g_message ("** gpgme_data_rewind failed: %s", gpgme_strerror (err));
341
342                 /* insert blank line to avoid some trouble... */
343                 fputs ("\n", dstfp);
344                 
345     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
346         fwrite (buf, nread, 1, dstfp);
347     }
348
349     if (err != GPGME_EOF) {
350         g_warning ("** gpgme_data_read failed: %s", gpgme_strerror (err));
351     }
352
353     fclose (dstfp);
354
355     msginfo->plaintext_file = fname;
356     msginfo->decryption_failed = 0;
357
358 }
359
360 int pgptext_encrypt (const char *file, GSList *recp_list)
361 {
362 }
363
364 int pgptext_sign (const char *file, PrefsAccount *ac)
365 {
366 }
367
368 #endif  /* USE_GPGME */
369