summaryrefslogtreecommitdiff
path: root/src/rfc2015.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rfc2015.c')
-rw-r--r--src/rfc2015.c1992
1 files changed, 1992 insertions, 0 deletions
diff --git a/src/rfc2015.c b/src/rfc2015.c
new file mode 100644
index 0000000..ebfa96c
--- /dev/null
+++ b/src/rfc2015.c
@@ -0,0 +1,1992 @@
1/*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001 Werner Koch (dd9jn)
4 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#if USE_GPGME
26
27#include "defs.h"
28
29#include <glib.h>
30#include <glib/gi18n.h>
31#include <stdio.h>
32#include <string.h>
33#include <locale.h>
34#include <ctype.h>
35#include <errno.h>
36
37#include <gpgme.h>
38
39#include "procmsg.h"
40#include "procmime.h"
41#include "procheader.h"
42#include "base64.h"
43#include "uuencode.h"
44#include "unmime.h"
45#include "codeconv.h"
46#include "utils.h"
47#include "prefs_common.h"
48#include "passphrase.h"
49#include "select-keys.h"
50#include "sigstatus.h"
51#include "rfc2015.h"
52
53#define DIM(v) (sizeof(v) / sizeof((v)[0]))
54
55static gboolean gpg_available = TRUE;
56
57static gchar *content_names[] = {
58 "Content-Type",
59 "Content-Disposition",
60 "Content-Transfer-Encoding",
61 NULL
62};
63
64static gchar *mime_version_name[] = {
65 "Mime-Version",
66 NULL
67};
68
69#if 0
70static void dump_mimeinfo(const gchar *text, MimeInfo *x)
71{
72 debug_print("MimeInfo[%s] %p level=%d\n", text, x, x ? x->level : 0);
73 if (!x)
74 return;
75
76 debug_print(" enc=`%s' enc_type=%d mime_type=%d\n",
77 x->encoding, x->encoding_type, x->mime_type);
78 debug_print(" cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
79 x->content_type, x->charset, x->name, x->boundary );
80 debug_print(" cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
81 x->content_disposition, x->filename, x->fpos, x->size,
82 x->level );
83 dump_mimeinfo(".main", x->main );
84 dump_mimeinfo(".sub", x->sub );
85 dump_mimeinfo(".next", x->next );
86 debug_print("MimeInfo[.parent] %p\n", x );
87 dump_mimeinfo(".children", x->children );
88 dump_mimeinfo(".plaintext", x->plaintext );
89}
90
91static void dump_part(MimeInfo *mimeinfo, FILE *fp)
92{
93 guint size = mimeinfo->size;
94 gint c;
95
96 if (fseek(fp, mimeinfo->fpos, SEEK_SET)) {
97 debug_print("dump_part: fseek error\n");
98 return;
99 }
100
101 debug_print("--- begin dump_part ----\n");
102 while (size-- && (c = getc (fp)) != EOF)
103 putc(c, stderr);
104 if (ferror(fp))
105 debug_print("dump_part: read error\n");
106 debug_print("--- end dump_part ----\n");
107}
108#endif
109
110void rfc2015_disable_all(void)
111{
112 gpg_available = FALSE;
113}
114
115gboolean rfc2015_is_available(void)
116{
117 return gpg_available;
118}
119
120void rfc2015_secure_remove(const gchar *fname)
121{
122 if (!fname)
123 return;
124 /* fixme: overwrite the file first */
125 g_remove(fname);
126}
127
128static void sig_status_for_key(GString *str, gpgme_ctx_t ctx,
129 gpgme_signature_t sig)
130{
131 gpgme_key_t key;
132 gpgme_user_id_t user;
133 gpgme_error_t err;
134
135 err = gpgme_get_key(ctx, sig->fpr, &key, 0);
136 if (err || key == NULL || key->uids->uid == NULL) {
137 if (err)
138 debug_print("gpgme_get_key failed: %s\n",
139 gpgme_strerror(err));
140 g_string_sprintfa(str, "%s\n",
141 gpgmegtk_sig_status_to_string (sig, FALSE));
142 if ((sig->fpr != NULL) && (*(sig->fpr) != '\0'))
143 g_string_sprintfa
144 (str, "Key fingerprint: %s\n", sig->fpr);
145 g_string_append(str, _("Cannot find user ID for this key."));
146 g_string_append(str, "\n");
147 return;
148 }
149 user = key->uids;
150 g_string_sprintfa
151 (str, gpgmegtk_sig_status_to_string (sig, TRUE), user->uid);
152 g_string_append(str, "\n");
153
154 user = user->next;
155 while (user) {
156 g_string_sprintfa
157 (str, _(" aka \"%s\"\n"), user->uid);
158 user = user->next;
159 }
160}
161
162static gchar *sig_status_full(gpgme_ctx_t ctx, gpgme_verify_result_t result)
163{
164 GString *str;
165 gpgme_signature_t sig;
166 time_t created;
167 struct tm *ctime_val;
168 gchar ctime_str[80];
169 gchar *ctime_str_utf8;
170 gchar *retval;
171
172 g_return_val_if_fail(result != NULL, NULL);
173
174 str = g_string_new("");
175
176 sig = result->signatures;
177 while (sig != NULL) {
178 if (sig->timestamp != 0) {
179 created = sig->timestamp;
180 ctime_val = localtime(&created);
181 my_strftime(ctime_str, sizeof (ctime_str), "%c",
182 ctime_val);
183 ctime_str_utf8 = g_locale_to_utf8(ctime_str, -1,
184 NULL, NULL, NULL);
185 if (!ctime_str_utf8)
186 ctime_str_utf8 = g_strdup(ctime_str);
187 g_string_sprintfa(str, _("Signature made at %s\n"),
188 ctime_str_utf8);
189 g_free(ctime_str_utf8);
190 }
191 sig_status_for_key(str, ctx, sig);
192 if (sig->next)
193 g_string_append(str, "\n\n");
194 sig = sig->next;
195 }
196
197 retval = str->str;
198 g_string_free(str, FALSE);
199 return retval;
200}
201
202static void check_signature(MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
203{
204 gpgme_ctx_t ctx = NULL;
205 gpgme_error_t err;
206 gpgme_data_t sig = NULL, text = NULL;
207 gpgme_verify_result_t verifyresult = NULL;
208 GpgmegtkSigStatus statuswindow = NULL;
209 const gchar *result = NULL;
210 gchar *tmp_file;
211 gint n_exclude_chars = 0;
212
213 if (prefs_common.gpg_signature_popup)
214 statuswindow = gpgmegtk_sig_status_create();
215
216 err = gpgme_new(&ctx);
217 if (err) {
218 debug_print("gpgme_new failed: %s\n", gpgme_strerror(err));
219 goto leave;
220 }
221
222 if (rfc2015_is_pkcs7_signature_part(partinfo)) {
223 debug_print("pkcs7 signature detected\n");
224 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
225 }
226
227 /* don't include the last empty line.
228 It does not belong to the signed text */
229 if (mimeinfo->children->size > 0) {
230 if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1,
231 SEEK_SET) < 0) {
232 perror("fseek");
233 goto leave;
234 }
235 if (fgetc(fp) == '\n') {
236 n_exclude_chars++;
237 if (mimeinfo->children->size > 1) {
238 if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2,
239 SEEK_SET) < 0) {
240 perror("fseek");
241 goto leave;
242 }
243 if (fgetc(fp) == '\r')
244 n_exclude_chars++;
245 }
246 }
247 }
248
249 /* canonicalize the file part. */
250 tmp_file = get_tmp_file();
251 if (copy_file_part(fp, mimeinfo->children->fpos,
252 mimeinfo->children->size - n_exclude_chars,
253 tmp_file) < 0) {
254 g_free(tmp_file);
255 goto leave;
256 }
257 if (canonicalize_file_replace(tmp_file) < 0) {
258 g_unlink(tmp_file);
259 g_free(tmp_file);
260 goto leave;
261 }
262
263 err = gpgme_data_new_from_file(&text, tmp_file, 1);
264
265 g_unlink(tmp_file);
266 g_free(tmp_file);
267
268 if (!err)
269 err = gpgme_data_new_from_filepart(&sig, NULL, fp,
270 partinfo->fpos,
271 partinfo->size);
272 if (err) {
273 debug_print("gpgme_data_new_from_filepart failed: %s\n",
274 gpgme_strerror (err));
275 goto leave;
276 }
277
278#if 0
279 if (partinfo->encoding_type == ENC_BASE64) {
280 err = gpgme_data_set_encoding(sig, GPGME_DATA_ENCODING_BASE64);
281 if (err) {
282 debug_print("gpgme_data_set_encoding failed: %s\n",
283 gpgme_strerror (err));
284 goto leave;
285 }
286 }
287#endif
288
289 err = gpgme_op_verify(ctx, sig, text, NULL);
290 if (err) {
291 debug_print("gpgme_op_verify failed: %s\n",
292 gpgme_strerror (err));
293 goto leave;
294 }
295 verifyresult = gpgme_op_verify_result(ctx);
296
297 /* FIXME: check what the heck this sig_status_full stuff is.
298 * Maybe it belongs in sigstatus.c
299 *
300 * I think it belongs here as it is interfacing with gmime (Toshio). */
301 g_free (partinfo->sigstatus_full);
302 partinfo->sigstatus_full = sig_status_full(ctx, verifyresult);
303
304leave:
305 if (verifyresult) {
306 result = gpgmegtk_sig_status_to_string
307 (verifyresult->signatures, FALSE);
308 } else {
309 result = _("Error verifying the signature");
310 }
311 debug_print("verification status: %s\n", result);
312 if (prefs_common.gpg_signature_popup)
313 gpgmegtk_sig_status_update(statuswindow, ctx);
314
315 g_free (partinfo->sigstatus);
316 partinfo->sigstatus = g_strdup (result);
317
318 gpgme_data_release(sig);
319 gpgme_data_release(text);
320 if (ctx)
321 gpgme_release(ctx);
322 if (prefs_common.gpg_signature_popup)
323 gpgmegtk_sig_status_destroy(statuswindow);
324}
325
326/*
327 * Copy a gpgme data object to a temporary file and
328 * return this filename
329 */
330#if 0
331static gchar *copy_gpgmedata_to_temp(GpgmeData data, guint *length)
332{
333 static gint id;
334 gchar *tmp;
335 FILE *fp;
336 gchar buf[100];
337 size_t nread;
338 GpgmeError err;
339
340 tmp = g_strdup_printf("%s%cgpgtmp.%08x",
341 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
342
343 if ((fp = g_fopen(tmp, "wb")) == NULL) {
344 FILE_OP_ERROR(tmp, "fopen");
345 g_free(tmp);
346 return NULL;
347 }
348
349 err = gpgme_data_rewind(data);
350 if (err)
351 debug_print("gpgme_data_rewind failed: %s\n",
352 gpgme_strerror(err));
353
354 while (!(err = gpgme_data_read(data, buf, 100, &nread))) {
355 fwrite(buf, nread, 1, fp);
356 }
357
358 if (err != GPGME_EOF)
359 debug_print("gpgme_data_read failed: %s\n",
360 gpgme_strerror(err));
361
362 fclose (fp);
363 *length = nread;
364
365 return tmp;
366}
367#endif
368
369static gpgme_data_t pgp_decrypt(MsgInfo *msginfo, MimeInfo *partinfo, FILE *fp)
370{
371 gpgme_ctx_t ctx = NULL;
372 gpgme_error_t err;
373 gpgme_data_t cipher = NULL, plain = NULL;
374 struct passphrase_cb_info_s info;
375 gpgme_verify_result_t verifyresult = NULL;
376 const gchar *result = NULL;
377
378 memset(&info, 0, sizeof info);
379
380 err = gpgme_new(&ctx);
381 if (err) {
382 debug_print("gpgme_new failed: %s\n", gpgme_strerror(err));
383 goto leave;
384 }
385
386 err = gpgme_data_new_from_filepart(&cipher, NULL, fp,
387 partinfo->fpos, partinfo->size);
388 if (err) {
389 debug_print("gpgme_data_new_from_filepart failed: %s\n",
390 gpgme_strerror(err));
391 goto leave;
392 }
393
394 err = gpgme_data_new(&plain);
395 if (err) {
396 debug_print("gpgme_new failed: %s\n", gpgme_strerror(err));
397 goto leave;
398 }
399
400 if (!g_getenv("GPG_AGENT_INFO")) {
401 info.c = ctx;
402 gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info);
403 }
404
405 err = gpgme_op_decrypt_verify(ctx, cipher, plain);
406
407 msginfo->encinfo = g_new0(MsgEncryptInfo, 1);
408
409 if (err) {
410 gpgmegtk_free_passphrase();
411 debug_print("decryption failed: %s\n", gpgme_strerror(err));
412 gpgme_data_release(plain);
413 plain = NULL;
414 msginfo->encinfo->decryption_failed = TRUE;
415 goto leave;
416 }
417
418 debug_print("** decryption succeeded\n");
419
420 verifyresult = gpgme_op_verify_result(ctx);
421 if (verifyresult && verifyresult->signatures) {
422 result = gpgmegtk_sig_status_to_string(verifyresult->signatures,
423 FALSE);
424 msginfo->encinfo->sigstatus = g_strdup(result);
425 msginfo->encinfo->sigstatus_full =
426 sig_status_full(ctx, verifyresult);
427 debug_print("verification status: %s\n", result);
428 debug_print("full status: %s\n",
429 msginfo->encinfo->sigstatus_full);
430 if (prefs_common.gpg_signature_popup) {
431 GpgmegtkSigStatus statuswindow;
432 statuswindow = gpgmegtk_sig_status_create();
433 gpgmegtk_sig_status_update(statuswindow, ctx);
434 gpgmegtk_sig_status_destroy(statuswindow);
435 }
436 }
437
438
439leave:
440 gpgme_data_release(cipher);
441 if (ctx)
442 gpgme_release(ctx);
443 return plain;
444}
445
446MimeInfo **rfc2015_find_signature(MimeInfo *mimeinfo)
447{
448 MimeInfo *partinfo;
449 MimeInfo **signedinfo = NULL;
450 gint n = 0;
451
452 if (!mimeinfo)
453 return NULL;
454
455 /* We could have a signature nested within multipart/mixed so
456 * recurse to find it.
457 */
458 if (!g_ascii_strcasecmp(mimeinfo->content_type, "multipart/mixed")) {
459 for (partinfo = mimeinfo->children; partinfo != NULL;
460 partinfo = partinfo->next) {
461 signedinfo = rfc2015_find_signature(partinfo);
462 if (signedinfo) {
463 return signedinfo;
464 }
465 }
466 return NULL;
467 }
468 if (g_ascii_strcasecmp(mimeinfo->content_type, "multipart/signed"))
469 return NULL;
470
471 debug_print("** multipart/signed encountered\n");
472
473 /* check that we have at least 2 parts of the correct type */
474 for (partinfo = mimeinfo->children;
475 partinfo != NULL; partinfo = partinfo->next) {
476 if (++n > 1 && rfc2015_is_signature_part(partinfo))
477 break;
478 }
479
480 if (partinfo) {
481 signedinfo = g_malloc(sizeof(MimeInfo *) * 2);
482 signedinfo[0] = mimeinfo;
483 signedinfo[1] = partinfo;
484 }
485 /* This is NULL if partinfo was not set */
486 return signedinfo;
487}
488
489gboolean rfc2015_has_signature(MimeInfo *mimeinfo)
490{
491 return rfc2015_find_signature(mimeinfo) != NULL;
492}
493
494void rfc2015_check_signature(MimeInfo *mimeinfo, FILE *fp)
495{
496 MimeInfo **signedinfo;
497
498 signedinfo = rfc2015_find_signature(mimeinfo);
499 if (!signedinfo)
500 return;
501
502#if 0
503 g_message("** yep, it is a pgp signature");
504 dump_mimeinfo("gpg-signature", partinfo );
505 dump_part(partinfo, fp );
506 dump_mimeinfo("signed text", mimeinfo->children );
507 dump_part(mimeinfo->children, fp);
508#endif
509
510 check_signature(signedinfo[0], signedinfo[1], fp);
511 g_free(signedinfo);
512}
513
514gboolean rfc2015_is_pgp_signature_part(MimeInfo *mimeinfo)
515{
516 if (!mimeinfo || !mimeinfo->content_type)
517 return FALSE;
518
519 return !g_ascii_strcasecmp(mimeinfo->content_type,
520 "application/pgp-signature");
521}
522
523gboolean rfc2015_is_pkcs7_signature_part(MimeInfo *mimeinfo)
524{
525 const gchar *c_type;
526
527 if (!mimeinfo || !mimeinfo->content_type)
528 return FALSE;
529
530 c_type = mimeinfo->content_type;
531
532 return (!g_ascii_strcasecmp(c_type, "application/pkcs7-signature") ||
533 !g_ascii_strcasecmp(c_type, "application/x-pkcs7-signature"));
534}
535
536gboolean rfc2015_is_signature_part(MimeInfo *mimeinfo)
537{
538 return (rfc2015_is_pgp_signature_part(mimeinfo) ||
539 rfc2015_is_pkcs7_signature_part(mimeinfo));
540}
541
542gint rfc2015_is_encrypted(MimeInfo *mimeinfo)
543{
544 if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART)
545 return 0;
546 if (g_ascii_strcasecmp(mimeinfo->content_type, "multipart/encrypted"))
547 return 0;
548 /* fixme: we should check the protocol parameter */
549 return 1;
550}
551
552gboolean rfc2015_msg_is_encrypted(const gchar *file)
553{
554 FILE *fp;
555 MimeInfo *mimeinfo;
556 gint ret;
557
558 if ((fp = g_fopen(file, "rb")) == NULL)
559 return FALSE;
560
561 mimeinfo = procmime_scan_mime_header(fp);
562 if(!mimeinfo) {
563 fclose(fp);
564 return FALSE;
565 }
566
567 ret = rfc2015_is_encrypted(mimeinfo);
568 procmime_mimeinfo_free_all(mimeinfo);
569 fclose(fp);
570
571 return ret != 0 ? TRUE : FALSE;
572}
573
574static gint name_cmp(const gchar *a, const gchar *b)
575{
576 for( ; *a && *b; a++, b++) {
577 if (*a != *b &&
578 g_ascii_toupper(*(guchar *)a) != g_ascii_toupper(*(guchar *)b))
579 return 1;
580 }
581
582 return *a != *b;
583}
584
585static gint headerp(gchar *p, gchar **names)
586{
587 gint i, c;
588 gchar *p2;
589
590 p2 = strchr(p, ':');
591 if (!p2 || p == p2) {
592 return 0;
593 }
594 if (p2[-1] == ' ' || p2[-1] == '\t') {
595 return 0;
596 }
597
598 if (!names[0])
599 return 1;
600
601 c = *p2;
602 *p2 = 0;
603 for(i = 0 ; names[i] != NULL; i++) {
604 if (!name_cmp(names[i], p))
605 break;
606 }
607 *p2 = c;
608
609 return names[i] != NULL;
610}
611
612
613#define DECRYPTION_ABORT() \
614{ \
615 procmime_mimeinfo_free_all(tmpinfo); \
616 if (msginfo->encinfo) \
617 msginfo->encinfo->decryption_failed = TRUE; \
618 return; \
619}
620
621void rfc2015_decrypt_message(MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
622{
623 static gint id;
624 MimeInfo *tmpinfo, *partinfo;
625 gint ver_ok = 0;
626 gchar *fname;
627 gpgme_data_t plain;
628 FILE *dstfp;
629 ssize_t nread;
630 gchar buf[BUFFSIZE];
631 gint in_cline;
632 gpgme_error_t err;
633
634 g_return_if_fail(msginfo != NULL);
635 g_return_if_fail(mimeinfo != NULL);
636 g_return_if_fail(fp != NULL);
637 g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART);
638
639 debug_print("** decrypting multipart/encrypted message\n");
640
641 /* skip headers */
642 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
643 perror("fseek");
644 tmpinfo = procmime_scan_mime_header(fp);
645 if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
646 DECRYPTION_ABORT();
647 }
648
649 procmime_scan_multipart_message(tmpinfo, fp);
650
651 /* check that we have the 2 parts */
652 partinfo = tmpinfo->children;
653 if (!partinfo || !partinfo->next) {
654 DECRYPTION_ABORT();
655 }
656 if (!g_ascii_strcasecmp(partinfo->content_type,
657 "application/pgp-encrypted")) {
658 /* Fixme: check that the version is 1 */
659 ver_ok = 1;
660 }
661 partinfo = partinfo->next;
662 if (ver_ok &&
663 !g_ascii_strcasecmp(partinfo->content_type,
664 "application/octet-stream")) {
665 if (partinfo->next)
666 g_warning("oops: pgp_encrypted with more than 2 parts");
667 } else {
668 DECRYPTION_ABORT();
669 }
670
671 debug_print("** yep, it is pgp encrypted\n");
672
673 plain = pgp_decrypt(msginfo, partinfo, fp);
674 if (!plain) {
675 DECRYPTION_ABORT();
676 }
677
678 fname = g_strdup_printf("%s%cplaintext.%08x",
679 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
680
681 if ((dstfp = g_fopen(fname, "wb")) == NULL) {
682 FILE_OP_ERROR(fname, "fopen");
683 g_free(fname);
684 DECRYPTION_ABORT();
685 }
686
687 /* write the orginal header to the new file */
688 if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
689 perror("fseek");
690
691 in_cline = 0;
692 while (fgets(buf, sizeof(buf), fp)) {
693 if (headerp(buf, content_names)) {
694 in_cline = 1;
695 continue;
696 }
697 if (in_cline) {
698 if (buf[0] == ' ' || buf[0] == '\t')
699 continue;
700 in_cline = 0;
701 }
702 if (buf[0] == '\r' || buf[0] == '\n')
703 break;
704 fputs(buf, dstfp);
705 }
706
707 err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ?
708 gpgme_error_from_errno(errno) : 0;
709 if (err)
710 debug_print("gpgme_data_seek failed: %s\n", gpgme_strerror(err));
711
712 nread = gpgme_data_read(plain, buf, sizeof(buf));
713 while (nread > 0) {
714 fwrite (buf, nread, 1, dstfp);
715 nread = gpgme_data_read(plain, buf, sizeof(buf));
716 }
717
718 if (nread != 0) {
719 debug_print("gpgme_data_read failed: %s\n",
720 gpgme_strerror(gpgme_error_from_errno(errno)));
721 }
722
723 fclose(dstfp);
724 procmime_mimeinfo_free_all(tmpinfo);
725
726 msginfo->encinfo->plaintext_file = fname;
727}
728
729#undef DECRYPTION_ABORT
730
731FILE *rfc2015_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
732{
733 FILE *fp;
734 MimeInfo *mimeinfo_;
735 glong fpos;
736
737 g_return_val_if_fail(msginfo != NULL, NULL);
738
739 if (mimeinfo) *mimeinfo = NULL;
740
741 if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL;
742
743 mimeinfo_ = procmime_scan_mime_header(fp);
744 if (!mimeinfo_) {
745 fclose(fp);
746 return NULL;
747 }
748
749 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
750 rfc2015_is_encrypted(mimeinfo_)) {
751 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
752 }
753
754 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
755 (!msginfo->encinfo ||
756 (!msginfo->encinfo->plaintext_file &&
757 !msginfo->encinfo->decryption_failed))) {
758 fpos = ftell(fp);
759 rfc2015_decrypt_message(msginfo, mimeinfo_, fp);
760 if (msginfo->encinfo &&
761 msginfo->encinfo->plaintext_file &&
762 !msginfo->encinfo->decryption_failed) {
763 fclose(fp);
764 procmime_mimeinfo_free_all(mimeinfo_);
765
766 if ((fp = procmsg_open_message(msginfo)) == NULL)
767 return NULL;
768 mimeinfo_ = procmime_scan_mime_header(fp);
769 if (!mimeinfo_) {
770 fclose(fp);
771 return NULL;
772 }
773 } else {
774 if (fseek(fp, fpos, SEEK_SET) < 0)
775 perror("fseek");
776 }
777 }
778
779 if (mimeinfo) *mimeinfo = mimeinfo_;
780 return fp;
781}
782
783
784/*
785 * plain contains an entire mime object.
786 * Encrypt it and return an GpgmeData object with the encrypted version of
787 * the file or NULL in case of error.
788 */
789static gpgme_data_t pgp_encrypt(gpgme_data_t plain, gpgme_key_t kset[])
790{
791 gpgme_ctx_t ctx = NULL;
792 gpgme_error_t err;
793 gpgme_data_t cipher = NULL;
794
795 err = gpgme_new(&ctx);
796 if (!err)
797 err = gpgme_data_new(&cipher);
798 if (!err) {
799 gpgme_set_armor(ctx, 1);
800 err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ?
801 gpgme_error_from_errno(errno) : 0;
802 if (!err) {
803 /*
804 * Note -- it is currently the responsibility of select-keys.c::
805 * gpgmegtk_recipient_selection() to prompt the user whether to
806 * encrypt to recipients whose key is not trusted.
807 */
808 err = gpgme_op_encrypt(ctx, kset,
809 GPGME_ENCRYPT_ALWAYS_TRUST,
810 plain, cipher);
811 }
812 }
813
814 if (err) {
815 g_warning("pgp_encrypt(): encryption failed: %s\n",
816 gpgme_strerror(err));
817 gpgme_data_release(cipher);
818 cipher = NULL;
819 } else {
820 debug_print("** encryption succeeded\n");
821 }
822
823 if (ctx)
824 gpgme_release(ctx);
825 return cipher;
826}
827
828/*
829 * Create and return a list of keys matching a key id
830 */
831
832GSList *rfc2015_create_signers_list(const gchar *keyid)
833{
834 GSList *key_list = NULL;
835 gpgme_ctx_t list_ctx = NULL;
836 GSList *p;
837 gpgme_error_t err;
838 gpgme_key_t key;
839
840 err = gpgme_new(&list_ctx);
841 if (err)
842 goto leave;
843 err = gpgme_op_keylist_start(list_ctx, keyid, 1);
844 if (err)
845 goto leave;
846 while (!(err = gpgme_op_keylist_next(list_ctx, &key))) {
847 key_list = g_slist_append(key_list, key);
848 }
849 if (gpgme_err_code(err) != GPG_ERR_EOF)
850 goto leave;
851 err = 0;
852 if (key_list == NULL) {
853 debug_print("no keys found for keyid \"%s\"\n", keyid);
854 }
855
856leave:
857 if (err) {
858 debug_print("rfc2015_create_signers_list failed: %s\n",
859 gpgme_strerror(err));
860 for (p = key_list; p != NULL; p = p->next)
861 gpgme_key_unref((gpgme_key_t)p->data);
862 g_slist_free(key_list);
863 }
864 if (list_ctx)
865 gpgme_release(list_ctx);
866 return err ? NULL : key_list;
867}
868
869/*
870 * Encrypt the file by extracting all recipients and finding the
871 * encryption keys for all of them. The file content is then replaced
872 * by the encrypted one. */
873gint rfc2015_encrypt(const gchar *file, GSList *recp_list)
874{
875 FILE *fp = NULL;
876 gchar buf[BUFFSIZE];
877 gint i, clineidx, saved_last;
878 gchar *clines[3] = {NULL};
879 gpgme_error_t err;
880 gpgme_data_t header = NULL;
881 gpgme_data_t plain = NULL;
882 gpgme_data_t cipher = NULL;
883 gpgme_key_t *kset = NULL;
884 ssize_t bytesRW = 0;
885 gint mime_version_seen = 0;
886 gchar *boundary;
887
888 boundary = generate_mime_boundary("Encrypt");
889
890 /* Create the list of recipients */
891 kset = gpgmegtk_recipient_selection(recp_list);
892 if (!kset) {
893 debug_print("error creating recipient list\n");
894 goto failure;
895 }
896
897 /* Open the source file */
898 if ((fp = g_fopen(file, "rb")) == NULL) {
899 FILE_OP_ERROR(file, "fopen");
900 goto failure;
901 }
902
903 err = gpgme_data_new(&header);
904 if (!err)
905 err = gpgme_data_new(&plain);
906 if (err) {
907 debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
908 goto failure;
909 }
910
911 /* get the content header lines from the source */
912 clineidx = 0;
913 saved_last = 0;
914 while (!err && fgets(buf, sizeof(buf), fp)) {
915 /* fixme: check for overlong lines */
916 if (headerp(buf, content_names)) {
917 if (clineidx >= DIM(clines)) {
918 debug_print("rfc2015_encrypt: too many content lines\n");
919 goto failure;
920 }
921 clines[clineidx++] = g_strdup(buf);
922 saved_last = 1;
923 continue;
924 }
925 if (saved_last) {
926 if (*buf == ' ' || *buf == '\t') {
927 gchar *last = clines[clineidx - 1];
928 clines[clineidx - 1] = g_strconcat(last, buf, NULL);
929 g_free(last);
930 continue;
931 }
932 saved_last = 0;
933 }
934
935 if (headerp(buf, mime_version_name))
936 mime_version_seen = 1;
937
938 if (buf[0] == '\r' || buf[0] == '\n')
939 break;
940 bytesRW = gpgme_data_write(header, buf, strlen(buf));
941 }
942 if (ferror(fp)) {
943 FILE_OP_ERROR(file, "fgets");
944 goto failure;
945 }
946
947 /* write them to the temp data and add the rest of the message */
948 for (i = 0; (bytesRW != -1) && i < clineidx; i++) {
949 debug_print("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
950 bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i]));
951 }
952 if (bytesRW != -1)
953 bytesRW = gpgme_data_write (plain, "\r\n", 2);
954 while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) {
955 bytesRW = gpgme_data_write(plain, buf, strlen(buf));
956 }
957 if (ferror(fp)) {
958 FILE_OP_ERROR(file, "fgets");
959 goto failure;
960 }
961 if (bytesRW == -1) {
962 debug_print("gpgme_data_write failed: %s\n",
963 gpgme_strerror(gpgme_error_from_errno(errno)));
964 goto failure;
965 }
966
967 cipher = pgp_encrypt(plain, kset);
968 gpgme_data_release(plain);
969 plain = NULL;
970 i = 0;
971 while (kset[i] != NULL) {
972 gpgme_key_unref(kset[i]);
973 i++;
974 }
975 g_free(kset);
976 kset = NULL;
977 if (!cipher)
978 goto failure;
979
980 /* we have the encrypted message available in cipher and now we
981 * are going to rewrite the source file. To be sure that file has
982 * been truncated we use an approach which should work everywhere:
983 * close the file and then reopen it for writing. It is important
984 * that this works, otherwise it may happen that parts of the
985 * plaintext are still in the file (The encrypted stuff is, due to
986 * compression, usually shorter than the plaintext).
987 *
988 * Yes, there is a race condition here, but everyone, who is so
989 * stupid to store the temp file with the plaintext in a public
990 * directory has to live with this anyway. */
991 if (fclose (fp)) {
992 FILE_OP_ERROR(file, "fclose");
993 goto failure;
994 }
995 if ((fp = g_fopen(file, "wb")) == NULL) {
996 FILE_OP_ERROR(file, "fopen");
997 goto failure;
998 }
999
1000 /* Write the header, append new content lines, part 1 and part 2 header */
1001 err = (gpgme_data_seek(header, 0 , SEEK_SET) == -1) ?
1002 gpgme_error_from_errno(errno) : 0;
1003 if (err) {
1004 debug_print("gpgme_data_seek failed: %s\n",
1005 gpgme_strerror(err));
1006 goto failure;
1007 }
1008 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1009 while (bytesRW > 0) {
1010 fwrite (buf, bytesRW, 1, fp);
1011 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1012 }
1013
1014 if (bytesRW != 0) {
1015 debug_print("gpgme_data_read failed: %s\n",
1016 gpgme_strerror(gpgme_error_from_errno(errno)));
1017 goto failure;
1018 }
1019
1020 if (ferror (fp)) {
1021 FILE_OP_ERROR(file, "fwrite");
1022 goto failure;
1023 }
1024 gpgme_data_release(header);
1025 header = NULL;
1026
1027 if (!mime_version_seen)
1028 fputs("MIME-Version: 1.0\r\n", fp);
1029
1030 fprintf(fp,
1031 "Content-Type: multipart/encrypted;"
1032 " protocol=\"application/pgp-encrypted\";\r\n"
1033 " boundary=\"%s\"\r\n"
1034 "\r\n"
1035 "--%s\r\n"
1036 "Content-Type: application/pgp-encrypted\r\n"
1037 "\r\n"
1038 "Version: 1\r\n"
1039 "\r\n"
1040 "--%s\r\n"
1041 "Content-Type: application/octet-stream\r\n"
1042 "\r\n",
1043 boundary, boundary, boundary);
1044
1045 /* append the encrypted stuff */
1046 err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ?
1047 gpgme_error_from_errno(errno) : 0;
1048 if (err) {
1049 debug_print("** gpgme_data_seek on cipher failed: %s\n",
1050 gpgme_strerror(err));
1051 debug_print("gpgme_data_seek failed: %s\n",
1052 gpgme_strerror(err));
1053 goto failure;
1054 }
1055
1056 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1057 while (bytesRW > 0) {
1058 fwrite(buf, bytesRW, 1, fp);
1059 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1060 }
1061
1062 if (bytesRW != 0) {
1063 debug_print("** gpgme_data_read failed: %s\n",
1064 gpgme_strerror(gpgme_error_from_errno(errno)));
1065 goto failure;
1066 }
1067
1068 /* and the final boundary */
1069 fprintf(fp,
1070 "\r\n"
1071 "--%s--\r\n",
1072 boundary);
1073 fflush(fp);
1074 if (ferror(fp)) {
1075 FILE_OP_ERROR(file, "fwrite");
1076 goto failure;
1077 }
1078 fclose(fp);
1079 gpgme_data_release(cipher);
1080 return 0;
1081
1082failure:
1083 if (fp)
1084 fclose (fp);
1085 gpgme_data_release(header);
1086 gpgme_data_release(plain);
1087 gpgme_data_release(cipher);
1088
1089 if (kset != NULL) {
1090 i = 0;
1091 while (kset[i] != NULL) {
1092 gpgme_key_unref(kset[i]);
1093 i++;
1094 }
1095 g_free(kset);
1096 }
1097 g_free(boundary);
1098 return -1; /* error */
1099}
1100
1101gint rfc2015_encrypt_armored(const gchar *file, GSList *recp_list)
1102{
1103 FILE *fp = NULL;
1104 gchar buf[BUFFSIZE];
1105 gint i;
1106 gpgme_error_t err;
1107 gpgme_data_t plain = NULL;
1108 gpgme_data_t cipher = NULL;
1109 gpgme_key_t *kset = NULL;
1110 ssize_t bytesRW = 0;
1111
1112 kset = gpgmegtk_recipient_selection(recp_list);
1113 if (!kset) {
1114 debug_print("error creating recipient list\n");
1115 goto failure;
1116 }
1117
1118 /* Open the source file */
1119 if ((fp = g_fopen(file, "rb")) == NULL) {
1120 FILE_OP_ERROR(file, "fopen");
1121 goto failure;
1122 }
1123
1124 err = gpgme_data_new(&plain);
1125 if (err) {
1126 g_warning("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1127 goto failure;
1128 }
1129
1130 while (bytesRW != -1 && fgets(buf, sizeof(buf), fp)) {
1131 bytesRW = gpgme_data_write(plain, buf, strlen(buf));
1132 }
1133 if (ferror(fp)) {
1134 FILE_OP_ERROR(file, "fgets");
1135 goto failure;
1136 }
1137 if (bytesRW == -1) {
1138 debug_print("gpgme_data_write failed: %s\n",
1139 gpgme_strerror(gpgme_error_from_errno(errno)));
1140 goto failure;
1141 }
1142
1143 cipher = pgp_encrypt(plain, kset);
1144 gpgme_data_release(plain);
1145 plain = NULL;
1146 i = 0;
1147 while (kset[i] != NULL) {
1148 gpgme_key_unref(kset[i]);
1149 i++;
1150 }
1151 g_free(kset);
1152 kset = NULL;
1153 if (!cipher)
1154 goto failure;
1155
1156 if (fclose(fp)) {
1157 FILE_OP_ERROR(file, "fclose");
1158 goto failure;
1159 }
1160 if ((fp = g_fopen(file, "wb")) == NULL) {
1161 FILE_OP_ERROR(file, "fopen");
1162 goto failure;
1163 }
1164
1165 err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ?
1166 gpgme_error_from_errno(errno) : 0;
1167 if (err) {
1168 debug_print("** gpgme_data_seek on cipher failed: %s\n",
1169 gpgme_strerror(err));
1170 debug_print("gpgme_data_seek failed: %s\n",
1171 gpgme_strerror(err));
1172 goto failure;
1173 }
1174
1175 bytesRW = gpgme_data_read(cipher, buf, sizeof(buf));
1176 while (bytesRW > 0) {
1177 fwrite(buf, bytesRW, 1, fp);
1178 bytesRW = gpgme_data_read(cipher, buf, sizeof(buf));
1179 }
1180
1181 if (bytesRW != 0) {
1182 debug_print("** gpgme_data_read failed: %s\n",
1183 gpgme_strerror(gpgme_error_from_errno(errno)));
1184 goto failure;
1185 }
1186
1187 fflush(fp);
1188 if (ferror(fp)) {
1189 FILE_OP_ERROR(file, "fwrite");
1190 goto failure;
1191 }
1192 fclose(fp);
1193 gpgme_data_release(cipher);
1194 return 0;
1195
1196failure:
1197 if (fp)
1198 fclose(fp);
1199 gpgme_data_release(plain);
1200 gpgme_data_release(cipher);
1201 if (kset != NULL) {
1202 i = 0;
1203 while (kset[i] != NULL) {
1204 gpgme_key_unref(kset[i]);
1205 i++;
1206 }
1207 g_free(kset);
1208 }
1209
1210 return -1;
1211}
1212
1213/*
1214 * plain contains an entire mime object. Sign it and return an
1215 * GpgmeData object with the signature of it or NULL in case of error.
1216 * micalg returns the micalg information about the signature.
1217 */
1218static gpgme_data_t pgp_sign(gpgme_data_t plain, GSList *key_list,
1219 gboolean clearsign, gchar **micalg)
1220{
1221 GSList *p;
1222 gpgme_ctx_t ctx = NULL;
1223 gpgme_error_t err;
1224 gpgme_data_t sig = NULL;
1225 gpgme_sign_result_t result = NULL;
1226 struct passphrase_cb_info_s info;
1227
1228 *micalg = NULL;
1229 memset(&info, 0, sizeof info);
1230
1231 err = gpgme_new(&ctx);
1232 if (err)
1233 goto leave;
1234 err = gpgme_data_new(&sig);
1235 if (err)
1236 goto leave;
1237
1238 if (!g_getenv("GPG_AGENT_INFO")) {
1239 info.c = ctx;
1240 gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info);
1241 }
1242 gpgme_set_textmode(ctx, 1);
1243 gpgme_set_armor(ctx, 1);
1244 gpgme_signers_clear(ctx);
1245 for (p = key_list; p != NULL; p = p->next) {
1246 err = gpgme_signers_add(ctx, (gpgme_key_t) p->data);
1247 if (err)
1248 goto leave;
1249 }
1250 for (p = key_list; p != NULL; p = p->next)
1251 gpgme_key_unref((gpgme_key_t) p->data);
1252 g_slist_free(key_list);
1253
1254 err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ?
1255 gpgme_error_from_errno(errno) : 0;
1256 if (!err) {
1257 err = gpgme_op_sign(ctx, plain, sig,
1258 clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1259 }
1260 if (!err) {
1261 result = gpgme_op_sign_result(ctx);
1262 if (result && result->signatures) {
1263 if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
1264 *micalg = g_strdup_printf
1265 ("PGP-%s", gpgme_hash_algo_name(result->signatures->hash_algo));
1266 } else {
1267 *micalg = g_strdup(gpgme_hash_algo_name(result->signatures->hash_algo));
1268 }
1269 } else {
1270 /* can't get result (maybe no signing key?) */
1271 err = GPG_ERR_USER_1;
1272 }
1273 }
1274
1275leave:
1276 if (err) {
1277 gpgmegtk_free_passphrase();
1278 g_warning("pgp_sign(): signing failed: %s\n", gpgme_strerror(err));
1279 gpgme_data_release(sig);
1280 sig = NULL;
1281 } else {
1282 debug_print("signing succeeded\n");
1283 }
1284
1285 if (ctx)
1286 gpgme_release(ctx);
1287 return sig;
1288}
1289
1290/*
1291 * plain contains an entire mime object. Encrypt and sign it and return an
1292 * GpgmeData object with the encrypted and signed version of it or NULL in
1293 * case of error.
1294 * micalg returns the micalg information about the signature.
1295 */
1296static gpgme_data_t pgp_encrypt_sign(gpgme_data_t plain, gpgme_key_t kset[],
1297 GSList *key_list, gchar **micalg)
1298{
1299 GSList *p;
1300 gpgme_ctx_t ctx = NULL;
1301 gpgme_error_t err;
1302 gpgme_data_t cipher = NULL;
1303 gpgme_sign_result_t result = NULL;
1304 struct passphrase_cb_info_s info;
1305
1306 *micalg = NULL;
1307 memset(&info, 0, sizeof info);
1308
1309 err = gpgme_new(&ctx);
1310 if (err)
1311 goto leave;
1312 err = gpgme_data_new(&cipher);
1313 if (err)
1314 goto leave;
1315
1316 if (!g_getenv("GPG_AGENT_INFO")) {
1317 info.c = ctx;
1318 gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info);
1319 }
1320 gpgme_set_textmode(ctx, 1);
1321 gpgme_set_armor(ctx, 1);
1322 gpgme_signers_clear(ctx);
1323 for (p = key_list; p != NULL; p = p->next) {
1324 err = gpgme_signers_add(ctx, (gpgme_key_t) p->data);
1325 if (err)
1326 goto leave;
1327 }
1328 for (p = key_list; p != NULL; p = p->next)
1329 gpgme_key_unref((gpgme_key_t) p->data);
1330 g_slist_free(key_list);
1331
1332 err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ?
1333 gpgme_error_from_errno(errno) : 0;
1334 if (!err) {
1335 err = gpgme_op_encrypt_sign(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, plain, cipher);
1336 }
1337 if (!err) {
1338 result = gpgme_op_sign_result(ctx);
1339 if (result && result->signatures) {
1340 if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
1341 *micalg = g_strdup_printf
1342 ("PGP-%s", gpgme_hash_algo_name(result->signatures->hash_algo));
1343 } else {
1344 *micalg = g_strdup(gpgme_hash_algo_name(result->signatures->hash_algo));
1345 }
1346 } else {
1347 /* can't get result (maybe no signing key?) */
1348 err = GPG_ERR_USER_1;
1349 }
1350 }
1351
1352leave:
1353 if (err) {
1354 gpgmegtk_free_passphrase();
1355 g_warning("pgp_sign(): encryption and signing failed: %s\n", gpgme_strerror(err));
1356 gpgme_data_release(cipher);
1357 cipher = NULL;
1358 } else {
1359 debug_print("encryption and signing succeeded\n");
1360 }
1361
1362 if (ctx)
1363 gpgme_release(ctx);
1364 return cipher;
1365}
1366
1367/*
1368 * Sign the file and replace its content with the signed one.
1369 */
1370gint rfc2015_sign(const gchar *file, GSList *key_list)
1371{
1372 FILE *fp = NULL;
1373 gchar buf[BUFFSIZE];
1374 gint i, clineidx, saved_last;
1375 gchar *clines[3] = {NULL};
1376 gpgme_error_t err;
1377 gpgme_data_t header = NULL;
1378 gpgme_data_t plain = NULL;
1379 gpgme_data_t sigdata = NULL;
1380 ssize_t bytesRW = -1;
1381 gint mime_version_seen = 0;
1382 gchar *boundary;
1383 gchar *micalg = NULL;
1384
1385 boundary = generate_mime_boundary("Signature");
1386
1387 /* Open the source file */
1388 if ((fp = g_fopen(file, "rb")) == NULL) {
1389 FILE_OP_ERROR(file, "fopen");
1390 goto failure;
1391 }
1392
1393 err = gpgme_data_new(&header);
1394 if (!err)
1395 err = gpgme_data_new(&plain);
1396 if (err) {
1397 debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1398 goto failure;
1399 }
1400
1401 /* get the content header lines from the source */
1402 clineidx = 0;
1403 saved_last = 0;
1404 while (!err && fgets(buf, sizeof(buf), fp)) {
1405 /* fixme: check for overlong lines */
1406 if (headerp(buf, content_names)) {
1407 if (clineidx >= DIM(clines)) {
1408 debug_print("rfc2015_sign: too many content lines\n");
1409 goto failure;
1410 }
1411 clines[clineidx++] = g_strdup(buf);
1412 saved_last = 1;
1413 continue;
1414 }
1415 if (saved_last) {
1416 if (*buf == ' ' || *buf == '\t') {
1417 gchar *last = clines[clineidx - 1];
1418 clines[clineidx - 1] = g_strconcat(last, buf, NULL);
1419 g_free(last);
1420 continue;
1421 }
1422 saved_last = 0;
1423 }
1424
1425 if (headerp(buf, mime_version_name))
1426 mime_version_seen = 1;
1427
1428 if (buf[0] == '\r' || buf[0] == '\n')
1429 break;
1430 bytesRW = gpgme_data_write(header, buf, strlen (buf));
1431 }
1432 if (ferror (fp)) {
1433 FILE_OP_ERROR(file, "fgets");
1434 goto failure;
1435 }
1436
1437 /* write them to the temp data and add the rest of the message */
1438 for (i = 0; (bytesRW != -1) && i < clineidx; i++) {
1439 bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i]));
1440 }
1441 if (bytesRW != -1)
1442 bytesRW = gpgme_data_write(plain, "\r\n", 2 );
1443 while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) {
1444 bytesRW = gpgme_data_write(plain, buf, strlen(buf));
1445 }
1446 if (ferror(fp)) {
1447 FILE_OP_ERROR(file, "fgets");
1448 goto failure;
1449 }
1450 if (bytesRW == -1) {
1451 debug_print("gpgme_data_write failed: %s\n",
1452 gpgme_strerror(gpgme_error_from_errno(errno)));
1453 goto failure;
1454 }
1455
1456 sigdata = pgp_sign(plain, key_list, FALSE, &micalg);
1457 if (!sigdata)
1458 goto failure;
1459
1460 /* we have the signed message available in sigdata and now we are
1461 * going to rewrite the original file. To be sure that file has
1462 * been truncated we use an approach which should work everywhere:
1463 * close the file and then reopen it for writing. */
1464 if (fclose(fp)) {
1465 FILE_OP_ERROR(file, "fclose");
1466 goto failure;
1467 }
1468 if ((fp = g_fopen(file, "wb")) == NULL) {
1469 FILE_OP_ERROR(file, "fopen");
1470 goto failure;
1471 }
1472
1473 /* Write the rfc822 header and add new content lines */
1474 err = (gpgme_data_seek(header, 0, SEEK_SET) == -1) ?
1475 gpgme_error_from_errno(errno) : 0;
1476 if (err) {
1477 debug_print("gpgme_data_seek failed: %s\n",
1478 gpgme_strerror(err));
1479 goto failure;
1480 }
1481 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1482 while (bytesRW > 0) {
1483 fwrite(buf, bytesRW, 1, fp);
1484 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1485 }
1486 if (bytesRW != 0) {
1487 debug_print("gpgme_data_read failed: %s\n",
1488 gpgme_strerror(gpgme_error_from_errno(errno)));
1489 goto failure;
1490 }
1491 if (ferror(fp)) {
1492 FILE_OP_ERROR(file, "fwrite");
1493 goto failure;
1494 }
1495 gpgme_data_release(header);
1496 header = NULL;
1497
1498 if (!mime_version_seen)
1499 fputs("MIME-Version: 1.0\r\n", fp);
1500 fprintf(fp, "Content-Type: multipart/signed; "
1501 "protocol=\"application/pgp-signature\";\r\n");
1502 if (micalg)
1503 fprintf(fp, " micalg=\"%s\";\r\n", micalg);
1504 fprintf(fp, " boundary=\"%s\"\r\n", boundary);
1505
1506 /* Part 1: signed material */
1507 fprintf(fp, "\r\n"
1508 "--%s\r\n",
1509 boundary);
1510 err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ?
1511 gpgme_error_from_errno(errno) : 0;
1512 if (err) {
1513 debug_print("gpgme_data_seek on plain failed: %s\n",
1514 gpgme_strerror(err));
1515 goto failure;
1516 }
1517 bytesRW = gpgme_data_read(plain, buf, BUFFSIZE);
1518 while (bytesRW > 0) {
1519 fwrite(buf, bytesRW, 1, fp);
1520 bytesRW = gpgme_data_read(plain, buf, BUFFSIZE);
1521 }
1522 if (bytesRW != 0) {
1523 debug_print("gpgme_data_read failed: %s\n",
1524 gpgme_strerror(gpgme_error_from_errno(errno)));
1525 goto failure;
1526 }
1527
1528 /* Part 2: signature */
1529 fprintf(fp, "\r\n"
1530 "--%s\r\n",
1531 boundary);
1532 fputs("Content-Type: application/pgp-signature\r\n"
1533 "\r\n", fp);
1534
1535 err = (gpgme_data_seek(sigdata, 0, SEEK_SET) == -1) ?
1536 gpgme_error_from_errno(errno) : 0;
1537 if (err) {
1538 debug_print("gpgme_data_seek on sigdata failed: %s\n",
1539 gpgme_strerror(gpgme_error_from_errno(errno)));
1540 goto failure;
1541 }
1542
1543 bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE);
1544 while (bytesRW > 0) {
1545 fwrite(buf, bytesRW, 1, fp);
1546 bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE);
1547 }
1548 if (bytesRW != 0) {
1549 debug_print("gpgme_data_read failed: %s\n",
1550 gpgme_strerror(gpgme_error_from_errno(errno)));
1551 goto failure;
1552 }
1553
1554 /* Final boundary */
1555 fprintf(fp, "\r\n"
1556 "--%s--\r\n",
1557 boundary);
1558 fflush(fp);
1559 if (ferror(fp)) {
1560 FILE_OP_ERROR(file, "fwrite");
1561 goto failure;
1562 }
1563 fclose(fp);
1564 gpgme_data_release(header);
1565 gpgme_data_release(plain);
1566 gpgme_data_release(sigdata);
1567 g_free(boundary);
1568 g_free(micalg);
1569 return 0;
1570
1571failure:
1572 if (fp)
1573 fclose(fp);
1574 gpgme_data_release(header);
1575 gpgme_data_release(plain);
1576 gpgme_data_release(sigdata);
1577 g_free(boundary);
1578 g_free(micalg);
1579 return -1; /* error */
1580}
1581
1582
1583/*
1584 * Sign the file with clear text and replace its content with the signed one.
1585 */
1586gint rfc2015_clearsign(const gchar *file, GSList *key_list)
1587{
1588 FILE *fp;
1589 gchar buf[BUFFSIZE];
1590 gpgme_error_t err;
1591 gpgme_data_t text = NULL;
1592 gpgme_data_t sigdata = NULL;
1593 ssize_t bytesRW = 0;
1594 gchar *micalg = NULL;
1595
1596 if ((fp = g_fopen(file, "rb")) == NULL) {
1597 FILE_OP_ERROR(file, "fopen");
1598 goto failure;
1599 }
1600
1601 err = gpgme_data_new(&text);
1602 if (err) {
1603 debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1604 goto failure;
1605 }
1606
1607 while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) {
1608 bytesRW = gpgme_data_write(text, buf, strlen(buf));
1609 }
1610 if (ferror(fp)) {
1611 FILE_OP_ERROR(file, "fgets");
1612 goto failure;
1613 }
1614 if (bytesRW == -1) {
1615 debug_print("gpgme_data_write failed: %s\n",
1616 gpgme_strerror(gpgme_error_from_errno(errno)));
1617 goto failure;
1618 }
1619
1620 sigdata = pgp_sign(text, key_list, TRUE, &micalg);
1621 if (micalg) {
1622 g_free(micalg);
1623 }
1624 if (!sigdata)
1625 goto failure;
1626
1627 if (fclose(fp) == EOF) {
1628 FILE_OP_ERROR(file, "fclose");
1629 fp = NULL;
1630 goto failure;
1631 }
1632 if ((fp = g_fopen(file, "wb")) == NULL) {
1633 FILE_OP_ERROR(file, "fopen");
1634 goto failure;
1635 }
1636
1637 err = (gpgme_data_seek(sigdata, 0, SEEK_SET) == -1) ?
1638 gpgme_error_from_errno(errno) : 0;
1639 if (err) {
1640 debug_print("gpgme_data_seek on sigdata failed: %s\n",
1641 gpgme_strerror(err));
1642 goto failure;
1643 }
1644
1645 bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE);
1646 while (bytesRW > 0) {
1647 fwrite(buf, bytesRW, 1, fp);
1648 bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE);
1649 }
1650 if (bytesRW != 0) {
1651 debug_print("gpgme_data_read failed: %s\n",
1652 gpgme_strerror(gpgme_error_from_errno(errno)));
1653 goto failure;
1654 }
1655
1656 if (fclose(fp) == EOF) {
1657 FILE_OP_ERROR(file, "fclose");
1658 fp = NULL;
1659 goto failure;
1660 }
1661 gpgme_data_release(text);
1662 gpgme_data_release(sigdata);
1663 return 0;
1664
1665failure:
1666 if (fp)
1667 fclose(fp);
1668 gpgme_data_release(text);
1669 gpgme_data_release(sigdata);
1670 return -1;
1671}
1672
1673/*
1674 * Encrypt and sign the file and replace its content with the encrypted and
1675 * signed one.
1676 */
1677gint rfc2015_encrypt_sign(const gchar *file, GSList *recp_list,
1678 GSList *key_list)
1679{
1680 FILE *fp = NULL;
1681 gchar buf[BUFFSIZE];
1682 gint i, clineidx, saved_last;
1683 gchar *clines[3] = {NULL};
1684 gpgme_error_t err;
1685 gpgme_data_t header = NULL;
1686 gpgme_data_t plain = NULL;
1687 gpgme_data_t cipher = NULL;
1688 gpgme_key_t *kset = NULL;
1689 ssize_t bytesRW = -1;
1690 gint mime_version_seen = 0;
1691 gchar *boundary;
1692 gchar *micalg = NULL;
1693
1694 boundary = generate_mime_boundary("Encrypt");
1695
1696 /* Create the list of recipients */
1697 kset = gpgmegtk_recipient_selection(recp_list);
1698 if (!kset) {
1699 debug_print("error creating recipient list\n");
1700 goto failure;
1701 }
1702
1703 /* Open the source file */
1704 if ((fp = g_fopen(file, "rb")) == NULL) {
1705 FILE_OP_ERROR(file, "fopen");
1706 goto failure;
1707 }
1708
1709 err = gpgme_data_new(&header);
1710 if (!err)
1711 err = gpgme_data_new(&plain);
1712 if (err) {
1713 debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1714 goto failure;
1715 }
1716
1717 /* get the content header lines from the source */
1718 clineidx = 0;
1719 saved_last = 0;
1720 while (!err && fgets(buf, sizeof(buf), fp)) {
1721 /* fixme: check for overlong lines */
1722 if (headerp(buf, content_names)) {
1723 if (clineidx >= DIM(clines)) {
1724 debug_print("rfc2015_sign: too many content lines\n");
1725 goto failure;
1726 }
1727 clines[clineidx++] = g_strdup(buf);
1728 saved_last = 1;
1729 continue;
1730 }
1731 if (saved_last) {
1732 if (*buf == ' ' || *buf == '\t') {
1733 gchar *last = clines[clineidx - 1];
1734 clines[clineidx - 1] = g_strconcat(last, buf, NULL);
1735 g_free(last);
1736 continue;
1737 }
1738 saved_last = 0;
1739 }
1740
1741 if (headerp(buf, mime_version_name))
1742 mime_version_seen = 1;
1743
1744 if (buf[0] == '\r' || buf[0] == '\n')
1745 break;
1746 bytesRW = gpgme_data_write(header, buf, strlen (buf));
1747 }
1748 if (ferror (fp)) {
1749 FILE_OP_ERROR(file, "fgets");
1750 goto failure;
1751 }
1752
1753 /* write them to the temp data and add the rest of the message */
1754 for (i = 0; (bytesRW != -1) && i < clineidx; i++) {
1755 bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i]));
1756 }
1757 if (bytesRW != -1)
1758 bytesRW = gpgme_data_write(plain, "\r\n", 2 );
1759 while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) {
1760 bytesRW = gpgme_data_write(plain, buf, strlen(buf));
1761 }
1762 if (ferror(fp)) {
1763 FILE_OP_ERROR(file, "fgets");
1764 goto failure;
1765 }
1766 if (bytesRW == -1) {
1767 debug_print("gpgme_data_write failed: %s\n",
1768 gpgme_strerror(gpgme_error_from_errno(errno)));
1769 goto failure;
1770 }
1771
1772 cipher = pgp_encrypt_sign(plain, kset, key_list, &micalg);
1773 gpgme_data_release(plain);
1774 plain = NULL;
1775 for (i = 0; kset[i] != NULL; i++)
1776 gpgme_key_unref(kset[i]);
1777 g_free(kset);
1778 kset = NULL;
1779 if (!cipher)
1780 goto failure;
1781
1782 /* we have the signed message available in sigdata and now we are
1783 * going to rewrite the original file. To be sure that file has
1784 * been truncated we use an approach which should work everywhere:
1785 * close the file and then reopen it for writing. */
1786 if (fclose(fp)) {
1787 FILE_OP_ERROR(file, "fclose");
1788 goto failure;
1789 }
1790 if ((fp = g_fopen(file, "wb")) == NULL) {
1791 FILE_OP_ERROR(file, "fopen");
1792 goto failure;
1793 }
1794
1795 /* Write the rfc822 header and add new content lines */
1796 err = (gpgme_data_seek(header, 0, SEEK_SET) == -1) ?
1797 gpgme_error_from_errno(errno) : 0;
1798 if (err) {
1799 debug_print("gpgme_data_seek failed: %s\n",
1800 gpgme_strerror(err));
1801 goto failure;
1802 }
1803 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1804 while (bytesRW > 0) {
1805 fwrite(buf, bytesRW, 1, fp);
1806 bytesRW = gpgme_data_read(header, buf, BUFFSIZE);
1807 }
1808 if (bytesRW != 0) {
1809 debug_print("gpgme_data_read failed: %s\n",
1810 gpgme_strerror(gpgme_error_from_errno(errno)));
1811 goto failure;
1812 }
1813 if (ferror(fp)) {
1814 FILE_OP_ERROR(file, "fwrite");
1815 goto failure;
1816 }
1817 gpgme_data_release(header);
1818 header = NULL;
1819
1820 if (!mime_version_seen)
1821 fputs("MIME-Version: 1.0\r\n", fp);
1822 fprintf(fp, "Content-Type: multipart/encrypted;"
1823 " protocol=\"application/pgp-encrypted\";\r\n"
1824 " boundary=\"%s\"\r\n"
1825 "\r\n"
1826 "--%s\r\n"
1827 "Content-Type: application/pgp-encrypted\r\n"
1828 "\r\n"
1829 "Version: 1\r\n"
1830 "\r\n"
1831 "--%s\r\n"
1832 "Content-Type: application/octet-stream\r\n"
1833 "\r\n",
1834 boundary, boundary, boundary);
1835
1836 /* append the encrypted stuff */
1837 err = (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) ?
1838 gpgme_error_from_errno(errno) : 0;
1839 if (err) {
1840 debug_print("gpgme_data_seek on cipher failed: %s\n",
1841 gpgme_strerror(err));
1842 goto failure;
1843 }
1844
1845 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1846 while (bytesRW > 0) {
1847 fwrite(buf, bytesRW, 1, fp);
1848 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1849 }
1850 if (bytesRW != 0) {
1851 debug_print("gpgme_data_read failed: %s\n",
1852 gpgme_strerror(gpgme_error_from_errno(errno)));
1853 goto failure;
1854 }
1855
1856 /* Final boundary */
1857 fprintf(fp,
1858 "\r\n"
1859 "--%s--\r\n",
1860 boundary);
1861 fflush(fp);
1862 if (ferror(fp)) {
1863 FILE_OP_ERROR(file, "fwrite");
1864 goto failure;
1865 }
1866 fclose(fp);
1867 gpgme_data_release(cipher);
1868 g_free(boundary);
1869 g_free(micalg);
1870 return 0;
1871
1872failure:
1873 if (fp)
1874 fclose(fp);
1875 gpgme_data_release(header);
1876 gpgme_data_release(plain);
1877 gpgme_data_release(cipher);
1878
1879 if (kset != NULL) {
1880 for (i = 0; kset[i] != NULL; i++)
1881 gpgme_key_unref(kset[i]);
1882 g_free(kset);
1883 }
1884 g_free(boundary);
1885 g_free(micalg);
1886 return -1; /* error */
1887}
1888
1889gint rfc2015_encrypt_sign_armored(const gchar *file, GSList *recp_list,
1890 GSList *key_list)
1891{
1892 FILE *fp = NULL;
1893 gchar buf[BUFFSIZE];
1894 gint i;
1895 gpgme_error_t err;
1896 gpgme_data_t plain = NULL;
1897 gpgme_data_t cipher = NULL;
1898 gpgme_key_t *kset = NULL;
1899 ssize_t bytesRW = 0;
1900 gchar *micalg = NULL;
1901
1902 kset = gpgmegtk_recipient_selection(recp_list);
1903 if (!kset) {
1904 debug_print("error creating recipient list\n");
1905 goto failure;
1906 }
1907
1908 if ((fp = g_fopen(file, "rb")) == NULL) {
1909 FILE_OP_ERROR(file, "fopen");
1910 goto failure;
1911 }
1912
1913 err = gpgme_data_new(&plain);
1914 if (err) {
1915 debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1916 goto failure;
1917 }
1918
1919 while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) {
1920 bytesRW = gpgme_data_write(plain, buf, strlen(buf));
1921 }
1922 if (ferror(fp)) {
1923 FILE_OP_ERROR(file, "fgets");
1924 goto failure;
1925 }
1926 if (bytesRW == -1) {
1927 debug_print("gpgme_data_write failed: %s\n",
1928 gpgme_strerror(gpgme_error_from_errno(errno)));
1929 goto failure;
1930 }
1931
1932 cipher = pgp_encrypt_sign(plain, kset, key_list, &micalg);
1933 if (micalg)
1934 g_free(micalg);
1935 if (!cipher)
1936 goto failure;
1937
1938 if (fclose(fp) == EOF) {
1939 FILE_OP_ERROR(file, "fclose");
1940 fp = NULL;
1941 goto failure;
1942 }
1943 if ((fp = g_fopen(file, "wb")) == NULL) {
1944 FILE_OP_ERROR(file, "fopen");
1945 goto failure;
1946 }
1947
1948 err = (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) ?
1949 gpgme_error_from_errno(errno) : 0;
1950 if (err) {
1951 debug_print("gpgme_data_seek on cipher failed: %s\n",
1952 gpgme_strerror(err));
1953 goto failure;
1954 }
1955
1956 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1957 while (bytesRW > 0) {
1958 fwrite(buf, bytesRW, 1, fp);
1959 bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE);
1960 }
1961 if (bytesRW != 0) {
1962 debug_print("gpgme_data_read failed: %s\n",
1963 gpgme_strerror(gpgme_error_from_errno(errno)));
1964 goto failure;
1965 }
1966
1967 if (fclose(fp) == EOF) {
1968 FILE_OP_ERROR(file, "fclose");
1969 fp = NULL;
1970 goto failure;
1971 }
1972 gpgme_data_release(plain);
1973 gpgme_data_release(cipher);
1974 for (i = 0; kset[i] != NULL; i++)
1975 gpgme_key_unref(kset[i]);
1976 g_free(kset);
1977 return 0;
1978
1979failure:
1980 if (fp)
1981 fclose(fp);
1982 gpgme_data_release(plain);
1983 gpgme_data_release(cipher);
1984 if (kset != NULL) {
1985 for (i = 0; kset[i] != NULL; i++)
1986 gpgme_key_unref(kset[i]);
1987 g_free(kset);
1988 }
1989 return -1;
1990}
1991
1992#endif /* USE_GPGME */