update British English translation
[claws.git] / src / rfc2015.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001 Werner Koch (dd9jn)
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 #define DIM(v)     (sizeof(v)/sizeof((v)[0]))
51
52 static char *content_names[] = {
53     "Content-Type",
54     "Content-Disposition",
55     "Content-Transfer-Encoding",
56     NULL
57 };
58
59 static char *mime_version_name[] = {
60     "Mime-Version",
61     NULL
62 };
63
64 static void sig_expiration_check        (GString        *str,
65                                          GpgmeCtx        ctx,
66                                          GpgmeKey        key, 
67                                          GpgmeSigStat    status,
68                                          int             idx);
69 static void sig_expired                 (GString        *str,
70                                          GpgmeCtx        ctx,
71                                          int             idx);
72 static void sig_key_expired             (GString        *str,
73                                          GpgmeKey        key,
74                                          int             idx);
75
76 #if 0
77 static void dump_mimeinfo (const char *text, MimeInfo *x)
78 {
79     debug_print ("MimeInfo[%s] %p  level=%d\n",
80                text, x, x? x->level:0 );
81     if (!x)
82         return;
83
84     debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
85                x->encoding, x->encoding_type, x->mime_type );
86     debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
87                x->content_type, x->charset, x->name, x->boundary );
88     debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
89                x->content_disposition, x->filename, x->fpos, x->size,
90                x->level );
91     dump_mimeinfo (".main", x->main );
92     dump_mimeinfo (".sub", x->sub );
93     dump_mimeinfo (".next", x->next );
94     debug_print ("MimeInfo[.parent] %p\n", x ); 
95     dump_mimeinfo (".children", x->children );
96     dump_mimeinfo (".plaintext", x->plaintext );
97 }
98
99 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
100 {
101     unsigned int size = mimeinfo->size;
102     int c;
103
104     if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
105         debug_print ("dump_part: fseek error\n");
106         return;
107     }
108
109     debug_print ("--- begin dump_part ----\n");
110     while (size-- && (c = getc (fp)) != EOF) 
111         putc (c, stderr);
112     if (ferror (fp))
113         debug_print ("dump_part: read error\n");
114     debug_print ("--- end dump_part ----\n");
115 }
116 #endif
117
118 void
119 rfc2015_disable_all (void)
120 {
121     /* FIXME: set a flag, so that we don't bother the user with failed
122      * gpgme messages */
123 }
124
125
126 void
127 rfc2015_secure_remove (const char *fname)
128 {
129     if (!fname)
130         return;
131     /* fixme: overwrite the file first */
132     remove (fname);
133 }
134
135
136 static const gchar *
137 sig_status_to_string (GpgmeSigStat status)
138 {
139     const gchar *result;
140
141     switch (status) {
142       case GPGME_SIG_STAT_NONE:
143         result = _("Oops: Signature not verified");
144         break;
145       case GPGME_SIG_STAT_NOSIG:
146         result = _("No signature found");
147         break;
148       case GPGME_SIG_STAT_GOOD:
149         result = _("Good signature");
150         break;
151       case GPGME_SIG_STAT_GOOD_EXP:     
152         result = _("Good signature but it has expired");
153         break;
154       case GPGME_SIG_STAT_GOOD_EXPKEY:
155         result = _("Good signature but the key has expired");
156         break;
157       case GPGME_SIG_STAT_BAD:
158         result = _("BAD signature");
159         break;
160       case GPGME_SIG_STAT_NOKEY:
161         result = _("No public key to verify the signature");
162         break;
163       case GPGME_SIG_STAT_ERROR:
164         result = _("Error verifying the signature");
165         break;
166       case GPGME_SIG_STAT_DIFF:
167         result = _("Different results for signatures");
168         break;
169       default:
170         result = _("Error: Unknown status");
171         break;
172     }
173
174     return result;
175 }
176
177 static const gchar *
178 sig_status_with_name (GpgmeSigStat status)
179 {
180     const gchar *result;
181
182     switch (status) {
183       case GPGME_SIG_STAT_NONE:
184         result = _("Oops: Signature not verified");
185         break;
186       case GPGME_SIG_STAT_NOSIG:
187         result = _("No signature found");
188         break;
189       case GPGME_SIG_STAT_GOOD:
190         result = _("Good signature from \"%s\"");
191         break;
192       case GPGME_SIG_STAT_GOOD_EXP:
193         result = _("Good signature from \"%s\" but it has expired");
194         break;
195       case GPGME_SIG_STAT_GOOD_EXPKEY:
196         result = _("Good signature from \"%s\" but the key has expired");
197         break;
198       case GPGME_SIG_STAT_BAD:
199         result = _("BAD signature from \"%s\"");
200         break;
201       case GPGME_SIG_STAT_NOKEY:
202         result = _("No public key to verify the signature");
203         break;
204       case GPGME_SIG_STAT_ERROR:
205         result = _("Error verifying the signature");
206         break;
207       case GPGME_SIG_STAT_DIFF:
208         result = _("Different results for signatures");
209         break;
210       default:
211         result = _("Error: Unknown status");
212         break;
213     }
214
215     return result;
216 }
217
218 static void
219 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
220                    GpgmeKey key, const gchar *fpr)
221 {
222         gint idx = 0;
223         const char *uid;
224
225         uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
226         if (uid == NULL) {
227                 g_string_sprintfa (str, "%s\n",
228                                    sig_status_to_string (status));
229                 if ((fpr != NULL) && (*fpr != '\0'))
230                         g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
231                 g_string_append (str, _("Cannot find user ID for this key."));
232                 sig_expiration_check(str, ctx, key, status, 0);
233                 return;
234         }
235         g_string_sprintfa (str, sig_status_with_name (status), uid);
236         g_string_append (str, "\n");
237
238         while (1) {
239                 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
240                                                  NULL, ++idx);
241                 if (uid == NULL)
242                         break;
243                 g_string_sprintfa (str, _("                aka \"%s\"\n"),
244                                    uid);
245         }
246         sig_expiration_check(str, ctx, key, status, 0);
247 }
248
249 static void
250 sig_expiration_check(GString *str, GpgmeCtx ctx, GpgmeKey key, 
251                      GpgmeSigStat status, int idx)
252 {
253         if (status == GPGME_SIG_STAT_GOOD_EXP)
254                 sig_expired(str, ctx, idx);
255         else if (status == GPGME_SIG_STAT_GOOD_EXPKEY)
256                 sig_key_expired(str, key, idx);
257 }
258
259 static void
260 sig_expired(GString *str, GpgmeCtx ctx, int idx)
261 {
262         unsigned long exp_time;
263         exp_time = gpgme_get_sig_ulong_attr(ctx, idx, GPGME_ATTR_EXPIRE, 0);
264         g_string_sprintfa(str, _("Signature expired %s"), ctime(&exp_time));    
265 }
266
267 static void
268 sig_key_expired(GString *str, GpgmeKey key, int idx)
269 {
270         unsigned long exp_time;
271         exp_time = gpgme_key_get_ulong_attr(key, GPGME_ATTR_EXPIRE, NULL, idx);
272         g_string_sprintfa(str, _("Key expired %s"), ctime(&exp_time));  
273 }
274
275 static gchar *
276 sig_status_full (GpgmeCtx ctx)
277 {
278         GString *str;
279         gint sig_idx = 0;
280         GpgmeError err;
281         GpgmeSigStat status;
282         GpgmeKey key;
283         const char *fpr;
284         time_t created;
285         struct tm *ctime_val;
286         char ctime_str[80];
287         gchar *retval;
288
289         str = g_string_new ("");
290
291         fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
292         while (fpr != NULL) {
293                 if (created != 0) {
294                         ctime_val = localtime (&created);
295                         strftime (ctime_str, sizeof (ctime_str), "%c", 
296                                   ctime_val);
297                         g_string_sprintfa (str,
298                                            _("Signature made at %s\n"),
299                                            ctime_str);
300                 }
301                 err = gpgme_get_sig_key (ctx, sig_idx, &key);
302                 if (err != 0) {
303                         g_string_sprintfa (str, "%s\n",
304                                            sig_status_to_string (status));
305                         if ((fpr != NULL) && (*fpr != '\0'))
306                                 g_string_sprintfa (str, 
307                                                    _("Key fingerprint: %s\n"),
308                                                    fpr);
309                 } else {
310                         const char *key_id_str;
311                         
312                         sig_status_for_key (str, ctx, status, key, fpr);
313                         key_id_str = gpgme_key_get_string_attr(key, 
314                                                 GPGME_ATTR_KEYID, NULL, 0);
315                         key_id_str += 8;
316                         g_string_sprintfa (str, _("Key ID: %s\n"), key_id_str);
317                         gpgme_key_unref (key);
318                 }
319                 g_string_append (str, "\n\n");
320
321                 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
322         }
323
324         retval = str->str;
325         g_string_free (str, FALSE);
326         return retval;
327 }
328
329 static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
330 {
331     GpgmeCtx ctx = NULL;
332     GpgmeError err;
333     GpgmeData sig = NULL, text = NULL;
334     GpgmeSigStat status = GPGME_SIG_STAT_NONE;
335     GpgmegtkSigStatus statuswindow = NULL;
336     const char *result = NULL;
337     gchar *tmp_file;
338     gint n_exclude_chars = 0;
339
340     if (prefs_common.gpg_signature_popup)
341         statuswindow = gpgmegtk_sig_status_create ();
342
343     err = gpgme_new (&ctx);
344     if (err) {
345         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
346         goto leave;
347     }
348
349     /* don't include the last empty line.
350        It does not belong to the signed text */
351     if (mimeinfo->children->size > 0) {
352         if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1,
353                   SEEK_SET) < 0) {
354             perror("fseek");
355             goto leave;
356         }
357         if (fgetc(fp) == '\n') {
358             n_exclude_chars++;
359             if (mimeinfo->children->size > 1) {
360                 if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2,
361                           SEEK_SET) < 0) {
362                     perror("fseek");
363                     goto leave;
364                 }
365                 if (fgetc(fp) == '\r')
366                     n_exclude_chars++;
367             }
368         }
369     }
370
371     /* canonicalize the file part. */
372     tmp_file = get_tmp_file();
373     if (copy_file_part(fp, mimeinfo->children->fpos,
374                        mimeinfo->children->size - n_exclude_chars,
375                        tmp_file) < 0) {
376         g_free(tmp_file);
377         goto leave;
378     }
379     if (canonicalize_file_replace(tmp_file) < 0) {
380         unlink(tmp_file);
381         g_free(tmp_file);
382         goto leave;
383     }
384
385     err = gpgme_data_new_from_file(&text, tmp_file, 1);
386
387     unlink(tmp_file);
388     g_free(tmp_file);
389
390     if (!err)
391         err = gpgme_data_new_from_filepart (&sig, NULL, fp,
392                                             partinfo->fpos, partinfo->size);
393     if (err) {
394         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
395                    gpgme_strerror (err));
396         goto leave;
397     }
398
399     err = gpgme_op_verify (ctx, sig, text, &status);
400     if (err)  {
401         debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
402         goto leave;
403     }
404
405     /* FIXME: check what the heck this sig_status_full stuff is.
406      * it should better go into sigstatus.c */
407     g_free (partinfo->sigstatus_full);
408     partinfo->sigstatus_full = sig_status_full (ctx);
409
410 leave:
411     result = gpgmegtk_sig_status_to_string(status);
412     debug_print("verification status: %s\n", result);
413     if (prefs_common.gpg_signature_popup)
414         gpgmegtk_sig_status_update (statuswindow, ctx);
415
416     g_free (partinfo->sigstatus);
417     partinfo->sigstatus = g_strdup (result);
418     partinfo->sig_ok = (status == GPGME_SIG_STAT_GOOD);
419     partinfo->sig_unknown = (status == GPGME_SIG_STAT_NOKEY);
420     partinfo->sig_expired = (status == GPGME_SIG_STAT_GOOD_EXP);
421     partinfo->key_expired = (status == GPGME_SIG_STAT_GOOD_EXPKEY);
422
423     gpgme_data_release (sig);
424     gpgme_data_release (text);
425     gpgme_release (ctx);
426     if (prefs_common.gpg_signature_popup)
427         gpgmegtk_sig_status_destroy (statuswindow);
428 }
429
430 /*
431  * Copy a gpgme data object to a temporary file and
432  * return this filename 
433  */
434 #if 0
435 static char *
436 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
437 {
438     static int id;
439     char *tmp;
440     FILE *fp;
441     char buf[100];
442     size_t nread;
443     GpgmeError err;
444     
445     tmp = g_strdup_printf("%s%cgpgtmp.%08x",
446                           get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
447
448     if ((fp = fopen(tmp, "wb")) == NULL) {
449         FILE_OP_ERROR(tmp, "fopen");
450         g_free(tmp);
451         return NULL;
452     }
453
454     err = gpgme_data_rewind ( data );
455     if (err)
456         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
457
458     while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
459         fwrite ( buf, nread, 1, fp );
460     }
461
462     if (err != GPGME_EOF)
463         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
464
465     fclose (fp);
466     *length = nread;
467
468     return tmp;
469 }
470 #endif
471
472 static GpgmeData
473 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
474 {
475     GpgmeCtx ctx = NULL;
476     GpgmeError err;
477     GpgmeData cipher = NULL, plain = NULL;
478     struct passphrase_cb_info_s info;
479
480     memset (&info, 0, sizeof info);
481
482     err = gpgme_new (&ctx);
483     if (err) {
484         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
485         goto leave;
486     }
487
488     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
489                                         partinfo->fpos, partinfo->size);
490     if (err) {
491         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
492                      gpgme_strerror (err));
493         goto leave;
494     }
495
496     err = gpgme_data_new (&plain);
497     if (err) {
498         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
499         goto leave;
500     }
501
502     if (!getenv("GPG_AGENT_INFO")) {
503         info.c = ctx;
504         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
505     } 
506
507     err = gpgme_op_decrypt (ctx, cipher, plain);
508
509 leave:
510     gpgme_data_release (cipher);
511     if (err) {
512         gpgmegtk_free_passphrase();
513         debug_print ("decryption failed: %s\n", gpgme_strerror (err));
514         gpgme_data_release (plain);
515         plain = NULL;
516     }
517     else
518         debug_print ("** decryption succeeded\n");
519
520     gpgme_release (ctx);
521     return plain;
522 }
523
524 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
525 {
526     MimeInfo *partinfo;
527     int n = 0;
528
529     if (!mimeinfo)
530         return NULL;
531     if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
532         return NULL;
533
534     debug_print ("** multipart/signed encountered\n");
535
536     /* check that we have at least 2 parts of the correct type */
537     for (partinfo = mimeinfo->children;
538          partinfo != NULL; partinfo = partinfo->next) {
539         if (++n > 1  && !g_strcasecmp (partinfo->content_type,
540                                        "application/pgp-signature"))
541             break;
542     }
543
544     return partinfo;
545 }
546
547 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
548 {
549     return rfc2015_find_signature (mimeinfo) != NULL;
550 }
551
552 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
553 {
554     MimeInfo *partinfo;
555
556     partinfo = rfc2015_find_signature (mimeinfo);
557     if (!partinfo)
558         return;
559
560 #if 0
561     g_message ("** yep, it is a pgp signature");
562     dump_mimeinfo ("gpg-signature", partinfo );
563     dump_part (partinfo, fp );
564     dump_mimeinfo ("signed text", mimeinfo->children );
565     dump_part (mimeinfo->children, fp);
566 #endif
567
568     check_signature (mimeinfo, partinfo, fp);
569 }
570
571 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
572 {
573     if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART)
574         return 0;
575     if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
576         return 0;
577     /* fixme: we should check the protocol parameter */
578     return 1;
579 }
580
581 gboolean rfc2015_msg_is_encrypted (const gchar *file)
582 {
583         FILE *fp;
584         MimeInfo *mimeinfo;
585         int ret;
586
587         if ((fp = fopen(file, "rb")) == NULL)
588                 return FALSE;
589
590         mimeinfo = procmime_scan_mime_header(fp);
591         if(!mimeinfo) {
592                 fclose(fp);
593                 return FALSE;
594         }
595
596         ret = rfc2015_is_encrypted(mimeinfo);
597         procmime_mimeinfo_free_all(mimeinfo);
598         return ret != 0 ? TRUE : FALSE;
599 }
600
601 static int
602 name_cmp(const char *a, const char *b)
603 {
604     for( ; *a && *b; a++, b++) {
605         if(*a != *b
606            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
607             return 1;
608     }
609
610     return *a != *b;
611 }
612
613 static int
614 headerp(char *p, char **names)
615 {
616     int i, c;
617     char *p2;
618
619     p2 = strchr(p, ':');
620     if(!p2 || p == p2) {
621         return 0;
622     }
623     if(p2[-1] == ' ' || p2[-1] == '\t') {
624         return 0;
625     }
626
627     if(!names[0])
628         return 1;  
629
630     c = *p2;
631     *p2 = 0;
632     for(i = 0 ; names[i] != NULL; i++) {
633         if(!name_cmp (names[i], p))
634             break;
635     }
636     *p2 = c;
637
638     return names[i] != NULL;
639 }
640
641
642 #define DECRYPTION_ABORT() \
643 { \
644     procmime_mimeinfo_free_all(tmpinfo); \
645     msginfo->decryption_failed = 1; \
646     return; \
647 }
648
649 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
650 {
651     static int id;
652     MimeInfo *tmpinfo, *partinfo;
653     int ver_ok = 0;
654     char *fname;
655     GpgmeData plain;
656     FILE *dstfp;
657     size_t nread;
658     char buf[BUFFSIZE];
659     int in_cline;
660     GpgmeError err;
661
662     g_return_if_fail (msginfo != NULL);
663     g_return_if_fail (mimeinfo != NULL);
664     g_return_if_fail (fp != NULL);
665     g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
666
667     debug_print ("** decrypting multipart/encrypted message\n");
668
669     /* skip headers */
670     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
671         perror("fseek");
672     tmpinfo = procmime_scan_mime_header(fp);
673     if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
674         DECRYPTION_ABORT();
675     }
676
677     procmime_scan_multipart_message(tmpinfo, fp);
678
679     /* check that we have the 2 parts */
680     partinfo = tmpinfo->children;
681     if (!partinfo || !partinfo->next) {
682         DECRYPTION_ABORT();
683     }
684     if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
685         /* Fixme: check that the version is 1 */
686         ver_ok = 1;
687     }
688     partinfo = partinfo->next;
689     if (ver_ok &&
690         !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
691         if (partinfo->next)
692             g_warning ("oops: pgp_encrypted with more than 2 parts");
693     }
694     else {
695         DECRYPTION_ABORT();
696     }
697
698     debug_print ("** yep, it is pgp encrypted\n");
699
700     plain = pgp_decrypt (partinfo, fp);
701     if (!plain) {
702         DECRYPTION_ABORT();
703     }
704
705     fname = g_strdup_printf("%s%cplaintext.%08x",
706                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
707
708     if ((dstfp = fopen(fname, "wb")) == NULL) {
709         FILE_OP_ERROR(fname, "fopen");
710         g_free(fname);
711         DECRYPTION_ABORT();
712     }
713
714     /* write the orginal header to the new file */
715     if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
716         perror("fseek");
717
718     in_cline = 0;
719     while (fgets(buf, sizeof(buf), fp)) {
720         if (headerp (buf, content_names)) {
721             in_cline = 1;
722             continue;
723         }
724         if (in_cline) {
725             if (buf[0] == ' ' || buf[0] == '\t')
726                 continue;
727             in_cline = 0;
728         }
729         if (buf[0] == '\r' || buf[0] == '\n')
730             break;
731         fputs (buf, dstfp);
732     }
733
734     err = gpgme_data_rewind (plain);
735     if (err)
736         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
737
738     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
739         fwrite (buf, nread, 1, dstfp);
740     }
741
742     if (err != GPGME_EOF) {
743         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
744     }
745
746     fclose (dstfp);
747     procmime_mimeinfo_free_all(tmpinfo);
748
749     msginfo->plaintext_file = fname;
750     msginfo->decryption_failed = 0;
751 }
752
753 #undef DECRYPTION_ABORT
754
755
756 /*
757  * plain contains an entire mime object.
758  * Encrypt it and return an GpgmeData object with the encrypted version of
759  * the file or NULL in case of error.
760  */
761 static GpgmeData
762 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
763 {
764     GpgmeCtx ctx = NULL;
765     GpgmeError err;
766     GpgmeData cipher = NULL;
767
768     err = gpgme_new (&ctx);
769     if (!err)
770         err = gpgme_data_new (&cipher);
771     if (!err) {
772         gpgme_set_armor (ctx, 1);
773         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
774     }
775
776     if (err) {
777         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
778         gpgme_data_release (cipher);
779         cipher = NULL;
780     }
781     else {
782         debug_print ("** encryption succeeded\n");
783     }
784
785     gpgme_release (ctx);
786     return cipher;
787 }
788
789 /*
790  * Create and return a list of keys matching a key id
791  */
792
793 GSList *rfc2015_create_signers_list (const char *keyid)
794 {
795         GSList *key_list = NULL;
796         GpgmeCtx list_ctx = NULL;
797         GSList *p;
798         GpgmeError err;
799         GpgmeKey key;
800
801         err = gpgme_new (&list_ctx);
802         if (err)
803                 goto leave;
804         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
805         if (err)
806                 goto leave;
807         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
808                 key_list = g_slist_append (key_list, key);
809         }
810         if (err != GPGME_EOF)
811                 goto leave;
812         err = 0;
813         if (key_list == NULL) {
814                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
815         }
816
817 leave:
818         if (err) {
819                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
820                 for (p = key_list; p != NULL; p = p->next)
821                         gpgme_key_unref ((GpgmeKey) p->data);
822                 g_slist_free (key_list);
823         }
824         if (list_ctx)
825                 gpgme_release (list_ctx);
826         return err ? NULL : key_list;
827 }
828
829 /*
830  * Encrypt the file by extracting all recipients and finding the
831  * encryption keys for all of them.  The file content is then replaced
832  * by the encrypted one.  */
833 int
834 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
835 {
836     FILE *fp = NULL;
837     char buf[BUFFSIZE];
838     int i, clineidx, saved_last;
839     char *clines[3] = {NULL};
840     GpgmeError err;
841     GpgmeData header = NULL;
842     GpgmeData plain = NULL;
843     GpgmeData cipher = NULL;
844     GpgmeRecipients rset = NULL;
845     size_t nread;
846     int mime_version_seen = 0;
847     char *boundary;
848
849     boundary = generate_mime_boundary ("Encrypt");
850
851     /* Create the list of recipients */
852     rset = gpgmegtk_recipient_selection (recp_list);
853     if (!rset) {
854         debug_print ("error creating recipient list\n" );
855         goto failure;
856     }
857
858     /* Open the source file */
859     if ((fp = fopen(file, "rb")) == NULL) {
860         FILE_OP_ERROR(file, "fopen");
861         goto failure;
862     }
863
864     err = gpgme_data_new (&header);
865     if (!err)
866         err = gpgme_data_new (&plain);
867     if (err) {
868         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
869         goto failure;
870     }
871
872     /* get the content header lines from the source */
873     clineidx = 0;
874     saved_last = 0;
875     while (!err && fgets(buf, sizeof(buf), fp)) {
876         /* fixme: check for overlong lines */
877         if (headerp (buf, content_names)) {
878             if (clineidx >= DIM (clines)) {
879                 debug_print ("rfc2015_encrypt: too many content lines\n");
880                 goto failure;
881             }
882             clines[clineidx++] = g_strdup (buf);
883             saved_last = 1;
884             continue;
885         }
886         if (saved_last) {
887             if (*buf == ' ' || *buf == '\t') {
888                 char *last = clines[clineidx - 1];
889                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
890                 g_free (last);
891                 continue;
892             }
893             saved_last = 0;
894         }
895
896         if (headerp (buf, mime_version_name)) 
897             mime_version_seen = 1;
898
899         if (buf[0] == '\r' || buf[0] == '\n')
900             break;
901         err = gpgme_data_write (header, buf, strlen (buf));
902     }
903     if (ferror (fp)) {
904         FILE_OP_ERROR (file, "fgets");
905         goto failure;
906     }
907
908     /* write them to the temp data and add the rest of the message */
909     for (i = 0; !err && i < clineidx; i++) {
910         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
911         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
912     }
913     if (!err)
914         err = gpgme_data_write (plain, "\r\n", 2);
915     while (!err && fgets(buf, sizeof(buf), fp)) {
916         err = gpgme_data_write (plain, buf, strlen (buf));
917     }
918     if (ferror (fp)) {
919         FILE_OP_ERROR (file, "fgets");
920         goto failure;
921     }
922     if (err) {
923         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
924         goto failure;
925     }
926
927     cipher = pgp_encrypt (plain, rset);
928     gpgme_data_release (plain); plain = NULL;
929     gpgme_recipients_release (rset); rset = NULL;
930     if (!cipher)
931         goto failure;
932
933     /* we have the encrypted message available in cipher and now we
934      * are going to rewrite the source file. To be sure that file has
935      * been truncated we use an approach which should work everywhere:
936      * close the file and then reopen it for writing. It is important
937      * that this works, otherwise it may happen that parts of the
938      * plaintext are still in the file (The encrypted stuff is, due to
939      * compression, usually shorter than the plaintext). 
940      * 
941      * Yes, there is a race condition here, but everyone, who is so
942      * stupid to store the temp file with the plaintext in a public
943      * directory has to live with this anyway. */
944     if (fclose (fp)) {
945         FILE_OP_ERROR(file, "fclose");
946         goto failure;
947     }
948     if ((fp = fopen(file, "wb")) == NULL) {
949         FILE_OP_ERROR(file, "fopen");
950         goto failure;
951     }
952
953     /* Write the header, append new content lines, part 1 and part 2 header */
954     err = gpgme_data_rewind (header);
955     if (err) {
956         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
957         goto failure;
958     }
959     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
960         fwrite (buf, nread, 1, fp);
961     }
962     if (err != GPGME_EOF) {
963         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
964         goto failure;
965     }
966     if (ferror (fp)) {
967         FILE_OP_ERROR (file, "fwrite");
968         goto failure;
969     }
970     gpgme_data_release (header); header = NULL;
971     
972     if (!mime_version_seen) 
973         fputs ("MIME-Version: 1\r\n", fp);
974
975     if (ascii_armored) {
976         fprintf(fp, 
977             "Content-Type: text/plain; charset=US-ASCII\r\n"
978             "Content-Transfer-Encoding: 7bit\r\n"  
979             "\r\n");
980     } else {
981         fprintf (fp,
982                 "Content-Type: multipart/encrypted;"
983                 " protocol=\"application/pgp-encrypted\";\r\n"
984                 " boundary=\"%s\"\r\n"
985                 "\r\n"
986                 "--%s\r\n"
987                 "Content-Type: application/pgp-encrypted\r\n"
988                 "\r\n"
989                 "Version: 1\r\n"
990                 "\r\n"
991                 "--%s\r\n"
992                 "Content-Type: application/octet-stream\r\n"
993                 "\r\n",
994                 boundary, boundary, boundary);
995     }
996
997     /* append the encrypted stuff */
998     err = gpgme_data_rewind (cipher);
999     if (err) {
1000         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
1001                    gpgme_strerror (err));
1002         goto failure;
1003     }
1004
1005     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
1006         fwrite (buf, nread, 1, fp);
1007     }
1008     if (err != GPGME_EOF) {
1009         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
1010         goto failure;
1011     }
1012
1013     /* and the final boundary */
1014     if (!ascii_armored) {
1015         fprintf (fp,
1016                  "\r\n"
1017                  "--%s--\r\n",
1018                  boundary);
1019     }
1020     fflush (fp);
1021     if (ferror (fp)) {
1022         FILE_OP_ERROR (file, "fwrite");
1023         goto failure;
1024     }
1025     fclose (fp);
1026     gpgme_data_release (cipher);
1027     return 0;
1028
1029 failure:
1030     if (fp) 
1031         fclose (fp);
1032     gpgme_data_release (header);
1033     gpgme_data_release (plain);
1034     gpgme_data_release (cipher);
1035     gpgme_recipients_release (rset);
1036     g_free (boundary);
1037     return -1; /* error */
1038 }
1039
1040 /* 
1041  * plain contains an entire mime object.  Sign it and return an
1042  * GpgmeData object with the signature of it or NULL in case of error.
1043  * r_siginfo returns an XML object with information about the signature.
1044  */
1045 static GpgmeData
1046 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
1047           char **r_siginfo)
1048 {
1049     GSList *p;
1050     GpgmeCtx ctx = NULL;
1051     GpgmeError err;
1052     GpgmeData sig = NULL;
1053     struct passphrase_cb_info_s info;
1054
1055     *r_siginfo = NULL;
1056     memset (&info, 0, sizeof info);
1057
1058     err = gpgme_new (&ctx);
1059     if (err)
1060         goto leave;
1061     err = gpgme_data_new (&sig);
1062     if (err)
1063         goto leave;
1064
1065     if (!getenv("GPG_AGENT_INFO")) {
1066         info.c = ctx;
1067         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1068     }
1069     gpgme_set_textmode (ctx, 1);
1070     gpgme_set_armor (ctx, 1);
1071     gpgme_signers_clear (ctx);
1072     for (p = key_list; p != NULL; p = p->next) {
1073         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1074         if (err)
1075             goto leave;
1076     }
1077     for (p = key_list; p != NULL; p = p->next)
1078         gpgme_key_unref ((GpgmeKey) p->data);
1079     g_slist_free (key_list);
1080
1081     if (err)
1082         goto leave;
1083     err = gpgme_op_sign
1084         (ctx, plain, sig,
1085          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1086     if (!err)
1087         *r_siginfo = gpgme_get_op_info (ctx, 0);
1088
1089 leave:
1090     if (err) {
1091         gpgmegtk_free_passphrase();
1092         debug_print ("signing failed: %s\n", gpgme_strerror (err));
1093         gpgme_data_release (sig);
1094         sig = NULL;
1095     }
1096     else {
1097         debug_print ("signing succeeded\n");
1098     }
1099
1100     gpgme_release (ctx);
1101     return sig;
1102 }
1103
1104 /*
1105  * Find TAG in XML and return a pointer into xml set just behind the
1106  * closing angle.  Return NULL if not found. 
1107  */
1108 static const char *
1109 find_xml_tag (const char *xml, const char *tag)
1110 {
1111     int taglen = strlen (tag);
1112     const char *s = xml;
1113  
1114     while ( (s = strchr (s, '<')) ) {
1115         s++;
1116         if (!strncmp (s, tag, taglen)) {
1117             const char *s2 = s + taglen;
1118             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1119                 /* found */
1120                 while (*s2 && *s2 != '>') /* skip attributes */
1121                     s2++;
1122                 /* fixme: do need to handle angles inside attribute vallues? */
1123                 return *s2? (s2+1):NULL;
1124             }
1125         }
1126         while (*s && *s != '>') /* skip to end of tag */
1127             s++;
1128     }
1129     return NULL;
1130 }
1131
1132
1133 /*
1134  * Extract the micalg from an GnupgOperationInfo XML container.
1135  */
1136 static char *
1137 extract_micalg (char *xml)
1138 {
1139     const char *s;
1140
1141     s = find_xml_tag (xml, "GnupgOperationInfo");
1142     if (s) {
1143         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1144         s = find_xml_tag (s, "signature");
1145         if (s && s_end && s < s_end) {
1146             const char *s_end2 = find_xml_tag (s, "/signature");
1147             if (s_end2 && s_end2 < s_end) {
1148                 s = find_xml_tag (s, "micalg");
1149                 if (s && s < s_end2) {
1150                     s_end = strchr (s, '<');
1151                     if (s_end) {
1152                         char *p = g_malloc (s_end - s + 1);
1153                         memcpy (p, s, s_end - s);
1154                         p[s_end-s] = 0;
1155                         return p;
1156                     }
1157                 }
1158             }
1159         }
1160     }
1161     return NULL;
1162 }
1163
1164
1165 /*
1166  * Sign the file and replace its content with the signed one.
1167  */
1168 int
1169 rfc2015_sign (const char *file, GSList *key_list)
1170 {
1171     FILE *fp = NULL;
1172     char buf[BUFFSIZE];
1173     int i, clineidx, saved_last;
1174     char *clines[3] = {NULL};
1175     GpgmeError err;
1176     GpgmeData header = NULL;
1177     GpgmeData plain = NULL;
1178     GpgmeData sigdata = NULL;
1179     size_t nread;
1180     int mime_version_seen = 0;
1181     char *boundary;
1182     char *micalg = NULL;
1183     char *siginfo;
1184
1185     boundary = generate_mime_boundary ("Signature");
1186
1187     /* Open the source file */
1188     if ((fp = fopen(file, "rb")) == NULL) {
1189         FILE_OP_ERROR(file, "fopen");
1190         goto failure;
1191     }
1192
1193     err = gpgme_data_new (&header);
1194     if (!err)
1195         err = gpgme_data_new (&plain);
1196     if (err) {
1197         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1198         goto failure;
1199     }
1200
1201     /* get the content header lines from the source */
1202     clineidx = 0;
1203     saved_last = 0;
1204     while (!err && fgets(buf, sizeof(buf), fp)) {
1205         /* fixme: check for overlong lines */
1206         if (headerp (buf, content_names)) {
1207             if (clineidx >= DIM (clines)) {
1208                 debug_print ("rfc2015_sign: too many content lines\n");
1209                 goto failure;
1210             }
1211             clines[clineidx++] = g_strdup (buf);
1212             saved_last = 1;
1213             continue;
1214         }
1215         if (saved_last) {
1216             if (*buf == ' ' || *buf == '\t') {
1217                 char *last = clines[clineidx - 1];
1218                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1219                 g_free (last);
1220                 continue;
1221             }
1222             saved_last = 0;
1223         }
1224
1225         if (headerp (buf, mime_version_name)) 
1226             mime_version_seen = 1;
1227
1228         if (buf[0] == '\r' || buf[0] == '\n')
1229             break;
1230         err = gpgme_data_write (header, buf, strlen (buf));
1231     }
1232     if (ferror (fp)) {
1233         FILE_OP_ERROR (file, "fgets");
1234         goto failure;
1235     }
1236
1237     /* write them to the temp data and add the rest of the message */
1238     for (i = 0; !err && i < clineidx; i++) {
1239         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1240     }
1241     if (!err)
1242         err = gpgme_data_write (plain, "\r\n", 2 );
1243     while (!err && fgets(buf, sizeof(buf), fp)) {
1244         err = gpgme_data_write (plain, buf, strlen (buf));
1245     }
1246     if (ferror (fp)) {
1247         FILE_OP_ERROR (file, "fgets");
1248         goto failure;
1249     }
1250     if (err) {
1251         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1252         goto failure;
1253     }
1254
1255     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
1256     if (siginfo) {
1257         micalg = extract_micalg (siginfo);
1258         free (siginfo);
1259     }
1260     if (!sigdata) 
1261         goto failure;
1262
1263     /* we have the signed message available in sigdata and now we are
1264      * going to rewrite the original file. To be sure that file has
1265      * been truncated we use an approach which should work everywhere:
1266      * close the file and then reopen it for writing. */
1267     if (fclose (fp)) {
1268         FILE_OP_ERROR(file, "fclose");
1269         goto failure;
1270     }
1271     if ((fp = fopen(file, "wb")) == NULL) {
1272         FILE_OP_ERROR(file, "fopen");
1273         goto failure;
1274     }
1275
1276     /* Write the rfc822 header and add new content lines */
1277     err = gpgme_data_rewind (header);
1278     if (err)
1279         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1280     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1281         fwrite (buf, nread, 1, fp);
1282     }
1283     if (err != GPGME_EOF) {
1284         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1285         goto failure;
1286     }
1287     if (ferror (fp)) {
1288         FILE_OP_ERROR (file, "fwrite");
1289         goto failure;
1290     }
1291     gpgme_data_release (header);
1292     header = NULL;
1293
1294     if (!mime_version_seen) 
1295         fputs ("MIME-Version: 1.0\r\n", fp);
1296     fprintf (fp, "Content-Type: multipart/signed; "
1297              "protocol=\"application/pgp-signature\";\r\n");
1298     if (micalg)
1299         fprintf (fp, " micalg=\"%s\";\r\n", micalg);
1300     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1301
1302     /* Part 1: signed material */
1303     fprintf (fp, "\r\n"
1304                  "--%s\r\n",
1305                  boundary);
1306     err = gpgme_data_rewind (plain);
1307     if (err) {
1308         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1309                    gpgme_strerror (err));
1310         goto failure;
1311     }
1312     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1313         fwrite (buf, nread, 1, fp);   
1314     }
1315     if (err != GPGME_EOF) {
1316         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1317         goto failure;
1318     }
1319
1320     /* Part 2: signature */
1321     fprintf (fp, "\r\n"
1322                  "--%s\r\n",
1323                  boundary);
1324     fputs ("Content-Type: application/pgp-signature\r\n"
1325            "\r\n", fp);
1326
1327     err = gpgme_data_rewind (sigdata);
1328     if (err) {
1329         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1330                    gpgme_strerror (err));
1331         goto failure;
1332     }
1333
1334     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1335         fwrite (buf, nread, 1, fp);
1336     }
1337     if (err != GPGME_EOF) {
1338         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1339         goto failure;
1340     }
1341
1342     /* Final boundary */
1343     fprintf (fp, "\r\n"
1344                  "--%s--\r\n",
1345                  boundary);
1346     fflush (fp);
1347     if (ferror (fp)) {
1348         FILE_OP_ERROR (file, "fwrite");
1349         goto failure;
1350     }
1351     fclose (fp);
1352     gpgme_data_release (header);
1353     gpgme_data_release (plain);
1354     gpgme_data_release (sigdata);
1355     g_free (boundary);
1356     g_free (micalg);
1357     return 0;
1358
1359 failure:
1360     if (fp) 
1361         fclose (fp);
1362     gpgme_data_release (header);
1363     gpgme_data_release (plain);
1364     gpgme_data_release (sigdata);
1365     g_free (boundary);
1366     g_free (micalg);
1367     return -1; /* error */
1368 }
1369
1370
1371 /*
1372  * Sign the file with clear text and replace its content with the signed one.
1373  */
1374 gint
1375 rfc2015_clearsign (const gchar *file, GSList *key_list)
1376 {
1377     FILE *fp;
1378     gchar buf[BUFFSIZE];
1379     GpgmeError err;
1380     GpgmeData text = NULL;
1381     GpgmeData sigdata = NULL;
1382     size_t nread;
1383     gchar *siginfo;
1384
1385     if ((fp = fopen(file, "rb")) == NULL) {
1386         FILE_OP_ERROR(file, "fopen");
1387         goto failure;
1388     }
1389
1390     err = gpgme_data_new(&text);
1391     if (err) {
1392         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1393         goto failure;
1394     }
1395
1396     while (!err && fgets(buf, sizeof(buf), fp)) {
1397         err = gpgme_data_write(text, buf, strlen(buf));
1398     }
1399     if (ferror(fp)) {
1400         FILE_OP_ERROR(file, "fgets");
1401         goto failure;
1402     }
1403     if (err) {
1404         debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1405         goto failure;
1406     }
1407
1408     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1409     if (siginfo) {
1410         g_free(siginfo);
1411     }
1412     if (!sigdata)
1413         goto failure;
1414
1415     if (fclose(fp) == EOF) {
1416         FILE_OP_ERROR(file, "fclose");
1417         fp = NULL;
1418         goto failure;
1419     }
1420     if ((fp = fopen(file, "wb")) == NULL) {
1421         FILE_OP_ERROR(file, "fopen");
1422         goto failure;
1423     }
1424
1425     err = gpgme_data_rewind(sigdata);
1426     if (err) {
1427         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1428                     gpgme_strerror(err));
1429         goto failure;
1430     }
1431
1432     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1433         fwrite(buf, nread, 1, fp);
1434     }
1435     if (err != GPGME_EOF) {
1436         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1437         goto failure;
1438     }
1439
1440     if (fclose(fp) == EOF) {
1441         FILE_OP_ERROR(file, "fclose");
1442         fp = NULL;
1443         goto failure;
1444     }
1445     gpgme_data_release(text);
1446     gpgme_data_release(sigdata);
1447     return 0;
1448
1449 failure:
1450     if (fp)
1451         fclose(fp);
1452     gpgme_data_release(text);
1453     gpgme_data_release(sigdata);
1454     return -1;
1455 }
1456
1457 #endif /* USE_GPGME */