cb9bc1883e3c00d05e9177565a2cf9b6e3597c61
[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     int in_cline;
654     GpgmeError err;
655
656     g_return_if_fail (msginfo != NULL);
657     g_return_if_fail (mimeinfo != NULL);
658     g_return_if_fail (fp != NULL);
659     g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
660
661     debug_print ("** decrypting multipart/encrypted message\n");
662
663     /* skip headers */
664     if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
665         perror("fseek");
666     tmpinfo = procmime_scan_mime_header(fp);
667     if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
668         DECRYPTION_ABORT();
669     }
670
671     procmime_scan_multipart_message(tmpinfo, fp);
672
673     /* check that we have the 2 parts */
674     partinfo = tmpinfo->children;
675     if (!partinfo || !partinfo->next) {
676         DECRYPTION_ABORT();
677     }
678     if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
679         /* Fixme: check that the version is 1 */
680         ver_ok = 1;
681     }
682     partinfo = partinfo->next;
683     if (ver_ok &&
684         !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
685         if (partinfo->next)
686             g_warning ("oops: pgp_encrypted with more than 2 parts");
687     }
688     else {
689         DECRYPTION_ABORT();
690     }
691
692     debug_print ("** yep, it is pgp encrypted\n");
693
694     plain = pgp_decrypt (partinfo, fp);
695     if (!plain) {
696         DECRYPTION_ABORT();
697     }
698
699     fname = g_strdup_printf("%s%cplaintext.%08x",
700                             get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
701
702     if ((dstfp = fopen(fname, "wb")) == NULL) {
703         FILE_OP_ERROR(fname, "fopen");
704         g_free(fname);
705         DECRYPTION_ABORT();
706     }
707
708     /* write the orginal header to the new file */
709     if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
710         perror("fseek");
711
712     in_cline = 0;
713     while (fgets(buf, sizeof(buf), fp)) {
714         if (headerp (buf, content_names)) {
715             in_cline = 1;
716             continue;
717         }
718         if (in_cline) {
719             if (buf[0] == ' ' || buf[0] == '\t')
720                 continue;
721             in_cline = 0;
722         }
723         if (buf[0] == '\r' || buf[0] == '\n')
724             break;
725         fputs (buf, dstfp);
726     }
727
728     err = gpgme_data_rewind (plain);
729     if (err)
730         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
731
732     while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
733         fwrite (buf, nread, 1, dstfp);
734     }
735
736     if (err != GPGME_EOF) {
737         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
738     }
739
740     fclose (dstfp);
741     procmime_mimeinfo_free_all(tmpinfo);
742
743     msginfo->plaintext_file = fname;
744     msginfo->decryption_failed = 0;
745 }
746
747 #undef DECRYPTION_ABORT
748
749
750 /*
751  * plain contains an entire mime object.
752  * Encrypt it and return an GpgmeData object with the encrypted version of
753  * the file or NULL in case of error.
754  */
755 static GpgmeData
756 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
757 {
758     GpgmeCtx ctx = NULL;
759     GpgmeError err;
760     GpgmeData cipher = NULL;
761
762     err = gpgme_new (&ctx);
763     if (!err)
764         err = gpgme_data_new (&cipher);
765     if (!err) {
766         gpgme_set_armor (ctx, 1);
767         err = gpgme_op_encrypt (ctx, rset, plain, cipher);
768     }
769
770     if (err) {
771         debug_print ("encryption failed: %s\n", gpgme_strerror (err));
772         gpgme_data_release (cipher);
773         cipher = NULL;
774     }
775     else {
776         debug_print ("** encryption succeeded\n");
777     }
778
779     gpgme_release (ctx);
780     return cipher;
781 }
782
783 /*
784  * Create and return a list of keys matching a key id
785  */
786
787 GSList *rfc2015_create_signers_list (const char *keyid)
788 {
789         GSList *key_list = NULL;
790         GpgmeCtx list_ctx = NULL;
791         GSList *p;
792         GpgmeError err;
793         GpgmeKey key;
794
795         err = gpgme_new (&list_ctx);
796         if (err)
797                 goto leave;
798         err = gpgme_op_keylist_start (list_ctx, keyid, 1);
799         if (err)
800                 goto leave;
801         while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
802                 key_list = g_slist_append (key_list, key);
803         }
804         if (err != GPGME_EOF)
805                 goto leave;
806         err = 0;
807         if (key_list == NULL) {
808                 debug_print ("no keys found for keyid \"%s\"\n", keyid);
809         }
810
811 leave:
812         if (err) {
813                 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
814                 for (p = key_list; p != NULL; p = p->next)
815                         gpgme_key_unref ((GpgmeKey) p->data);
816                 g_slist_free (key_list);
817         }
818         if (list_ctx)
819                 gpgme_release (list_ctx);
820         return err ? NULL : key_list;
821 }
822
823 /*
824  * Encrypt the file by extracting all recipients and finding the
825  * encryption keys for all of them.  The file content is then replaced
826  * by the encrypted one.  */
827 int
828 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
829 {
830     FILE *fp = NULL;
831     char buf[BUFFSIZE];
832     int i, clineidx, saved_last;
833     char *clines[3] = {NULL};
834     GpgmeError err;
835     GpgmeData header = NULL;
836     GpgmeData plain = NULL;
837     GpgmeData cipher = NULL;
838     GpgmeRecipients rset = NULL;
839     size_t nread;
840     int mime_version_seen = 0;
841     char *boundary;
842
843     boundary = generate_mime_boundary ("Encrypt");
844
845     /* Create the list of recipients */
846     rset = gpgmegtk_recipient_selection (recp_list);
847     if (!rset) {
848         debug_print ("error creating recipient list\n" );
849         goto failure;
850     }
851
852     /* Open the source file */
853     if ((fp = fopen(file, "rb")) == NULL) {
854         FILE_OP_ERROR(file, "fopen");
855         goto failure;
856     }
857
858     err = gpgme_data_new (&header);
859     if (!err)
860         err = gpgme_data_new (&plain);
861     if (err) {
862         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
863         goto failure;
864     }
865
866     /* get the content header lines from the source */
867     clineidx = 0;
868     saved_last = 0;
869     while (!err && fgets(buf, sizeof(buf), fp)) {
870         /* fixme: check for overlong lines */
871         if (headerp (buf, content_names)) {
872             if (clineidx >= DIM (clines)) {
873                 debug_print ("rfc2015_encrypt: too many content lines\n");
874                 goto failure;
875             }
876             clines[clineidx++] = g_strdup (buf);
877             saved_last = 1;
878             continue;
879         }
880         if (saved_last) {
881             if (*buf == ' ' || *buf == '\t') {
882                 char *last = clines[clineidx - 1];
883                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
884                 g_free (last);
885                 continue;
886             }
887             saved_last = 0;
888         }
889
890         if (headerp (buf, mime_version_name)) 
891             mime_version_seen = 1;
892
893         if (buf[0] == '\r' || buf[0] == '\n')
894             break;
895         err = gpgme_data_write (header, buf, strlen (buf));
896     }
897     if (ferror (fp)) {
898         FILE_OP_ERROR (file, "fgets");
899         goto failure;
900     }
901
902     /* write them to the temp data and add the rest of the message */
903     for (i = 0; !err && i < clineidx; i++) {
904         debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
905         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
906     }
907     if (!err)
908         err = gpgme_data_write (plain, "\r\n", 2);
909     while (!err && fgets(buf, sizeof(buf), fp)) {
910         err = gpgme_data_write (plain, buf, strlen (buf));
911     }
912     if (ferror (fp)) {
913         FILE_OP_ERROR (file, "fgets");
914         goto failure;
915     }
916     if (err) {
917         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
918         goto failure;
919     }
920
921     cipher = pgp_encrypt (plain, rset);
922     gpgme_data_release (plain); plain = NULL;
923     gpgme_recipients_release (rset); rset = NULL;
924     if (!cipher)
925         goto failure;
926
927     /* we have the encrypted message available in cipher and now we
928      * are going to rewrite the source file. To be sure that file has
929      * been truncated we use an approach which should work everywhere:
930      * close the file and then reopen it for writing. It is important
931      * that this works, otherwise it may happen that parts of the
932      * plaintext are still in the file (The encrypted stuff is, due to
933      * compression, usually shorter than the plaintext). 
934      * 
935      * Yes, there is a race condition here, but everyone, who is so
936      * stupid to store the temp file with the plaintext in a public
937      * directory has to live with this anyway. */
938     if (fclose (fp)) {
939         FILE_OP_ERROR(file, "fclose");
940         goto failure;
941     }
942     if ((fp = fopen(file, "wb")) == NULL) {
943         FILE_OP_ERROR(file, "fopen");
944         goto failure;
945     }
946
947     /* Write the header, append new content lines, part 1 and part 2 header */
948     err = gpgme_data_rewind (header);
949     if (err) {
950         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
951         goto failure;
952     }
953     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
954         fwrite (buf, nread, 1, fp);
955     }
956     if (err != GPGME_EOF) {
957         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
958         goto failure;
959     }
960     if (ferror (fp)) {
961         FILE_OP_ERROR (file, "fwrite");
962         goto failure;
963     }
964     gpgme_data_release (header); header = NULL;
965     
966     if (!mime_version_seen) 
967         fputs ("MIME-Version: 1\r\n", fp);
968
969     if (ascii_armored) {
970         fprintf(fp, 
971             "Content-Type: text/plain; charset=US-ASCII\r\n"
972             "Content-Transfer-Encoding: 7bit\r\n"  
973             "\r\n");
974     } else {
975         fprintf (fp,
976                 "Content-Type: multipart/encrypted;"
977                 " protocol=\"application/pgp-encrypted\";\r\n"
978                 " boundary=\"%s\"\r\n"
979                 "\r\n"
980                 "--%s\r\n"
981                 "Content-Type: application/pgp-encrypted\r\n"
982                 "\r\n"
983                 "Version: 1\r\n"
984                 "\r\n"
985                 "--%s\r\n"
986                 "Content-Type: application/octet-stream\r\n"
987                 "\r\n",
988                 boundary, boundary, boundary);
989     }
990
991     /* append the encrypted stuff */
992     err = gpgme_data_rewind (cipher);
993     if (err) {
994         debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
995                    gpgme_strerror (err));
996         goto failure;
997     }
998
999     while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
1000         fwrite (buf, nread, 1, fp);
1001     }
1002     if (err != GPGME_EOF) {
1003         debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
1004         goto failure;
1005     }
1006
1007     /* and the final boundary */
1008     if (!ascii_armored) {
1009         fprintf (fp,
1010                  "\r\n"
1011                  "--%s--\r\n",
1012                  boundary);
1013     }
1014     fflush (fp);
1015     if (ferror (fp)) {
1016         FILE_OP_ERROR (file, "fwrite");
1017         goto failure;
1018     }
1019     fclose (fp);
1020     gpgme_data_release (cipher);
1021     return 0;
1022
1023 failure:
1024     if (fp) 
1025         fclose (fp);
1026     gpgme_data_release (header);
1027     gpgme_data_release (plain);
1028     gpgme_data_release (cipher);
1029     gpgme_recipients_release (rset);
1030     g_free (boundary);
1031     return -1; /* error */
1032 }
1033
1034 /* 
1035  * plain contains an entire mime object.  Sign it and return an
1036  * GpgmeData object with the signature of it or NULL in case of error.
1037  * r_siginfo returns an XML object with information about the signature.
1038  */
1039 static GpgmeData
1040 pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
1041           char **r_siginfo)
1042 {
1043     GSList *p;
1044     GpgmeCtx ctx = NULL;
1045     GpgmeError err;
1046     GpgmeData sig = NULL;
1047     struct passphrase_cb_info_s info;
1048
1049     *r_siginfo = NULL;
1050     memset (&info, 0, sizeof info);
1051
1052     err = gpgme_new (&ctx);
1053     if (err)
1054         goto leave;
1055     err = gpgme_data_new (&sig);
1056     if (err)
1057         goto leave;
1058
1059     if (!getenv("GPG_AGENT_INFO")) {
1060         info.c = ctx;
1061         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1062     }
1063     gpgme_set_textmode (ctx, 1);
1064     gpgme_set_armor (ctx, 1);
1065     gpgme_signers_clear (ctx);
1066     for (p = key_list; p != NULL; p = p->next) {
1067         err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1068         if (err)
1069             goto leave;
1070     }
1071     for (p = key_list; p != NULL; p = p->next)
1072         gpgme_key_unref ((GpgmeKey) p->data);
1073     g_slist_free (key_list);
1074
1075     if (err)
1076         goto leave;
1077     err = gpgme_op_sign
1078         (ctx, plain, sig,
1079          clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1080     if (!err)
1081         *r_siginfo = gpgme_get_op_info (ctx, 0);
1082
1083 leave:
1084     if (err) {
1085         gpgmegtk_free_passphrase();
1086         debug_print ("signing failed: %s\n", gpgme_strerror (err));
1087         gpgme_data_release (sig);
1088         sig = NULL;
1089     }
1090     else {
1091         debug_print ("signing succeeded\n");
1092     }
1093
1094     gpgme_release (ctx);
1095     return sig;
1096 }
1097
1098 /*
1099  * Find TAG in XML and return a pointer into xml set just behind the
1100  * closing angle.  Return NULL if not found. 
1101  */
1102 static const char *
1103 find_xml_tag (const char *xml, const char *tag)
1104 {
1105     int taglen = strlen (tag);
1106     const char *s = xml;
1107  
1108     while ( (s = strchr (s, '<')) ) {
1109         s++;
1110         if (!strncmp (s, tag, taglen)) {
1111             const char *s2 = s + taglen;
1112             if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1113                 /* found */
1114                 while (*s2 && *s2 != '>') /* skip attributes */
1115                     s2++;
1116                 /* fixme: do need to handle angles inside attribute vallues? */
1117                 return *s2? (s2+1):NULL;
1118             }
1119         }
1120         while (*s && *s != '>') /* skip to end of tag */
1121             s++;
1122     }
1123     return NULL;
1124 }
1125
1126
1127 /*
1128  * Extract the micalg from an GnupgOperationInfo XML container.
1129  */
1130 static char *
1131 extract_micalg (char *xml)
1132 {
1133     const char *s;
1134
1135     s = find_xml_tag (xml, "GnupgOperationInfo");
1136     if (s) {
1137         const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1138         s = find_xml_tag (s, "signature");
1139         if (s && s_end && s < s_end) {
1140             const char *s_end2 = find_xml_tag (s, "/signature");
1141             if (s_end2 && s_end2 < s_end) {
1142                 s = find_xml_tag (s, "micalg");
1143                 if (s && s < s_end2) {
1144                     s_end = strchr (s, '<');
1145                     if (s_end) {
1146                         char *p = g_malloc (s_end - s + 1);
1147                         memcpy (p, s, s_end - s);
1148                         p[s_end-s] = 0;
1149                         return p;
1150                     }
1151                 }
1152             }
1153         }
1154     }
1155     return NULL;
1156 }
1157
1158
1159 /*
1160  * Sign the file and replace its content with the signed one.
1161  */
1162 int
1163 rfc2015_sign (const char *file, GSList *key_list)
1164 {
1165     FILE *fp = NULL;
1166     char buf[BUFFSIZE];
1167     int i, clineidx, saved_last;
1168     char *clines[3] = {NULL};
1169     GpgmeError err;
1170     GpgmeData header = NULL;
1171     GpgmeData plain = NULL;
1172     GpgmeData sigdata = NULL;
1173     size_t nread;
1174     int mime_version_seen = 0;
1175     char *boundary;
1176     char *micalg = NULL;
1177     char *siginfo;
1178
1179     boundary = generate_mime_boundary ("Signature");
1180
1181     /* Open the source file */
1182     if ((fp = fopen(file, "rb")) == NULL) {
1183         FILE_OP_ERROR(file, "fopen");
1184         goto failure;
1185     }
1186
1187     err = gpgme_data_new (&header);
1188     if (!err)
1189         err = gpgme_data_new (&plain);
1190     if (err) {
1191         debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1192         goto failure;
1193     }
1194
1195     /* get the content header lines from the source */
1196     clineidx = 0;
1197     saved_last = 0;
1198     while (!err && fgets(buf, sizeof(buf), fp)) {
1199         /* fixme: check for overlong lines */
1200         if (headerp (buf, content_names)) {
1201             if (clineidx >= DIM (clines)) {
1202                 debug_print ("rfc2015_sign: too many content lines\n");
1203                 goto failure;
1204             }
1205             clines[clineidx++] = g_strdup (buf);
1206             saved_last = 1;
1207             continue;
1208         }
1209         if (saved_last) {
1210             if (*buf == ' ' || *buf == '\t') {
1211                 char *last = clines[clineidx - 1];
1212                 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1213                 g_free (last);
1214                 continue;
1215             }
1216             saved_last = 0;
1217         }
1218
1219         if (headerp (buf, mime_version_name)) 
1220             mime_version_seen = 1;
1221
1222         if (buf[0] == '\r' || buf[0] == '\n')
1223             break;
1224         err = gpgme_data_write (header, buf, strlen (buf));
1225     }
1226     if (ferror (fp)) {
1227         FILE_OP_ERROR (file, "fgets");
1228         goto failure;
1229     }
1230
1231     /* write them to the temp data and add the rest of the message */
1232     for (i = 0; !err && i < clineidx; i++) {
1233         err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1234     }
1235     if (!err)
1236         err = gpgme_data_write (plain, "\r\n", 2 );
1237     while (!err && fgets(buf, sizeof(buf), fp)) {
1238         err = gpgme_data_write (plain, buf, strlen (buf));
1239     }
1240     if (ferror (fp)) {
1241         FILE_OP_ERROR (file, "fgets");
1242         goto failure;
1243     }
1244     if (err) {
1245         debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1246         goto failure;
1247     }
1248
1249     sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
1250     if (siginfo) {
1251         micalg = extract_micalg (siginfo);
1252         free (siginfo);
1253     }
1254     if (!sigdata) 
1255         goto failure;
1256
1257     /* we have the signed message available in sigdata and now we are
1258      * going to rewrite the original file. To be sure that file has
1259      * been truncated we use an approach which should work everywhere:
1260      * close the file and then reopen it for writing. */
1261     if (fclose (fp)) {
1262         FILE_OP_ERROR(file, "fclose");
1263         goto failure;
1264     }
1265     if ((fp = fopen(file, "wb")) == NULL) {
1266         FILE_OP_ERROR(file, "fopen");
1267         goto failure;
1268     }
1269
1270     /* Write the rfc822 header and add new content lines */
1271     err = gpgme_data_rewind (header);
1272     if (err)
1273         debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1274     while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1275         fwrite (buf, nread, 1, fp);
1276     }
1277     if (err != GPGME_EOF) {
1278         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1279         goto failure;
1280     }
1281     if (ferror (fp)) {
1282         FILE_OP_ERROR (file, "fwrite");
1283         goto failure;
1284     }
1285     gpgme_data_release (header);
1286     header = NULL;
1287
1288     if (!mime_version_seen) 
1289         fputs ("MIME-Version: 1.0\r\n", fp);
1290     fprintf (fp, "Content-Type: multipart/signed; "
1291              "protocol=\"application/pgp-signature\";\r\n");
1292     if (micalg)
1293         fprintf (fp, " micalg=\"%s\";\r\n", micalg);
1294     fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1295
1296     /* Part 1: signed material */
1297     fprintf (fp, "\r\n"
1298                  "--%s\r\n",
1299                  boundary);
1300     err = gpgme_data_rewind (plain);
1301     if (err) {
1302         debug_print ("gpgme_data_rewind on plain failed: %s\n",
1303                    gpgme_strerror (err));
1304         goto failure;
1305     }
1306     while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1307         fwrite (buf, nread, 1, fp);   
1308     }
1309     if (err != GPGME_EOF) {
1310         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1311         goto failure;
1312     }
1313
1314     /* Part 2: signature */
1315     fprintf (fp, "\r\n"
1316                  "--%s\r\n",
1317                  boundary);
1318     fputs ("Content-Type: application/pgp-signature\r\n"
1319            "\r\n", fp);
1320
1321     err = gpgme_data_rewind (sigdata);
1322     if (err) {
1323         debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1324                    gpgme_strerror (err));
1325         goto failure;
1326     }
1327
1328     while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1329         fwrite (buf, nread, 1, fp);
1330     }
1331     if (err != GPGME_EOF) {
1332         debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1333         goto failure;
1334     }
1335
1336     /* Final boundary */
1337     fprintf (fp, "\r\n"
1338                  "--%s--\r\n",
1339                  boundary);
1340     fflush (fp);
1341     if (ferror (fp)) {
1342         FILE_OP_ERROR (file, "fwrite");
1343         goto failure;
1344     }
1345     fclose (fp);
1346     gpgme_data_release (header);
1347     gpgme_data_release (plain);
1348     gpgme_data_release (sigdata);
1349     g_free (boundary);
1350     g_free (micalg);
1351     return 0;
1352
1353 failure:
1354     if (fp) 
1355         fclose (fp);
1356     gpgme_data_release (header);
1357     gpgme_data_release (plain);
1358     gpgme_data_release (sigdata);
1359     g_free (boundary);
1360     g_free (micalg);
1361     return -1; /* error */
1362 }
1363
1364
1365 /*
1366  * Sign the file with clear text and replace its content with the signed one.
1367  */
1368 gint
1369 rfc2015_clearsign (const gchar *file, GSList *key_list)
1370 {
1371     FILE *fp;
1372     gchar buf[BUFFSIZE];
1373     GpgmeError err;
1374     GpgmeData text = NULL;
1375     GpgmeData sigdata = NULL;
1376     size_t nread;
1377     gchar *siginfo;
1378
1379     if ((fp = fopen(file, "rb")) == NULL) {
1380         FILE_OP_ERROR(file, "fopen");
1381         goto failure;
1382     }
1383
1384     err = gpgme_data_new(&text);
1385     if (err) {
1386         debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1387         goto failure;
1388     }
1389
1390     while (!err && fgets(buf, sizeof(buf), fp)) {
1391         err = gpgme_data_write(text, buf, strlen(buf));
1392     }
1393     if (ferror(fp)) {
1394         FILE_OP_ERROR(file, "fgets");
1395         goto failure;
1396     }
1397     if (err) {
1398         debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1399         goto failure;
1400     }
1401
1402     sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1403     if (siginfo) {
1404         g_free(siginfo);
1405     }
1406     if (!sigdata)
1407         goto failure;
1408
1409     if (fclose(fp) == EOF) {
1410         FILE_OP_ERROR(file, "fclose");
1411         fp = NULL;
1412         goto failure;
1413     }
1414     if ((fp = fopen(file, "wb")) == NULL) {
1415         FILE_OP_ERROR(file, "fopen");
1416         goto failure;
1417     }
1418
1419     err = gpgme_data_rewind(sigdata);
1420     if (err) {
1421         debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1422                     gpgme_strerror(err));
1423         goto failure;
1424     }
1425
1426     while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1427         fwrite(buf, nread, 1, fp);
1428     }
1429     if (err != GPGME_EOF) {
1430         debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1431         goto failure;
1432     }
1433
1434     if (fclose(fp) == EOF) {
1435         FILE_OP_ERROR(file, "fclose");
1436         fp = NULL;
1437         goto failure;
1438     }
1439     gpgme_data_release(text);
1440     gpgme_data_release(sigdata);
1441     return 0;
1442
1443 failure:
1444     if (fp)
1445         fclose(fp);
1446     gpgme_data_release(text);
1447     gpgme_data_release(sigdata);
1448     return -1;
1449 }
1450
1451 #endif /* USE_GPGME */