sync with sylpheed 0.7.4cvs2
[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 #if 0
73 static void dump_mimeinfo (const char *text, MimeInfo *x)
74 {
75     debug_print ("MimeInfo[%s] %p  level=%d\n",
76                text, x, x? x->level:0 );
77     if (!x)
78         return;
79
80     debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
81                x->encoding, x->encoding_type, x->mime_type );
82     debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
83                x->content_type, x->charset, x->name, x->boundary );
84     debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
85                x->content_disposition, x->filename, x->fpos, x->size,
86                x->level );
87     dump_mimeinfo (".main", x->main );
88     dump_mimeinfo (".sub", x->sub );
89     dump_mimeinfo (".next", x->next );
90     debug_print ("MimeInfo[.parent] %p\n", x ); 
91     dump_mimeinfo (".children", x->children );
92     dump_mimeinfo (".plaintext", x->plaintext );
93 }
94
95 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
96 {
97     unsigned int size = mimeinfo->size;
98     int c;
99
100     if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
101         debug_print ("dump_part: fseek error\n");
102         return;
103     }
104
105     debug_print ("--- begin dump_part ----\n");
106     while (size-- && (c = getc (fp)) != EOF) 
107         putc (c, stderr);
108     if (ferror (fp))
109         debug_print ("dump_part: read error\n");
110     debug_print ("--- end dump_part ----\n");
111 }
112 #endif
113
114 void
115 rfc2015_disable_all (void)
116 {
117     /* FIXME: set a flag, so that we don't bother the user with failed
118      * gpgme messages */
119 }
120
121
122 void
123 rfc2015_secure_remove (const char *fname)
124 {
125     if (!fname)
126         return;
127     /* fixme: overwrite the file first */
128     remove (fname);
129 }
130
131
132 static const gchar *
133 sig_status_to_string (GpgmeSigStat status)
134 {
135     const gchar *result;
136
137     switch (status) {
138       case GPGME_SIG_STAT_NONE:
139         result = _("Oops: Signature not verified");
140         break;
141       case GPGME_SIG_STAT_NOSIG:
142         result = _("No signature found");
143         break;
144       case GPGME_SIG_STAT_GOOD:
145         result = _("Good signature");
146         break;
147       case GPGME_SIG_STAT_BAD:
148         result = _("BAD signature");
149         break;
150       case GPGME_SIG_STAT_NOKEY:
151         result = _("No public key to verify the signature");
152         break;
153       case GPGME_SIG_STAT_ERROR:
154         result = _("Error verifying the signature");
155         break;
156       case GPGME_SIG_STAT_DIFF:
157         result = _("Different results for signatures");
158         break;
159       default:
160         result = _("Error: Unknown status");
161         break;
162     }
163
164     return result;
165 }
166
167 static const gchar *
168 sig_status_with_name (GpgmeSigStat status)
169 {
170     const gchar *result;
171
172     switch (status) {
173       case GPGME_SIG_STAT_NONE:
174         result = _("Oops: Signature not verified");
175         break;
176       case GPGME_SIG_STAT_NOSIG:
177         result = _("No signature found");
178         break;
179       case GPGME_SIG_STAT_GOOD:
180         result = _("Good signature from \"%s\"");
181         break;
182       case GPGME_SIG_STAT_BAD:
183         result = _("BAD signature  from \"%s\"");
184         break;
185       case GPGME_SIG_STAT_NOKEY:
186         result = _("No public key to verify the signature");
187         break;
188       case GPGME_SIG_STAT_ERROR:
189         result = _("Error verifying the signature");
190         break;
191       case GPGME_SIG_STAT_DIFF:
192         result = _("Different results for signatures");
193         break;
194       default:
195         result = _("Error: Unknown status");
196         break;
197     }
198
199     return result;
200 }
201
202 static void
203 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
204                    GpgmeKey key, const gchar *fpr)
205 {
206         gint idx = 0;
207         const char *uid;
208
209         uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
210         if (uid == NULL) {
211                 g_string_sprintfa (str, "%s\n",
212                                    sig_status_to_string (status));
213                 if ((fpr != NULL) && (*fpr != '\0'))
214                         g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
215                 g_string_append (str, _("Cannot find user ID for this key."));
216                 return;
217         }
218         g_string_sprintfa (str, sig_status_with_name (status), uid);
219         g_string_append (str, "\n");
220
221         while (1) {
222                 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
223                                                  NULL, ++idx);
224                 if (uid == NULL)
225                         break;
226                 g_string_sprintfa (str, _("                aka \"%s\"\n"),
227                                    uid);
228         }
229 }
230
231 static gchar *
232 sig_status_full (GpgmeCtx ctx)
233 {
234         GString *str;
235         gint sig_idx = 0;
236         GpgmeError err;
237         GpgmeSigStat status;
238         GpgmeKey key;
239         const char *fpr;
240         time_t created;
241         struct tm *ctime_val;
242         char ctime_str[80];
243         gchar *retval;
244
245         str = g_string_new ("");
246
247         fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
248         while (fpr != NULL) {
249                 if (created != 0) {
250                         ctime_val = localtime (&created);
251                         strftime (ctime_str, sizeof (ctime_str), "%c", 
252                                   ctime_val);
253                         g_string_sprintfa (str,
254                                            _("Signature made %s\n"),
255                                            ctime_str);
256                 }
257                 err = gpgme_get_sig_key (ctx, sig_idx, &key);
258                 if (err != 0) {
259                         g_string_sprintfa (str, "%s\n",
260                                            sig_status_to_string (status));
261                         if ((fpr != NULL) && (*fpr != '\0'))
262                                 g_string_sprintfa (str, 
263                                                    _("Key fingerprint: %s\n"),
264                                                    fpr);
265                 } else {
266                         sig_status_for_key (str, ctx, status, key, fpr);
267                         gpgme_key_unref (key);
268                 }
269                 g_string_append (str, "\n\n");
270
271                 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
272         }
273
274         retval = str->str;
275         g_string_free (str, FALSE);
276         return retval;
277 }
278
279 static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
280 {
281     GpgmeCtx ctx = NULL;
282     GpgmeError err;
283     GpgmeData sig = NULL, text = NULL;
284     GpgmeSigStat status = GPGME_SIG_STAT_NONE;
285     GpgmegtkSigStatus statuswindow = NULL;
286     const char *result = NULL;
287
288     if (prefs_common.gpg_signature_popup)
289         statuswindow = gpgmegtk_sig_status_create ();
290
291     err = gpgme_new (&ctx);
292     if (err) {
293         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
294         goto leave;
295     }
296
297     /* don't include the last character (LF). It does not belong to the
298      * signed text */
299     err = gpgme_data_new_from_filepart (&text, NULL, fp,
300                                         mimeinfo->children->fpos,
301                                         mimeinfo->children->size ?
302                                         (mimeinfo->children->size - 1) : 0 );
303     if (!err)
304         err = gpgme_data_new_from_filepart (&sig, NULL, fp,
305                                             partinfo->fpos, partinfo->size);
306     if (err) {
307         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
308                    gpgme_strerror (err));
309         goto leave;
310     }
311
312     err = gpgme_op_verify (ctx, sig, text, &status);
313     if (err) 
314         debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
315
316     /* FIXME: check what the heck this sig_status_full stuff is.
317      * it should better go into sigstatus.c */
318     g_free (partinfo->sigstatus_full);
319     partinfo->sigstatus_full = sig_status_full (ctx);
320
321 leave:
322     result = gpgmegtk_sig_status_to_string(status);
323     debug_print("verification status: %s\n", result);
324     if (prefs_common.gpg_signature_popup)
325         gpgmegtk_sig_status_update (statuswindow,ctx);
326
327     g_assert (!err); /* FIXME: Hey: this may indeed happen */
328     g_free (partinfo->sigstatus);
329     partinfo->sigstatus = g_strdup (result);
330
331     gpgme_data_release (sig);
332     gpgme_data_release (text);
333     gpgme_release (ctx);
334     if (prefs_common.gpg_signature_popup)
335         gpgmegtk_sig_status_destroy (statuswindow);
336 }
337
338 static const char *
339 passphrase_cb (void *opaque, const char *desc, void **r_hd)
340 {
341     struct passphrase_cb_info_s *info = opaque;
342     GpgmeCtx ctx = info ? info->c : NULL;
343     const char *pass;
344
345     if (!desc) {
346         /* FIXME: cleanup by looking at **r_hd */
347         return NULL;
348     }
349
350     gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
351     debug_print ("%% requesting passphrase for `%s': ", desc );
352     pass = gpgmegtk_passphrase_mbox (desc);
353     if (!pass) {
354         debug_print ("%% cancel passphrase entry");
355         gpgme_cancel (ctx);
356     }
357     else
358         debug_print ("%% sending passphrase");
359
360     return pass;
361 }
362
363 /*
364  * Copy a gpgme data object to a temporary file and
365  * return this filename 
366  */
367 #if 0
368 static char *
369 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
370 {
371     static int id;
372     char *tmp;
373     FILE *fp;
374     char buf[100];
375     size_t nread;
376     GpgmeError err;
377     
378     tmp = g_strdup_printf("%s%cgpgtmp.%08x",
379                           get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
380
381     if ((fp = fopen(tmp, "wb")) == NULL) {
382         FILE_OP_ERROR(tmp, "fopen");
383         g_free(tmp);
384         return NULL;
385     }
386
387     err = gpgme_data_rewind ( data );
388     if (err)
389         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
390
391     while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
392         fwrite ( buf, nread, 1, fp );
393     }
394
395     if (err != GPGME_EOF)
396         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
397
398     fclose (fp);
399     *length = nread;
400
401     return tmp;
402 }
403 #endif
404
405 static GpgmeData
406 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
407 {
408     GpgmeCtx ctx = NULL;
409     GpgmeError err;
410     GpgmeData cipher = NULL, plain = NULL;
411     struct passphrase_cb_info_s info;
412
413     memset (&info, 0, sizeof info);
414
415     err = gpgme_new (&ctx);
416     if (err) {
417         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
418         goto leave;
419     }
420
421     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
422                                         partinfo->fpos, partinfo->size);
423     if (err) {
424         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
425                      gpgme_strerror (err));
426         goto leave;
427     }
428
429     err = gpgme_data_new (&plain);
430     if (err) {
431         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
432         goto leave;
433     }
434
435     if (!getenv("GPG_AGENT_INFO")) {
436         info.c = ctx;
437         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
438     } 
439
440     err = gpgme_op_decrypt (ctx, cipher, plain);
441
442 leave:
443     gpgme_data_release (cipher);
444     if (err) {
445         debug_print ("decryption failed: %s\n", gpgme_strerror (err));
446         gpgme_data_release (plain);
447         plain = NULL;
448     }
449     else
450         debug_print ("** decryption succeeded");
451
452     gpgme_release (ctx);
453     return plain;
454 }
455
456 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
457 {
458     MimeInfo *partinfo;
459     int n = 0;
460
461     if (!mimeinfo)
462         return NULL;
463     if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
464         return NULL;
465
466     debug_print ("** multipart/signed encountered");
467
468     /* check that we have at least 2 parts of the correct type */
469     for (partinfo = mimeinfo->children;
470          partinfo != NULL; partinfo = partinfo->next) {
471         if (++n > 1  && !g_strcasecmp (partinfo->content_type,
472                                        "application/pgp-signature"))
473             break;
474     }
475
476     return partinfo;
477 }
478
479 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
480 {
481     return rfc2015_find_signature (mimeinfo) != NULL;
482 }
483
484 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
485 {
486     MimeInfo *partinfo;
487
488     partinfo = rfc2015_find_signature (mimeinfo);
489     if (!partinfo)
490         return;
491
492 #if 0
493     g_message ("** yep, it is a pgp signature");
494     dump_mimeinfo ("gpg-signature", partinfo );
495     dump_part (partinfo, fp );
496     dump_mimeinfo ("signed text", mimeinfo->children );
497     dump_part (mimeinfo->children, fp);
498 #endif
499
500     check_signature (mimeinfo, partinfo, fp);
501 }
502
503 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
504 {
505     if (!mimeinfo)
506         return 0;
507     if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
508         return 0;
509     /* fixme: we should schek the protocol parameter */
510     return 1;
511 }
512
513 static int
514 name_cmp(const char *a, const char *b)
515 {
516     for( ; *a && *b; a++, b++) {
517         if(*a != *b
518            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
519             return 1;
520     }
521
522     return *a != *b;
523 }
524
525 static int
526 headerp(char *p, char **names)
527 {
528     int i, c;
529     char *p2;
530
531     p2 = strchr(p, ':');
532     if(!p2 || p == p2) {
533         return 0;
534     }
535     if(p2[-1] == ' ' || p2[-1] == '\t') {
536         return 0;
537     }
538
539     if(!names[0])
540         return 1;  
541
542     c = *p2;
543     *p2 = 0;
544     for(i = 0 ; names[i] != NULL; i++) {
545         if(!name_cmp (names[i], p))
546             break;
547     }
548     *p2 = c;
549
550     return names[i] != NULL;
551 }
552
553
554 #define DECRYPTION_ABORT() \
555 { \
556     procmime_mimeinfo_free_all(tmpinfo); \
557     msginfo->decryption_failed = 1; \
558     return; \
559 }
560
561 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
562 {
563     static int id;
564     MimeInfo *tmpinfo, *partinfo;
565     int ver_ok = 0;
566     char *fname;
567     GpgmeData plain;
568     FILE *dstfp;
569     size_t nread;
570     char buf[BUFFSIZE];
571     GpgmeError err;
572
573     g_return_if_fail (msginfo != NULL);
574     g_return_if_fail (mimeinfo != NULL);
575     g_return_if_fail (fp != NULL);
576     g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
577
578     debug_print ("** decrypting multipart/encrypted message");
579
580     /* skip headers */
581     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
582         perror("fseek");
583     tmpinfo = procmime_scan_mime_header(fp);
584     if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
585         DECRYPTION_ABORT();
586     }
587
588     procmime_scan_multipart_message(tmpinfo, fp);
589
590     /* check that we have the 2 parts */
591     partinfo = tmpinfo->children;
592     if (!partinfo || !partinfo->next) {
593         DECRYPTION_ABORT();
594     }
595     if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
596         /* Fixme: check that the version is 1 */
597         ver_ok = 1;
598     }
599     partinfo = partinfo->next;
600     if (ver_ok &&
601         !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
602         if (partinfo->next)
603             g_warning ("oops: pgp_encrypted with more than 2 parts");
604     }
605     else {
606         DECRYPTION_ABORT();
607     }
608
609     debug_print ("** yep, it is pgp encrypted");
610
611     plain = pgp_decrypt (partinfo, fp);
612     if (!plain) {
613         DECRYPTION_ABORT();
614     }
615
616     fname = g_strdup_printf("%s%cplaintext.%08x",
617                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
618
619     if ((dstfp = fopen(fname, "wb")) == NULL) {
620         FILE_OP_ERROR(fname, "fopen");
621         g_free(fname);
622         DECRYPTION_ABORT();
623     }
624
625     /* write the orginal header to the new file */
626     if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
627         perror("fseek");
628
629     while (fgets(buf, sizeof(buf), fp)) {
630         if (headerp (buf, content_names))
631             continue;
632         if (buf[0] == '\r' || buf[0] == '\n')
633             break;
634         fputs (buf, dstfp);
635     }
636
637     err = gpgme_data_rewind (plain);
638     if (err)
639         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
640
641     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
642         fwrite (buf, nread, 1, dstfp);
643     }
644
645     if (err != GPGME_EOF) {
646         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
647     }
648
649     fclose (dstfp);
650     procmime_mimeinfo_free_all(tmpinfo);
651
652     msginfo->plaintext_file = fname;
653     msginfo->decryption_failed = 0;
654 }
655
656 #undef DECRYPTION_ABORT
657
658
659 /*
660  * plain contains an entire mime object.
661  * Encrypt it and return an GpgmeData object with the encrypted version of
662  * the file or NULL in case of error.
663  */
664 static GpgmeData
665 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
666 {
667     GpgmeCtx ctx = NULL;
668     GpgmeError err;
669     GpgmeData cipher = NULL;
670
671     err = gpgme_new (&ctx);
672     if (!err)
673         err = gpgme_data_new (&cipher);
674     if (!err) {
675         gpgme_set_armor (ctx, 1);
676         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
677     }
678
679     if (err) {
680         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
681         gpgme_data_release (cipher);
682         cipher = NULL;
683     }
684     else {
685         debug_print ("** encryption succeeded");
686     }
687
688     gpgme_release (ctx);
689     return cipher;
690 }
691
692 /*
693  * Create and return a list of keys matching a key id
694  */
695
696 GSList *rfc2015_create_signers_list (const char *keyid)
697 {
698         GSList *key_list = NULL;
699         GpgmeCtx list_ctx = NULL;
700         GSList *p;
701         GpgmeError err;
702         GpgmeKey key;
703
704         err = gpgme_new (&list_ctx);
705         if (err)
706                 goto leave;
707         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
708         if (err)
709                 goto leave;
710         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
711                 key_list = g_slist_append (key_list, key);
712         }
713         if (err != GPGME_EOF)
714                 goto leave;
715         err = 0;
716         if (key_list == NULL) {
717                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
718         }
719
720 leave:
721         if (err) {
722                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
723                 for (p = key_list; p != NULL; p = p->next)
724                         gpgme_key_unref ((GpgmeKey) p->data);
725                 g_slist_free (key_list);
726         }
727         if (list_ctx)
728                 gpgme_release (list_ctx);
729         return err ? NULL : key_list;
730 }
731
732 /*
733  * Encrypt the file by extracting all recipients and finding the
734  * encryption keys for all of them.  The file content is then replaced
735  * by the encrypted one.  */
736 int
737 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
738 {
739     FILE *fp = NULL;
740     char buf[BUFFSIZE];
741     int i, clineidx, saved_last;
742     char *clines[3] = {NULL};
743     GpgmeError err;
744     GpgmeData header = NULL;
745     GpgmeData plain = NULL;
746     GpgmeData cipher = NULL;
747     GpgmeRecipients rset = NULL;
748     size_t nread;
749     int mime_version_seen = 0;
750     char *boundary = create_boundary ();
751
752     /* Create the list of recipients */
753     rset = gpgmegtk_recipient_selection (recp_list);
754     if (!rset) {
755         debug_print ("error creating recipient list\n" );
756         goto failure;
757     }
758
759     /* Open the source file */
760     if ((fp = fopen(file, "rb")) == NULL) {
761         FILE_OP_ERROR(file, "fopen");
762         goto failure;
763     }
764
765     err = gpgme_data_new (&header);
766     if (!err)
767         err = gpgme_data_new (&plain);
768     if (err) {
769         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
770         goto failure;
771     }
772
773     /* get the content header lines from the source */
774     clineidx=0;
775     saved_last = 0;
776     while (!err && fgets(buf, sizeof(buf), fp)) {
777         /* fixme: check for overlong lines */
778         if (headerp (buf, content_names)) {
779             if (clineidx >= DIM (clines)) {
780                 debug_print ("rfc2015_encrypt: too many content lines\n");
781                 goto failure;
782             }
783             clines[clineidx++] = g_strdup (buf);
784             saved_last = 1;
785             continue;
786         }
787         if (saved_last) {
788             saved_last = 0;
789             if (*buf == ' ' || *buf == '\t') {
790                 char *last = clines[clineidx-1];
791                 clines[clineidx-1] = g_strconcat (last, buf, NULL);
792                 g_free (last);
793                 continue;
794             }
795         }
796
797         if (headerp (buf, mime_version_name)) 
798             mime_version_seen = 1;
799
800         if (buf[0] == '\r' || buf[0] == '\n')
801             break;
802         err = gpgme_data_write (header, buf, strlen (buf));
803     }
804     if (ferror (fp)) {
805         FILE_OP_ERROR (file, "fgets");
806         goto failure;
807     }
808
809     /* write them to the temp data and add the rest of the message */
810     for (i = 0; !err && i < clineidx; i++) {
811         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
812         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
813     }
814     if (!err)
815         err = gpgme_data_write (plain, "\r\n", 2);
816     while (!err && fgets(buf, sizeof(buf), fp)) {
817         err = gpgme_data_write (plain, buf, strlen (buf));
818     }
819     if (ferror (fp)) {
820         FILE_OP_ERROR (file, "fgets");
821         goto failure;
822     }
823     if (err) {
824         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
825         goto failure;
826     }
827
828     cipher = pgp_encrypt (plain, rset);
829     gpgme_data_release (plain); plain = NULL;
830     gpgme_recipients_release (rset); rset = NULL;
831     if (!cipher)
832         goto failure;
833
834     /* we have the encrypted message available in cipher and now we
835      * are going to rewrite the source file. To be sure that file has
836      * been truncated we use an approach which should work everywhere:
837      * close the file and then reopen it for writing. It is important
838      * that this works, otherwise it may happen that parts of the
839      * plaintext are still in the file (The encrypted stuff is, due to
840      * compression, usually shorter than the plaintext). 
841      * 
842      * Yes, there is a race condition here, but everyone, who is so
843      * stupid to store the temp file with the plaintext in a public
844      * directory has to live with this anyway. */
845     if (fclose (fp)) {
846         FILE_OP_ERROR(file, "fclose");
847         goto failure;
848     }
849     if ((fp = fopen(file, "wb")) == NULL) {
850         FILE_OP_ERROR(file, "fopen");
851         goto failure;
852     }
853
854     /* Write the header, append new content lines, part 1 and part 2 header */
855     err = gpgme_data_rewind (header);
856     if (err) {
857         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
858         goto failure;
859     }
860     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
861         fwrite (buf, nread, 1, fp);
862     }
863     if (err != GPGME_EOF) {
864         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
865         goto failure;
866     }
867     if (ferror (fp)) {
868         FILE_OP_ERROR (file, "fwrite");
869         goto failure;
870     }
871     gpgme_data_release (header); header = NULL;
872     
873     if (!mime_version_seen) 
874         fputs ("MIME-Version: 1\r\n", fp);
875
876     if (ascii_armored) {
877         fprintf(fp, 
878             "Content-Type: text/plain; charset=us-ascii\r\n"
879             "Content-Transfer-Encoding: 7bit\r\n"  
880             "\r\n");
881     } else {
882         fprintf (fp,
883                 "Content-Type: multipart/encrypted;"
884                 " protocol=\"application/pgp-encrypted\";\r\n"
885                 " boundary=\"%s\"\r\n"
886                 "\r\n"
887                 "--%s\r\n"
888                 "Content-Type: application/pgp-encrypted\r\n"
889                 "\r\n"
890                 "Version: 1\r\n"
891                 "\r\n"
892                 "--%s\r\n"
893                 "Content-Type: application/octet-stream\r\n"
894                 "\r\n",
895                 boundary, boundary, boundary);
896     }
897
898     /* append the encrypted stuff */
899     err = gpgme_data_rewind (cipher);
900     if (err) {
901         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
902                    gpgme_strerror (err));
903         goto failure;
904     }
905
906     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
907         fwrite (buf, nread, 1, fp);
908     }
909     if (err != GPGME_EOF) {
910         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
911         goto failure;
912     }
913
914     /* and the final boundary */
915     if (!ascii_armored) {
916         fprintf (fp,
917                 "\r\n"
918                 "--%s--\r\n"
919                 "\r\n",
920             boundary);
921     }
922     fflush (fp);
923     if (ferror (fp)) {
924         FILE_OP_ERROR (file, "fwrite");
925         goto failure;
926     }
927     fclose (fp);
928     gpgme_data_release (cipher);
929     return 0;
930
931 failure:
932     if (fp) 
933         fclose (fp);
934     gpgme_data_release (header);
935     gpgme_data_release (plain);
936     gpgme_data_release (cipher);
937     gpgme_recipients_release (rset);
938     g_free (boundary);
939     return -1; /* error */
940 }
941
942 /* 
943  * plain contains an entire mime object.  Sign it and return an
944  * GpgmeData object with the signature of it or NULL in case of error.
945  */
946 static GpgmeData
947 pgp_sign (GpgmeData plain, GSList *key_list)
948 {
949     GSList *p;
950     GpgmeCtx ctx = NULL;
951     GpgmeError err;
952     GpgmeData sig = NULL;
953     struct passphrase_cb_info_s info;
954
955     memset (&info, 0, sizeof info);
956
957     err = gpgme_new (&ctx);
958     if (err)
959         goto leave;
960     err = gpgme_data_new (&sig);
961     if (err)
962         goto leave;
963
964     if (!getenv("GPG_AGENT_INFO")) {
965         info.c = ctx;
966         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
967     }
968     gpgme_set_textmode (ctx, 1);
969     gpgme_set_armor (ctx, 1);
970     gpgme_signers_clear (ctx);
971     for (p = key_list; p != NULL; p = p->next) {
972         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
973         if (err)
974             goto leave;
975     }
976     for (p = key_list; p != NULL; p = p->next)
977         gpgme_key_unref ((GpgmeKey) p->data);
978     g_slist_free (key_list);
979
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, GSList *key_list)
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, key_list);
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 */