bring in Hiro's last wm class fixes of 0.7.0 to claws
[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
65 struct passphrase_cb_info_s {
66     GpgmeCtx c;
67     int did_it;
68 };
69
70 static char *create_boundary (void);
71
72 static void dump_mimeinfo (const char *text, MimeInfo *x)
73 {
74     debug_print ("MimeInfo[%s] %p  level=%d\n",
75                text, x, x? x->level:0 );
76     if (!x)
77         return;
78
79     debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
80                x->encoding, x->encoding_type, x->mime_type );
81     debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
82                x->content_type, x->charset, x->name, x->boundary );
83     debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
84                x->content_disposition, x->filename, x->fpos, x->size,
85                x->level );
86     dump_mimeinfo (".main", x->main );
87     dump_mimeinfo (".sub", x->sub );
88     dump_mimeinfo (".next", x->next );
89     debug_print ("MimeInfo[.parent] %p\n", x ); 
90     dump_mimeinfo (".children", x->children );
91     dump_mimeinfo (".plaintext", x->plaintext );
92 }
93
94 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
95 {
96     unsigned int size = mimeinfo->size;
97     int c;
98
99     if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
100         debug_print ("dump_part: fseek error\n");
101         return;
102     }
103
104     debug_print ("--- begin dump_part ----\n");
105     while (size-- && (c = getc (fp)) != EOF) 
106         putc (c, stderr);
107     if (ferror (fp))
108         debug_print ("dump_part: read error\n");
109     debug_print ("--- end dump_part ----\n");
110 }
111
112 void
113 rfc2015_disable_all (void)
114 {
115     /* FIXME: set a flag, so that we don't bother the user with failed
116      * gpgme messages */
117 }
118
119
120 void
121 rfc2015_secure_remove (const char *fname)
122 {
123     if (!fname)
124         return;
125     /* fixme: overwrite the file first */
126     remove (fname);
127 }
128
129
130 static const gchar *
131 sig_status_to_string (GpgmeSigStat status)
132 {
133     const gchar *result;
134
135     switch (status) {
136       case GPGME_SIG_STAT_NONE:
137         result = _("Oops: Signature not verified");
138         break;
139       case GPGME_SIG_STAT_NOSIG:
140         result = _("No signature found");
141         break;
142       case GPGME_SIG_STAT_GOOD:
143         result = _("Good signature");
144         break;
145       case GPGME_SIG_STAT_BAD:
146         result = _("BAD signature");
147         break;
148       case GPGME_SIG_STAT_NOKEY:
149         result = _("No public key to verify the signature");
150         break;
151       case GPGME_SIG_STAT_ERROR:
152         result = _("Error verifying the signature");
153         break;
154       case GPGME_SIG_STAT_DIFF:
155         result = _("Different results for signatures");
156         break;
157       default:
158         result = _("Error: Unknown status");
159         break;
160     }
161
162     return result;
163 }
164
165 static const gchar *
166 sig_status_with_name (GpgmeSigStat status)
167 {
168     const gchar *result;
169
170     switch (status) {
171       case GPGME_SIG_STAT_NONE:
172         result = _("Oops: Signature not verified");
173         break;
174       case GPGME_SIG_STAT_NOSIG:
175         result = _("No signature found");
176         break;
177       case GPGME_SIG_STAT_GOOD:
178         result = _("Good signature from \"%s\"");
179         break;
180       case GPGME_SIG_STAT_BAD:
181         result = _("BAD signature  from \"%s\"");
182         break;
183       case GPGME_SIG_STAT_NOKEY:
184         result = _("No public key to verify the signature");
185         break;
186       case GPGME_SIG_STAT_ERROR:
187         result = _("Error verifying the signature");
188         break;
189       case GPGME_SIG_STAT_DIFF:
190         result = _("Different results for signatures");
191         break;
192       default:
193         result = _("Error: Unknown status");
194         break;
195     }
196
197     return result;
198 }
199
200 static void
201 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
202                    GpgmeKey key, const gchar *fpr)
203 {
204         gint idx = 0;
205         const char *uid;
206
207         uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
208         if (uid == NULL) {
209                 g_string_sprintfa (str, "%s\n",
210                                    sig_status_to_string (status));
211                 if ((fpr != NULL) && (*fpr != '\0'))
212                         g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
213                 g_string_append (str, _("Cannot find user ID for this key."));
214                 return;
215         }
216         g_string_sprintfa (str, sig_status_with_name (status), uid);
217         g_string_append (str, "\n");
218
219         while (1) {
220                 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
221                                                  NULL, ++idx);
222                 if (uid == NULL)
223                         break;
224                 g_string_sprintfa (str, _("                aka \"%s\"\n"),
225                                    uid);
226         }
227 }
228
229 static gchar *
230 sig_status_full (GpgmeCtx ctx)
231 {
232         GString *str;
233         gint sig_idx = 0;
234         GpgmeError err;
235         GpgmeSigStat status;
236         GpgmeKey key;
237         const char *fpr;
238         time_t created;
239         struct tm *ctime_val;
240         char ctime_str[80];
241         gchar *retval;
242
243         str = g_string_new ("");
244
245         fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
246         while (fpr != NULL) {
247                 if (created != 0) {
248                         ctime_val = localtime (&created);
249                         strftime (ctime_str, sizeof (ctime_str), "%c", 
250                                   ctime_val);
251                         g_string_sprintfa (str,
252                                            _("Signature made %s\n"),
253                                            ctime_str);
254                 }
255                 err = gpgme_get_sig_key (ctx, sig_idx, &key);
256                 if (err != 0) {
257                         g_string_sprintfa (str, "%s\n",
258                                            sig_status_to_string (status));
259                         if ((fpr != NULL) && (*fpr != '\0'))
260                                 g_string_sprintfa (str, 
261                                                    _("Key fingerprint: %s\n"),
262                                                    fpr);
263                 } else {
264                         sig_status_for_key (str, ctx, status, key, fpr);
265                         gpgme_key_unref (key);
266                 }
267                 g_string_append (str, "\n\n");
268
269                 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
270         }
271
272         retval = str->str;
273         g_string_free (str, FALSE);
274         return retval;
275 }
276
277 static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
278 {
279     GpgmeCtx ctx = NULL;
280     GpgmeError err;
281     GpgmeData sig = NULL, text = NULL;
282     GpgmeSigStat status = GPGME_SIG_STAT_NONE;
283     GpgmegtkSigStatus statuswindow = NULL;
284     const char *result = NULL;
285
286     if (prefs_common.gpg_signature_popup)
287         statuswindow = gpgmegtk_sig_status_create ();
288
289     err = gpgme_new (&ctx);
290     if (err) {
291         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
292         goto leave;
293     }
294
295     /* don't include the last character (LF). It does not belong to the
296      * signed text */
297     err = gpgme_data_new_from_filepart (&text, NULL, fp,
298                                         mimeinfo->children->fpos,
299                                         mimeinfo->children->size ?
300                                         (mimeinfo->children->size - 1) : 0 );
301     if (!err)
302         err = gpgme_data_new_from_filepart (&sig, NULL, fp,
303                                             partinfo->fpos, partinfo->size);
304     if (err) {
305         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
306                    gpgme_strerror (err));
307         goto leave;
308     }
309
310     err = gpgme_op_verify (ctx, sig, text, &status);
311     if (err) 
312         debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
313
314     /* FIXME: check what the heck this sig_status_full stuff is.
315      * it should better go into sigstatus.c */
316     g_free (partinfo->sigstatus_full);
317     partinfo->sigstatus_full = sig_status_full (ctx);
318
319 leave:
320     result = gpgmegtk_sig_status_to_string(status);
321     debug_print("verification status: %s\n", result);
322     if (prefs_common.gpg_signature_popup)
323         gpgmegtk_sig_status_update (statuswindow,ctx);
324
325     g_assert (!err); /* FIXME: Hey: this may indeed happen */
326     g_free (partinfo->sigstatus);
327     partinfo->sigstatus = g_strdup (result);
328
329     gpgme_data_release (sig);
330     gpgme_data_release (text);
331     gpgme_release (ctx);
332     if (prefs_common.gpg_signature_popup)
333         gpgmegtk_sig_status_destroy (statuswindow);
334 }
335
336 static const char *
337 passphrase_cb (void *opaque, const char *desc, void *r_hd)
338 {
339     struct passphrase_cb_info_s *info = opaque;
340     GpgmeCtx ctx = info ? info->c : NULL;
341     const char *pass;
342
343     if (!desc) {
344         /* FIXME: cleanup by looking at *r_hd */
345         return NULL;
346     }
347
348     gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
349     debug_print ("requesting passphrase for `%s': ", desc );
350     pass = gpgmegtk_passphrase_mbox (desc);
351     if (!pass) {
352         debug_print ("cancel passphrase entry\n");
353         gpgme_cancel (ctx);
354     }
355     else
356         debug_print ("sending passphrase\n");
357
358     return pass;
359 }
360
361 /*
362  * Copy a gpgme data object to a temporary file and
363  * return this filename 
364  */
365 static char *
366 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
367 {
368     static int id;
369     char *tmp;
370     FILE *fp;
371     char buf[100];
372     size_t nread;
373     GpgmeError err;
374     
375     tmp = g_strdup_printf("%s%cgpgtmp.%08x",
376                           get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
377
378     if ((fp = fopen(tmp, "w")) == NULL) {
379         FILE_OP_ERROR(tmp, "fopen");
380         g_free(tmp);
381         return NULL;
382     }
383
384     err = gpgme_data_rewind ( data );
385     if (err)
386         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
387
388     while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
389         fwrite ( buf, nread, 1, fp );
390     }
391
392     if (err != GPGME_EOF)
393         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
394
395     fclose (fp);
396     *length = nread;
397
398     return tmp;
399 }
400
401 static GpgmeData
402 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
403 {
404     GpgmeCtx ctx = NULL;
405     GpgmeError err;
406     GpgmeData cipher = NULL, plain = NULL;
407     struct passphrase_cb_info_s info;
408
409     memset (&info, 0, sizeof info);
410
411     err = gpgme_new (&ctx);
412     if (err) {
413         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
414         goto leave;
415     }
416
417     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
418                                         partinfo->fpos, partinfo->size);
419     if (err) {
420         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
421                      gpgme_strerror (err));
422         goto leave;
423     }
424
425     err = gpgme_data_new (&plain);
426     if (err) {
427         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
428         goto leave;
429     }
430
431     if (!getenv("GPG_AGENT_INFO")) {
432         info.c = ctx;
433         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
434     } 
435
436     err = gpgme_op_decrypt (ctx, cipher, plain);
437
438 leave:
439     gpgme_data_release (cipher);
440     if (err) {
441         debug_print ("decryption failed: %s\n", gpgme_strerror (err));
442         gpgme_data_release (plain);
443         plain = NULL;
444     }
445     else
446         debug_print ("decryption succeeded\n");
447
448     gpgme_release (ctx);
449     return plain;
450 }
451
452 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
453 {
454     MimeInfo *partinfo;
455     int n = 0;
456
457     if (!mimeinfo)
458         return NULL;
459     if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
460         return NULL;
461
462     debug_print ("multipart/signed encountered\n");
463
464     /* check that we have at least 2 parts of the correct type */
465     for (partinfo = mimeinfo->children;
466          partinfo != NULL; partinfo = partinfo->next) {
467         if (++n > 1  && !g_strcasecmp (partinfo->content_type,
468                                        "application/pgp-signature"))
469             break;
470     }
471
472     return partinfo;
473 }
474
475 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
476 {
477     return rfc2015_find_signature (mimeinfo) != NULL;
478 }
479
480 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
481 {
482     MimeInfo *partinfo;
483
484     partinfo = rfc2015_find_signature (mimeinfo);
485     if (!partinfo)
486         return;
487
488 #if 0
489     g_message ("** yep, it is a pgp signature");
490     dump_mimeinfo ("gpg-signature", partinfo );
491     dump_part (partinfo, fp );
492     dump_mimeinfo ("signed text", mimeinfo->children );
493     dump_part (mimeinfo->children, fp);
494 #endif
495
496     check_signature (mimeinfo, partinfo, fp);
497 }
498
499 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
500 {
501     if (!mimeinfo)
502         return 0;
503     if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
504         return 0;
505     /* fixme: we should schek the protocol parameter */
506     return 1;
507 }
508
509 static int
510 name_cmp(const char *a, const char *b)
511 {
512     for( ; *a && *b; a++, b++) {
513         if(*a != *b
514            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
515             return 1;
516     }
517
518     return *a != *b;
519 }
520
521 static int
522 headerp(char *p, char **names)
523 {
524     int i, c;
525     char *p2;
526
527     p2 = strchr(p, ':');
528     if(!p2 || p == p2) {
529         return 0;
530     }
531     if(p2[-1] == ' ' || p2[-1] == '\t') {
532         return 0;
533     }
534
535     if(!names[0])
536         return 1;  
537
538     c = *p2;
539     *p2 = 0;
540     for(i = 0 ; names[i] != NULL; i++) {
541         if(!name_cmp (names[i], p))
542             break;
543     }
544     *p2 = c;
545
546     return names[i] != NULL;
547 }
548
549
550 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
551 {
552     static int id;
553     MimeInfo *partinfo;
554     int n, found;
555     int ver_okay=0;
556     char *fname;
557     GpgmeData plain;
558     FILE *dstfp;
559     size_t nread;
560     char buf[BUFFSIZE];
561     GpgmeError err;
562
563     g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
564
565     debug_print ("multipart/encrypted encountered\n");
566
567     /* skip headers */
568     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
569         perror("fseek");
570     while (fgets(buf, sizeof(buf), fp) != NULL)
571         if (buf[0] == '\r' || buf[0] == '\n') break;
572     
573     procmime_scan_multipart_message(mimeinfo, fp);
574
575     /* check that we have the 2 parts */
576     n = found = 0;
577     for (partinfo = mimeinfo->children; partinfo; partinfo = partinfo->next) {
578         if (++n == 1 && !g_strcasecmp (partinfo->content_type,
579                                        "application/pgp-encrypted")) {
580             /* Fixme: check that the version is 1 */
581             ver_okay = 1;
582         }
583         else if (n == 2 && !g_strcasecmp (partinfo->content_type,
584                                           "application/octet-stream")) {
585             if (partinfo->next)
586                 debug_print ("oops: pgp_encrypted with more than 2 parts\n");
587             break;
588         }
589     }
590
591     if (!ver_okay || !partinfo) {
592         msginfo->decryption_failed = 1;
593         /* fixme: remove the stuff, that the above procmime_scan_multiparts() 
594          * has appended to mimeino */
595         return;
596     }
597
598     debug_print ("yep, it is pgp encrypted\n");
599
600     plain = pgp_decrypt (partinfo, fp);
601     if (!plain) {
602         msginfo->decryption_failed = 1;
603         return;
604     }
605     
606     fname = g_strdup_printf("%s%cplaintext.%08x",
607                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
608
609     if ((dstfp = fopen(fname, "w")) == NULL) {
610         FILE_OP_ERROR(fname, "fopen");
611         g_free(fname);
612         msginfo->decryption_failed = 1;
613         return;
614     }
615
616     /* write the orginal header to the new file */
617     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
618         perror("fseek");
619
620     while (fgets(buf, sizeof(buf), fp)) {
621         if (headerp (buf, content_names))
622             continue;
623         if (buf[0] == '\r' || buf[0] == '\n')
624             break;
625         fputs (buf, dstfp);
626     }
627
628     err = gpgme_data_rewind (plain);
629     if (err)
630         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
631
632     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
633         fwrite (buf, nread, 1, dstfp);
634     }
635
636     if (err != GPGME_EOF) {
637         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
638     }
639
640     fclose (dstfp);
641
642     msginfo->plaintext_file = fname;
643     msginfo->decryption_failed = 0;
644 }
645
646
647 /* 
648  * plain contains an entire mime object.
649  * Encrypt it and return an GpgmeData object with the encrypted version of
650  * the file or NULL in case of error.
651  */
652 static GpgmeData
653 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
654 {
655     GpgmeCtx ctx = NULL;
656     GpgmeError err;
657     GpgmeData cipher = NULL;
658
659     err = gpgme_new (&ctx);
660     if (!err)
661         err = gpgme_data_new (&cipher);
662     if (!err) {
663         gpgme_set_armor (ctx, 1);
664         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
665     }
666
667     if (err) {
668         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
669         gpgme_data_release (cipher);
670         cipher = NULL;
671     }
672     else {
673         debug_print ("encryption succeeded\n");
674     }
675
676     gpgme_release (ctx);
677     return cipher;
678 }
679
680
681 /*
682  * Encrypt the file by extracting all recipients and finding the
683  * encryption keys for all of them.  The file content is then replaced
684  * by the encrypted one.  */
685 int
686 rfc2015_encrypt (const char *file, GSList *recp_list)
687 {
688     FILE *fp = NULL;
689     char buf[BUFFSIZE];
690     int i, clineidx, saved_last;
691     char *clines[3] = {NULL};
692     GpgmeError err;
693     GpgmeData header = NULL;
694     GpgmeData plain = NULL;
695     GpgmeData cipher = NULL;
696     GpgmeRecipients rset = NULL;
697     size_t nread;
698     int mime_version_seen = 0;
699     char *boundary = create_boundary ();
700
701     /* Create the list of recipients */
702     rset = gpgmegtk_recipient_selection (recp_list);
703     if (!rset) {
704         debug_print ("error creating recipient list\n" );
705         goto failure;
706     }
707
708     /* Open the source file */
709     if ((fp = fopen(file, "rb")) == NULL) {
710         FILE_OP_ERROR(file, "fopen");
711         goto failure;
712     }
713
714     err = gpgme_data_new (&header);
715     if (!err)
716         err = gpgme_data_new (&plain);
717     if (err) {
718         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
719         goto failure;
720     }
721
722     /* get the content header lines from the source */
723     clineidx=0;
724     saved_last = 0;
725     while (!err && fgets(buf, sizeof(buf), fp)) {
726         /* fixme: check for overlong lines */
727         if (headerp (buf, content_names)) {
728             if (clineidx >= DIM (clines)) {
729                 debug_print ("rfc2015_encrypt: too many content lines\n");
730                 goto failure;
731             }
732             clines[clineidx++] = g_strdup (buf);
733             saved_last = 1;
734             continue;
735         }
736         if (saved_last) {
737             saved_last = 0;
738             if (*buf == ' ' || *buf == '\t') {
739                 char *last = clines[clineidx-1];
740                 clines[clineidx-1] = g_strconcat (last, buf, NULL);
741                 g_free (last);
742                 continue;
743             }
744         }
745
746         if (headerp (buf, mime_version_name)) 
747             mime_version_seen = 1;
748
749         if (buf[0] == '\r' || buf[0] == '\n')
750             break;
751         err = gpgme_data_write (header, buf, strlen (buf));
752     }
753     if (ferror (fp)) {
754         FILE_OP_ERROR (file, "fgets");
755         goto failure;
756     }
757
758     /* write them to the temp data and add the rest of the message */
759     for (i = 0; !err && i < clineidx; i++) {
760         debug_print ("%s:%d: cline=`%s'\n", __FILE__ ,__LINE__, clines[i]);
761         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
762     }
763     if (!err)
764         err = gpgme_data_write (plain, "\r\n", 2);
765     while (!err && fgets(buf, sizeof(buf), fp)) {
766         err = gpgme_data_write (plain, buf, strlen (buf));
767     }
768     if (ferror (fp)) {
769         FILE_OP_ERROR (file, "fgets");
770         goto failure;
771     }
772     if (err) {
773         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
774         goto failure;
775     }
776
777     cipher = pgp_encrypt (plain, rset);
778     gpgme_data_release (plain); plain = NULL;
779     gpgme_recipients_release (rset); rset = NULL;
780     if (!cipher)
781         goto failure;
782
783     /* we have the encrypted message available in cipher and now we
784      * are going to rewrite the source file. To be sure that file has
785      * been truncated we use an approach which should work everywhere:
786      * close the file and then reopen it for writing. It is important
787      * that this works, otherwise it may happen that parts of the
788      * plaintext are still in the file (The encrypted stuff is, due to
789      * compression, usually shorter than the plaintext). 
790      * 
791      * Yes, there is a race condition here, but everyone, who is so
792      * stupid to store the temp file with the plaintext in a public
793      * directory has to live with this anyway. */
794     if (fclose (fp)) {
795         FILE_OP_ERROR(file, "fclose");
796         goto failure;
797     }
798     if ((fp = fopen(file, "wb")) == NULL) {
799         FILE_OP_ERROR(file, "fopen");
800         goto failure;
801     }
802
803     /* Write the header, append new content lines, part 1 and part 2 header */
804     err = gpgme_data_rewind (header);
805     if (err) {
806         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
807         goto failure;
808     }
809     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
810         fwrite (buf, nread, 1, fp);
811     }
812     if (err != GPGME_EOF) {
813         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
814         goto failure;
815     }
816     if (ferror (fp)) {
817         FILE_OP_ERROR (file, "fwrite");
818         goto failure;
819     }
820     gpgme_data_release (header); header = NULL;
821     
822     if (!mime_version_seen) 
823         fputs ("MIME-Version: 1\r\n", fp);
824
825     if (prefs_common.ascii_armored) {
826         fprintf(fp, 
827             "Content-Type: text/plain; charset=us-ascii\r\n"
828             "Content-Transfer-Encoding: 7bit\r\n"  
829             "\r\n");
830     } else {
831         fprintf (fp,
832                 "Content-Type: multipart/encrypted;"
833                 " protocol=\"application/pgp-encrypted\";\r\n"
834                 " boundary=\"%s\"\r\n"
835                 "\r\n"
836                 "--%s\r\n"
837                 "Content-Type: application/pgp-encrypted\r\n"
838                 "\r\n"
839                 "Version: 1\r\n"
840                 "\r\n"
841                 "--%s\r\n"
842                 "Content-Type: application/octet-stream\r\n"
843                 "\r\n",
844                 boundary, boundary, boundary);
845     }
846
847     /* append the encrypted stuff */
848     err = gpgme_data_rewind (cipher);
849     if (err) {
850         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
851                    gpgme_strerror (err));
852         goto failure;
853     }
854
855     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
856         fwrite (buf, nread, 1, fp);
857     }
858     if (err != GPGME_EOF) {
859         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
860         goto failure;
861     }
862
863     /* and the final boundary */
864     if (!prefs_common.ascii_armored) {
865         fprintf (fp,
866                 "\r\n"
867                 "--%s--\r\n"
868                 "\r\n",
869             boundary);
870     }
871     fflush (fp);
872     if (ferror (fp)) {
873         FILE_OP_ERROR (file, "fwrite");
874         goto failure;
875     }
876     fclose (fp);
877     gpgme_data_release (cipher);
878     return 0;
879
880 failure:
881     if (fp) 
882         fclose (fp);
883     gpgme_data_release (header);
884     gpgme_data_release (plain);
885     gpgme_data_release (cipher);
886     gpgme_recipients_release (rset);
887     g_free (boundary);
888     return -1; /* error */
889 }
890
891 int
892 set_signers (GpgmeCtx ctx, PrefsAccount *ac)
893 {
894     GSList *key_list = NULL;
895     GpgmeCtx list_ctx = NULL;
896     const char *keyid = NULL;
897     GSList *p;
898     GpgmeError err;
899     GpgmeKey key;
900
901     if (ac == NULL)
902         return 0;
903
904     switch (ac->sign_key) {
905     case SIGN_KEY_DEFAULT:
906         return 0;               /* nothing to do */
907
908     case SIGN_KEY_BY_FROM:
909         keyid = ac->address;
910         break;
911
912     case SIGN_KEY_CUSTOM:
913         keyid = ac->sign_key_id;
914         break;
915
916     default:
917         g_assert_not_reached ();
918     }
919
920     err = gpgme_new (&list_ctx);
921     if (err)
922         goto leave;
923     err = gpgme_op_keylist_start (list_ctx, keyid, 1);
924     if (err)
925         goto leave;
926     while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
927         key_list = g_slist_append (key_list, key);
928     }
929     if (err != GPGME_EOF)
930         goto leave;
931     if (key_list == NULL) {
932         debug_print ("no keys found for keyid \"%s\"\n", keyid);
933     }
934     gpgme_signers_clear (ctx);
935     for (p = key_list; p != NULL; p = p->next) {
936         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
937         if (err)
938             goto leave;
939     }
940
941 leave:
942     if (err)
943         debug_print ("set_signers failed: %s\n", gpgme_strerror (err));
944     for (p = key_list; p != NULL; p = p->next)
945         gpgme_key_unref ((GpgmeKey) p->data);
946     g_slist_free (key_list);
947     if (list_ctx)
948         gpgme_release (list_ctx);
949     return err;
950 }
951
952 /* 
953  * plain contains an entire mime object.  Sign it and return an
954  * GpgmeData object with the signature of it or NULL in case of error.
955  */
956 static GpgmeData
957 pgp_sign (GpgmeData plain, PrefsAccount *ac)
958 {
959     GpgmeCtx ctx = NULL;
960     GpgmeError err;
961     GpgmeData sig = NULL;
962     struct passphrase_cb_info_s info;
963
964     memset (&info, 0, sizeof info);
965
966     err = gpgme_new (&ctx);
967     if (err)
968         goto leave;
969     err = gpgme_data_new (&sig);
970     if (err)
971         goto leave;
972
973     if (!getenv("GPG_AGENT_INFO")) {
974         info.c = ctx;
975         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
976     }
977     gpgme_set_textmode (ctx, 1);
978     gpgme_set_armor (ctx, 1);
979     err = set_signers (ctx, ac);
980     if (err)
981         goto leave;
982     err = gpgme_op_sign (ctx, plain, sig, GPGME_SIG_MODE_DETACH);
983
984 leave:
985     if (err) {
986         debug_print ("signing failed: %s\n", gpgme_strerror (err));
987         gpgme_data_release (sig);
988         sig = NULL;
989     }
990     else {
991         debug_print ("signing succeeded\n");
992     }
993
994     gpgme_release (ctx);
995     return sig;
996 }
997
998 /*
999  * Sign the file and replace its content with the signed one.
1000  */
1001 int
1002 rfc2015_sign (const char *file, PrefsAccount *ac)
1003 {
1004     FILE *fp = NULL;
1005     char buf[BUFFSIZE];
1006     int i, clineidx, saved_last;
1007     char *clines[3] = {NULL};
1008     GpgmeError err;
1009     GpgmeData header = NULL;
1010     GpgmeData plain = NULL;
1011     GpgmeData sigdata = NULL;
1012     size_t nread;
1013     int mime_version_seen = 0;
1014     char *boundary = create_boundary ();
1015
1016     /* Open the source file */
1017     if ((fp = fopen(file, "rb")) == NULL) {
1018         FILE_OP_ERROR(file, "fopen");
1019         goto failure;
1020     }
1021
1022     err = gpgme_data_new (&header);
1023     if (!err)
1024         err = gpgme_data_new (&plain);
1025     if (err) {
1026         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1027         goto failure;
1028     }
1029
1030     /* get the content header lines from the source */
1031     clineidx = 0;
1032     saved_last = 0;
1033     while (!err && fgets(buf, sizeof(buf), fp)) {
1034         /* fixme: check for overlong lines */
1035         if (headerp (buf, content_names)) {
1036             if (clineidx >= DIM (clines)) {
1037                 debug_print ("rfc2015_sign: too many content lines\n");
1038                 goto failure;
1039             }
1040             clines[clineidx++] = g_strdup (buf);
1041             saved_last = 1;
1042             continue;
1043         }
1044         if (saved_last) {
1045             saved_last = 0;
1046             if (*buf == ' ' || *buf == '\t') {
1047                 char *last = clines[clineidx - 1];
1048                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1049                 g_free (last);
1050                 continue;
1051             }
1052         }
1053
1054         if (headerp (buf, mime_version_name)) 
1055             mime_version_seen = 1;
1056
1057         if (buf[0] == '\r' || buf[0] == '\n')
1058             break;
1059         err = gpgme_data_write (header, buf, strlen (buf));
1060     }
1061     if (ferror (fp)) {
1062         FILE_OP_ERROR (file, "fgets");
1063         goto failure;
1064     }
1065
1066     /* write them to the temp data and add the rest of the message */
1067     for (i = 0; !err && i < clineidx; i++) {
1068         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1069     }
1070     if (!err)
1071         err = gpgme_data_write (plain, "\r\n", 2 );
1072     while (!err && fgets(buf, sizeof(buf), fp)) {
1073         err = gpgme_data_write (plain, buf, strlen (buf));
1074     }
1075     if (ferror (fp)) {
1076         FILE_OP_ERROR (file, "fgets");
1077         goto failure;
1078     }
1079     if (err) {
1080         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1081         goto failure;
1082     }
1083
1084     sigdata = pgp_sign (plain, ac);
1085     if (!sigdata) 
1086         goto failure;
1087
1088     /* we have the signed message available in sigdata and now we are
1089      * going to rewrite the original file. To be sure that file has
1090      * been truncated we use an approach which should work everywhere:
1091      * close the file and then reopen it for writing. */
1092     if (fclose (fp)) {
1093         FILE_OP_ERROR(file, "fclose");
1094         goto failure;
1095     }
1096     if ((fp = fopen(file, "wb")) == NULL) {
1097         FILE_OP_ERROR(file, "fopen");
1098         goto failure;
1099     }
1100
1101     /* Write the rfc822 header and add new content lines */
1102     err = gpgme_data_rewind (header);
1103     if (err)
1104         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1105     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1106         fwrite (buf, nread, 1, fp);
1107     }
1108     if (err != GPGME_EOF) {
1109         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1110         goto failure;
1111     }
1112     if (ferror (fp)) {
1113         FILE_OP_ERROR (file, "fwrite");
1114         goto failure;
1115     }
1116     gpgme_data_release (header);
1117     header = NULL;
1118
1119     if (!mime_version_seen) 
1120         fputs ("MIME-Version: 1\r\n", fp);
1121     fprintf (fp, "Content-Type: multipart/signed; "
1122                  "protocol=\"application/pgp-signature\";\r\n"
1123                  " boundary=\"%s\"\r\n", boundary );
1124
1125     /* Part 1: signed material */
1126     fprintf (fp, "\r\n--%s\r\n", boundary);
1127     err = gpgme_data_rewind (plain);
1128     if (err) {
1129         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1130                    gpgme_strerror (err));
1131         goto failure;
1132     }
1133     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1134         fwrite (buf, nread, 1, fp);   
1135     }
1136     if (err != GPGME_EOF) {
1137         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1138         goto failure;
1139     }
1140
1141     /* Part 2: signature */
1142     fprintf (fp, "\r\n--%s\r\n", boundary);
1143     fputs ("Content-Type: application/pgp-signature\r\n"
1144            "\r\n", fp);
1145
1146     err = gpgme_data_rewind (sigdata);
1147     if (err) {
1148         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1149                    gpgme_strerror (err));
1150         goto failure;
1151     }
1152
1153     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1154         fwrite (buf, nread, 1, fp);
1155     }
1156     if (err != GPGME_EOF) {
1157         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1158         goto failure;
1159     }
1160
1161     /* Final boundary */
1162     fprintf (fp, "\r\n--%s--\r\n\r\n", boundary);
1163     fflush (fp);
1164     if (ferror (fp)) {
1165         FILE_OP_ERROR (file, "fwrite");
1166         goto failure;
1167     }
1168     fclose (fp);
1169     gpgme_data_release (header);
1170     gpgme_data_release (plain);
1171     gpgme_data_release (sigdata);
1172     g_free (boundary);
1173     return 0;
1174
1175 failure:
1176     if (fp) 
1177         fclose (fp);
1178     gpgme_data_release (header);
1179     gpgme_data_release (plain);
1180     gpgme_data_release (sigdata);
1181     g_free (boundary);
1182     return -1; /* error */
1183 }
1184
1185
1186 /****************
1187  * Create a new boundary in a way that it is very unlikely that this
1188  * will occur in the following text.  It would be easy to ensure
1189  * uniqueness if everything is either quoted-printable or base64
1190  * encoded (note that conversion is allowed), but because MIME bodies
1191  * may be nested, it may happen that the same boundary has already
1192  * been used. We avoid scanning the message for conflicts and hope the
1193  * best.
1194  *
1195  *   boundary := 0*69<bchars> bcharsnospace
1196  *   bchars := bcharsnospace / " "
1197  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
1198  *                    "+" / "_" / "," / "-" / "." /
1199  *                    "/" / ":" / "=" / "?"  
1200  */
1201
1202 static char *
1203 create_boundary (void)
1204 {
1205     static char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1206                         "abcdefghijklmnopqrstuvwxyz"
1207                         "1234567890'()+_,./:=?";
1208     char buf[17];
1209     int i, equal;
1210     int pid;
1211
1212     pid = getpid();
1213
1214     /* We make the boundary depend on the pid, so that all running
1215      * processed generate different values even when they have been
1216      * started within the same second and srand48(time(NULL)) has been
1217      * used.  I can't see whether this is really an advantage but it
1218      * doesn't do any harm.
1219      */
1220     equal = -1;
1221     for(i = 0; i < sizeof(buf) - 1; i++) {
1222         buf[i] = tbl[(lrand48() ^ pid) % (sizeof(tbl) - 1)]; /* fill with random */
1223         if(buf[i] == '=' && equal == -1)
1224             equal = i;
1225     }
1226     buf[i] = 0;
1227
1228     /* now make sure that we do have the sequence "=." in it which cannot
1229      * be matched by quoted-printable or base64 encoding */
1230     if(equal != -1 && (equal+1) < i)
1231         buf[equal+1] = '.';
1232     else {
1233         buf[0] = '=';
1234         buf[1] = '.';
1235     }
1236
1237     return g_strdup(buf);
1238 }
1239
1240 #endif /* USE_GPGME */