0.9.6claws97
[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 {
238     FILE *fp = NULL;
239     char buf[BUFFSIZE];
240     int i, clineidx, saved_last;
241     char *clines[3] = {NULL};
242     GpgmeError err;
243     GpgmeData header = NULL;
244     GpgmeData plain = NULL;
245     GpgmeData cipher = NULL;
246     GpgmeRecipients rset = NULL;
247     size_t nread;
248     int mime_version_seen = 0;
249     char *boundary;
250
251     boundary = generate_mime_boundary ("Encrypt");
252
253     /* Create the list of recipients */
254     rset = gpgmegtk_recipient_selection (recp_list);
255     if (!rset) {
256         debug_print ("error creating recipient list\n" );
257         goto failure;
258     }
259
260     /* Open the source file */
261     if ((fp = fopen(file, "rb")) == NULL) {
262         FILE_OP_ERROR(file, "fopen");
263         goto failure;
264     }
265
266     err = gpgme_data_new (&header);
267     if (!err)
268         err = gpgme_data_new (&plain);
269     if (err) {
270         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
271         goto failure;
272     }
273
274     /* get the content header lines from the source */
275     clineidx = 0;
276     saved_last = 0;
277     while (!err && fgets(buf, sizeof(buf), fp)) {
278         /* fixme: check for overlong lines */
279         if (headerp (buf, content_names)) {
280             if (clineidx >= DIM (clines)) {
281                 debug_print ("rfc2015_encrypt: too many content lines\n");
282                 goto failure;
283             }
284             clines[clineidx++] = g_strdup (buf);
285             saved_last = 1;
286             continue;
287         }
288         if (saved_last) {
289             if (*buf == ' ' || *buf == '\t') {
290                 char *last = clines[clineidx - 1];
291                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
292                 g_free (last);
293                 continue;
294             }
295             saved_last = 0;
296         }
297
298         if (headerp (buf, mime_version_name)) 
299             mime_version_seen = 1;
300
301         if (buf[0] == '\r' || buf[0] == '\n')
302             break;
303         err = gpgme_data_write (header, buf, strlen (buf));
304     }
305     if (ferror (fp)) {
306         FILE_OP_ERROR (file, "fgets");
307         goto failure;
308     }
309
310     /* write them to the temp data and add the rest of the message */
311     for (i = 0; !err && i < clineidx; i++) {
312         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
313         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
314     }
315     if (!err)
316         err = gpgme_data_write (plain, "\r\n", 2);
317     while (!err && fgets(buf, sizeof(buf), fp)) {
318         err = gpgme_data_write (plain, buf, strlen (buf));
319     }
320     if (ferror (fp)) {
321         FILE_OP_ERROR (file, "fgets");
322         goto failure;
323     }
324     if (err) {
325         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
326         goto failure;
327     }
328
329     cipher = pgp_encrypt (plain, rset);
330     gpgme_data_release (plain); plain = NULL;
331     gpgme_recipients_release (rset); rset = NULL;
332     if (!cipher)
333         goto failure;
334
335     /* we have the encrypted message available in cipher and now we
336      * are going to rewrite the source file. To be sure that file has
337      * been truncated we use an approach which should work everywhere:
338      * close the file and then reopen it for writing. It is important
339      * that this works, otherwise it may happen that parts of the
340      * plaintext are still in the file (The encrypted stuff is, due to
341      * compression, usually shorter than the plaintext). 
342      * 
343      * Yes, there is a race condition here, but everyone, who is so
344      * stupid to store the temp file with the plaintext in a public
345      * directory has to live with this anyway. */
346     if (fclose (fp)) {
347         FILE_OP_ERROR(file, "fclose");
348         goto failure;
349     }
350     if ((fp = fopen(file, "wb")) == NULL) {
351         FILE_OP_ERROR(file, "fopen");
352         goto failure;
353     }
354
355     /* Write the header, append new content lines, part 1 and part 2 header */
356     err = gpgme_data_rewind (header);
357     if (err) {
358         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
359         goto failure;
360     }
361     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
362         fwrite (buf, nread, 1, fp);
363     }
364     if (err != GPGME_EOF) {
365         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
366         goto failure;
367     }
368     if (ferror (fp)) {
369         FILE_OP_ERROR (file, "fwrite");
370         goto failure;
371     }
372     gpgme_data_release (header); header = NULL;
373     
374     if (!mime_version_seen) 
375         fputs ("MIME-Version: 1\r\n", fp);
376
377     if (ascii_armored) {
378         fprintf(fp, 
379             "Content-Type: text/plain; charset=US-ASCII\r\n"
380             "Content-Transfer-Encoding: 7bit\r\n"  
381             "\r\n");
382     } else {
383         fprintf (fp,
384                 "Content-Type: multipart/encrypted;"
385                 " protocol=\"application/pgp-encrypted\";\r\n"
386                 " boundary=\"%s\"\r\n"
387                 "\r\n"
388                 "--%s\r\n"
389                 "Content-Type: application/pgp-encrypted\r\n"
390                 "\r\n"
391                 "Version: 1\r\n"
392                 "\r\n"
393                 "--%s\r\n"
394                 "Content-Type: application/octet-stream\r\n"
395                 "\r\n",
396                 boundary, boundary, boundary);
397     }
398
399     /* append the encrypted stuff */
400     err = gpgme_data_rewind (cipher);
401     if (err) {
402         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
403                    gpgme_strerror (err));
404         goto failure;
405     }
406
407     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
408         fwrite (buf, nread, 1, fp);
409     }
410     if (err != GPGME_EOF) {
411         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
412         goto failure;
413     }
414
415     /* and the final boundary */
416     if (!ascii_armored) {
417         fprintf (fp,
418                  "\r\n"
419                  "--%s--\r\n",
420                  boundary);
421     }
422     fflush (fp);
423     if (ferror (fp)) {
424         FILE_OP_ERROR (file, "fwrite");
425         goto failure;
426     }
427     fclose (fp);
428     gpgme_data_release (cipher);
429     return 0;
430
431 failure:
432     if (fp) 
433         fclose (fp);
434     gpgme_data_release (header);
435     gpgme_data_release (plain);
436     gpgme_data_release (cipher);
437     gpgme_recipients_release (rset);
438     g_free (boundary);
439     return -1; /* error */
440 }
441
442 /* 
443  * plain contains an entire mime object.  Sign it and return an
444  * GpgmeData object with the signature of it or NULL in case of error.
445  * r_siginfo returns an XML object with information about the signature.
446  */
447 static GpgmeData
448 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
449           char **r_siginfo)
450 {
451     GSList *p;
452     GpgmeCtx ctx = NULL;
453     GpgmeError err;
454     GpgmeData sig = NULL;
455     struct passphrase_cb_info_s info;
456
457     *r_siginfo = NULL;
458     memset (&info, 0, sizeof info);
459
460     err = gpgme_new (&ctx);
461     if (err)
462         goto leave;
463     err = gpgme_data_new (&sig);
464     if (err)
465         goto leave;
466
467     if (!getenv("GPG_AGENT_INFO")) {
468         info.c = ctx;
469         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
470     }
471     gpgme_set_textmode (ctx, 1);
472     gpgme_set_armor (ctx, 1);
473     gpgme_signers_clear (ctx);
474     for (p = key_list; p != NULL; p = p->next) {
475         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
476         if (err)
477             goto leave;
478     }
479     for (p = key_list; p != NULL; p = p->next)
480         gpgme_key_unref ((GpgmeKey) p->data);
481     g_slist_free (key_list);
482
483     if (err)
484         goto leave;
485     err = gpgme_op_sign
486         (ctx, plain, sig,
487          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
488     if (!err)
489         *r_siginfo = gpgme_get_op_info (ctx, 0);
490
491 leave:
492     if (err) {
493         gpgmegtk_free_passphrase();
494         debug_print ("signing failed: %s\n", gpgme_strerror (err));
495         gpgme_data_release (sig);
496         sig = NULL;
497     }
498     else {
499         debug_print ("signing succeeded\n");
500     }
501
502     gpgme_release (ctx);
503     return sig;
504 }
505
506 /*
507  * Find TAG in XML and return a pointer into xml set just behind the
508  * closing angle.  Return NULL if not found. 
509  */
510 static const char *
511 find_xml_tag (const char *xml, const char *tag)
512 {
513     int taglen = strlen (tag);
514     const char *s = xml;
515  
516     while ( (s = strchr (s, '<')) ) {
517         s++;
518         if (!strncmp (s, tag, taglen)) {
519             const char *s2 = s + taglen;
520             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
521                 /* found */
522                 while (*s2 && *s2 != '>') /* skip attributes */
523                     s2++;
524                 /* fixme: do need to handle angles inside attribute vallues? */
525                 return *s2? (s2+1):NULL;
526             }
527         }
528         while (*s && *s != '>') /* skip to end of tag */
529             s++;
530     }
531     return NULL;
532 }
533
534
535 /*
536  * Extract the micalg from an GnupgOperationInfo XML container.
537  */
538 static char *
539 extract_micalg (char *xml)
540 {
541     const char *s;
542
543     s = find_xml_tag (xml, "GnupgOperationInfo");
544     if (s) {
545         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
546         s = find_xml_tag (s, "signature");
547         if (s && s_end && s < s_end) {
548             const char *s_end2 = find_xml_tag (s, "/signature");
549             if (s_end2 && s_end2 < s_end) {
550                 s = find_xml_tag (s, "micalg");
551                 if (s && s < s_end2) {
552                     s_end = strchr (s, '<');
553                     if (s_end) {
554                         char *p = g_malloc (s_end - s + 1);
555                         memcpy (p, s, s_end - s);
556                         p[s_end-s] = 0;
557                         return p;
558                     }
559                 }
560             }
561         }
562     }
563     return NULL;
564 }
565
566
567 /*
568  * Sign the file and replace its content with the signed one.
569  */
570 int
571 rfc2015_sign (const char *file, GSList *key_list)
572 {
573     FILE *fp = NULL;
574     char buf[BUFFSIZE];
575     int i, clineidx, saved_last;
576     char *clines[3] = {NULL};
577     GpgmeError err;
578     GpgmeData header = NULL;
579     GpgmeData plain = NULL;
580     GpgmeData sigdata = NULL;
581     size_t nread;
582     int mime_version_seen = 0;
583     char *boundary;
584     char *micalg = NULL;
585     char *siginfo;
586
587     boundary = generate_mime_boundary ("Signature");
588
589     /* Open the source file */
590     if ((fp = fopen(file, "rb")) == NULL) {
591         FILE_OP_ERROR(file, "fopen");
592         goto failure;
593     }
594
595     err = gpgme_data_new (&header);
596     if (!err)
597         err = gpgme_data_new (&plain);
598     if (err) {
599         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
600         goto failure;
601     }
602
603     /* get the content header lines from the source */
604     clineidx = 0;
605     saved_last = 0;
606     while (!err && fgets(buf, sizeof(buf), fp)) {
607         /* fixme: check for overlong lines */
608         if (headerp (buf, content_names)) {
609             if (clineidx >= DIM (clines)) {
610                 debug_print ("rfc2015_sign: too many content lines\n");
611                 goto failure;
612             }
613             clines[clineidx++] = g_strdup (buf);
614             saved_last = 1;
615             continue;
616         }
617         if (saved_last) {
618             if (*buf == ' ' || *buf == '\t') {
619                 char *last = clines[clineidx - 1];
620                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
621                 g_free (last);
622                 continue;
623             }
624             saved_last = 0;
625         }
626
627         if (headerp (buf, mime_version_name)) 
628             mime_version_seen = 1;
629
630         if (buf[0] == '\r' || buf[0] == '\n')
631             break;
632         err = gpgme_data_write (header, buf, strlen (buf));
633     }
634     if (ferror (fp)) {
635         FILE_OP_ERROR (file, "fgets");
636         goto failure;
637     }
638
639     /* write them to the temp data and add the rest of the message */
640     for (i = 0; !err && i < clineidx; i++) {
641         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
642     }
643     if (!err)
644         err = gpgme_data_write (plain, "\r\n", 2 );
645     while (!err && fgets(buf, sizeof(buf), fp)) {
646         err = gpgme_data_write (plain, buf, strlen (buf));
647     }
648     if (ferror (fp)) {
649         FILE_OP_ERROR (file, "fgets");
650         goto failure;
651     }
652     if (err) {
653         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
654         goto failure;
655     }
656
657     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
658     if (siginfo) {
659         micalg = extract_micalg (siginfo);
660         free (siginfo);
661     }
662     if (!sigdata) 
663         goto failure;
664
665     /* we have the signed message available in sigdata and now we are
666      * going to rewrite the original file. To be sure that file has
667      * been truncated we use an approach which should work everywhere:
668      * close the file and then reopen it for writing. */
669     if (fclose (fp)) {
670         FILE_OP_ERROR(file, "fclose");
671         goto failure;
672     }
673     if ((fp = fopen(file, "wb")) == NULL) {
674         FILE_OP_ERROR(file, "fopen");
675         goto failure;
676     }
677
678     /* Write the rfc822 header and add new content lines */
679     err = gpgme_data_rewind (header);
680     if (err)
681         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
682     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
683         fwrite (buf, nread, 1, fp);
684     }
685     if (err != GPGME_EOF) {
686         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
687         goto failure;
688     }
689     if (ferror (fp)) {
690         FILE_OP_ERROR (file, "fwrite");
691         goto failure;
692     }
693     gpgme_data_release (header);
694     header = NULL;
695
696     if (!mime_version_seen) 
697         fputs ("MIME-Version: 1.0\r\n", fp);
698     fprintf (fp, "Content-Type: multipart/signed; "
699              "protocol=\"application/pgp-signature\";\r\n");
700     if (micalg)
701         fprintf (fp, " micalg=\"%s\";\r\n", micalg);
702     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
703
704     /* Part 1: signed material */
705     fprintf (fp, "\r\n"
706                  "--%s\r\n",
707                  boundary);
708     err = gpgme_data_rewind (plain);
709     if (err) {
710         debug_print ("gpgme_data_rewind on plain failed: %s\n",
711                    gpgme_strerror (err));
712         goto failure;
713     }
714     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
715         fwrite (buf, nread, 1, fp);   
716     }
717     if (err != GPGME_EOF) {
718         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
719         goto failure;
720     }
721
722     /* Part 2: signature */
723     fprintf (fp, "\r\n"
724                  "--%s\r\n",
725                  boundary);
726     fputs ("Content-Type: application/pgp-signature\r\n"
727            "\r\n", fp);
728
729     err = gpgme_data_rewind (sigdata);
730     if (err) {
731         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
732                    gpgme_strerror (err));
733         goto failure;
734     }
735
736     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
737         fwrite (buf, nread, 1, fp);
738     }
739     if (err != GPGME_EOF) {
740         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
741         goto failure;
742     }
743
744     /* Final boundary */
745     fprintf (fp, "\r\n"
746                  "--%s--\r\n",
747                  boundary);
748     fflush (fp);
749     if (ferror (fp)) {
750         FILE_OP_ERROR (file, "fwrite");
751         goto failure;
752     }
753     fclose (fp);
754     gpgme_data_release (header);
755     gpgme_data_release (plain);
756     gpgme_data_release (sigdata);
757     g_free (boundary);
758     g_free (micalg);
759     return 0;
760
761 failure:
762     if (fp) 
763         fclose (fp);
764     gpgme_data_release (header);
765     gpgme_data_release (plain);
766     gpgme_data_release (sigdata);
767     g_free (boundary);
768     g_free (micalg);
769     return -1; /* error */
770 }
771
772
773 /*
774  * Sign the file with clear text and replace its content with the signed one.
775  */
776 gint
777 rfc2015_clearsign (const gchar *file, GSList *key_list)
778 {
779     FILE *fp;
780     gchar buf[BUFFSIZE];
781     GpgmeError err;
782     GpgmeData text = NULL;
783     GpgmeData sigdata = NULL;
784     size_t nread;
785     gchar *siginfo;
786
787     if ((fp = fopen(file, "rb")) == NULL) {
788         FILE_OP_ERROR(file, "fopen");
789         goto failure;
790     }
791
792     err = gpgme_data_new(&text);
793     if (err) {
794         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
795         goto failure;
796     }
797
798     while (!err && fgets(buf, sizeof(buf), fp)) {
799         err = gpgme_data_write(text, buf, strlen(buf));
800     }
801     if (ferror(fp)) {
802         FILE_OP_ERROR(file, "fgets");
803         goto failure;
804     }
805     if (err) {
806         debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
807         goto failure;
808     }
809
810     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
811     if (siginfo) {
812         g_free(siginfo);
813     }
814     if (!sigdata)
815         goto failure;
816
817     if (fclose(fp) == EOF) {
818         FILE_OP_ERROR(file, "fclose");
819         fp = NULL;
820         goto failure;
821     }
822     if ((fp = fopen(file, "wb")) == NULL) {
823         FILE_OP_ERROR(file, "fopen");
824         goto failure;
825     }
826
827     err = gpgme_data_rewind(sigdata);
828     if (err) {
829         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
830                     gpgme_strerror(err));
831         goto failure;
832     }
833
834     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
835         fwrite(buf, nread, 1, fp);
836     }
837     if (err != GPGME_EOF) {
838         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
839         goto failure;
840     }
841
842     if (fclose(fp) == EOF) {
843         FILE_OP_ERROR(file, "fclose");
844         fp = NULL;
845         goto failure;
846     }
847     gpgme_data_release(text);
848     gpgme_data_release(sigdata);
849     return 0;
850
851 failure:
852     if (fp)
853         fclose(fp);
854     gpgme_data_release(text);
855     gpgme_data_release(sigdata);
856     return -1;
857 }
858
859 #endif /* USE_GPGME */