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