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