0.9.5claws4
[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 static void sig_expiration_check        (GString        *str,
65                                          GpgmeCtx        ctx,
66                                          GpgmeKey        key, 
67                                          GpgmeSigStat    status,
68                                          int             idx);
69 static void sig_expired                 (GString        *str,
70                                          GpgmeCtx        ctx,
71                                          int             idx);
72 static void sig_key_expired             (GString        *str,
73                                          GpgmeKey        key,
74                                          int             idx);
75
76 #if 0
77 static void dump_mimeinfo (const char *text, MimeInfo *x)
78 {
79     debug_print ("MimeInfo[%s] %p  level=%d\n",
80                text, x, x? x->level:0 );
81     if (!x)
82         return;
83
84     debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
85                x->encoding, x->encoding_type, x->mime_type );
86     debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
87                x->content_type, x->charset, x->name, x->boundary );
88     debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
89                x->content_disposition, x->filename, x->fpos, x->size,
90                x->level );
91     dump_mimeinfo (".main", x->main );
92     dump_mimeinfo (".sub", x->sub );
93     dump_mimeinfo (".next", x->next );
94     debug_print ("MimeInfo[.parent] %p\n", x ); 
95     dump_mimeinfo (".children", x->children );
96     dump_mimeinfo (".plaintext", x->plaintext );
97 }
98
99 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
100 {
101     unsigned int size = mimeinfo->size;
102     int c;
103
104     if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
105         debug_print ("dump_part: fseek error\n");
106         return;
107     }
108
109     debug_print ("--- begin dump_part ----\n");
110     while (size-- && (c = getc (fp)) != EOF) 
111         putc (c, stderr);
112     if (ferror (fp))
113         debug_print ("dump_part: read error\n");
114     debug_print ("--- end dump_part ----\n");
115 }
116 #endif
117
118 void
119 rfc2015_disable_all (void)
120 {
121     /* FIXME: set a flag, so that we don't bother the user with failed
122      * gpgme messages */
123 }
124
125
126 void
127 rfc2015_secure_remove (const char *fname)
128 {
129     if (!fname)
130         return;
131     /* fixme: overwrite the file first */
132     remove (fname);
133 }
134
135
136 static const gchar *
137 sig_status_to_string (GpgmeSigStat status)
138 {
139     const gchar *result;
140
141     switch (status) {
142       case GPGME_SIG_STAT_NONE:
143         result = _("Oops: Signature not verified");
144         break;
145       case GPGME_SIG_STAT_NOSIG:
146         result = _("No signature found");
147         break;
148       case GPGME_SIG_STAT_GOOD:
149         result = _("Good signature");
150         break;
151       case GPGME_SIG_STAT_GOOD_EXP:     
152         result = _("Good signature but it has expired");
153         break;
154       case GPGME_SIG_STAT_GOOD_EXPKEY:
155         result = _("Good signature but the key has expired");
156         break;
157       case GPGME_SIG_STAT_BAD:
158         result = _("BAD signature");
159         break;
160       case GPGME_SIG_STAT_NOKEY:
161         result = _("No public key to verify the signature");
162         break;
163       case GPGME_SIG_STAT_ERROR:
164         result = _("Error verifying the signature");
165         break;
166       case GPGME_SIG_STAT_DIFF:
167         result = _("Different results for signatures");
168         break;
169       default:
170         result = _("Error: Unknown status");
171         break;
172     }
173
174     return result;
175 }
176
177 static const gchar *
178 sig_status_with_name (GpgmeSigStat status)
179 {
180     const gchar *result;
181
182     switch (status) {
183       case GPGME_SIG_STAT_NONE:
184         result = _("Oops: Signature not verified");
185         break;
186       case GPGME_SIG_STAT_NOSIG:
187         result = _("No signature found");
188         break;
189       case GPGME_SIG_STAT_GOOD:
190         result = _("Good signature from \"%s\"");
191         break;
192       case GPGME_SIG_STAT_GOOD_EXP:
193         result = _("Good signature from \"%s\" but it has expired");
194         break;
195       case GPGME_SIG_STAT_GOOD_EXPKEY:
196         result = _("Good signature from \"%s\" but the key has expired");
197         break;
198       case GPGME_SIG_STAT_BAD:
199         result = _("BAD signature from \"%s\"");
200         break;
201       case GPGME_SIG_STAT_NOKEY:
202         result = _("No public key to verify the signature");
203         break;
204       case GPGME_SIG_STAT_ERROR:
205         result = _("Error verifying the signature");
206         break;
207       case GPGME_SIG_STAT_DIFF:
208         result = _("Different results for signatures");
209         break;
210       default:
211         result = _("Error: Unknown status");
212         break;
213     }
214
215     return result;
216 }
217
218 static void
219 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
220                    GpgmeKey key, const gchar *fpr)
221 {
222         gint idx = 0;
223         const char *uid;
224
225         uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
226         if (uid == NULL) {
227                 g_string_sprintfa (str, "%s\n",
228                                    sig_status_to_string (status));
229                 if ((fpr != NULL) && (*fpr != '\0'))
230                         g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
231                 g_string_append (str, _("Cannot find user ID for this key."));
232                 sig_expiration_check(str, ctx, key, status, 0);
233                 return;
234         }
235         g_string_sprintfa (str, sig_status_with_name (status), uid);
236         g_string_append (str, "\n");
237
238         while (1) {
239                 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
240                                                  NULL, ++idx);
241                 if (uid == NULL)
242                         break;
243                 g_string_sprintfa (str, _("                aka \"%s\"\n"),
244                                    uid);
245         }
246         sig_expiration_check(str, ctx, key, status, 0);
247 }
248
249 static void
250 sig_expiration_check(GString *str, GpgmeCtx ctx, GpgmeKey key, 
251                      GpgmeSigStat status, int idx)
252 {
253         if (status == GPGME_SIG_STAT_GOOD_EXP)
254                 sig_expired(str, ctx, idx);
255         else if (status == GPGME_SIG_STAT_GOOD_EXPKEY)
256                 sig_key_expired(str, key, idx);
257 }
258
259 static void
260 sig_expired(GString *str, GpgmeCtx ctx, int idx)
261 {
262         unsigned long exp_time;
263         exp_time = gpgme_get_sig_ulong_attr(ctx, idx, GPGME_ATTR_EXPIRE, 0);
264         g_string_sprintfa(str, _("Signature expired %s"), ctime(&exp_time));    
265 }
266
267 static void
268 sig_key_expired(GString *str, GpgmeKey key, int idx)
269 {
270         unsigned long exp_time;
271         exp_time = gpgme_key_get_ulong_attr(key, GPGME_ATTR_EXPIRE, NULL, idx);
272         g_string_sprintfa(str, _("Key expired %s"), ctime(&exp_time));  
273 }
274
275 static gchar *
276 sig_status_full (GpgmeCtx ctx)
277 {
278         GString *str;
279         gint sig_idx = 0;
280         GpgmeError err;
281         GpgmeSigStat status;
282         GpgmeKey key;
283         const char *fpr;
284         time_t created;
285         struct tm *ctime_val;
286         char ctime_str[80];
287         gchar *retval;
288
289         str = g_string_new ("");
290
291         fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
292         while (fpr != NULL) {
293                 if (created != 0) {
294                         ctime_val = localtime (&created);
295                         strftime (ctime_str, sizeof (ctime_str), "%c", 
296                                   ctime_val);
297                         g_string_sprintfa (str,
298                                            _("Signature made at %s\n"),
299                                            ctime_str);
300                 }
301                 err = gpgme_get_sig_key (ctx, sig_idx, &key);
302                 if (err != 0) {
303                         g_string_sprintfa (str, "%s\n",
304                                            sig_status_to_string (status));
305                         if ((fpr != NULL) && (*fpr != '\0'))
306                                 g_string_sprintfa (str, 
307                                                    _("Key fingerprint: %s\n"),
308                                                    fpr);
309                 } else {
310                         sig_status_for_key (str, ctx, status, key, fpr);
311                         gpgme_key_unref (key);
312                 }
313                 g_string_append (str, "\n\n");
314
315                 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
316         }
317
318         retval = str->str;
319         g_string_free (str, FALSE);
320         return retval;
321 }
322
323 static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
324 {
325     GpgmeCtx ctx = NULL;
326     GpgmeError err;
327     GpgmeData sig = NULL, text = NULL;
328     GpgmeSigStat status = GPGME_SIG_STAT_NONE;
329     GpgmegtkSigStatus statuswindow = NULL;
330     const char *result = NULL;
331     gchar *tmp_file;
332     gint n_exclude_chars = 0;
333
334     if (prefs_common.gpg_signature_popup)
335         statuswindow = gpgmegtk_sig_status_create ();
336
337     err = gpgme_new (&ctx);
338     if (err) {
339         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
340         goto leave;
341     }
342
343     /* don't include the last empty line.
344        It does not belong to the signed text */
345     if (mimeinfo->children->size > 0) {
346         if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1,
347                   SEEK_SET) < 0) {
348             perror("fseek");
349             goto leave;
350         }
351         if (fgetc(fp) == '\n') {
352             n_exclude_chars++;
353             if (mimeinfo->children->size > 1) {
354                 if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2,
355                           SEEK_SET) < 0) {
356                     perror("fseek");
357                     goto leave;
358                 }
359                 if (fgetc(fp) == '\r')
360                     n_exclude_chars++;
361             }
362         }
363     }
364
365     /* canonicalize the file part. */
366     tmp_file = get_tmp_file();
367     if (copy_file_part(fp, mimeinfo->children->fpos,
368                        mimeinfo->children->size - n_exclude_chars,
369                        tmp_file) < 0) {
370         g_free(tmp_file);
371         goto leave;
372     }
373     if (canonicalize_file_replace(tmp_file) < 0) {
374         unlink(tmp_file);
375         g_free(tmp_file);
376         goto leave;
377     }
378
379     err = gpgme_data_new_from_file(&text, tmp_file, 1);
380
381     unlink(tmp_file);
382     g_free(tmp_file);
383
384     if (!err)
385         err = gpgme_data_new_from_filepart (&sig, NULL, fp,
386                                             partinfo->fpos, partinfo->size);
387     if (err) {
388         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
389                    gpgme_strerror (err));
390         goto leave;
391     }
392
393     err = gpgme_op_verify (ctx, sig, text, &status);
394     if (err)  {
395         debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
396         goto leave;
397     }
398
399     /* FIXME: check what the heck this sig_status_full stuff is.
400      * it should better go into sigstatus.c */
401     g_free (partinfo->sigstatus_full);
402     partinfo->sigstatus_full = sig_status_full (ctx);
403
404 leave:
405     result = gpgmegtk_sig_status_to_string(status);
406     debug_print("verification status: %s\n", result);
407     if (prefs_common.gpg_signature_popup)
408         gpgmegtk_sig_status_update (statuswindow, ctx);
409
410     g_free (partinfo->sigstatus);
411     partinfo->sigstatus = g_strdup (result);
412     partinfo->sig_ok = (status == GPGME_SIG_STAT_GOOD);
413     partinfo->sig_unknown = (status == GPGME_SIG_STAT_NOKEY);
414     partinfo->sig_expired = (status == GPGME_SIG_STAT_GOOD_EXP);
415     partinfo->key_expired = (status == GPGME_SIG_STAT_GOOD_EXPKEY);
416
417     gpgme_data_release (sig);
418     gpgme_data_release (text);
419     gpgme_release (ctx);
420     if (prefs_common.gpg_signature_popup)
421         gpgmegtk_sig_status_destroy (statuswindow);
422 }
423
424 /*
425  * Copy a gpgme data object to a temporary file and
426  * return this filename 
427  */
428 #if 0
429 static char *
430 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
431 {
432     static int id;
433     char *tmp;
434     FILE *fp;
435     char buf[100];
436     size_t nread;
437     GpgmeError err;
438     
439     tmp = g_strdup_printf("%s%cgpgtmp.%08x",
440                           get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
441
442     if ((fp = fopen(tmp, "wb")) == NULL) {
443         FILE_OP_ERROR(tmp, "fopen");
444         g_free(tmp);
445         return NULL;
446     }
447
448     err = gpgme_data_rewind ( data );
449     if (err)
450         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
451
452     while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
453         fwrite ( buf, nread, 1, fp );
454     }
455
456     if (err != GPGME_EOF)
457         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
458
459     fclose (fp);
460     *length = nread;
461
462     return tmp;
463 }
464 #endif
465
466 static GpgmeData
467 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
468 {
469     GpgmeCtx ctx = NULL;
470     GpgmeError err;
471     GpgmeData cipher = NULL, plain = NULL;
472     struct passphrase_cb_info_s info;
473
474     memset (&info, 0, sizeof info);
475
476     err = gpgme_new (&ctx);
477     if (err) {
478         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
479         goto leave;
480     }
481
482     err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
483                                         partinfo->fpos, partinfo->size);
484     if (err) {
485         debug_print ("gpgme_data_new_from_filepart failed: %s\n",
486                      gpgme_strerror (err));
487         goto leave;
488     }
489
490     err = gpgme_data_new (&plain);
491     if (err) {
492         debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
493         goto leave;
494     }
495
496     if (!getenv("GPG_AGENT_INFO")) {
497         info.c = ctx;
498         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
499     } 
500
501     err = gpgme_op_decrypt (ctx, cipher, plain);
502
503 leave:
504     gpgme_data_release (cipher);
505     if (err) {
506         gpgmegtk_free_passphrase();
507         debug_print ("decryption failed: %s\n", gpgme_strerror (err));
508         gpgme_data_release (plain);
509         plain = NULL;
510     }
511     else
512         debug_print ("** decryption succeeded\n");
513
514     gpgme_release (ctx);
515     return plain;
516 }
517
518 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
519 {
520     MimeInfo *partinfo;
521     int n = 0;
522
523     if (!mimeinfo)
524         return NULL;
525     if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
526         return NULL;
527
528     debug_print ("** multipart/signed encountered\n");
529
530     /* check that we have at least 2 parts of the correct type */
531     for (partinfo = mimeinfo->children;
532          partinfo != NULL; partinfo = partinfo->next) {
533         if (++n > 1  && !g_strcasecmp (partinfo->content_type,
534                                        "application/pgp-signature"))
535             break;
536     }
537
538     return partinfo;
539 }
540
541 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
542 {
543     return rfc2015_find_signature (mimeinfo) != NULL;
544 }
545
546 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
547 {
548     MimeInfo *partinfo;
549
550     partinfo = rfc2015_find_signature (mimeinfo);
551     if (!partinfo)
552         return;
553
554 #if 0
555     g_message ("** yep, it is a pgp signature");
556     dump_mimeinfo ("gpg-signature", partinfo );
557     dump_part (partinfo, fp );
558     dump_mimeinfo ("signed text", mimeinfo->children );
559     dump_part (mimeinfo->children, fp);
560 #endif
561
562     check_signature (mimeinfo, partinfo, fp);
563 }
564
565 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
566 {
567     if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART)
568         return 0;
569     if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
570         return 0;
571     /* fixme: we should check the protocol parameter */
572     return 1;
573 }
574
575 gboolean rfc2015_msg_is_encrypted (const gchar *file)
576 {
577         FILE *fp;
578         MimeInfo *mimeinfo;
579         int ret;
580
581         if ((fp = fopen(file, "rb")) == NULL)
582                 return FALSE;
583
584         mimeinfo = procmime_scan_mime_header(fp);
585         if(!mimeinfo) {
586                 fclose(fp);
587                 return FALSE;
588         }
589
590         ret = rfc2015_is_encrypted(mimeinfo);
591         procmime_mimeinfo_free_all(mimeinfo);
592         return ret != 0 ? TRUE : FALSE;
593 }
594
595 static int
596 name_cmp(const char *a, const char *b)
597 {
598     for( ; *a && *b; a++, b++) {
599         if(*a != *b
600            && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
601             return 1;
602     }
603
604     return *a != *b;
605 }
606
607 static int
608 headerp(char *p, char **names)
609 {
610     int i, c;
611     char *p2;
612
613     p2 = strchr(p, ':');
614     if(!p2 || p == p2) {
615         return 0;
616     }
617     if(p2[-1] == ' ' || p2[-1] == '\t') {
618         return 0;
619     }
620
621     if(!names[0])
622         return 1;  
623
624     c = *p2;
625     *p2 = 0;
626     for(i = 0 ; names[i] != NULL; i++) {
627         if(!name_cmp (names[i], p))
628             break;
629     }
630     *p2 = c;
631
632     return names[i] != NULL;
633 }
634
635
636 #define DECRYPTION_ABORT() \
637 { \
638     procmime_mimeinfo_free_all(tmpinfo); \
639     msginfo->decryption_failed = 1; \
640     return; \
641 }
642
643 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
644 {
645     static int id;
646     MimeInfo *tmpinfo, *partinfo;
647     int ver_ok = 0;
648     char *fname;
649     GpgmeData plain;
650     FILE *dstfp;
651     size_t nread;
652     char buf[BUFFSIZE];
653     GpgmeError err;
654
655     g_return_if_fail (msginfo != NULL);
656     g_return_if_fail (mimeinfo != NULL);
657     g_return_if_fail (fp != NULL);
658     g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
659
660     debug_print ("** decrypting multipart/encrypted message\n");
661
662     /* skip headers */
663     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
664         perror("fseek");
665     tmpinfo = procmime_scan_mime_header(fp);
666     if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
667         DECRYPTION_ABORT();
668     }
669
670     procmime_scan_multipart_message(tmpinfo, fp);
671
672     /* check that we have the 2 parts */
673     partinfo = tmpinfo->children;
674     if (!partinfo || !partinfo->next) {
675         DECRYPTION_ABORT();
676     }
677     if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
678         /* Fixme: check that the version is 1 */
679         ver_ok = 1;
680     }
681     partinfo = partinfo->next;
682     if (ver_ok &&
683         !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
684         if (partinfo->next)
685             g_warning ("oops: pgp_encrypted with more than 2 parts");
686     }
687     else {
688         DECRYPTION_ABORT();
689     }
690
691     debug_print ("** yep, it is pgp encrypted\n");
692
693     plain = pgp_decrypt (partinfo, fp);
694     if (!plain) {
695         DECRYPTION_ABORT();
696     }
697
698     fname = g_strdup_printf("%s%cplaintext.%08x",
699                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
700
701     if ((dstfp = fopen(fname, "wb")) == NULL) {
702         FILE_OP_ERROR(fname, "fopen");
703         g_free(fname);
704         DECRYPTION_ABORT();
705     }
706
707     /* write the orginal header to the new file */
708     if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
709         perror("fseek");
710
711     while (fgets(buf, sizeof(buf), fp)) {
712         if (headerp (buf, content_names))
713             continue;
714         if (buf[0] == '\r' || buf[0] == '\n')
715             break;
716         fputs (buf, dstfp);
717     }
718
719     err = gpgme_data_rewind (plain);
720     if (err)
721         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
722
723     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
724         fwrite (buf, nread, 1, dstfp);
725     }
726
727     if (err != GPGME_EOF) {
728         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
729     }
730
731     fclose (dstfp);
732     procmime_mimeinfo_free_all(tmpinfo);
733
734     msginfo->plaintext_file = fname;
735     msginfo->decryption_failed = 0;
736 }
737
738 #undef DECRYPTION_ABORT
739
740
741 /*
742  * plain contains an entire mime object.
743  * Encrypt it and return an GpgmeData object with the encrypted version of
744  * the file or NULL in case of error.
745  */
746 static GpgmeData
747 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
748 {
749     GpgmeCtx ctx = NULL;
750     GpgmeError err;
751     GpgmeData cipher = NULL;
752
753     err = gpgme_new (&ctx);
754     if (!err)
755         err = gpgme_data_new (&cipher);
756     if (!err) {
757         gpgme_set_armor (ctx, 1);
758         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
759     }
760
761     if (err) {
762         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
763         gpgme_data_release (cipher);
764         cipher = NULL;
765     }
766     else {
767         debug_print ("** encryption succeeded\n");
768     }
769
770     gpgme_release (ctx);
771     return cipher;
772 }
773
774 /*
775  * Create and return a list of keys matching a key id
776  */
777
778 GSList *rfc2015_create_signers_list (const char *keyid)
779 {
780         GSList *key_list = NULL;
781         GpgmeCtx list_ctx = NULL;
782         GSList *p;
783         GpgmeError err;
784         GpgmeKey key;
785
786         err = gpgme_new (&list_ctx);
787         if (err)
788                 goto leave;
789         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
790         if (err)
791                 goto leave;
792         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
793                 key_list = g_slist_append (key_list, key);
794         }
795         if (err != GPGME_EOF)
796                 goto leave;
797         err = 0;
798         if (key_list == NULL) {
799                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
800         }
801
802 leave:
803         if (err) {
804                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
805                 for (p = key_list; p != NULL; p = p->next)
806                         gpgme_key_unref ((GpgmeKey) p->data);
807                 g_slist_free (key_list);
808         }
809         if (list_ctx)
810                 gpgme_release (list_ctx);
811         return err ? NULL : key_list;
812 }
813
814 /*
815  * Encrypt the file by extracting all recipients and finding the
816  * encryption keys for all of them.  The file content is then replaced
817  * by the encrypted one.  */
818 int
819 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
820 {
821     FILE *fp = NULL;
822     char buf[BUFFSIZE];
823     int i, clineidx, saved_last;
824     char *clines[3] = {NULL};
825     GpgmeError err;
826     GpgmeData header = NULL;
827     GpgmeData plain = NULL;
828     GpgmeData cipher = NULL;
829     GpgmeRecipients rset = NULL;
830     size_t nread;
831     int mime_version_seen = 0;
832     char *boundary = generate_mime_boundary ("Encrypt");
833
834     /* Create the list of recipients */
835     rset = gpgmegtk_recipient_selection (recp_list);
836     if (!rset) {
837         debug_print ("error creating recipient list\n" );
838         goto failure;
839     }
840
841     /* Open the source file */
842     if ((fp = fopen(file, "rb")) == NULL) {
843         FILE_OP_ERROR(file, "fopen");
844         goto failure;
845     }
846
847     err = gpgme_data_new (&header);
848     if (!err)
849         err = gpgme_data_new (&plain);
850     if (err) {
851         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
852         goto failure;
853     }
854
855     /* get the content header lines from the source */
856     clineidx=0;
857     saved_last = 0;
858     while (!err && fgets(buf, sizeof(buf), fp)) {
859         /* fixme: check for overlong lines */
860         if (headerp (buf, content_names)) {
861             if (clineidx >= DIM (clines)) {
862                 debug_print ("rfc2015_encrypt: too many content lines\n");
863                 goto failure;
864             }
865             clines[clineidx++] = g_strdup (buf);
866             saved_last = 1;
867             continue;
868         }
869         if (saved_last) {
870             saved_last = 0;
871             if (*buf == ' ' || *buf == '\t') {
872                 char *last = clines[clineidx-1];
873                 clines[clineidx-1] = g_strconcat (last, buf, NULL);
874                 g_free (last);
875                 continue;
876             }
877         }
878
879         if (headerp (buf, mime_version_name)) 
880             mime_version_seen = 1;
881
882         if (buf[0] == '\r' || buf[0] == '\n')
883             break;
884         err = gpgme_data_write (header, buf, strlen (buf));
885     }
886     if (ferror (fp)) {
887         FILE_OP_ERROR (file, "fgets");
888         goto failure;
889     }
890
891     /* write them to the temp data and add the rest of the message */
892     for (i = 0; !err && i < clineidx; i++) {
893         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
894         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
895     }
896     if (!err)
897         err = gpgme_data_write (plain, "\r\n", 2);
898     while (!err && fgets(buf, sizeof(buf), fp)) {
899         err = gpgme_data_write (plain, buf, strlen (buf));
900     }
901     if (ferror (fp)) {
902         FILE_OP_ERROR (file, "fgets");
903         goto failure;
904     }
905     if (err) {
906         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
907         goto failure;
908     }
909
910     cipher = pgp_encrypt (plain, rset);
911     gpgme_data_release (plain); plain = NULL;
912     gpgme_recipients_release (rset); rset = NULL;
913     if (!cipher)
914         goto failure;
915
916     /* we have the encrypted message available in cipher and now we
917      * are going to rewrite the source file. To be sure that file has
918      * been truncated we use an approach which should work everywhere:
919      * close the file and then reopen it for writing. It is important
920      * that this works, otherwise it may happen that parts of the
921      * plaintext are still in the file (The encrypted stuff is, due to
922      * compression, usually shorter than the plaintext). 
923      * 
924      * Yes, there is a race condition here, but everyone, who is so
925      * stupid to store the temp file with the plaintext in a public
926      * directory has to live with this anyway. */
927     if (fclose (fp)) {
928         FILE_OP_ERROR(file, "fclose");
929         goto failure;
930     }
931     if ((fp = fopen(file, "wb")) == NULL) {
932         FILE_OP_ERROR(file, "fopen");
933         goto failure;
934     }
935
936     /* Write the header, append new content lines, part 1 and part 2 header */
937     err = gpgme_data_rewind (header);
938     if (err) {
939         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
940         goto failure;
941     }
942     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
943         fwrite (buf, nread, 1, fp);
944     }
945     if (err != GPGME_EOF) {
946         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
947         goto failure;
948     }
949     if (ferror (fp)) {
950         FILE_OP_ERROR (file, "fwrite");
951         goto failure;
952     }
953     gpgme_data_release (header); header = NULL;
954     
955     if (!mime_version_seen) 
956         fputs ("MIME-Version: 1\r\n", fp);
957
958     if (ascii_armored) {
959         fprintf(fp, 
960             "Content-Type: text/plain; charset=US-ASCII\r\n"
961             "Content-Transfer-Encoding: 7bit\r\n"  
962             "\r\n");
963     } else {
964         fprintf (fp,
965                 "Content-Type: multipart/encrypted;"
966                 " protocol=\"application/pgp-encrypted\";\r\n"
967                 " boundary=\"%s\"\r\n"
968                 "\r\n"
969                 "--%s\r\n"
970                 "Content-Type: application/pgp-encrypted\r\n"
971                 "\r\n"
972                 "Version: 1\r\n"
973                 "\r\n"
974                 "--%s\r\n"
975                 "Content-Type: application/octet-stream\r\n"
976                 "\r\n",
977                 boundary, boundary, boundary);
978     }
979
980     /* append the encrypted stuff */
981     err = gpgme_data_rewind (cipher);
982     if (err) {
983         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
984                    gpgme_strerror (err));
985         goto failure;
986     }
987
988     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
989         fwrite (buf, nread, 1, fp);
990     }
991     if (err != GPGME_EOF) {
992         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
993         goto failure;
994     }
995
996     /* and the final boundary */
997     if (!ascii_armored) {
998         fprintf (fp,
999                  "\r\n"
1000                  "--%s--\r\n",
1001                  boundary);
1002     }
1003     fflush (fp);
1004     if (ferror (fp)) {
1005         FILE_OP_ERROR (file, "fwrite");
1006         goto failure;
1007     }
1008     fclose (fp);
1009     gpgme_data_release (cipher);
1010     return 0;
1011
1012 failure:
1013     if (fp) 
1014         fclose (fp);
1015     gpgme_data_release (header);
1016     gpgme_data_release (plain);
1017     gpgme_data_release (cipher);
1018     gpgme_recipients_release (rset);
1019     g_free (boundary);
1020     return -1; /* error */
1021 }
1022
1023 /* 
1024  * plain contains an entire mime object.  Sign it and return an
1025  * GpgmeData object with the signature of it or NULL in case of error.
1026  * r_siginfo returns an XML object with information about the signature.
1027  */
1028 static GpgmeData
1029 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
1030           char **r_siginfo)
1031 {
1032     GSList *p;
1033     GpgmeCtx ctx = NULL;
1034     GpgmeError err;
1035     GpgmeData sig = NULL;
1036     struct passphrase_cb_info_s info;
1037
1038     *r_siginfo = NULL;
1039     memset (&info, 0, sizeof info);
1040
1041     err = gpgme_new (&ctx);
1042     if (err)
1043         goto leave;
1044     err = gpgme_data_new (&sig);
1045     if (err)
1046         goto leave;
1047
1048     if (!getenv("GPG_AGENT_INFO")) {
1049         info.c = ctx;
1050         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1051     }
1052     gpgme_set_textmode (ctx, 1);
1053     gpgme_set_armor (ctx, 1);
1054     gpgme_signers_clear (ctx);
1055     for (p = key_list; p != NULL; p = p->next) {
1056         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1057         if (err)
1058             goto leave;
1059     }
1060     for (p = key_list; p != NULL; p = p->next)
1061         gpgme_key_unref ((GpgmeKey) p->data);
1062     g_slist_free (key_list);
1063
1064     if (err)
1065         goto leave;
1066     err = gpgme_op_sign
1067         (ctx, plain, sig,
1068          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1069     if (!err)
1070         *r_siginfo = gpgme_get_op_info (ctx, 0);
1071
1072 leave:
1073     if (err) {
1074         gpgmegtk_free_passphrase();
1075         debug_print ("signing failed: %s\n", gpgme_strerror (err));
1076         gpgme_data_release (sig);
1077         sig = NULL;
1078     }
1079     else {
1080         debug_print ("signing succeeded\n");
1081     }
1082
1083     gpgme_release (ctx);
1084     return sig;
1085 }
1086
1087 /*
1088  * Find TAG in XML and return a pointer into xml set just behind the
1089  * closing angle.  Return NULL if not found. 
1090  */
1091 static const char *
1092 find_xml_tag (const char *xml, const char *tag)
1093 {
1094     int taglen = strlen (tag);
1095     const char *s = xml;
1096  
1097     while ( (s = strchr (s, '<')) ) {
1098         s++;
1099         if (!strncmp (s, tag, taglen)) {
1100             const char *s2 = s + taglen;
1101             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1102                 /* found */
1103                 while (*s2 && *s2 != '>') /* skip attributes */
1104                     s2++;
1105                 /* fixme: do need to handle angles inside attribute vallues? */
1106                 return *s2? (s2+1):NULL;
1107             }
1108         }
1109         while (*s && *s != '>') /* skip to end of tag */
1110             s++;
1111     }
1112     return NULL;
1113 }
1114
1115
1116 /*
1117  * Extract the micalg from an GnupgOperationInfo XML container.
1118  */
1119 static char *
1120 extract_micalg (char *xml)
1121 {
1122     const char *s;
1123
1124     s = find_xml_tag (xml, "GnupgOperationInfo");
1125     if (s) {
1126         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1127         s = find_xml_tag (s, "signature");
1128         if (s && s_end && s < s_end) {
1129             const char *s_end2 = find_xml_tag (s, "/signature");
1130             if (s_end2 && s_end2 < s_end) {
1131                 s = find_xml_tag (s, "micalg");
1132                 if (s && s < s_end2) {
1133                     s_end = strchr (s, '<');
1134                     if (s_end) {
1135                         char *p = g_malloc (s_end - s + 1);
1136                         memcpy (p, s, s_end - s);
1137                         p[s_end-s] = 0;
1138                         return p;
1139                     }
1140                 }
1141             }
1142         }
1143     }
1144     return NULL;
1145 }
1146
1147
1148 /*
1149  * Sign the file and replace its content with the signed one.
1150  */
1151 int
1152 rfc2015_sign (const char *file, GSList *key_list)
1153 {
1154     FILE *fp = NULL;
1155     char buf[BUFFSIZE];
1156     int i, clineidx, saved_last;
1157     char *clines[3] = {NULL};
1158     GpgmeError err;
1159     GpgmeData header = NULL;
1160     GpgmeData plain = NULL;
1161     GpgmeData sigdata = NULL;
1162     size_t nread;
1163     int mime_version_seen = 0;
1164     char *boundary = generate_mime_boundary ("Signature");
1165     char *micalg = NULL;
1166     char *siginfo;
1167
1168     /* Open the source file */
1169     if ((fp = fopen(file, "rb")) == NULL) {
1170         FILE_OP_ERROR(file, "fopen");
1171         goto failure;
1172     }
1173
1174     err = gpgme_data_new (&header);
1175     if (!err)
1176         err = gpgme_data_new (&plain);
1177     if (err) {
1178         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1179         goto failure;
1180     }
1181
1182     /* get the content header lines from the source */
1183     clineidx = 0;
1184     saved_last = 0;
1185     while (!err && fgets(buf, sizeof(buf), fp)) {
1186         /* fixme: check for overlong lines */
1187         if (headerp (buf, content_names)) {
1188             if (clineidx >= DIM (clines)) {
1189                 debug_print ("rfc2015_sign: too many content lines\n");
1190                 goto failure;
1191             }
1192             clines[clineidx++] = g_strdup (buf);
1193             saved_last = 1;
1194             continue;
1195         }
1196         if (saved_last) {
1197             saved_last = 0;
1198             if (*buf == ' ' || *buf == '\t') {
1199                 char *last = clines[clineidx - 1];
1200                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1201                 g_free (last);
1202                 continue;
1203             }
1204         }
1205
1206         if (headerp (buf, mime_version_name)) 
1207             mime_version_seen = 1;
1208
1209         if (buf[0] == '\r' || buf[0] == '\n')
1210             break;
1211         err = gpgme_data_write (header, buf, strlen (buf));
1212     }
1213     if (ferror (fp)) {
1214         FILE_OP_ERROR (file, "fgets");
1215         goto failure;
1216     }
1217
1218     /* write them to the temp data and add the rest of the message */
1219     for (i = 0; !err && i < clineidx; i++) {
1220         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1221     }
1222     if (!err)
1223         err = gpgme_data_write (plain, "\r\n", 2 );
1224     while (!err && fgets(buf, sizeof(buf), fp)) {
1225         err = gpgme_data_write (plain, buf, strlen (buf));
1226     }
1227     if (ferror (fp)) {
1228         FILE_OP_ERROR (file, "fgets");
1229         goto failure;
1230     }
1231     if (err) {
1232         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1233         goto failure;
1234     }
1235
1236     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
1237     if (siginfo) {
1238         micalg = extract_micalg (siginfo);
1239         free (siginfo);
1240     }
1241     if (!sigdata) 
1242         goto failure;
1243
1244     /* we have the signed message available in sigdata and now we are
1245      * going to rewrite the original file. To be sure that file has
1246      * been truncated we use an approach which should work everywhere:
1247      * close the file and then reopen it for writing. */
1248     if (fclose (fp)) {
1249         FILE_OP_ERROR(file, "fclose");
1250         goto failure;
1251     }
1252     if ((fp = fopen(file, "wb")) == NULL) {
1253         FILE_OP_ERROR(file, "fopen");
1254         goto failure;
1255     }
1256
1257     /* Write the rfc822 header and add new content lines */
1258     err = gpgme_data_rewind (header);
1259     if (err)
1260         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1261     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1262         fwrite (buf, nread, 1, fp);
1263     }
1264     if (err != GPGME_EOF) {
1265         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1266         goto failure;
1267     }
1268     if (ferror (fp)) {
1269         FILE_OP_ERROR (file, "fwrite");
1270         goto failure;
1271     }
1272     gpgme_data_release (header);
1273     header = NULL;
1274
1275     if (!mime_version_seen) 
1276         fputs ("MIME-Version: 1.0\r\n", fp);
1277     fprintf (fp, "Content-Type: multipart/signed; "
1278              "protocol=\"application/pgp-signature\";\r\n");
1279     if (micalg)
1280         fprintf (fp, " micalg=\"%s\";", micalg);
1281     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1282
1283     /* Part 1: signed material */
1284     fprintf (fp, "\r\n"
1285                  "--%s\r\n",
1286                  boundary);
1287     err = gpgme_data_rewind (plain);
1288     if (err) {
1289         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1290                    gpgme_strerror (err));
1291         goto failure;
1292     }
1293     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1294         fwrite (buf, nread, 1, fp);   
1295     }
1296     if (err != GPGME_EOF) {
1297         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1298         goto failure;
1299     }
1300
1301     /* Part 2: signature */
1302     fprintf (fp, "\r\n"
1303                  "--%s\r\n",
1304                  boundary);
1305     fputs ("Content-Type: application/pgp-signature\r\n"
1306            "\r\n", fp);
1307
1308     err = gpgme_data_rewind (sigdata);
1309     if (err) {
1310         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1311                    gpgme_strerror (err));
1312         goto failure;
1313     }
1314
1315     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1316         fwrite (buf, nread, 1, fp);
1317     }
1318     if (err != GPGME_EOF) {
1319         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1320         goto failure;
1321     }
1322
1323     /* Final boundary */
1324     fprintf (fp, "\r\n"
1325                  "--%s--\r\n",
1326                  boundary);
1327     fflush (fp);
1328     if (ferror (fp)) {
1329         FILE_OP_ERROR (file, "fwrite");
1330         goto failure;
1331     }
1332     fclose (fp);
1333     gpgme_data_release (header);
1334     gpgme_data_release (plain);
1335     gpgme_data_release (sigdata);
1336     g_free (boundary);
1337     g_free (micalg);
1338     return 0;
1339
1340 failure:
1341     if (fp) 
1342         fclose (fp);
1343     gpgme_data_release (header);
1344     gpgme_data_release (plain);
1345     gpgme_data_release (sigdata);
1346     g_free (boundary);
1347     g_free (micalg);
1348     return -1; /* error */
1349 }
1350
1351
1352 /*
1353  * Sign the file with clear text and replace its content with the signed one.
1354  */
1355 gint
1356 rfc2015_clearsign (const gchar *file, GSList *key_list)
1357 {
1358     FILE *fp;
1359     gchar buf[BUFFSIZE];
1360     GpgmeError err;
1361     GpgmeData text = NULL;
1362     GpgmeData sigdata = NULL;
1363     size_t nread;
1364     gchar *siginfo;
1365
1366     if ((fp = fopen(file, "rb")) == NULL) {
1367         FILE_OP_ERROR(file, "fopen");
1368         goto failure;
1369     }
1370
1371     err = gpgme_data_new(&text);
1372     if (err) {
1373         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1374         goto failure;
1375     }
1376
1377     while (!err && fgets(buf, sizeof(buf), fp)) {
1378         err = gpgme_data_write(text, buf, strlen(buf));
1379     }
1380     if (ferror(fp)) {
1381         FILE_OP_ERROR(file, "fgets");
1382         goto failure;
1383     }
1384     if (err) {
1385         debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1386         goto failure;
1387     }
1388
1389     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1390     if (siginfo) {
1391         g_free(siginfo);
1392     }
1393     if (!sigdata)
1394         goto failure;
1395
1396     if (fclose(fp) == EOF) {
1397         FILE_OP_ERROR(file, "fclose");
1398         fp = NULL;
1399         goto failure;
1400     }
1401     if ((fp = fopen(file, "wb")) == NULL) {
1402         FILE_OP_ERROR(file, "fopen");
1403         goto failure;
1404     }
1405
1406     err = gpgme_data_rewind(sigdata);
1407     if (err) {
1408         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1409                     gpgme_strerror(err));
1410         goto failure;
1411     }
1412
1413     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1414         fwrite(buf, nread, 1, fp);
1415     }
1416     if (err != GPGME_EOF) {
1417         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1418         goto failure;
1419     }
1420
1421     if (fclose(fp) == EOF) {
1422         FILE_OP_ERROR(file, "fclose");
1423         fp = NULL;
1424         goto failure;
1425     }
1426     gpgme_data_release(text);
1427     gpgme_data_release(sigdata);
1428     return 0;
1429
1430 failure:
1431     if (fp)
1432         fclose(fp);
1433     gpgme_data_release(text);
1434     gpgme_data_release(sigdata);
1435     return -1;
1436 }
1437
1438 #endif /* USE_GPGME */