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