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