better support for mime/digest
[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 /* stolen from rfc2015.c */
73 static int
74 gpg_name_cmp(const char *a, const char *b)
75 {
76     for( ; *a && *b; a++, b++) {
77         if(*a != *b
78            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
79             return 1;
80     }
81
82     return *a != *b;
83 }
84
85 static const char *
86 passphrase_cb (void *opaque, const char *desc, void *r_hd)
87 {
88     struct passphrase_cb_info_s *info = opaque;
89     GpgmeCtx ctx = info ? info->c : NULL;
90     const char *pass;
91
92     if (!desc) {
93         /* FIXME: cleanup by looking at *r_hd */
94         return NULL;
95     }
96
97     gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
98     g_message ("%% requesting passphrase for `%s': ", desc );
99     pass = gpgmegtk_passphrase_mbox (desc);
100     if (!pass) {
101         g_message ("%% cancel passphrase entry");
102         gpgme_cancel (ctx);
103     }
104     else
105         g_message ("%% sending passphrase");
106
107     return pass;
108 }
109
110 static GpgmeData
111 pgptext_decrypt (MimeInfo *partinfo, FILE *fp)
112 {
113     GpgmeCtx ctx = NULL;
114     GpgmeError err;
115     GpgmeData cipher = NULL, plain = NULL;
116     struct passphrase_cb_info_s info;
117
118     memset (&info, 0, sizeof info);
119
120     err = gpgme_new (&ctx);
121     if (err) {
122         g_message ("gpgme_new failed: %s", gpgme_strerror (err));
123         goto leave;
124     }
125
126     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
127                                         partinfo->fpos, partinfo->size);
128     if (err) {
129         g_message ("gpgme_data_new_from_filepart failed: %s",
130                    gpgme_strerror (err));
131         goto leave;
132     }
133
134     err = gpgme_data_new (&plain);
135     if (err) {
136         g_message ("gpgme_new failed: %s", gpgme_strerror (err));
137         goto leave;
138     }
139
140     if (!getenv("GPG_AGENT_INFO")) {
141         info.c = ctx;
142         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
143     } 
144
145     err = gpgme_op_decrypt (ctx, cipher, plain);
146
147 leave:
148     gpgme_data_release (cipher);
149     if (err) {
150         g_warning ("** decryption failed: %s", gpgme_strerror (err));
151         gpgme_data_release (plain);
152         plain = NULL;
153     }
154     else
155         g_message ("** decryption succeeded");
156
157     gpgme_release (ctx);
158     return plain;
159 }
160
161 static int
162 headerp(char *p, char **names)
163 {
164     int i, c;
165     char *p2;
166
167     p2 = strchr(p, ':');
168     if(!p2 || p == p2) {
169         return 0;
170     }
171     if(p2[-1] == ' ' || p2[-1] == '\t') {
172         return 0;
173     }
174
175     if(!names[0])
176         return 1;  
177
178     c = *p2;
179     *p2 = 0;
180     for(i = 0 ; names[i] != NULL; i++) {
181         if(!gpg_name_cmp (names[i], p))
182             break;
183     }
184     *p2 = c;
185
186     return names[i] != NULL;
187 }
188
189 MimeInfo * pgptext_find_signature (MimeInfo *mimeinfo)
190 {
191 }
192
193 gboolean pgptext_has_signature (MimeInfo *mimeinfo)
194 {
195         /*
196          * check for the following strings:
197          *   -----BEGIN PGP SIGNED MESSAGE-----
198          *   ----- ????
199          *   -----BEGIN PGP SIGNATURE-----
200          *   -----END PGP SIGNATURE-----
201          */
202         
203         return 0;
204 }
205
206 void pgptext_check_signature (MimeInfo *mimeinfo, FILE *fp)
207 {
208 }
209
210 int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
211 {
212         FILE *fp;
213         gchar *file, *tmpchk;
214         gchar buf[BUFFSIZE];
215         gboolean has_begin_pgp_msg = FALSE;
216         gboolean has_end_pgp_msg = FALSE;
217         gchar *check_begin_pgp_msg = "-----BEGIN PGP MESSAGE-----\n";
218         gchar *check_end_pgp_msg = "-----END PGP MESSAGE-----\n";
219         
220         g_return_if_fail(msginfo != NULL);
221         
222         if (!mimeinfo)
223                 return 0;
224         
225         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
226         mimeinfo = procmime_scan_mime_header(fp, MIME_TEXT);
227         fclose(fp);
228         if (!mimeinfo) return;
229
230         file = procmsg_get_message_file_path(msginfo);
231         g_return_if_fail(file != NULL);
232
233         if (mimeinfo->mime_type != MIME_TEXT) {
234                 if ((fp = fopen(file, "r")) == NULL) {
235                         FILE_OP_ERROR(file, "fopen");
236                         return;
237                 }
238                 /* skip headers */
239                 if (mimeinfo->mime_type == MIME_MULTIPART) {
240                         if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
241                         perror("fseek");
242                         while (fgets(buf, sizeof(buf), fp) != NULL)
243                                 if (buf[0] == '\r' || buf[0] == '\n') break;
244                 }
245                 /* now check for a pgptext encrypted message */
246                 while (fgets(buf, sizeof(buf), fp) != NULL) {
247                         tmpchk = g_strnfill(sizeof(buf), '\n');
248                         memmove(tmpchk, &buf, sizeof(buf));
249                         
250                         if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
251                                 has_begin_pgp_msg = TRUE;
252                         if (strstr(tmpchk, check_end_pgp_msg) != NULL)
253                                 has_end_pgp_msg = TRUE;
254                         
255                         g_free(tmpchk);
256                 }
257                 fclose(fp);
258         } else {
259                 if ((fp = fopen(file, "r")) == NULL) {
260                         FILE_OP_ERROR(file, "fopen");
261                         return;
262                 }
263                 /* skip headers */
264                 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
265                 perror("fseek");
266                 while (fgets(buf, sizeof(buf), fp) != NULL)
267                         if (buf[0] == '\r' || buf[0] == '\n') break;
268                 
269                 /* now check for a pgptext encrypted message */
270                 while (fgets(buf, sizeof(buf), fp) != NULL) {
271                         tmpchk = g_strnfill(sizeof(buf), '\n');
272                         memmove(tmpchk, &buf, sizeof(buf));
273                         
274                         if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
275                                 has_begin_pgp_msg = TRUE;
276                         if (strstr(tmpchk, check_end_pgp_msg) != NULL)
277                                 has_end_pgp_msg = TRUE;
278                         
279                         g_free(tmpchk);
280                 }
281                 fclose(fp);
282         }
283         
284         g_free(file);   
285         
286         /* do we have a proper message? */
287         if (has_begin_pgp_msg && has_end_pgp_msg)
288                 return 1;
289         else
290                 return 0;
291 }
292
293 void pgptext_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
294 {
295     static int id;
296     MimeInfo *partinfo;
297     int n, found;
298     int ver_okay=0;
299     char *fname;
300     GpgmeData plain;
301     FILE *dstfp;
302     size_t nread;
303     char buf[BUFFSIZE];
304     GpgmeError err;
305
306     g_return_if_fail (mimeinfo->mime_type == MIME_TEXT);
307
308     g_message ("** text/plain with pgptext encountered");
309
310     partinfo = procmime_scan_message(msginfo);
311                 
312     /* skip headers */
313     if (fseek(fp, partinfo->fpos, SEEK_SET) < 0)
314         perror("fseek");
315     while (fgets(buf, sizeof(buf), fp) != NULL) {
316                                 partinfo->fpos = partinfo->fpos + strlen(buf);
317         if (buf[0] == '\r' || buf[0] == '\n') break;
318                 }
319     /* get size */
320     while (fgets(buf, sizeof(buf), fp) != NULL)
321                                 partinfo->size = partinfo->size + strlen(buf);
322                 
323     plain = pgptext_decrypt (partinfo, fp);
324     if (!plain) {
325         msginfo->decryption_failed = 1;
326         return;
327     }
328     
329     fname = g_strdup_printf("%s%cplaintext.%08x",
330                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
331
332     if ((dstfp = fopen(fname, "w")) == NULL) {
333         FILE_OP_ERROR(fname, "fopen");
334         g_free(fname);
335         msginfo->decryption_failed = 1;
336         return;
337     }
338
339     /* write the orginal header to the new file */
340     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
341         perror("fseek");
342
343     while (fgets(buf, sizeof(buf), fp)) {
344         if (headerp (buf, content_names))
345             continue;
346         if (buf[0] == '\r' || buf[0] == '\n')
347             break;
348         fputs (buf, dstfp);
349     }
350                 
351     err = gpgme_data_rewind (plain);
352     if (err)
353         g_message ("** gpgme_data_rewind failed: %s", gpgme_strerror (err));
354
355                 /* insert blank line to avoid some trouble... */
356                 fputs ("\n", dstfp);
357                 
358     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
359         fwrite (buf, nread, 1, dstfp);
360     }
361
362     if (err != GPGME_EOF) {
363         g_warning ("** gpgme_data_read failed: %s", gpgme_strerror (err));
364     }
365
366     fclose (dstfp);
367
368     msginfo->plaintext_file = fname;
369     msginfo->decryption_failed = 0;
370
371 }
372
373 int pgptext_encrypt (const char *file, GSList *recp_list)
374 {
375 }
376
377 int pgptext_sign (const char *file, PrefsAccount *ac)
378 {
379 }
380
381 #endif  /* USE_GPGME */
382