0.9.6claws11
[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 #if 0 /* FIXME */
332     GpgmeCtx ctx = NULL;
333     GpgmeError err;
334     GpgmeData sig = NULL, text = NULL;
335     GpgmeSigStat status = GPGME_SIG_STAT_NONE;
336     GpgmegtkSigStatus statuswindow = NULL;
337     const char *result = NULL;
338     gchar *tmp_file;
339     gint n_exclude_chars = 0;
340
341     if (prefs_common.gpg_signature_popup)
342         statuswindow = gpgmegtk_sig_status_create ();
343
344     err = gpgme_new (&ctx);
345     if (err) {
346         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
347         goto leave;
348     }
349
350     /* don't include the last empty line.
351        It does not belong to the signed text */
352     if (mimeinfo->children->length > 0) {
353         if (fseek(fp, mimeinfo->children->offset + mimeinfo->children->length - 1,
354                   SEEK_SET) < 0) {
355             perror("fseek");
356             goto leave;
357         }
358         if (fgetc(fp) == '\n') {
359             n_exclude_chars++;
360             if (mimeinfo->children->length > 1) {
361                 if (fseek(fp, mimeinfo->children->offset + mimeinfo->children->length - 2,
362                           SEEK_SET) < 0) {
363                     perror("fseek");
364                     goto leave;
365                 }
366                 if (fgetc(fp) == '\r')
367                     n_exclude_chars++;
368             }
369         }
370     }
371
372     /* canonicalize the file part. */
373     tmp_file = get_tmp_file();
374     if (copy_file_part(fp, mimeinfo->children->offset,
375                        mimeinfo->children->length - n_exclude_chars,
376                        tmp_file) < 0) {
377         g_free(tmp_file);
378         goto leave;
379     }
380     if (canonicalize_file_replace(tmp_file) < 0) {
381         unlink(tmp_file);
382         g_free(tmp_file);
383         goto leave;
384     }
385
386     err = gpgme_data_new_from_file(&text, tmp_file, 1);
387
388     unlink(tmp_file);
389     g_free(tmp_file);
390
391     if (!err)
392         err = gpgme_data_new_from_filepart (&sig, NULL, fp,
393                                             partinfo->offset, partinfo->length);
394     if (err) {
395         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
396                    gpgme_strerror (err));
397         goto leave;
398     }
399
400     err = gpgme_op_verify (ctx, sig, text, &status);
401     if (err)  {
402         debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
403         goto leave;
404     }
405
406     /* FIXME: check what the heck this sig_status_full stuff is.
407      * it should better go into sigstatus.c */
408     g_free (partinfo->sigstatus_full);
409     partinfo->sigstatus_full = sig_status_full (ctx);
410
411 leave:
412     result = gpgmegtk_sig_status_to_string(status);
413     debug_print("verification status: %s\n", result);
414     if (prefs_common.gpg_signature_popup)
415         gpgmegtk_sig_status_update (statuswindow, ctx);
416
417     g_free (partinfo->sigstatus);
418     partinfo->sigstatus = g_strdup (result);
419     partinfo->sig_ok = (status == GPGME_SIG_STAT_GOOD);
420     partinfo->sig_unknown = (status == GPGME_SIG_STAT_NOKEY);
421     partinfo->sig_expired = (status == GPGME_SIG_STAT_GOOD_EXP);
422     partinfo->key_expired = (status == GPGME_SIG_STAT_GOOD_EXPKEY);
423
424     gpgme_data_release (sig);
425     gpgme_data_release (text);
426     gpgme_release (ctx);
427     if (prefs_common.gpg_signature_popup)
428         gpgmegtk_sig_status_destroy (statuswindow);
429 #endif
430 }
431
432 /*
433  * Copy a gpgme data object to a temporary file and
434  * return this filename 
435  */
436 #if 0
437 static char *
438 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
439 {
440     static int id;
441     char *tmp;
442     FILE *fp;
443     char buf[100];
444     size_t nread;
445     GpgmeError err;
446     
447     tmp = g_strdup_printf("%s%cgpgtmp.%08x",
448                           get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
449
450     if ((fp = fopen(tmp, "wb")) == NULL) {
451         FILE_OP_ERROR(tmp, "fopen");
452         g_free(tmp);
453         return NULL;
454     }
455
456     err = gpgme_data_rewind ( data );
457     if (err)
458         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
459
460     while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
461         fwrite ( buf, nread, 1, fp );
462     }
463
464     if (err != GPGME_EOF)
465         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
466
467     fclose (fp);
468     *length = nread;
469
470     return tmp;
471 }
472 #endif
473
474 static GpgmeData
475 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
476 {
477     GpgmeCtx ctx = NULL;
478     GpgmeError err;
479     GpgmeData cipher = NULL, plain = NULL;
480     struct passphrase_cb_info_s info;
481
482     memset (&info, 0, sizeof info);
483
484     err = gpgme_new (&ctx);
485     if (err) {
486         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
487         goto leave;
488     }
489
490     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
491                                         partinfo->offset, partinfo->length);
492     if (err) {
493         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
494                      gpgme_strerror (err));
495         goto leave;
496     }
497
498     err = gpgme_data_new (&plain);
499     if (err) {
500         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
501         goto leave;
502     }
503
504     if (!getenv("GPG_AGENT_INFO")) {
505         info.c = ctx;
506         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
507     } 
508
509     err = gpgme_op_decrypt (ctx, cipher, plain);
510
511 leave:
512     gpgme_data_release (cipher);
513     if (err) {
514         gpgmegtk_free_passphrase();
515         debug_print ("decryption failed: %s\n", gpgme_strerror (err));
516         gpgme_data_release (plain);
517         plain = NULL;
518     }
519     else
520         debug_print ("** decryption succeeded\n");
521
522     gpgme_release (ctx);
523     return plain;
524 }
525
526 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
527 {
528     MimeInfo *partinfo;
529     int n = 0;
530
531     if (!mimeinfo)
532         return NULL;
533     if (!((mimeinfo->type == MIMETYPE_MULTIPART) && !g_strcasecmp(mimeinfo->subtype, "signed")))
534         return NULL;
535
536     debug_print ("** multipart/signed encountered\n");
537
538     /* check that we have at least 2 parts of the correct type */
539     for (partinfo = mimeinfo->children;
540          partinfo != NULL; partinfo = partinfo->next) {
541         if (++n > 1 && (partinfo->type == MIMETYPE_APPLICATION && 
542                 !g_strcasecmp (partinfo->subtype, "pgp-signature")))
543             break;
544     }
545
546     return partinfo;
547 }
548
549 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
550 {
551     return rfc2015_find_signature (mimeinfo) != NULL;
552 }
553
554 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
555 {
556     MimeInfo *partinfo;
557
558     partinfo = rfc2015_find_signature (mimeinfo);
559     if (!partinfo)
560         return;
561
562 #if 0
563     g_message ("** yep, it is a pgp signature");
564     dump_mimeinfo ("gpg-signature", partinfo );
565     dump_part (partinfo, fp );
566     dump_mimeinfo ("signed text", mimeinfo->children );
567     dump_part (mimeinfo->children, fp);
568 #endif
569
570     check_signature (mimeinfo, partinfo, fp);
571 }
572
573 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
574 {
575     if (!mimeinfo || mimeinfo->type != MIMETYPE_MULTIPART)
576         return 0;
577     if (g_strcasecmp (mimeinfo->subtype, "encrypted"))
578         return 0;
579     /* fixme: we should check the protocol parameter */
580     return 1;
581 }
582
583 gboolean rfc2015_msg_is_encrypted (const gchar *file)
584 {
585         FILE *fp;
586         MimeInfo *mimeinfo;
587         int ret = 0;
588
589 #if 0   /* FIXME */
590         if ((fp = fopen(file, "rb")) == NULL)
591                 return FALSE;
592
593         mimeinfo = procmime_scan_mime_header(fp);
594         if(!mimeinfo) {
595                 fclose(fp);
596                 return FALSE;
597         }
598
599         ret = rfc2015_is_encrypted(mimeinfo);
600         procmime_mimeinfo_free_all(mimeinfo);
601 #endif
602         return ret != 0 ? TRUE : FALSE;
603 }
604
605 static int
606 name_cmp(const char *a, const char *b)
607 {
608     for( ; *a && *b; a++, b++) {
609         if(*a != *b
610            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
611             return 1;
612     }
613
614     return *a != *b;
615 }
616
617 static int
618 headerp(char *p, char **names)
619 {
620     int i, c;
621     char *p2;
622
623     p2 = strchr(p, ':');
624     if(!p2 || p == p2) {
625         return 0;
626     }
627     if(p2[-1] == ' ' || p2[-1] == '\t') {
628         return 0;
629     }
630
631     if(!names[0])
632         return 1;  
633
634     c = *p2;
635     *p2 = 0;
636     for(i = 0 ; names[i] != NULL; i++) {
637         if(!name_cmp (names[i], p))
638             break;
639     }
640     *p2 = c;
641
642     return names[i] != NULL;
643 }
644
645
646 #define DECRYPTION_ABORT() \
647 { \
648     procmime_mimeinfo_free_all(tmpinfo); \
649     msginfo->decryption_failed = 1; \
650     return; \
651 }
652
653 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
654 {
655     static int id;
656     MimeInfo *tmpinfo, *partinfo;
657     int ver_ok = 0;
658     char *fname;
659     GpgmeData plain;
660     FILE *dstfp;
661     size_t nread;
662     char buf[BUFFSIZE];
663     int in_cline;
664     GpgmeError err;
665
666     g_return_if_fail (msginfo != NULL);
667     g_return_if_fail (mimeinfo != NULL);
668     g_return_if_fail (fp != NULL);
669     g_return_if_fail (mimeinfo->type == MIMETYPE_MULTIPART);
670
671     debug_print ("** decrypting multipart/encrypted message\n");
672
673 #if 0 /* FIXME */
674
675     /* skip headers */
676     if (fseek(fp, mimeinfo->offset, SEEK_SET) < 0)
677         perror("fseek");
678     tmpinfo = procmime_scan_mime_header(fp);
679     if (!tmpinfo || tmpinfo->type != MIMETYPE_MULTIPART) {
680         DECRYPTION_ABORT();
681     }
682
683     procmime_scan_message(tmpinfo);
684
685     /* check that we have the 2 parts */
686     partinfo = tmpinfo->children;
687     if (!partinfo || !partinfo->next) {
688         DECRYPTION_ABORT();
689     }
690     if ((partinfo->type == MIMETYPE_APPLICATION) && !g_strcasecmp(partinfo->subtype, "pgp-encrypted")) {
691         /* Fixme: check that the version is 1 */
692         ver_ok = 1;
693     }
694     partinfo = partinfo->next;
695     if (ver_ok && (partinfo->type == MIMETYPE_APPLICATION) &&
696         !g_strcasecmp (partinfo->subtype, "octet-stream")) {
697         if (partinfo->next)
698             g_warning ("oops: pgp_encrypted with more than 2 parts");
699     }
700     else {
701         DECRYPTION_ABORT();
702     }
703
704     debug_print ("** yep, it is pgp encrypted\n");
705
706     plain = pgp_decrypt (partinfo, fp);
707     if (!plain) {
708         DECRYPTION_ABORT();
709     }
710
711     fname = g_strdup_printf("%s%cplaintext.%08x",
712                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
713
714     if ((dstfp = fopen(fname, "wb")) == NULL) {
715         FILE_OP_ERROR(fname, "fopen");
716         g_free(fname);
717         DECRYPTION_ABORT();
718     }
719
720     /* write the orginal header to the new file */
721     if (fseek(fp, tmpinfo->offset, SEEK_SET) < 0)
722         perror("fseek");
723
724     in_cline = 0;
725     while (fgets(buf, sizeof(buf), fp)) {
726         if (headerp (buf, content_names)) {
727             in_cline = 1;
728             continue;
729         }
730         if (in_cline) {
731             if (buf[0] == ' ' || buf[0] == '\t')
732                 continue;
733             in_cline = 0;
734         }
735         if (buf[0] == '\r' || buf[0] == '\n')
736             break;
737         fputs (buf, dstfp);
738     }
739
740     err = gpgme_data_rewind (plain);
741     if (err)
742         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
743
744     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
745         fwrite (buf, nread, 1, dstfp);
746     }
747
748     if (err != GPGME_EOF) {
749         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
750     }
751
752     fclose (dstfp);
753     procmime_mimeinfo_free_all(tmpinfo);
754
755     msginfo->plaintext_file = fname;
756     msginfo->decryption_failed = 0;
757
758 #endif
759 }
760
761 #undef DECRYPTION_ABORT
762
763
764 /*
765  * plain contains an entire mime object.
766  * Encrypt it and return an GpgmeData object with the encrypted version of
767  * the file or NULL in case of error.
768  */
769 static GpgmeData
770 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
771 {
772     GpgmeCtx ctx = NULL;
773     GpgmeError err;
774     GpgmeData cipher = NULL;
775
776     err = gpgme_new (&ctx);
777     if (!err)
778         err = gpgme_data_new (&cipher);
779     if (!err) {
780         gpgme_set_armor (ctx, 1);
781         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
782     }
783
784     if (err) {
785         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
786         gpgme_data_release (cipher);
787         cipher = NULL;
788     }
789     else {
790         debug_print ("** encryption succeeded\n");
791     }
792
793     gpgme_release (ctx);
794     return cipher;
795 }
796
797 /*
798  * Create and return a list of keys matching a key id
799  */
800
801 GSList *rfc2015_create_signers_list (const char *keyid)
802 {
803         GSList *key_list = NULL;
804         GpgmeCtx list_ctx = NULL;
805         GSList *p;
806         GpgmeError err;
807         GpgmeKey key;
808
809         err = gpgme_new (&list_ctx);
810         if (err)
811                 goto leave;
812         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
813         if (err)
814                 goto leave;
815         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
816                 key_list = g_slist_append (key_list, key);
817         }
818         if (err != GPGME_EOF)
819                 goto leave;
820         err = 0;
821         if (key_list == NULL) {
822                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
823         }
824
825 leave:
826         if (err) {
827                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
828                 for (p = key_list; p != NULL; p = p->next)
829                         gpgme_key_unref ((GpgmeKey) p->data);
830                 g_slist_free (key_list);
831         }
832         if (list_ctx)
833                 gpgme_release (list_ctx);
834         return err ? NULL : key_list;
835 }
836
837 /*
838  * Encrypt the file by extracting all recipients and finding the
839  * encryption keys for all of them.  The file content is then replaced
840  * by the encrypted one.  */
841 int
842 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
843 {
844     FILE *fp = NULL;
845     char buf[BUFFSIZE];
846     int i, clineidx, saved_last;
847     char *clines[3] = {NULL};
848     GpgmeError err;
849     GpgmeData header = NULL;
850     GpgmeData plain = NULL;
851     GpgmeData cipher = NULL;
852     GpgmeRecipients rset = NULL;
853     size_t nread;
854     int mime_version_seen = 0;
855     char *boundary;
856
857     boundary = generate_mime_boundary ("Encrypt");
858
859     /* Create the list of recipients */
860     rset = gpgmegtk_recipient_selection (recp_list);
861     if (!rset) {
862         debug_print ("error creating recipient list\n" );
863         goto failure;
864     }
865
866     /* Open the source file */
867     if ((fp = fopen(file, "rb")) == NULL) {
868         FILE_OP_ERROR(file, "fopen");
869         goto failure;
870     }
871
872     err = gpgme_data_new (&header);
873     if (!err)
874         err = gpgme_data_new (&plain);
875     if (err) {
876         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
877         goto failure;
878     }
879
880     /* get the content header lines from the source */
881     clineidx = 0;
882     saved_last = 0;
883     while (!err && fgets(buf, sizeof(buf), fp)) {
884         /* fixme: check for overlong lines */
885         if (headerp (buf, content_names)) {
886             if (clineidx >= DIM (clines)) {
887                 debug_print ("rfc2015_encrypt: too many content lines\n");
888                 goto failure;
889             }
890             clines[clineidx++] = g_strdup (buf);
891             saved_last = 1;
892             continue;
893         }
894         if (saved_last) {
895             if (*buf == ' ' || *buf == '\t') {
896                 char *last = clines[clineidx - 1];
897                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
898                 g_free (last);
899                 continue;
900             }
901             saved_last = 0;
902         }
903
904         if (headerp (buf, mime_version_name)) 
905             mime_version_seen = 1;
906
907         if (buf[0] == '\r' || buf[0] == '\n')
908             break;
909         err = gpgme_data_write (header, buf, strlen (buf));
910     }
911     if (ferror (fp)) {
912         FILE_OP_ERROR (file, "fgets");
913         goto failure;
914     }
915
916     /* write them to the temp data and add the rest of the message */
917     for (i = 0; !err && i < clineidx; i++) {
918         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
919         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
920     }
921     if (!err)
922         err = gpgme_data_write (plain, "\r\n", 2);
923     while (!err && fgets(buf, sizeof(buf), fp)) {
924         err = gpgme_data_write (plain, buf, strlen (buf));
925     }
926     if (ferror (fp)) {
927         FILE_OP_ERROR (file, "fgets");
928         goto failure;
929     }
930     if (err) {
931         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
932         goto failure;
933     }
934
935     cipher = pgp_encrypt (plain, rset);
936     gpgme_data_release (plain); plain = NULL;
937     gpgme_recipients_release (rset); rset = NULL;
938     if (!cipher)
939         goto failure;
940
941     /* we have the encrypted message available in cipher and now we
942      * are going to rewrite the source file. To be sure that file has
943      * been truncated we use an approach which should work everywhere:
944      * close the file and then reopen it for writing. It is important
945      * that this works, otherwise it may happen that parts of the
946      * plaintext are still in the file (The encrypted stuff is, due to
947      * compression, usually shorter than the plaintext). 
948      * 
949      * Yes, there is a race condition here, but everyone, who is so
950      * stupid to store the temp file with the plaintext in a public
951      * directory has to live with this anyway. */
952     if (fclose (fp)) {
953         FILE_OP_ERROR(file, "fclose");
954         goto failure;
955     }
956     if ((fp = fopen(file, "wb")) == NULL) {
957         FILE_OP_ERROR(file, "fopen");
958         goto failure;
959     }
960
961     /* Write the header, append new content lines, part 1 and part 2 header */
962     err = gpgme_data_rewind (header);
963     if (err) {
964         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
965         goto failure;
966     }
967     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
968         fwrite (buf, nread, 1, fp);
969     }
970     if (err != GPGME_EOF) {
971         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
972         goto failure;
973     }
974     if (ferror (fp)) {
975         FILE_OP_ERROR (file, "fwrite");
976         goto failure;
977     }
978     gpgme_data_release (header); header = NULL;
979     
980     if (!mime_version_seen) 
981         fputs ("MIME-Version: 1\r\n", fp);
982
983     if (ascii_armored) {
984         fprintf(fp, 
985             "Content-Type: text/plain; charset=US-ASCII\r\n"
986             "Content-Transfer-Encoding: 7bit\r\n"  
987             "\r\n");
988     } else {
989         fprintf (fp,
990                 "Content-Type: multipart/encrypted;"
991                 " protocol=\"application/pgp-encrypted\";\r\n"
992                 " boundary=\"%s\"\r\n"
993                 "\r\n"
994                 "--%s\r\n"
995                 "Content-Type: application/pgp-encrypted\r\n"
996                 "\r\n"
997                 "Version: 1\r\n"
998                 "\r\n"
999                 "--%s\r\n"
1000                 "Content-Type: application/octet-stream\r\n"
1001                 "\r\n",
1002                 boundary, boundary, boundary);
1003     }
1004
1005     /* append the encrypted stuff */
1006     err = gpgme_data_rewind (cipher);
1007     if (err) {
1008         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
1009                    gpgme_strerror (err));
1010         goto failure;
1011     }
1012
1013     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
1014         fwrite (buf, nread, 1, fp);
1015     }
1016     if (err != GPGME_EOF) {
1017         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
1018         goto failure;
1019     }
1020
1021     /* and the final boundary */
1022     if (!ascii_armored) {
1023         fprintf (fp,
1024                  "\r\n"
1025                  "--%s--\r\n",
1026                  boundary);
1027     }
1028     fflush (fp);
1029     if (ferror (fp)) {
1030         FILE_OP_ERROR (file, "fwrite");
1031         goto failure;
1032     }
1033     fclose (fp);
1034     gpgme_data_release (cipher);
1035     return 0;
1036
1037 failure:
1038     if (fp) 
1039         fclose (fp);
1040     gpgme_data_release (header);
1041     gpgme_data_release (plain);
1042     gpgme_data_release (cipher);
1043     gpgme_recipients_release (rset);
1044     g_free (boundary);
1045     return -1; /* error */
1046 }
1047
1048 /* 
1049  * plain contains an entire mime object.  Sign it and return an
1050  * GpgmeData object with the signature of it or NULL in case of error.
1051  * r_siginfo returns an XML object with information about the signature.
1052  */
1053 static GpgmeData
1054 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
1055           char **r_siginfo)
1056 {
1057     GSList *p;
1058     GpgmeCtx ctx = NULL;
1059     GpgmeError err;
1060     GpgmeData sig = NULL;
1061     struct passphrase_cb_info_s info;
1062
1063     *r_siginfo = NULL;
1064     memset (&info, 0, sizeof info);
1065
1066     err = gpgme_new (&ctx);
1067     if (err)
1068         goto leave;
1069     err = gpgme_data_new (&sig);
1070     if (err)
1071         goto leave;
1072
1073     if (!getenv("GPG_AGENT_INFO")) {
1074         info.c = ctx;
1075         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1076     }
1077     gpgme_set_textmode (ctx, 1);
1078     gpgme_set_armor (ctx, 1);
1079     gpgme_signers_clear (ctx);
1080     for (p = key_list; p != NULL; p = p->next) {
1081         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1082         if (err)
1083             goto leave;
1084     }
1085     for (p = key_list; p != NULL; p = p->next)
1086         gpgme_key_unref ((GpgmeKey) p->data);
1087     g_slist_free (key_list);
1088
1089     if (err)
1090         goto leave;
1091     err = gpgme_op_sign
1092         (ctx, plain, sig,
1093          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1094     if (!err)
1095         *r_siginfo = gpgme_get_op_info (ctx, 0);
1096
1097 leave:
1098     if (err) {
1099         gpgmegtk_free_passphrase();
1100         debug_print ("signing failed: %s\n", gpgme_strerror (err));
1101         gpgme_data_release (sig);
1102         sig = NULL;
1103     }
1104     else {
1105         debug_print ("signing succeeded\n");
1106     }
1107
1108     gpgme_release (ctx);
1109     return sig;
1110 }
1111
1112 /*
1113  * Find TAG in XML and return a pointer into xml set just behind the
1114  * closing angle.  Return NULL if not found. 
1115  */
1116 static const char *
1117 find_xml_tag (const char *xml, const char *tag)
1118 {
1119     int taglen = strlen (tag);
1120     const char *s = xml;
1121  
1122     while ( (s = strchr (s, '<')) ) {
1123         s++;
1124         if (!strncmp (s, tag, taglen)) {
1125             const char *s2 = s + taglen;
1126             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1127                 /* found */
1128                 while (*s2 && *s2 != '>') /* skip attributes */
1129                     s2++;
1130                 /* fixme: do need to handle angles inside attribute vallues? */
1131                 return *s2? (s2+1):NULL;
1132             }
1133         }
1134         while (*s && *s != '>') /* skip to end of tag */
1135             s++;
1136     }
1137     return NULL;
1138 }
1139
1140
1141 /*
1142  * Extract the micalg from an GnupgOperationInfo XML container.
1143  */
1144 static char *
1145 extract_micalg (char *xml)
1146 {
1147     const char *s;
1148
1149     s = find_xml_tag (xml, "GnupgOperationInfo");
1150     if (s) {
1151         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1152         s = find_xml_tag (s, "signature");
1153         if (s && s_end && s < s_end) {
1154             const char *s_end2 = find_xml_tag (s, "/signature");
1155             if (s_end2 && s_end2 < s_end) {
1156                 s = find_xml_tag (s, "micalg");
1157                 if (s && s < s_end2) {
1158                     s_end = strchr (s, '<');
1159                     if (s_end) {
1160                         char *p = g_malloc (s_end - s + 1);
1161                         memcpy (p, s, s_end - s);
1162                         p[s_end-s] = 0;
1163                         return p;
1164                     }
1165                 }
1166             }
1167         }
1168     }
1169     return NULL;
1170 }
1171
1172
1173 /*
1174  * Sign the file and replace its content with the signed one.
1175  */
1176 int
1177 rfc2015_sign (const char *file, GSList *key_list)
1178 {
1179     FILE *fp = NULL;
1180     char buf[BUFFSIZE];
1181     int i, clineidx, saved_last;
1182     char *clines[3] = {NULL};
1183     GpgmeError err;
1184     GpgmeData header = NULL;
1185     GpgmeData plain = NULL;
1186     GpgmeData sigdata = NULL;
1187     size_t nread;
1188     int mime_version_seen = 0;
1189     char *boundary;
1190     char *micalg = NULL;
1191     char *siginfo;
1192
1193     boundary = generate_mime_boundary ("Signature");
1194
1195     /* Open the source file */
1196     if ((fp = fopen(file, "rb")) == NULL) {
1197         FILE_OP_ERROR(file, "fopen");
1198         goto failure;
1199     }
1200
1201     err = gpgme_data_new (&header);
1202     if (!err)
1203         err = gpgme_data_new (&plain);
1204     if (err) {
1205         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1206         goto failure;
1207     }
1208
1209     /* get the content header lines from the source */
1210     clineidx = 0;
1211     saved_last = 0;
1212     while (!err && fgets(buf, sizeof(buf), fp)) {
1213         /* fixme: check for overlong lines */
1214         if (headerp (buf, content_names)) {
1215             if (clineidx >= DIM (clines)) {
1216                 debug_print ("rfc2015_sign: too many content lines\n");
1217                 goto failure;
1218             }
1219             clines[clineidx++] = g_strdup (buf);
1220             saved_last = 1;
1221             continue;
1222         }
1223         if (saved_last) {
1224             if (*buf == ' ' || *buf == '\t') {
1225                 char *last = clines[clineidx - 1];
1226                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1227                 g_free (last);
1228                 continue;
1229             }
1230             saved_last = 0;
1231         }
1232
1233         if (headerp (buf, mime_version_name)) 
1234             mime_version_seen = 1;
1235
1236         if (buf[0] == '\r' || buf[0] == '\n')
1237             break;
1238         err = gpgme_data_write (header, buf, strlen (buf));
1239     }
1240     if (ferror (fp)) {
1241         FILE_OP_ERROR (file, "fgets");
1242         goto failure;
1243     }
1244
1245     /* write them to the temp data and add the rest of the message */
1246     for (i = 0; !err && i < clineidx; i++) {
1247         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1248     }
1249     if (!err)
1250         err = gpgme_data_write (plain, "\r\n", 2 );
1251     while (!err && fgets(buf, sizeof(buf), fp)) {
1252         err = gpgme_data_write (plain, buf, strlen (buf));
1253     }
1254     if (ferror (fp)) {
1255         FILE_OP_ERROR (file, "fgets");
1256         goto failure;
1257     }
1258     if (err) {
1259         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1260         goto failure;
1261     }
1262
1263     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
1264     if (siginfo) {
1265         micalg = extract_micalg (siginfo);
1266         free (siginfo);
1267     }
1268     if (!sigdata) 
1269         goto failure;
1270
1271     /* we have the signed message available in sigdata and now we are
1272      * going to rewrite the original file. To be sure that file has
1273      * been truncated we use an approach which should work everywhere:
1274      * close the file and then reopen it for writing. */
1275     if (fclose (fp)) {
1276         FILE_OP_ERROR(file, "fclose");
1277         goto failure;
1278     }
1279     if ((fp = fopen(file, "wb")) == NULL) {
1280         FILE_OP_ERROR(file, "fopen");
1281         goto failure;
1282     }
1283
1284     /* Write the rfc822 header and add new content lines */
1285     err = gpgme_data_rewind (header);
1286     if (err)
1287         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1288     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1289         fwrite (buf, nread, 1, fp);
1290     }
1291     if (err != GPGME_EOF) {
1292         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1293         goto failure;
1294     }
1295     if (ferror (fp)) {
1296         FILE_OP_ERROR (file, "fwrite");
1297         goto failure;
1298     }
1299     gpgme_data_release (header);
1300     header = NULL;
1301
1302     if (!mime_version_seen) 
1303         fputs ("MIME-Version: 1.0\r\n", fp);
1304     fprintf (fp, "Content-Type: multipart/signed; "
1305              "protocol=\"application/pgp-signature\";\r\n");
1306     if (micalg)
1307         fprintf (fp, " micalg=\"%s\";\r\n", micalg);
1308     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1309
1310     /* Part 1: signed material */
1311     fprintf (fp, "\r\n"
1312                  "--%s\r\n",
1313                  boundary);
1314     err = gpgme_data_rewind (plain);
1315     if (err) {
1316         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1317                    gpgme_strerror (err));
1318         goto failure;
1319     }
1320     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1321         fwrite (buf, nread, 1, fp);   
1322     }
1323     if (err != GPGME_EOF) {
1324         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1325         goto failure;
1326     }
1327
1328     /* Part 2: signature */
1329     fprintf (fp, "\r\n"
1330                  "--%s\r\n",
1331                  boundary);
1332     fputs ("Content-Type: application/pgp-signature\r\n"
1333            "\r\n", fp);
1334
1335     err = gpgme_data_rewind (sigdata);
1336     if (err) {
1337         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1338                    gpgme_strerror (err));
1339         goto failure;
1340     }
1341
1342     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1343         fwrite (buf, nread, 1, fp);
1344     }
1345     if (err != GPGME_EOF) {
1346         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1347         goto failure;
1348     }
1349
1350     /* Final boundary */
1351     fprintf (fp, "\r\n"
1352                  "--%s--\r\n",
1353                  boundary);
1354     fflush (fp);
1355     if (ferror (fp)) {
1356         FILE_OP_ERROR (file, "fwrite");
1357         goto failure;
1358     }
1359     fclose (fp);
1360     gpgme_data_release (header);
1361     gpgme_data_release (plain);
1362     gpgme_data_release (sigdata);
1363     g_free (boundary);
1364     g_free (micalg);
1365     return 0;
1366
1367 failure:
1368     if (fp) 
1369         fclose (fp);
1370     gpgme_data_release (header);
1371     gpgme_data_release (plain);
1372     gpgme_data_release (sigdata);
1373     g_free (boundary);
1374     g_free (micalg);
1375     return -1; /* error */
1376 }
1377
1378
1379 /*
1380  * Sign the file with clear text and replace its content with the signed one.
1381  */
1382 gint
1383 rfc2015_clearsign (const gchar *file, GSList *key_list)
1384 {
1385     FILE *fp;
1386     gchar buf[BUFFSIZE];
1387     GpgmeError err;
1388     GpgmeData text = NULL;
1389     GpgmeData sigdata = NULL;
1390     size_t nread;
1391     gchar *siginfo;
1392
1393     if ((fp = fopen(file, "rb")) == NULL) {
1394         FILE_OP_ERROR(file, "fopen");
1395         goto failure;
1396     }
1397
1398     err = gpgme_data_new(&text);
1399     if (err) {
1400         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1401         goto failure;
1402     }
1403
1404     while (!err && fgets(buf, sizeof(buf), fp)) {
1405         err = gpgme_data_write(text, buf, strlen(buf));
1406     }
1407     if (ferror(fp)) {
1408         FILE_OP_ERROR(file, "fgets");
1409         goto failure;
1410     }
1411     if (err) {
1412         debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1413         goto failure;
1414     }
1415
1416     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1417     if (siginfo) {
1418         g_free(siginfo);
1419     }
1420     if (!sigdata)
1421         goto failure;
1422
1423     if (fclose(fp) == EOF) {
1424         FILE_OP_ERROR(file, "fclose");
1425         fp = NULL;
1426         goto failure;
1427     }
1428     if ((fp = fopen(file, "wb")) == NULL) {
1429         FILE_OP_ERROR(file, "fopen");
1430         goto failure;
1431     }
1432
1433     err = gpgme_data_rewind(sigdata);
1434     if (err) {
1435         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1436                     gpgme_strerror(err));
1437         goto failure;
1438     }
1439
1440     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1441         fwrite(buf, nread, 1, fp);
1442     }
1443     if (err != GPGME_EOF) {
1444         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1445         goto failure;
1446     }
1447
1448     if (fclose(fp) == EOF) {
1449         FILE_OP_ERROR(file, "fclose");
1450         fp = NULL;
1451         goto failure;
1452     }
1453     gpgme_data_release(text);
1454     gpgme_data_release(sigdata);
1455     return 0;
1456
1457 failure:
1458     if (fp)
1459         fclose(fp);
1460     gpgme_data_release(text);
1461     gpgme_data_release(sigdata);
1462     return -1;
1463 }
1464
1465 #endif /* USE_GPGME */