bring in Simon Schubert's general GPG clean up patch
[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  * Create and return a list of keys matching a key id
682  */
683
684 GSList *rfc2015_create_signers_list (const char *keyid)
685 {
686         GSList *key_list = NULL;
687         GpgmeCtx list_ctx = NULL;
688         GSList *p;
689         GpgmeError err;
690         GpgmeKey key;
691
692         err = gpgme_new (&list_ctx);
693         if (err)
694                 goto leave;
695         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
696         if (err)
697                 goto leave;
698         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
699                 key_list = g_slist_append (key_list, key);
700         }
701         if (err != GPGME_EOF)
702                 goto leave;
703         err = 0;
704         if (key_list == NULL) {
705                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
706         }
707
708 leave:
709         if (err) {
710                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
711                 for (p = key_list; p != NULL; p = p->next)
712                         gpgme_key_unref ((GpgmeKey) p->data);
713                 g_slist_free (key_list);
714         }
715         if (list_ctx)
716                 gpgme_release (list_ctx);
717         return err ? NULL : key_list;
718 }
719
720 /*
721  * Encrypt the file by extracting all recipients and finding the
722  * encryption keys for all of them.  The file content is then replaced
723  * by the encrypted one.  */
724 int
725 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
726 {
727     FILE *fp = NULL;
728     char buf[BUFFSIZE];
729     int i, clineidx, saved_last;
730     char *clines[3] = {NULL};
731     GpgmeError err;
732     GpgmeData header = NULL;
733     GpgmeData plain = NULL;
734     GpgmeData cipher = NULL;
735     GpgmeRecipients rset = NULL;
736     size_t nread;
737     int mime_version_seen = 0;
738     char *boundary = create_boundary ();
739
740     /* Create the list of recipients */
741     rset = gpgmegtk_recipient_selection (recp_list);
742     if (!rset) {
743         debug_print ("error creating recipient list\n" );
744         goto failure;
745     }
746
747     /* Open the source file */
748     if ((fp = fopen(file, "rb")) == NULL) {
749         FILE_OP_ERROR(file, "fopen");
750         goto failure;
751     }
752
753     err = gpgme_data_new (&header);
754     if (!err)
755         err = gpgme_data_new (&plain);
756     if (err) {
757         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
758         goto failure;
759     }
760
761     /* get the content header lines from the source */
762     clineidx=0;
763     saved_last = 0;
764     while (!err && fgets(buf, sizeof(buf), fp)) {
765         /* fixme: check for overlong lines */
766         if (headerp (buf, content_names)) {
767             if (clineidx >= DIM (clines)) {
768                 debug_print ("rfc2015_encrypt: too many content lines\n");
769                 goto failure;
770             }
771             clines[clineidx++] = g_strdup (buf);
772             saved_last = 1;
773             continue;
774         }
775         if (saved_last) {
776             saved_last = 0;
777             if (*buf == ' ' || *buf == '\t') {
778                 char *last = clines[clineidx-1];
779                 clines[clineidx-1] = g_strconcat (last, buf, NULL);
780                 g_free (last);
781                 continue;
782             }
783         }
784
785         if (headerp (buf, mime_version_name)) 
786             mime_version_seen = 1;
787
788         if (buf[0] == '\r' || buf[0] == '\n')
789             break;
790         err = gpgme_data_write (header, buf, strlen (buf));
791     }
792     if (ferror (fp)) {
793         FILE_OP_ERROR (file, "fgets");
794         goto failure;
795     }
796
797     /* write them to the temp data and add the rest of the message */
798     for (i = 0; !err && i < clineidx; i++) {
799         debug_print ("%s:%d: cline=`%s'\n", __FILE__ ,__LINE__, clines[i]);
800         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
801     }
802     if (!err)
803         err = gpgme_data_write (plain, "\r\n", 2);
804     while (!err && fgets(buf, sizeof(buf), fp)) {
805         err = gpgme_data_write (plain, buf, strlen (buf));
806     }
807     if (ferror (fp)) {
808         FILE_OP_ERROR (file, "fgets");
809         goto failure;
810     }
811     if (err) {
812         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
813         goto failure;
814     }
815
816     cipher = pgp_encrypt (plain, rset);
817     gpgme_data_release (plain); plain = NULL;
818     gpgme_recipients_release (rset); rset = NULL;
819     if (!cipher)
820         goto failure;
821
822     /* we have the encrypted message available in cipher and now we
823      * are going to rewrite the source file. To be sure that file has
824      * been truncated we use an approach which should work everywhere:
825      * close the file and then reopen it for writing. It is important
826      * that this works, otherwise it may happen that parts of the
827      * plaintext are still in the file (The encrypted stuff is, due to
828      * compression, usually shorter than the plaintext). 
829      * 
830      * Yes, there is a race condition here, but everyone, who is so
831      * stupid to store the temp file with the plaintext in a public
832      * directory has to live with this anyway. */
833     if (fclose (fp)) {
834         FILE_OP_ERROR(file, "fclose");
835         goto failure;
836     }
837     if ((fp = fopen(file, "wb")) == NULL) {
838         FILE_OP_ERROR(file, "fopen");
839         goto failure;
840     }
841
842     /* Write the header, append new content lines, part 1 and part 2 header */
843     err = gpgme_data_rewind (header);
844     if (err) {
845         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
846         goto failure;
847     }
848     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
849         fwrite (buf, nread, 1, fp);
850     }
851     if (err != GPGME_EOF) {
852         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
853         goto failure;
854     }
855     if (ferror (fp)) {
856         FILE_OP_ERROR (file, "fwrite");
857         goto failure;
858     }
859     gpgme_data_release (header); header = NULL;
860     
861     if (!mime_version_seen) 
862         fputs ("MIME-Version: 1\r\n", fp);
863
864     if (ascii_armored) {
865         fprintf(fp, 
866             "Content-Type: text/plain; charset=us-ascii\r\n"
867             "Content-Transfer-Encoding: 7bit\r\n"  
868             "\r\n");
869     } else {
870         fprintf (fp,
871                 "Content-Type: multipart/encrypted;"
872                 " protocol=\"application/pgp-encrypted\";\r\n"
873                 " boundary=\"%s\"\r\n"
874                 "\r\n"
875                 "--%s\r\n"
876                 "Content-Type: application/pgp-encrypted\r\n"
877                 "\r\n"
878                 "Version: 1\r\n"
879                 "\r\n"
880                 "--%s\r\n"
881                 "Content-Type: application/octet-stream\r\n"
882                 "\r\n",
883                 boundary, boundary, boundary);
884     }
885
886     /* append the encrypted stuff */
887     err = gpgme_data_rewind (cipher);
888     if (err) {
889         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
890                    gpgme_strerror (err));
891         goto failure;
892     }
893
894     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
895         fwrite (buf, nread, 1, fp);
896     }
897     if (err != GPGME_EOF) {
898         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
899         goto failure;
900     }
901
902     /* and the final boundary */
903     if (!ascii_armored) {
904         fprintf (fp,
905                 "\r\n"
906                 "--%s--\r\n"
907                 "\r\n",
908             boundary);
909     }
910     fflush (fp);
911     if (ferror (fp)) {
912         FILE_OP_ERROR (file, "fwrite");
913         goto failure;
914     }
915     fclose (fp);
916     gpgme_data_release (cipher);
917     return 0;
918
919 failure:
920     if (fp) 
921         fclose (fp);
922     gpgme_data_release (header);
923     gpgme_data_release (plain);
924     gpgme_data_release (cipher);
925     gpgme_recipients_release (rset);
926     g_free (boundary);
927     return -1; /* error */
928 }
929
930 /* 
931  * plain contains an entire mime object.  Sign it and return an
932  * GpgmeData object with the signature of it or NULL in case of error.
933  */
934 static GpgmeData
935 pgp_sign (GpgmeData plain, GSList *key_list)
936 {
937     GSList *p;
938     GpgmeCtx ctx = NULL;
939     GpgmeError err;
940     GpgmeData sig = NULL;
941     struct passphrase_cb_info_s info;
942
943     memset (&info, 0, sizeof info);
944
945     err = gpgme_new (&ctx);
946     if (err)
947         goto leave;
948     err = gpgme_data_new (&sig);
949     if (err)
950         goto leave;
951
952     if (!getenv("GPG_AGENT_INFO")) {
953         info.c = ctx;
954         gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
955     }
956     gpgme_set_textmode (ctx, 1);
957     gpgme_set_armor (ctx, 1);
958     gpgme_signers_clear (ctx);
959     for (p = key_list; p != NULL; p = p->next) {
960         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
961         if (err)
962             goto leave;
963     }
964     for (p = key_list; p != NULL; p = p->next)
965         gpgme_key_unref ((GpgmeKey) p->data);
966     g_slist_free (key_list);
967
968     if (err)
969         goto leave;
970     err = gpgme_op_sign (ctx, plain, sig, GPGME_SIG_MODE_DETACH);
971
972 leave:
973     if (err) {
974         debug_print ("signing failed: %s\n", gpgme_strerror (err));
975         gpgme_data_release (sig);
976         sig = NULL;
977     }
978     else {
979         debug_print ("signing succeeded\n");
980     }
981
982     gpgme_release (ctx);
983     return sig;
984 }
985
986 /*
987  * Sign the file and replace its content with the signed one.
988  */
989 int
990 rfc2015_sign (const char *file, GSList *key_list)
991 {
992     FILE *fp = NULL;
993     char buf[BUFFSIZE];
994     int i, clineidx, saved_last;
995     char *clines[3] = {NULL};
996     GpgmeError err;
997     GpgmeData header = NULL;
998     GpgmeData plain = NULL;
999     GpgmeData sigdata = NULL;
1000     size_t nread;
1001     int mime_version_seen = 0;
1002     char *boundary = create_boundary ();
1003
1004     /* Open the source file */
1005     if ((fp = fopen(file, "rb")) == NULL) {
1006         FILE_OP_ERROR(file, "fopen");
1007         goto failure;
1008     }
1009
1010     err = gpgme_data_new (&header);
1011     if (!err)
1012         err = gpgme_data_new (&plain);
1013     if (err) {
1014         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1015         goto failure;
1016     }
1017
1018     /* get the content header lines from the source */
1019     clineidx = 0;
1020     saved_last = 0;
1021     while (!err && fgets(buf, sizeof(buf), fp)) {
1022         /* fixme: check for overlong lines */
1023         if (headerp (buf, content_names)) {
1024             if (clineidx >= DIM (clines)) {
1025                 debug_print ("rfc2015_sign: too many content lines\n");
1026                 goto failure;
1027             }
1028             clines[clineidx++] = g_strdup (buf);
1029             saved_last = 1;
1030             continue;
1031         }
1032         if (saved_last) {
1033             saved_last = 0;
1034             if (*buf == ' ' || *buf == '\t') {
1035                 char *last = clines[clineidx - 1];
1036                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1037                 g_free (last);
1038                 continue;
1039             }
1040         }
1041
1042         if (headerp (buf, mime_version_name)) 
1043             mime_version_seen = 1;
1044
1045         if (buf[0] == '\r' || buf[0] == '\n')
1046             break;
1047         err = gpgme_data_write (header, buf, strlen (buf));
1048     }
1049     if (ferror (fp)) {
1050         FILE_OP_ERROR (file, "fgets");
1051         goto failure;
1052     }
1053
1054     /* write them to the temp data and add the rest of the message */
1055     for (i = 0; !err && i < clineidx; i++) {
1056         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1057     }
1058     if (!err)
1059         err = gpgme_data_write (plain, "\r\n", 2 );
1060     while (!err && fgets(buf, sizeof(buf), fp)) {
1061         err = gpgme_data_write (plain, buf, strlen (buf));
1062     }
1063     if (ferror (fp)) {
1064         FILE_OP_ERROR (file, "fgets");
1065         goto failure;
1066     }
1067     if (err) {
1068         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1069         goto failure;
1070     }
1071
1072     sigdata = pgp_sign (plain, key_list);
1073     if (!sigdata) 
1074         goto failure;
1075
1076     /* we have the signed message available in sigdata and now we are
1077      * going to rewrite the original file. To be sure that file has
1078      * been truncated we use an approach which should work everywhere:
1079      * close the file and then reopen it for writing. */
1080     if (fclose (fp)) {
1081         FILE_OP_ERROR(file, "fclose");
1082         goto failure;
1083     }
1084     if ((fp = fopen(file, "wb")) == NULL) {
1085         FILE_OP_ERROR(file, "fopen");
1086         goto failure;
1087     }
1088
1089     /* Write the rfc822 header and add new content lines */
1090     err = gpgme_data_rewind (header);
1091     if (err)
1092         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1093     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1094         fwrite (buf, nread, 1, fp);
1095     }
1096     if (err != GPGME_EOF) {
1097         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1098         goto failure;
1099     }
1100     if (ferror (fp)) {
1101         FILE_OP_ERROR (file, "fwrite");
1102         goto failure;
1103     }
1104     gpgme_data_release (header);
1105     header = NULL;
1106
1107     if (!mime_version_seen) 
1108         fputs ("MIME-Version: 1\r\n", fp);
1109     fprintf (fp, "Content-Type: multipart/signed; "
1110                  "protocol=\"application/pgp-signature\";\r\n"
1111                  " boundary=\"%s\"\r\n", boundary );
1112
1113     /* Part 1: signed material */
1114     fprintf (fp, "\r\n--%s\r\n", boundary);
1115     err = gpgme_data_rewind (plain);
1116     if (err) {
1117         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1118                    gpgme_strerror (err));
1119         goto failure;
1120     }
1121     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1122         fwrite (buf, nread, 1, fp);   
1123     }
1124     if (err != GPGME_EOF) {
1125         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1126         goto failure;
1127     }
1128
1129     /* Part 2: signature */
1130     fprintf (fp, "\r\n--%s\r\n", boundary);
1131     fputs ("Content-Type: application/pgp-signature\r\n"
1132            "\r\n", fp);
1133
1134     err = gpgme_data_rewind (sigdata);
1135     if (err) {
1136         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1137                    gpgme_strerror (err));
1138         goto failure;
1139     }
1140
1141     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1142         fwrite (buf, nread, 1, fp);
1143     }
1144     if (err != GPGME_EOF) {
1145         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1146         goto failure;
1147     }
1148
1149     /* Final boundary */
1150     fprintf (fp, "\r\n--%s--\r\n\r\n", boundary);
1151     fflush (fp);
1152     if (ferror (fp)) {
1153         FILE_OP_ERROR (file, "fwrite");
1154         goto failure;
1155     }
1156     fclose (fp);
1157     gpgme_data_release (header);
1158     gpgme_data_release (plain);
1159     gpgme_data_release (sigdata);
1160     g_free (boundary);
1161     return 0;
1162
1163 failure:
1164     if (fp) 
1165         fclose (fp);
1166     gpgme_data_release (header);
1167     gpgme_data_release (plain);
1168     gpgme_data_release (sigdata);
1169     g_free (boundary);
1170     return -1; /* error */
1171 }
1172
1173
1174 /****************
1175  * Create a new boundary in a way that it is very unlikely that this
1176  * will occur in the following text.  It would be easy to ensure
1177  * uniqueness if everything is either quoted-printable or base64
1178  * encoded (note that conversion is allowed), but because MIME bodies
1179  * may be nested, it may happen that the same boundary has already
1180  * been used. We avoid scanning the message for conflicts and hope the
1181  * best.
1182  *
1183  *   boundary := 0*69<bchars> bcharsnospace
1184  *   bchars := bcharsnospace / " "
1185  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
1186  *                    "+" / "_" / "," / "-" / "." /
1187  *                    "/" / ":" / "=" / "?"  
1188  */
1189
1190 static char *
1191 create_boundary (void)
1192 {
1193     static char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1194                         "abcdefghijklmnopqrstuvwxyz"
1195                         "1234567890'()+_,./:=?";
1196     char buf[17];
1197     int i, equal;
1198     int pid;
1199
1200     pid = getpid();
1201
1202     /* We make the boundary depend on the pid, so that all running
1203      * processed generate different values even when they have been
1204      * started within the same second and srand48(time(NULL)) has been
1205      * used.  I can't see whether this is really an advantage but it
1206      * doesn't do any harm.
1207      */
1208     equal = -1;
1209     for(i = 0; i < sizeof(buf) - 1; i++) {
1210         buf[i] = tbl[(lrand48() ^ pid) % (sizeof(tbl) - 1)]; /* fill with random */
1211         if(buf[i] == '=' && equal == -1)
1212             equal = i;
1213     }
1214     buf[i] = 0;
1215
1216     /* now make sure that we do have the sequence "=." in it which cannot
1217      * be matched by quoted-printable or base64 encoding */
1218     if(equal != -1 && (equal+1) < i)
1219         buf[equal+1] = '.';
1220     else {
1221         buf[0] = '=';
1222         buf[1] = '.';
1223     }
1224
1225     return g_strdup(buf);
1226 }
1227
1228 #endif /* USE_GPGME */