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