2004-08-23 [colin] 0.9.12cvs78.1
[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 "rfc2015.h"
48 #include "alertpanel.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 #if 0
65 static void dump_mimeinfo (const char *text, MimeInfo *x)
66 {
67     debug_print ("MimeInfo[%s] %p  level=%d\n",
68                text, x, x? x->level:0 );
69     if (!x)
70         return;
71
72     debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
73                x->encoding, x->encoding_type, x->mime_type );
74     debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
75                x->content_type, x->charset, x->name, x->boundary );
76     debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
77                x->content_disposition, x->filename, x->fpos, x->size,
78                x->level );
79     dump_mimeinfo (".main", x->main );
80     dump_mimeinfo (".sub", x->sub );
81     dump_mimeinfo (".next", x->next );
82     debug_print ("MimeInfo[.parent] %p\n", x ); 
83     dump_mimeinfo (".children", x->children );
84     dump_mimeinfo (".plaintext", x->plaintext );
85 }
86
87 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
88 {
89     unsigned int size = mimeinfo->size;
90     int c;
91
92     if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
93         debug_print ("dump_part: fseek error\n");
94         return;
95     }
96
97     debug_print ("--- begin dump_part ----\n");
98     while (size-- && (c = getc (fp)) != EOF) 
99         putc (c, stderr);
100     if (ferror (fp))
101         debug_print ("dump_part: read error\n");
102     debug_print ("--- end dump_part ----\n");
103 }
104 #endif
105
106 void
107 rfc2015_secure_remove (const char *fname)
108 {
109     if (!fname)
110         return;
111     /* fixme: overwrite the file first */
112     remove (fname);
113 }
114
115
116
117
118 static int
119 name_cmp(const char *a, const char *b)
120 {
121     for( ; *a && *b; a++, b++) {
122         if(*a != *b
123            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
124             return 1;
125     }
126
127     return *a != *b;
128 }
129
130 static int
131 headerp(char *p, char **names)
132 {
133     int i, c;
134     char *p2;
135
136     p2 = strchr(p, ':');
137     if(!p2 || p == p2) {
138         return 0;
139     }
140     if(p2[-1] == ' ' || p2[-1] == '\t') {
141         return 0;
142     }
143
144     if(!names[0])
145         return 1;  
146
147     c = *p2;
148     *p2 = 0;
149     for(i = 0 ; names[i] != NULL; i++) {
150         if(!name_cmp (names[i], p))
151             break;
152     }
153     *p2 = c;
154
155     return names[i] != NULL;
156 }
157
158 /*
159  * plain contains an entire mime object.
160  * Encrypt it and return an GpgmeData object with the encrypted version of
161  * the file or NULL in case of error.
162  */
163 static GpgmeData
164 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
165 {
166     GpgmeCtx ctx = NULL;
167     GpgmeError err;
168     GpgmeData cipher = NULL;
169
170     err = gpgme_new (&ctx);
171     if (!err)
172         err = gpgme_data_new (&cipher);
173     if (!err) {
174         gpgme_set_armor (ctx, 1);
175         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
176     }
177
178     if (err) {
179         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
180         gpgme_data_release (cipher);
181         cipher = NULL;
182     }
183     else {
184         debug_print ("** encryption succeeded\n");
185     }
186
187     gpgme_release (ctx);
188     return cipher;
189 }
190
191 /*
192  * Create and return a list of keys matching a key id
193  */
194
195 GSList *rfc2015_create_signers_list (const char *keyid)
196 {
197         GSList *key_list = NULL;
198         GpgmeCtx list_ctx = NULL;
199         GSList *p;
200         GpgmeError err;
201         GpgmeKey key;
202
203         err = gpgme_new (&list_ctx);
204         if (err)
205                 goto leave;
206         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
207         if (err)
208                 goto leave;
209         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
210                 key_list = g_slist_append (key_list, key);
211         }
212         if (err != GPGME_EOF)
213                 goto leave;
214         err = 0;
215         if (key_list == NULL) {
216                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
217         }
218
219 leave:
220         if (err) {
221                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
222                 for (p = key_list; p != NULL; p = p->next)
223                         gpgme_key_unref ((GpgmeKey) p->data);
224                 g_slist_free (key_list);
225         }
226         if (list_ctx)
227                 gpgme_release (list_ctx);
228         return err ? NULL : key_list;
229 }
230
231 /*
232  * Encrypt the file by extracting all recipients and finding the
233  * encryption keys for all of them.  The file content is then replaced
234  * by the encrypted one.  */
235 int
236 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored,
237                  const gchar *out_codeset)
238 {
239     FILE *fp = NULL;
240     char buf[BUFFSIZE];
241     int i, clineidx, saved_last;
242     char *clines[3] = {NULL};
243     GpgmeError err;
244     GpgmeData header = NULL;
245     GpgmeData plain = NULL;
246     GpgmeData cipher = NULL;
247     GpgmeRecipients rset = NULL;
248     size_t nread;
249     int mime_version_seen = 0;
250     char *boundary;
251
252     boundary = generate_mime_boundary ("Encrypt");
253
254     /* Create the list of recipients */
255     rset = gpgmegtk_recipient_selection (recp_list);
256     if (!rset) {
257         debug_print ("error creating recipient list\n" );
258         goto failure;
259     }
260
261     /* Open the source file */
262     if ((fp = fopen(file, "rb")) == NULL) {
263         FILE_OP_ERROR(file, "fopen");
264         goto failure;
265     }
266
267     err = gpgme_data_new (&header);
268     if (!err)
269         err = gpgme_data_new (&plain);
270     if (err) {
271         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
272         goto failure;
273     }
274
275     /* get the content header lines from the source */
276     clineidx = 0;
277     saved_last = 0;
278     while (!err && fgets(buf, sizeof(buf), fp)) {
279         /* fixme: check for overlong lines */
280         if (headerp (buf, content_names)) {
281             if (clineidx >= DIM (clines)) {
282                 debug_print ("rfc2015_encrypt: too many content lines\n");
283                 goto failure;
284             }
285             clines[clineidx++] = g_strdup (buf);
286             saved_last = 1;
287             continue;
288         }
289
290         if (saved_last) {
291             if (*buf == ' ' || *buf == '\t') {
292                 char *last = clines[clineidx - 1];
293                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
294                 g_free (last);
295                 continue;
296             }
297             saved_last = 0;
298         }
299
300         if (headerp (buf, mime_version_name)) 
301             mime_version_seen = 1;
302
303         if (buf[0] == '\r' || buf[0] == '\n')
304             break;
305         
306        err = gpgme_data_write (header, buf, strlen (buf));
307     }
308     if (ferror (fp)) {
309         FILE_OP_ERROR (file, "fgets");
310         goto failure;
311     }
312
313     /* write them to the temp data and add the rest of the message */
314     for (i = 0; !err && i < clineidx; i++) {
315         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
316         if (!ascii_armored)
317                 err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
318     }
319     if (!err && !ascii_armored)
320         err = gpgme_data_write (plain, "\r\n", 2);
321
322     while (!err && fgets(buf, sizeof(buf), fp)) {
323         err = gpgme_data_write (plain, buf, strlen (buf));
324     }
325     if (ferror (fp)) {
326         FILE_OP_ERROR (file, "fgets");
327         goto failure;
328     }
329     if (err) {
330         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
331         goto failure;
332     }
333
334     cipher = pgp_encrypt (plain, rset);
335     gpgme_data_release (plain); plain = NULL;
336     gpgme_recipients_release (rset); rset = NULL;
337     if (!cipher)
338         goto failure;
339
340     /* we have the encrypted message available in cipher and now we
341      * are going to rewrite the source file. To be sure that file has
342      * been truncated we use an approach which should work everywhere:
343      * close the file and then reopen it for writing. It is important
344      * that this works, otherwise it may happen that parts of the
345      * plaintext are still in the file (The encrypted stuff is, due to
346      * compression, usually shorter than the plaintext). 
347      * 
348      * Yes, there is a race condition here, but everyone, who is so
349      * stupid to store the temp file with the plaintext in a public
350      * directory has to live with this anyway. */
351     if (fclose (fp)) {
352         FILE_OP_ERROR(file, "fclose");
353         goto failure;
354     }
355     if ((fp = fopen(file, "wb")) == NULL) {
356         FILE_OP_ERROR(file, "fopen");
357         goto failure;
358     }
359
360     /* Write the header, append new content lines, part 1 and part 2 header */
361     err = gpgme_data_rewind (header);
362     if (err) {
363         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
364         goto failure;
365     }
366     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
367         fwrite (buf, nread, 1, fp);
368     }
369     if (err != GPGME_EOF) {
370         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
371         goto failure;
372     }
373     if (ferror (fp)) {
374         FILE_OP_ERROR (file, "fwrite");
375         goto failure;
376     }
377     gpgme_data_release (header); header = NULL;
378     
379     if (!mime_version_seen) 
380         fputs ("MIME-Version: 1\r\n", fp);
381
382     if (ascii_armored) {
383         fprintf(fp, 
384             "Content-Type: text/plain; charset=%s\r\n"
385             "Content-Disposition: inline\r\n"  
386             "Content-Transfer-Encoding: 8bit\r\n"  
387             "\r\n", out_codeset ? out_codeset : CS_US_ASCII);
388     } else {
389         fprintf (fp,
390                 "Content-Type: multipart/encrypted;"
391                 " protocol=\"application/pgp-encrypted\";\r\n"
392                 " boundary=\"%s\"\r\n"
393                 "\r\n"
394                 "--%s\r\n"
395                 "Content-Type: application/pgp-encrypted\r\n"
396                 "\r\n"
397                 "Version: 1\r\n"
398                 "\r\n"
399                 "--%s\r\n"
400                 "Content-Type: application/octet-stream\r\n"
401                 "\r\n",
402                 boundary, boundary, boundary);
403     }
404
405     /* append the encrypted stuff */
406     err = gpgme_data_rewind (cipher);
407     if (err) {
408         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
409                    gpgme_strerror (err));
410         goto failure;
411     }
412
413     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
414         fwrite (buf, nread, 1, fp);
415     }
416     if (err != GPGME_EOF) {
417         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
418         goto failure;
419     }
420
421     /* and the final boundary */
422     if (!ascii_armored) {
423         fprintf (fp,
424                  "\r\n"
425                  "--%s--\r\n",
426                  boundary);
427     }
428     fflush (fp);
429     if (ferror (fp)) {
430         FILE_OP_ERROR (file, "fwrite");
431         goto failure;
432     }
433     fclose (fp);
434     gpgme_data_release (cipher);
435     return 0;
436
437 failure:
438     if (fp) 
439         fclose (fp);
440     gpgme_data_release (header);
441     gpgme_data_release (plain);
442     gpgme_data_release (cipher);
443     gpgme_recipients_release (rset);
444     g_free (boundary);
445     return -1; /* error */
446 }
447
448 /* 
449  * plain contains an entire mime object.  Sign it and return an
450  * GpgmeData object with the signature of it or NULL in case of error.
451  * r_siginfo returns an XML object with information about the signature.
452  */
453 static GpgmeData
454 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
455           char **r_siginfo)
456 {
457     GSList *p;
458     GpgmeCtx ctx = NULL;
459     GpgmeError err;
460     GpgmeData sig = NULL;
461     struct passphrase_cb_info_s info;
462
463     *r_siginfo = NULL;
464     memset (&info, 0, sizeof info);
465
466     err = gpgme_new (&ctx);
467     if (err)
468         goto leave;
469     err = gpgme_data_new (&sig);
470     if (err)
471         goto leave;
472
473     if (!getenv("GPG_AGENT_INFO")) {
474         info.c = ctx;
475         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
476     }
477     gpgme_set_textmode (ctx, 1);
478     gpgme_set_armor (ctx, 1);
479     gpgme_signers_clear (ctx);
480     for (p = key_list; p != NULL; p = p->next) {
481         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
482         if (err)
483             goto leave;
484     }
485     for (p = key_list; p != NULL; p = p->next)
486         gpgme_key_unref ((GpgmeKey) p->data);
487     g_slist_free (key_list);
488
489     if (err)
490         goto leave;
491     err = gpgme_op_sign
492         (ctx, plain, sig,
493          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
494     if (!err)
495         *r_siginfo = gpgme_get_op_info (ctx, 0);
496
497 leave:
498     if (err) {
499         gpgmegtk_free_passphrase();
500         debug_print ("signing failed: %s\n", gpgme_strerror (err));
501         gpgme_data_release (sig);
502         sig = NULL;
503     }
504     else {
505         debug_print ("signing succeeded\n");
506     }
507
508     gpgme_release (ctx);
509     return sig;
510 }
511
512 /*
513  * Find TAG in XML and return a pointer into xml set just behind the
514  * closing angle.  Return NULL if not found. 
515  */
516 static const char *
517 find_xml_tag (const char *xml, const char *tag)
518 {
519     int taglen = strlen (tag);
520     const char *s = xml;
521  
522     while ( (s = strchr (s, '<')) ) {
523         s++;
524         if (!strncmp (s, tag, taglen)) {
525             const char *s2 = s + taglen;
526             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
527                 /* found */
528                 while (*s2 && *s2 != '>') /* skip attributes */
529                     s2++;
530                 /* fixme: do need to handle angles inside attribute vallues? */
531                 return *s2? (s2+1):NULL;
532             }
533         }
534         while (*s && *s != '>') /* skip to end of tag */
535             s++;
536     }
537     return NULL;
538 }
539
540
541 /*
542  * Extract the micalg from an GnupgOperationInfo XML container.
543  */
544 static char *
545 extract_micalg (char *xml)
546 {
547     const char *s;
548
549     s = find_xml_tag (xml, "GnupgOperationInfo");
550     if (s) {
551         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
552         s = find_xml_tag (s, "signature");
553         if (s && s_end && s < s_end) {
554             const char *s_end2 = find_xml_tag (s, "/signature");
555             if (s_end2 && s_end2 < s_end) {
556                 s = find_xml_tag (s, "micalg");
557                 if (s && s < s_end2) {
558                     s_end = strchr (s, '<');
559                     if (s_end) {
560                         char *p = g_malloc (s_end - s + 1);
561                         memcpy (p, s, s_end - s);
562                         p[s_end-s] = 0;
563                         return p;
564                     }
565                 }
566             }
567         }
568     }
569     return NULL;
570 }
571
572
573 /*
574  * Sign the file and replace its content with the signed one.
575  */
576 int
577 rfc2015_sign (const char *file, GSList *key_list)
578 {
579     FILE *fp = NULL;
580     char buf[BUFFSIZE];
581     int i, clineidx, saved_last;
582     char *clines[3] = {NULL};
583     GpgmeError err;
584     GpgmeData header = NULL;
585     GpgmeData plain = NULL;
586     GpgmeData sigdata = NULL;
587     size_t nread;
588     int mime_version_seen = 0;
589     char *boundary;
590     char *micalg = NULL;
591     char *siginfo;
592
593     boundary = generate_mime_boundary ("Signature");
594
595     /* Open the source file */
596     if ((fp = fopen(file, "rb")) == NULL) {
597         FILE_OP_ERROR(file, "fopen");
598         goto failure;
599     }
600
601     err = gpgme_data_new (&header);
602     if (!err)
603         err = gpgme_data_new (&plain);
604     if (err) {
605         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
606         goto failure;
607     }
608
609     /* get the content header lines from the source */
610     clineidx = 0;
611     saved_last = 0;
612     while (!err && fgets(buf, sizeof(buf), fp)) {
613         /* fixme: check for overlong lines */
614         if (headerp (buf, content_names)) {
615             if (clineidx >= DIM (clines)) {
616                 debug_print ("rfc2015_sign: too many content lines\n");
617                 goto failure;
618             }
619             clines[clineidx++] = g_strdup (buf);
620             saved_last = 1;
621             continue;
622         }
623         if (saved_last) {
624             if (*buf == ' ' || *buf == '\t') {
625                 char *last = clines[clineidx - 1];
626                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
627                 g_free (last);
628                 continue;
629             }
630             saved_last = 0;
631         }
632
633         if (headerp (buf, mime_version_name)) 
634             mime_version_seen = 1;
635
636         if (buf[0] == '\r' || buf[0] == '\n')
637             break;
638         err = gpgme_data_write (header, buf, strlen (buf));
639     }
640     if (ferror (fp)) {
641         FILE_OP_ERROR (file, "fgets");
642         goto failure;
643     }
644
645     /* write them to the temp data and add the rest of the message */
646     for (i = 0; !err && i < clineidx; i++) {
647         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
648     }
649     if (!err)
650         err = gpgme_data_write (plain, "\r\n", 2 );
651     while (!err && fgets(buf, sizeof(buf), fp)) {
652         err = gpgme_data_write (plain, buf, strlen (buf));
653     }
654     if (ferror (fp)) {
655         FILE_OP_ERROR (file, "fgets");
656         goto failure;
657     }
658     if (err) {
659         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
660         goto failure;
661     }
662
663     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
664     if (siginfo) {
665         micalg = extract_micalg (siginfo);
666         free (siginfo);
667     }
668     if (!sigdata) 
669         goto failure;
670
671     /* we have the signed message available in sigdata and now we are
672      * going to rewrite the original file. To be sure that file has
673      * been truncated we use an approach which should work everywhere:
674      * close the file and then reopen it for writing. */
675     if (fclose (fp)) {
676         FILE_OP_ERROR(file, "fclose");
677         goto failure;
678     }
679     if ((fp = fopen(file, "wb")) == NULL) {
680         FILE_OP_ERROR(file, "fopen");
681         goto failure;
682     }
683
684     /* Write the rfc822 header and add new content lines */
685     err = gpgme_data_rewind (header);
686     if (err)
687         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
688     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
689         fwrite (buf, nread, 1, fp);
690     }
691     if (err != GPGME_EOF) {
692         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
693         goto failure;
694     }
695     if (ferror (fp)) {
696         FILE_OP_ERROR (file, "fwrite");
697         goto failure;
698     }
699     gpgme_data_release (header);
700     header = NULL;
701
702     if (!mime_version_seen) 
703         fputs ("MIME-Version: 1.0\r\n", fp);
704     fprintf (fp, "Content-Type: multipart/signed; "
705              "protocol=\"application/pgp-signature\";\r\n");
706     if (micalg)
707         fprintf (fp, " micalg=\"%s\";\r\n", micalg);
708     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
709
710     /* Part 1: signed material */
711     fprintf (fp, "\r\n"
712                  "--%s\r\n",
713                  boundary);
714     err = gpgme_data_rewind (plain);
715     if (err) {
716         debug_print ("gpgme_data_rewind on plain failed: %s\n",
717                    gpgme_strerror (err));
718         goto failure;
719     }
720     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
721         fwrite (buf, nread, 1, fp);   
722     }
723     if (err != GPGME_EOF) {
724         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
725         goto failure;
726     }
727
728     /* Part 2: signature */
729     fprintf (fp, "\r\n"
730                  "--%s\r\n",
731                  boundary);
732     fputs ("Content-Type: application/pgp-signature\r\n"
733            "\r\n", fp);
734
735     err = gpgme_data_rewind (sigdata);
736     if (err) {
737         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
738                    gpgme_strerror (err));
739         goto failure;
740     }
741
742     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
743         fwrite (buf, nread, 1, fp);
744     }
745     if (err != GPGME_EOF) {
746         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
747         goto failure;
748     }
749
750     /* Final boundary */
751     fprintf (fp, "\r\n"
752                  "--%s--\r\n",
753                  boundary);
754     fflush (fp);
755     if (ferror (fp)) {
756         FILE_OP_ERROR (file, "fwrite");
757         goto failure;
758     }
759     fclose (fp);
760     gpgme_data_release (header);
761     gpgme_data_release (plain);
762     gpgme_data_release (sigdata);
763     g_free (boundary);
764     g_free (micalg);
765     return 0;
766
767 failure:
768     if (fp) 
769         fclose (fp);
770     gpgme_data_release (header);
771     gpgme_data_release (plain);
772     gpgme_data_release (sigdata);
773     g_free (boundary);
774     g_free (micalg);
775     return -1; /* error */
776 }
777
778
779 /*
780  * Sign the file with clear text and replace its content with the signed one.
781  */
782 gint
783 rfc2015_clearsign (const gchar *file, GSList *key_list)
784 {
785     FILE *fp;
786     gchar buf[BUFFSIZE];
787     GpgmeError err;
788     GpgmeData text = NULL;
789     GpgmeData sigdata = NULL;
790     size_t nread;
791     gchar *siginfo;
792
793     if ((fp = fopen(file, "rb")) == NULL) {
794         FILE_OP_ERROR(file, "fopen");
795         goto failure;
796     }
797
798     err = gpgme_data_new(&text);
799     if (err) {
800         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
801         goto failure;
802     }
803
804     while (!err && fgets(buf, sizeof(buf), fp)) {
805         err = gpgme_data_write(text, 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     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
817     if (siginfo) {
818         g_free(siginfo);
819     }
820     if (!sigdata)
821         goto failure;
822
823     if (fclose(fp) == EOF) {
824         FILE_OP_ERROR(file, "fclose");
825         fp = NULL;
826         goto failure;
827     }
828     if ((fp = fopen(file, "wb")) == NULL) {
829         FILE_OP_ERROR(file, "fopen");
830         goto failure;
831     }
832
833     err = gpgme_data_rewind(sigdata);
834     if (err) {
835         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
836                     gpgme_strerror(err));
837         goto failure;
838     }
839
840     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
841         fwrite(buf, nread, 1, fp);
842     }
843     if (err != GPGME_EOF) {
844         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
845         goto failure;
846     }
847
848     if (fclose(fp) == EOF) {
849         FILE_OP_ERROR(file, "fclose");
850         fp = NULL;
851         goto failure;
852     }
853     gpgme_data_release(text);
854     gpgme_data_release(sigdata);
855     return 0;
856
857 failure:
858     if (fp)
859         fclose(fp);
860     gpgme_data_release(text);
861     gpgme_data_release(sigdata);
862     return -1;
863 }
864
865 #endif /* USE_GPGME */