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