1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Authors: Jeffrey Stedfast <fejj@ximian.com>
5
* Copyright 2002 Ximian, Inc. (www.ximian.com)
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU Lesser General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
24
gpg:sign dump canonicalised to-be-signed data to a file
25
gpg:verify dump canonicalised verification and signature data to file
26
gpg:status print gpg status-fd output to stdout
43
#include <sys/types.h>
46
#include <sys/ioctl.h>
53
#include <glib/gi18n-lib.h>
54
#include <glib/gstdio.h>
56
#include <libedataserver/e-iconv.h>
58
#include "camel-debug.h"
59
#include "camel-gpg-context.h"
60
#include "camel-mime-filter-canon.h"
61
#include "camel-mime-filter-charset.h"
62
#include "camel-mime-part.h"
63
#include "camel-multipart-encrypted.h"
64
#include "camel-multipart-signed.h"
65
#include "camel-operation.h"
66
#include "camel-stream-filter.h"
67
#include "camel-stream-fs.h"
68
#include "camel-stream-mem.h"
76
static CamelCipherContextClass *parent_class = NULL;
79
* camel_gpg_context_new:
82
* Creates a new gpg cipher context object.
84
* Returns a new gpg cipher context object.
87
camel_gpg_context_new (CamelSession *session)
89
CamelCipherContext *cipher;
92
g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
94
ctx = (CamelGpgContext *) camel_object_new (camel_gpg_context_get_type ());
96
cipher = (CamelCipherContext *) ctx;
97
cipher->session = session;
98
camel_object_ref (session);
105
* camel_gpg_context_set_always_trust:
107
* @always_trust always truct flag
109
* Sets the @always_trust flag on the gpg context which is used for
113
camel_gpg_context_set_always_trust (CamelGpgContext *ctx, gboolean always_trust)
115
g_return_if_fail (CAMEL_IS_GPG_CONTEXT (ctx));
117
ctx->always_trust = always_trust;
122
gpg_hash_to_id (CamelCipherContext *context, CamelCipherHash hash)
125
case CAMEL_CIPHER_HASH_MD2:
127
case CAMEL_CIPHER_HASH_MD5:
129
case CAMEL_CIPHER_HASH_SHA1:
130
case CAMEL_CIPHER_HASH_DEFAULT:
132
case CAMEL_CIPHER_HASH_RIPEMD160:
133
return "pgp-ripemd160";
134
case CAMEL_CIPHER_HASH_TIGER192:
135
return "pgp-tiger192";
136
case CAMEL_CIPHER_HASH_HAVAL5160:
137
return "pgp-haval-5-160";
143
static CamelCipherHash
144
gpg_id_to_hash (CamelCipherContext *context, const char *id)
147
if (!strcmp (id, "pgp-md2"))
148
return CAMEL_CIPHER_HASH_MD2;
149
else if (!strcmp (id, "pgp-md5"))
150
return CAMEL_CIPHER_HASH_MD5;
151
else if (!strcmp (id, "pgp-sha1"))
152
return CAMEL_CIPHER_HASH_SHA1;
153
else if (!strcmp (id, "pgp-ripemd160"))
154
return CAMEL_CIPHER_HASH_RIPEMD160;
155
else if (!strcmp (id, "tiger192"))
156
return CAMEL_CIPHER_HASH_TIGER192;
157
else if (!strcmp (id, "haval-5-160"))
158
return CAMEL_CIPHER_HASH_HAVAL5160;
161
return CAMEL_CIPHER_HASH_DEFAULT;
168
GPG_CTX_MODE_ENCRYPT,
169
GPG_CTX_MODE_DECRYPT,
174
enum _GpgTrustMetric {
184
enum _GpgCtxMode mode;
185
CamelSession *session;
186
GHashTable *userid_hint;
191
GPtrArray *recipients;
192
CamelCipherHash hash;
198
int passwd_fd; /* only needed for sign/decrypt */
200
/* status-fd buffer */
201
unsigned char *statusbuf;
202
unsigned char *statusptr;
203
unsigned int statusleft;
208
CamelStream *istream;
209
CamelStream *ostream;
212
CamelStream *diagnostics;
216
unsigned int exited:1;
217
unsigned int complete:1;
218
unsigned int seen_eof1:1;
219
unsigned int seen_eof2:1;
220
unsigned int always_trust:1;
221
unsigned int armor:1;
222
unsigned int need_passwd:1;
223
unsigned int send_passwd:1;
225
unsigned int bad_passwds:2;
227
unsigned int hadsig:1;
228
unsigned int badsig:1;
229
unsigned int errsig:1;
230
unsigned int goodsig:1;
231
unsigned int validsig:1;
232
unsigned int nopubkey:1;
233
unsigned int nodata:1;
234
unsigned int trust:3;
236
unsigned int diagflushed:1;
240
unsigned int padding:10;
243
static struct _GpgCtx *
244
gpg_ctx_new (CamelSession *session)
250
gpg = g_new (struct _GpgCtx, 1);
251
gpg->mode = GPG_CTX_MODE_SIGN;
252
gpg->session = session;
253
camel_object_ref (session);
254
gpg->userid_hint = g_hash_table_new (g_str_hash, g_str_equal);
255
gpg->complete = FALSE;
256
gpg->seen_eof1 = TRUE;
257
gpg->seen_eof2 = FALSE;
258
gpg->pid = (pid_t) -1;
259
gpg->exit_status = 0;
264
gpg->recipients = NULL;
265
gpg->hash = CAMEL_CIPHER_HASH_DEFAULT;
266
gpg->always_trust = FALSE;
275
gpg->statusbuf = g_malloc (128);
276
gpg->statusptr = gpg->statusbuf;
277
gpg->statusleft = 128;
279
gpg->bad_passwds = 0;
280
gpg->need_passwd = FALSE;
281
gpg->send_passwd = FALSE;
289
gpg->goodsig = FALSE;
290
gpg->validsig = FALSE;
291
gpg->nopubkey = FALSE;
292
gpg->trust = GPG_TRUST_NONE;
297
stream = camel_stream_mem_new ();
298
gpg->diagbuf = CAMEL_STREAM_MEM (stream)->buffer;
299
gpg->diagflushed = FALSE;
301
if ((charset = e_iconv_locale_charset ()) && g_ascii_strcasecmp (charset, "UTF-8") != 0) {
302
CamelMimeFilterCharset *filter;
303
CamelStreamFilter *fstream;
307
if ((filter = camel_mime_filter_charset_new_convert (charset, "UTF-8"))) {
308
fstream = camel_stream_filter_new_with_stream (stream);
309
camel_stream_filter_add (fstream, (CamelMimeFilter *) filter);
310
camel_object_unref (filter);
311
camel_object_unref (stream);
313
stream = (CamelStream *) fstream;
319
gpg->diagnostics = stream;
325
gpg_ctx_set_mode (struct _GpgCtx *gpg, enum _GpgCtxMode mode)
328
gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT));
332
gpg_ctx_set_hash (struct _GpgCtx *gpg, CamelCipherHash hash)
338
gpg_ctx_set_always_trust (struct _GpgCtx *gpg, gboolean trust)
340
gpg->always_trust = trust;
344
gpg_ctx_set_userid (struct _GpgCtx *gpg, const char *userid)
346
g_free (gpg->userid);
347
gpg->userid = g_strdup (userid);
351
gpg_ctx_add_recipient (struct _GpgCtx *gpg, const char *keyid)
353
if (gpg->mode != GPG_CTX_MODE_ENCRYPT && gpg->mode != GPG_CTX_MODE_EXPORT)
356
if (!gpg->recipients)
357
gpg->recipients = g_ptr_array_new ();
359
g_ptr_array_add (gpg->recipients, g_strdup (keyid));
363
gpg_ctx_set_sigfile (struct _GpgCtx *gpg, const char *sigfile)
365
g_free (gpg->sigfile);
366
gpg->sigfile = g_strdup (sigfile);
370
gpg_ctx_set_armor (struct _GpgCtx *gpg, gboolean armor)
376
gpg_ctx_set_istream (struct _GpgCtx *gpg, CamelStream *istream)
378
camel_object_ref (istream);
380
camel_object_unref (gpg->istream);
381
gpg->istream = istream;
385
gpg_ctx_set_ostream (struct _GpgCtx *gpg, CamelStream *ostream)
387
camel_object_ref (ostream);
389
camel_object_unref (gpg->ostream);
390
gpg->ostream = ostream;
391
gpg->seen_eof1 = FALSE;
395
gpg_ctx_get_diagnostics (struct _GpgCtx *gpg)
397
if (!gpg->diagflushed) {
398
gpg->diagflushed = TRUE;
399
camel_stream_flush (gpg->diagnostics);
400
if (gpg->diagbuf->len == 0)
403
g_byte_array_append (gpg->diagbuf, (guchar*)"", 1);
406
return (const char *) gpg->diagbuf->data;
410
userid_hint_free (gpointer key, gpointer value, gpointer user_data)
417
gpg_ctx_free (struct _GpgCtx *gpg)
425
camel_object_unref (gpg->session);
427
g_hash_table_foreach (gpg->userid_hint, userid_hint_free, NULL);
428
g_hash_table_destroy (gpg->userid_hint);
430
g_free (gpg->userid);
432
g_free (gpg->sigfile);
434
if (gpg->recipients) {
435
for (i = 0; i < gpg->recipients->len; i++)
436
g_free (gpg->recipients->pdata[i]);
438
g_ptr_array_free (gpg->recipients, TRUE);
441
if (gpg->stdin_fd != -1)
442
close (gpg->stdin_fd);
443
if (gpg->stdout_fd != -1)
444
close (gpg->stdout_fd);
445
if (gpg->stderr_fd != -1)
446
close (gpg->stderr_fd);
447
if (gpg->status_fd != -1)
448
close (gpg->status_fd);
449
if (gpg->passwd_fd != -1)
450
close (gpg->passwd_fd);
452
g_free (gpg->statusbuf);
454
g_free (gpg->need_id);
457
memset (gpg->passwd, 0, strlen (gpg->passwd));
458
g_free (gpg->passwd);
462
camel_object_unref (gpg->istream);
465
camel_object_unref (gpg->ostream);
467
camel_object_unref (gpg->diagnostics);
475
gpg_hash_str (CamelCipherHash hash)
478
case CAMEL_CIPHER_HASH_MD2:
479
return "--digest-algo=MD2";
480
case CAMEL_CIPHER_HASH_MD5:
481
return "--digest-algo=MD5";
482
case CAMEL_CIPHER_HASH_SHA1:
483
return "--digest-algo=SHA1";
484
case CAMEL_CIPHER_HASH_RIPEMD160:
485
return "--digest-algo=RIPEMD160";
492
gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, char **sfd, int passwd_fd, char **pfd)
494
const char *hash_str;
499
argv = g_ptr_array_new ();
500
g_ptr_array_add (argv, "gpg");
502
g_ptr_array_add (argv, "--verbose");
503
g_ptr_array_add (argv, "--no-secmem-warning");
504
g_ptr_array_add (argv, "--no-greeting");
505
g_ptr_array_add (argv, "--no-tty");
506
if (passwd_fd == -1) {
507
/* only use batch mode if we don't intend on using the
508
interactive --command-fd option */
509
g_ptr_array_add (argv, "--batch");
510
g_ptr_array_add (argv, "--yes");
513
*sfd = buf = g_strdup_printf ("--status-fd=%d", status_fd);
514
g_ptr_array_add (argv, buf);
516
if (passwd_fd != -1) {
517
*pfd = buf = g_strdup_printf ("--command-fd=%d", passwd_fd);
518
g_ptr_array_add (argv, buf);
522
case GPG_CTX_MODE_SIGN:
523
g_ptr_array_add (argv, "--sign");
524
g_ptr_array_add (argv, "--detach");
526
g_ptr_array_add (argv, "--armor");
527
hash_str = gpg_hash_str (gpg->hash);
529
g_ptr_array_add (argv, (char *) hash_str);
531
g_ptr_array_add (argv, "-u");
532
g_ptr_array_add (argv, (char *) gpg->userid);
534
g_ptr_array_add (argv, "--output");
535
g_ptr_array_add (argv, "-");
537
case GPG_CTX_MODE_VERIFY:
538
if (!camel_session_is_online (gpg->session)) {
539
/* this is a deprecated flag to gpg since 1.0.7 */
540
/*g_ptr_array_add (argv, "--no-auto-key-retrieve");*/
541
g_ptr_array_add (argv, "--keyserver-options");
542
g_ptr_array_add (argv, "no-auto-key-retrieve");
544
g_ptr_array_add (argv, "--verify");
546
g_ptr_array_add (argv, gpg->sigfile);
547
g_ptr_array_add (argv, "-");
549
case GPG_CTX_MODE_ENCRYPT:
550
g_ptr_array_add (argv, "--encrypt");
552
g_ptr_array_add (argv, "--armor");
553
if (gpg->always_trust)
554
g_ptr_array_add (argv, "--always-trust");
556
g_ptr_array_add (argv, "-u");
557
g_ptr_array_add (argv, (char *) gpg->userid);
559
if (gpg->recipients) {
560
for (i = 0; i < gpg->recipients->len; i++) {
561
g_ptr_array_add (argv, "-r");
562
g_ptr_array_add (argv, gpg->recipients->pdata[i]);
565
g_ptr_array_add (argv, "--output");
566
g_ptr_array_add (argv, "-");
568
case GPG_CTX_MODE_DECRYPT:
569
g_ptr_array_add (argv, "--decrypt");
570
g_ptr_array_add (argv, "--output");
571
g_ptr_array_add (argv, "-");
573
case GPG_CTX_MODE_IMPORT:
574
g_ptr_array_add (argv, "--import");
575
g_ptr_array_add (argv, "-");
577
case GPG_CTX_MODE_EXPORT:
579
g_ptr_array_add (argv, "--armor");
580
g_ptr_array_add (argv, "--export");
581
for (i = 0; i < gpg->recipients->len; i++)
582
g_ptr_array_add (argv, gpg->recipients->pdata[i]);
586
g_ptr_array_add (argv, NULL);
594
gpg_ctx_op_start (struct _GpgCtx *gpg)
597
char *status_fd = NULL, *passwd_fd = NULL;
598
int i, maxfd, errnosave, fds[10];
602
for (i = 0; i < 10; i++)
605
maxfd = gpg->need_passwd ? 10 : 8;
606
for (i = 0; i < maxfd; i += 2) {
607
if (pipe (fds + i) == -1)
611
argv = gpg_ctx_get_argv (gpg, fds[7], &status_fd, fds[8], &passwd_fd);
613
if (!(gpg->pid = fork ())) {
616
if ((dup2 (fds[0], STDIN_FILENO) < 0 ) ||
617
(dup2 (fds[3], STDOUT_FILENO) < 0 ) ||
618
(dup2 (fds[5], STDERR_FILENO) < 0 )) {
622
/* Dissociate from camel's controlling terminal so
623
* that gpg won't be able to read from it.
627
maxfd = sysconf (_SC_OPEN_MAX);
628
/* Loop over all fds. */
629
for (i = 3; i < maxfd; i++) {
630
/* don't close the status-fd or passwd-fd */
631
if (i != fds[7] && i != fds[8])
632
fcntl (i, F_SETFD, FD_CLOEXEC);
636
execvp ("gpg", (char **) argv->pdata);
638
} else if (gpg->pid < 0) {
639
g_ptr_array_free (argv, TRUE);
645
g_ptr_array_free (argv, TRUE);
651
gpg->stdin_fd = fds[1];
652
gpg->stdout_fd = fds[2];
654
gpg->stderr_fd = fds[4];
656
gpg->status_fd = fds[6];
658
if (gpg->need_passwd) {
660
gpg->passwd_fd = fds[9];
661
flags = fcntl (gpg->passwd_fd, F_GETFL);
662
fcntl (gpg->passwd_fd, F_SETFL, flags | O_NONBLOCK);
665
flags = fcntl (gpg->stdin_fd, F_GETFL);
666
fcntl (gpg->stdin_fd, F_SETFL, flags | O_NONBLOCK);
668
flags = fcntl (gpg->stdout_fd, F_GETFL);
669
fcntl (gpg->stdout_fd, F_SETFL, flags | O_NONBLOCK);
671
flags = fcntl (gpg->stderr_fd, F_GETFL);
672
fcntl (gpg->stderr_fd, F_SETFL, flags | O_NONBLOCK);
674
flags = fcntl (gpg->status_fd, F_GETFL);
675
fcntl (gpg->status_fd, F_SETFL, flags | O_NONBLOCK);
683
for (i = 0; i < 10; i++) {
691
g_warning ("%s: Not implemented", __FUNCTION__);
702
next_token (const char *in, char **token)
704
const char *start, *inptr = in;
706
while (*inptr == ' ')
709
if (*inptr == '\0' || *inptr == '\n') {
716
while (*inptr && *inptr != ' ' && *inptr != '\n')
720
*token = g_strndup (start, inptr - start);
726
gpg_ctx_parse_status (struct _GpgCtx *gpg, CamelException *ex)
728
register unsigned char *inptr;
729
const unsigned char *status;
730
size_t nread, nwritten;
735
inptr = gpg->statusbuf;
736
while (inptr < gpg->statusptr && *inptr != '\n')
739
if (*inptr != '\n') {
740
/* we don't have enough data buffered to parse this status line */
745
status = gpg->statusbuf;
747
if (camel_debug("gpg:status"))
748
printf ("status: %s\n", status);
750
if (strncmp ((char*)status, "[GNUPG:] ", 9) != 0) {
751
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
752
_("Unexpected GnuPG status message encountered:\n\n%s"),
759
if (!strncmp ((char*)status, "USERID_HINT ", 12)) {
763
status = (const unsigned char *) next_token ((char*)status, &hint);
765
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
766
_("Failed to parse gpg userid hint."));
770
if (g_hash_table_lookup (gpg->userid_hint, hint)) {
771
/* we already have this userid hint... */
776
if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar*)status, -1, &nread, &nwritten, NULL)))
777
user = g_strdup ((gchar*)status);
781
g_hash_table_insert (gpg->userid_hint, hint, user);
782
} else if (!strncmp ((char*)status, "NEED_PASSPHRASE ", 16)) {
787
status = (const unsigned char *) next_token ((char*)status, &userid);
789
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
790
_("Failed to parse gpg passphrase request."));
794
g_free (gpg->need_id);
795
gpg->need_id = userid;
796
} else if (!strncmp ((char*)status, "NEED_PASSPHRASE_PIN ", 20)) {
801
status = (const unsigned char *)next_token ((char*)status, &userid);
803
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
804
_("Failed to parse gpg passphrase request."));
808
g_free (gpg->need_id);
809
gpg->need_id = userid;
810
} else if (!strncmp ((char*)status, "GET_HIDDEN ", 11)) {
811
const char *name = NULL;
812
char *prompt, *passwd;
817
if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
822
if (!strncmp ((char*)status, "passphrase.pin.ask", 18)) {
823
prompt = g_strdup_printf (_("You need a PIN to unlock the key for your\n"
824
"SmartCard: \"%s\""), name);
825
} else if (!strncmp ((char*)status, "passphrase.enter", 16)) {
826
prompt = g_strdup_printf (_("You need a passphrase to unlock the key for\n"
827
"user: \"%s\""), name);
829
next_token ((char*)status, &prompt);
830
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
831
_("Unexpected request from GnuPG for `%s'"), prompt);
836
flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
837
if ((passwd = camel_session_get_password (gpg->session, NULL, NULL, prompt, gpg->need_id, flags, ex))) {
839
char *opasswd = passwd;
841
if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
842
memset (opasswd, 0, strlen (opasswd));
849
gpg->passwd = g_strdup_printf ("%s\n", passwd);
850
memset (passwd, 0, strlen (passwd));
853
gpg->send_passwd = TRUE;
855
if (!camel_exception_is_set (ex))
856
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled.");
861
} else if (!strncmp ((char*)status, "GOOD_PASSPHRASE", 15)) {
862
gpg->bad_passwds = 0;
863
} else if (!strncmp ((char*)status, "BAD_PASSPHRASE", 14)) {
866
camel_session_forget_password (gpg->session, NULL, NULL, gpg->need_id, ex);
868
if (gpg->bad_passwds == 3) {
869
camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
870
_("Failed to unlock secret key: 3 bad passphrases given."));
873
} else if (!strncmp ((char*)status, "UNEXPECTED ", 11)) {
874
/* this is an error */
875
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
876
_("Unexpected response from GnuPG: %s"),
879
} else if (!strncmp ((char*)status, "NODATA", 6)) {
880
/* this is an error */
881
/* But we ignore it anyway, we should get other response codes to say why */
884
/* check to see if we are complete */
886
case GPG_CTX_MODE_SIGN:
887
if (!strncmp ((char*)status, "SIG_CREATED ", 12)) {
888
/* FIXME: save this state? */
891
case GPG_CTX_MODE_VERIFY:
892
if (!strncmp ((char*)status, "TRUST_", 6)) {
894
if (!strncmp ((char*)status, "NEVER", 5)) {
895
gpg->trust = GPG_TRUST_NEVER;
896
} else if (!strncmp ((char*)status, "MARGINAL", 8)) {
897
gpg->trust = GPG_TRUST_MARGINAL;
898
} else if (!strncmp ((char*)status, "FULLY", 5)) {
899
gpg->trust = GPG_TRUST_FULLY;
900
} else if (!strncmp ((char*)status, "ULTIMATE", 8)) {
901
gpg->trust = GPG_TRUST_ULTIMATE;
902
} else if (!strncmp ((char*)status, "UNDEFINED", 9)) {
903
gpg->trust = GPG_TRUST_UNDEFINED;
905
} else if (!strncmp ((char*)status, "GOODSIG ", 8)) {
908
} else if (!strncmp ((char*)status, "VALIDSIG ", 9)) {
909
gpg->validsig = TRUE;
910
} else if (!strncmp ((char*)status, "BADSIG ", 7)) {
913
} else if (!strncmp ((char*)status, "ERRSIG ", 7)) {
914
/* Note: NO_PUBKEY often comes after an ERRSIG */
917
} else if (!strncmp ((char*)status, "NO_PUBKEY ", 10)) {
918
gpg->nopubkey = TRUE;
921
case GPG_CTX_MODE_ENCRYPT:
922
if (!strncmp ((char*)status, "BEGIN_ENCRYPTION", 16)) {
923
/* nothing to do... but we know to expect data on stdout soon */
924
} else if (!strncmp ((char*)status, "END_ENCRYPTION", 14)) {
925
/* nothing to do, but we know the end is near? */
926
} else if (!strncmp ((char*)status, "NO_RECP", 7)) {
927
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
928
_("Failed to encrypt: No valid recipients specified."));
932
case GPG_CTX_MODE_DECRYPT:
933
if (!strncmp ((char*)status, "BEGIN_DECRYPTION", 16)) {
934
/* nothing to do... but we know to expect data on stdout soon */
935
} else if (!strncmp ((char*)status, "END_DECRYPTION", 14)) {
936
/* nothing to do, but we know the end is near? */
939
case GPG_CTX_MODE_IMPORT:
942
case GPG_CTX_MODE_EXPORT:
950
/* recycle our statusbuf by moving inptr to the beginning of statusbuf */
951
len = gpg->statusptr - inptr;
952
memmove (gpg->statusbuf, inptr, len);
954
len = inptr - gpg->statusbuf;
955
gpg->statusleft += len;
956
gpg->statusptr -= len;
958
/* if we have more data, try parsing the next line? */
959
if (gpg->statusptr > gpg->statusbuf)
967
#define status_backup(gpg, start, len) G_STMT_START { \
968
if (gpg->statusleft <= len) { \
969
unsigned int slen, soff; \
971
slen = soff = gpg->statusptr - gpg->statusbuf; \
972
slen = slen ? slen : 1; \
974
while (slen < soff + len) \
977
gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1); \
978
gpg->statusptr = gpg->statusbuf + soff; \
979
gpg->statusleft = slen - soff; \
982
memcpy (gpg->statusptr, start, len); \
983
gpg->statusptr += len; \
984
gpg->statusleft -= len; \
990
gpg_ctx_op_cancel (struct _GpgCtx *gpg)
998
kill (gpg->pid, SIGTERM);
1000
retval = waitpid (gpg->pid, &status, WNOHANG);
1001
if (retval == (pid_t) 0) {
1002
/* no more mr nice guy... */
1003
kill (gpg->pid, SIGKILL);
1005
waitpid (gpg->pid, &status, WNOHANG);
1012
gpg_ctx_op_step (struct _GpgCtx *gpg, CamelException *ex)
1015
struct pollfd polls[6];
1016
int status, i, cancel_fd;
1020
polls[i].events = 0;
1023
if (!gpg->seen_eof1) {
1024
polls[0].fd = gpg->stdout_fd;
1025
polls[0].events = POLLIN;
1028
if (!gpg->seen_eof2) {
1029
polls[1].fd = gpg->stderr_fd;
1030
polls[1].events = POLLIN;
1033
if (!gpg->complete) {
1034
polls[2].fd = gpg->status_fd;
1035
polls[2].events = POLLIN;
1038
polls[3].fd = gpg->stdin_fd;
1039
polls[3].events = POLLOUT;
1040
polls[4].fd = gpg->passwd_fd;
1041
polls[4].events = POLLOUT;
1042
cancel_fd = camel_operation_cancel_fd(NULL);
1043
polls[5].fd = cancel_fd;
1044
polls[5].events = POLLIN;
1048
polls[i].revents = 0;
1049
status = poll(polls, 6, 30*1000);
1050
} while (status == -1 && errno == EINTR);
1053
return 0; /* timed out */
1054
else if (status == -1)
1057
if ((polls[5].revents & POLLIN) && camel_operation_cancel_check(NULL)) {
1058
camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled.");
1059
gpg_ctx_op_cancel(gpg);
1063
/* Test each and every file descriptor to see if it's 'ready',
1064
and if so - do what we can with it and then drop through to
1065
the next file descriptor and so on until we've done what we
1066
can to all of them. If one fails along the way, return
1069
if (polls[2].revents & (POLLIN|POLLHUP)) {
1070
/* read the status message and decide what to do... */
1074
d(printf ("reading from gpg's status-fd...\n"));
1077
nread = read (gpg->status_fd, buffer, sizeof (buffer));
1078
} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1083
status_backup (gpg, buffer, nread);
1085
if (gpg_ctx_parse_status (gpg, ex) == -1)
1088
gpg->complete = TRUE;
1092
if ((polls[0].revents & (POLLIN|POLLHUP)) && gpg->ostream) {
1096
d(printf ("reading gpg's stdout...\n"));
1099
nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1100
} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1105
if (camel_stream_write (gpg->ostream, buffer, (size_t) nread) == -1)
1108
gpg->seen_eof1 = TRUE;
1112
if (polls[1].revents & (POLLIN|POLLHUP)) {
1116
d(printf ("reading gpg's stderr...\n"));
1119
nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
1120
} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1125
camel_stream_write (gpg->diagnostics, buffer, nread);
1127
gpg->seen_eof2 = TRUE;
1131
if ((polls[4].revents & (POLLOUT|POLLHUP)) && gpg->need_passwd && gpg->send_passwd) {
1132
ssize_t w, nwritten = 0;
1135
d(printf ("sending gpg our passphrase...\n"));
1137
/* send the passphrase to gpg */
1138
n = strlen (gpg->passwd);
1141
w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
1142
} while (w == -1 && (errno == EINTR || errno == EAGAIN));
1146
} while (nwritten < n && w != -1);
1148
/* zero and free our passwd buffer */
1149
memset (gpg->passwd, 0, n);
1150
g_free (gpg->passwd);
1156
gpg->send_passwd = FALSE;
1159
if ((polls[3].revents & (POLLOUT|POLLHUP)) && gpg->istream) {
1163
d(printf ("writing to gpg's stdin...\n"));
1165
/* write our stream to gpg's stdin */
1166
nread = camel_stream_read (gpg->istream, buffer, sizeof (buffer));
1168
ssize_t w, nwritten = 0;
1172
w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
1173
} while (w == -1 && (errno == EINTR || errno == EAGAIN));
1177
} while (nwritten < nread && w != -1);
1182
d(printf ("wrote %d (out of %d) bytes to gpg's stdin\n", nwritten, nread));
1185
if (camel_stream_eos (gpg->istream)) {
1186
d(printf ("closing gpg's stdin\n"));
1187
close (gpg->stdin_fd);
1195
/* always called on an i/o error */
1196
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg: %s"), g_strerror(errno));
1197
gpg_ctx_op_cancel(gpg);
1203
gpg_ctx_op_complete (struct _GpgCtx *gpg)
1205
return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
1210
gpg_ctx_op_exited (struct _GpgCtx *gpg)
1218
retval = waitpid (gpg->pid, &status, WNOHANG);
1219
if (retval == gpg->pid) {
1220
gpg->exit_status = status;
1230
gpg_ctx_op_wait (struct _GpgCtx *gpg)
1233
sigset_t mask, omask;
1238
sigemptyset (&mask);
1239
sigaddset (&mask, SIGALRM);
1240
sigprocmask (SIG_BLOCK, &mask, &omask);
1242
retval = waitpid (gpg->pid, &status, 0);
1244
sigprocmask (SIG_SETMASK, &omask, NULL);
1246
if (retval == (pid_t) -1 && errno == EINTR) {
1247
/* The child is hanging: send a friendly reminder. */
1248
kill (gpg->pid, SIGTERM);
1250
retval = waitpid (gpg->pid, &status, WNOHANG);
1251
if (retval == (pid_t) 0) {
1252
/* Still hanging; use brute force. */
1253
kill (gpg->pid, SIGKILL);
1255
retval = waitpid (gpg->pid, &status, WNOHANG);
1259
status = gpg->exit_status;
1263
if (retval != (pid_t) -1 && WIFEXITED (status))
1264
return WEXITSTATUS (status);
1275
gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1277
struct _GpgCtx *gpg = NULL;
1278
CamelStream *ostream = camel_stream_mem_new(), *istream;
1279
CamelDataWrapper *dw;
1280
CamelContentType *ct;
1282
CamelMimePart *sigpart;
1283
CamelMultipartSigned *mps;
1285
/* Note: see rfc2015 or rfc3156, section 5 */
1287
/* FIXME: stream this, we stream output at least */
1288
istream = camel_stream_mem_new();
1289
if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM,
1291
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1292
_("Could not generate signing data: %s"), g_strerror(errno));
1297
if (camel_debug_start("gpg:sign")) {
1301
name = g_strdup_printf("camel-gpg.%d.sign-data", logid++);
1302
out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1304
printf("Writing gpg signing data to '%s'\n", name);
1305
camel_stream_write_to_stream(istream, out);
1306
camel_stream_reset(istream);
1307
camel_object_unref(out);
1314
gpg = gpg_ctx_new (context->session);
1315
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
1316
gpg_ctx_set_hash (gpg, hash);
1317
gpg_ctx_set_armor (gpg, TRUE);
1318
gpg_ctx_set_userid (gpg, userid);
1319
gpg_ctx_set_istream (gpg, istream);
1320
gpg_ctx_set_ostream (gpg, ostream);
1322
if (gpg_ctx_op_start (gpg) == -1) {
1323
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1324
_("Failed to execute gpg: %s"), g_strerror (errno));
1328
while (!gpg_ctx_op_complete (gpg)) {
1329
if (gpg_ctx_op_step (gpg, ex) == -1)
1333
if (gpg_ctx_op_wait (gpg) != 0) {
1334
const char *diagnostics;
1336
diagnostics = gpg_ctx_get_diagnostics (gpg);
1337
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1338
diagnostics && *diagnostics ? diagnostics :
1339
_("Failed to execute gpg."));
1345
dw = camel_data_wrapper_new();
1346
camel_stream_reset(ostream);
1347
camel_data_wrapper_construct_from_stream(dw, ostream);
1349
sigpart = camel_mime_part_new();
1350
ct = camel_content_type_new("application", "pgp-signature");
1351
camel_content_type_set_param(ct, "name", "signature.asc");
1352
camel_data_wrapper_set_mime_type_field(dw, ct);
1353
camel_content_type_unref(ct);
1355
camel_medium_set_content_object((CamelMedium *)sigpart, dw);
1356
camel_object_unref(dw);
1358
camel_mime_part_set_description(sigpart, _("This is a digitally signed message part"));
1360
mps = camel_multipart_signed_new();
1361
ct = camel_content_type_new("multipart", "signed");
1362
camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash));
1363
camel_content_type_set_param(ct, "protocol", context->sign_protocol);
1364
camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct);
1365
camel_content_type_unref(ct);
1366
camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
1368
mps->signature = sigpart;
1369
mps->contentraw = istream;
1370
camel_stream_reset(istream);
1371
camel_object_ref(istream);
1373
camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps);
1375
camel_object_unref(ostream);
1385
swrite (CamelMimePart *sigpart)
1387
CamelStream *ostream;
1391
template = g_build_filename (g_get_tmp_dir (), "evolution-pgp.XXXXXX", NULL);
1392
if ((fd = g_mkstemp (template)) == -1) {
1397
/* TODO: This should probably just write the decoded message content out, not the part + headers */
1399
ostream = camel_stream_fs_new_with_fd (fd);
1400
ret = camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, ostream);
1402
ret = camel_stream_flush (ostream);
1404
ret = camel_stream_close (ostream);
1407
camel_object_unref(ostream);
1410
g_unlink (template);
1418
static CamelCipherValidity *
1419
gpg_verify (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex)
1421
CamelCipherValidity *validity;
1422
const char *diagnostics = NULL;
1423
struct _GpgCtx *gpg = NULL;
1424
char *sigfile = NULL;
1425
CamelContentType *ct;
1426
CamelMimePart *sigpart;
1427
CamelStream *istream = NULL;
1428
CamelMultipart *mps;
1430
mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart);
1431
ct = ((CamelDataWrapper *)mps)->mime_type;
1433
/* Inline signature (using our fake mime type) or PGP/Mime signature */
1434
if (camel_content_type_is(ct, "multipart", "signed")) {
1435
/* PGP/Mime Signature */
1438
tmp = camel_content_type_param(ct, "protocol");
1439
if (!CAMEL_IS_MULTIPART_SIGNED(mps)
1441
|| g_ascii_strcasecmp(tmp, context->sign_protocol) != 0) {
1442
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1443
_("Cannot verify message signature: Incorrect message format"));
1447
if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
1448
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1449
_("Cannot verify message signature: Incorrect message format"));
1453
if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
1454
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1455
_("Cannot verify message signature: Incorrect message format"));
1456
camel_object_unref (istream);
1460
} else if (camel_content_type_is(ct, "application", "x-inlinepgp-signed")) {
1462
CamelDataWrapper *content;
1463
content = camel_medium_get_content_object ((CamelMedium *) ipart);
1464
istream = camel_stream_mem_new();
1465
camel_data_wrapper_decode_to_stream (content, istream);
1466
camel_stream_reset(istream);
1469
/* Invalid Mimetype */
1470
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1471
_("Cannot verify message signature: Incorrect message format"));
1475
/* Now start the real work of verifying the message */
1477
if (camel_debug_start("gpg:sign")) {
1481
name = g_strdup_printf("camel-gpg.%d.verify.data", logid);
1482
out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1484
printf("Writing gpg verify data to '%s'\n", name);
1485
camel_stream_write_to_stream(istream, out);
1486
camel_stream_reset(istream);
1487
camel_object_unref(out);
1491
name = g_strdup_printf("camel-gpg.%d.verify.signature", logid++);
1492
out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1494
printf("Writing gpg verify signature to '%s'\n", name);
1495
camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, out);
1496
camel_object_unref(out);
1505
sigfile = swrite (sigpart);
1506
if (sigfile == NULL) {
1507
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1508
_("Cannot verify message signature: could not create temp file: %s"),
1509
g_strerror (errno));
1514
camel_stream_reset(istream);
1515
gpg = gpg_ctx_new (context->session);
1516
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
1518
gpg_ctx_set_sigfile (gpg, sigfile);
1519
gpg_ctx_set_istream (gpg, istream);
1521
if (gpg_ctx_op_start (gpg) == -1) {
1522
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1523
_("Failed to execute gpg."));
1527
while (!gpg_ctx_op_complete (gpg)) {
1528
if (gpg_ctx_op_step (gpg, ex) == -1)
1532
gpg_ctx_op_wait (gpg);
1533
validity = camel_cipher_validity_new ();
1534
diagnostics = gpg_ctx_get_diagnostics (gpg);
1535
camel_cipher_validity_set_description (validity, diagnostics);
1536
if (gpg->validsig) {
1537
if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1538
validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1539
else if (gpg->trust != GPG_TRUST_NEVER)
1540
validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1542
validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1544
validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1553
camel_object_unref(istream);
1563
camel_object_unref(istream);
1574
gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
1576
CamelGpgContext *ctx = (CamelGpgContext *) context;
1577
struct _GpgCtx *gpg;
1579
CamelStream *istream, *ostream, *vstream;
1580
CamelMimePart *encpart, *verpart;
1581
CamelDataWrapper *dw;
1582
CamelContentType *ct;
1583
CamelMultipartEncrypted *mpe;
1585
ostream = camel_stream_mem_new();
1586
istream = camel_stream_mem_new();
1587
if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream) == -1) {
1588
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1589
_("Could not generate encrypting data: %s"), g_strerror(errno));
1593
gpg = gpg_ctx_new (context->session);
1594
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
1595
gpg_ctx_set_armor (gpg, TRUE);
1596
gpg_ctx_set_userid (gpg, userid);
1597
gpg_ctx_set_istream (gpg, istream);
1598
gpg_ctx_set_ostream (gpg, ostream);
1599
gpg_ctx_set_always_trust (gpg, ctx->always_trust);
1601
for (i = 0; i < recipients->len; i++) {
1602
gpg_ctx_add_recipient (gpg, recipients->pdata[i]);
1605
if (gpg_ctx_op_start (gpg) == -1) {
1606
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg."));
1610
/* FIXME: move tihs to a common routine */
1611
while (!gpg_ctx_op_complete(gpg)) {
1612
if (gpg_ctx_op_step (gpg, ex) == -1)
1616
if (gpg_ctx_op_wait (gpg) != 0) {
1617
const char *diagnostics;
1619
diagnostics = gpg_ctx_get_diagnostics (gpg);
1620
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1621
diagnostics && *diagnostics ? diagnostics : _("Failed to execute gpg."));
1627
dw = camel_data_wrapper_new();
1628
camel_data_wrapper_construct_from_stream(dw, ostream);
1630
encpart = camel_mime_part_new();
1631
ct = camel_content_type_new("application", "octet-stream");
1632
camel_content_type_set_param(ct, "name", "encrypted.asc");
1633
camel_data_wrapper_set_mime_type_field(dw, ct);
1634
camel_content_type_unref(ct);
1636
camel_medium_set_content_object((CamelMedium *)encpart, dw);
1637
camel_object_unref(dw);
1639
camel_mime_part_set_description(encpart, _("This is a digitally encrypted message part"));
1641
vstream = camel_stream_mem_new();
1642
camel_stream_write(vstream, "Version: 1\n", strlen("Version: 1\n"));
1643
camel_stream_reset(vstream);
1645
verpart = camel_mime_part_new();
1646
dw = camel_data_wrapper_new();
1647
camel_data_wrapper_set_mime_type(dw, context->encrypt_protocol);
1648
camel_data_wrapper_construct_from_stream(dw, vstream);
1649
camel_object_unref(vstream);
1650
camel_medium_set_content_object((CamelMedium *)verpart, dw);
1651
camel_object_unref(dw);
1653
mpe = camel_multipart_encrypted_new();
1654
ct = camel_content_type_new("multipart", "encrypted");
1655
camel_content_type_set_param(ct, "protocol", context->encrypt_protocol);
1656
camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mpe, ct);
1657
camel_content_type_unref(ct);
1658
camel_multipart_set_boundary((CamelMultipart *)mpe, NULL);
1660
mpe->decrypted = ipart;
1661
camel_object_ref(ipart);
1663
camel_multipart_add_part((CamelMultipart *)mpe, verpart);
1664
camel_object_unref(verpart);
1665
camel_multipart_add_part((CamelMultipart *)mpe, encpart);
1666
camel_object_unref(encpart);
1668
camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mpe);
1672
camel_object_unref(istream);
1673
camel_object_unref(ostream);
1678
static CamelCipherValidity *
1679
gpg_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1681
struct _GpgCtx *gpg;
1682
CamelCipherValidity *valid = NULL;
1683
CamelStream *ostream, *istream;
1684
CamelDataWrapper *content;
1685
CamelMimePart *encrypted;
1687
CamelContentType *ct;
1690
content = camel_medium_get_content_object((CamelMedium *)ipart);
1691
ct = camel_mime_part_get_content_type((CamelMimePart *)content);
1692
/* Encrypted part (using our fake mime type) or PGP/Mime multipart */
1693
if (camel_content_type_is(ct, "multipart", "encrypted")) {
1694
mp = (CamelMultipart *) camel_medium_get_content_object ((CamelMedium *) ipart);
1695
if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
1696
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to decrypt MIME part: protocol error"));
1700
content = camel_medium_get_content_object ((CamelMedium *) encrypted);
1701
} else if (camel_content_type_is(ct, "application", "x-inlinepgp-encrypted")) {
1702
content = camel_medium_get_content_object ((CamelMedium *) ipart);
1705
/* Invalid Mimetype */
1706
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1707
_("Cannot decrypt message: Incorrect message format"));
1711
istream = camel_stream_mem_new();
1712
camel_data_wrapper_decode_to_stream (content, istream);
1713
camel_stream_reset(istream);
1715
ostream = camel_stream_mem_new();
1716
camel_stream_mem_set_secure((CamelStreamMem *)ostream);
1718
gpg = gpg_ctx_new (context->session);
1719
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
1720
gpg_ctx_set_istream (gpg, istream);
1721
gpg_ctx_set_ostream (gpg, ostream);
1723
if (gpg_ctx_op_start (gpg) == -1) {
1724
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1725
_("Failed to execute gpg."));
1729
while (!gpg_ctx_op_complete (gpg)) {
1730
if (gpg_ctx_op_step (gpg, ex) == -1)
1734
if (gpg_ctx_op_wait (gpg) != 0) {
1735
const char *diagnostics;
1737
diagnostics = gpg_ctx_get_diagnostics (gpg);
1738
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1739
diagnostics && *diagnostics ? diagnostics :
1740
_("Failed to execute gpg."));
1744
camel_stream_reset(ostream);
1745
if (camel_content_type_is(ct, "multipart", "encrypted")) {
1746
/* Multipart encrypted - parse a full mime part */
1747
rv = camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream);
1749
/* Inline signed - raw data (may not be a mime part) */
1750
CamelDataWrapper *dw;
1751
dw = camel_data_wrapper_new ();
1752
rv = camel_data_wrapper_construct_from_stream(dw, ostream);
1753
camel_data_wrapper_set_mime_type(dw, "application/octet-stream");
1754
camel_medium_set_content_object((CamelMedium *)opart, dw);
1755
camel_object_unref(dw);
1756
/* Set mime/type of this new part to application/octet-stream to force type snooping */
1757
camel_mime_part_set_content_type(opart, "application/octet-stream");
1760
valid = camel_cipher_validity_new();
1761
valid->encrypt.description = g_strdup(_("Encrypted content"));
1762
valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
1765
if (gpg->validsig) {
1766
if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1767
valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1768
else if (gpg->trust != GPG_TRUST_NEVER)
1769
valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1771
valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1772
} else if (gpg->nopubkey) {
1773
valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1775
valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1779
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1780
_("Unable to parse message content"));
1784
camel_object_unref(ostream);
1785
camel_object_unref(istream);
1792
gpg_import_keys (CamelCipherContext *context, CamelStream *istream, CamelException *ex)
1794
struct _GpgCtx *gpg;
1797
gpg = gpg_ctx_new (context->session);
1798
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_IMPORT);
1799
gpg_ctx_set_istream (gpg, istream);
1801
if (gpg_ctx_op_start (gpg) == -1) {
1802
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1803
_("Failed to execute gpg: %s"),
1804
errno ? g_strerror (errno) : _("Unknown"));
1808
while (!gpg_ctx_op_complete (gpg)) {
1809
if (gpg_ctx_op_step (gpg, ex) == -1)
1813
if (gpg_ctx_op_wait (gpg) != 0) {
1814
const char *diagnostics;
1816
diagnostics = gpg_ctx_get_diagnostics (gpg);
1817
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1818
diagnostics && *diagnostics ? diagnostics :
1819
_("Failed to execute gpg."));
1831
gpg_export_keys (CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex)
1833
struct _GpgCtx *gpg;
1837
gpg = gpg_ctx_new (context->session);
1838
gpg_ctx_set_mode (gpg, GPG_CTX_MODE_EXPORT);
1839
gpg_ctx_set_armor (gpg, TRUE);
1840
gpg_ctx_set_ostream (gpg, ostream);
1842
for (i = 0; i < keys->len; i++) {
1843
gpg_ctx_add_recipient (gpg, keys->pdata[i]);
1846
if (gpg_ctx_op_start (gpg) == -1) {
1847
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1848
_("Failed to execute gpg: %s"),
1849
errno ? g_strerror (errno) : _("Unknown"));
1853
while (!gpg_ctx_op_complete (gpg)) {
1854
if (gpg_ctx_op_step (gpg, ex) == -1)
1858
if (gpg_ctx_op_wait (gpg) != 0) {
1859
const char *diagnostics;
1861
diagnostics = gpg_ctx_get_diagnostics (gpg);
1862
camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1863
diagnostics && *diagnostics ? diagnostics :
1864
_("Failed to execute gpg."));
1875
/* ********************************************************************** */
1878
camel_gpg_context_class_init (CamelGpgContextClass *klass)
1880
CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS (klass);
1882
parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ()));
1884
cipher_class->hash_to_id = gpg_hash_to_id;
1885
cipher_class->id_to_hash = gpg_id_to_hash;
1886
cipher_class->sign = gpg_sign;
1887
cipher_class->verify = gpg_verify;
1888
cipher_class->encrypt = gpg_encrypt;
1889
cipher_class->decrypt = gpg_decrypt;
1890
cipher_class->import_keys = gpg_import_keys;
1891
cipher_class->export_keys = gpg_export_keys;
1895
camel_gpg_context_init (CamelGpgContext *context)
1897
CamelCipherContext *cipher = (CamelCipherContext *) context;
1899
context->always_trust = FALSE;
1901
cipher->sign_protocol = "application/pgp-signature";
1902
cipher->encrypt_protocol = "application/pgp-encrypted";
1903
cipher->key_protocol = "application/pgp-keys";
1907
camel_gpg_context_finalise (CamelObject *object)
1913
camel_gpg_context_get_type (void)
1915
static CamelType type = CAMEL_INVALID_TYPE;
1917
if (type == CAMEL_INVALID_TYPE) {
1918
type = camel_type_register (camel_cipher_context_get_type (),
1920
sizeof (CamelGpgContext),
1921
sizeof (CamelGpgContextClass),
1922
(CamelObjectClassInitFunc) camel_gpg_context_class_init,
1924
(CamelObjectInitFunc) camel_gpg_context_init,
1925
(CamelObjectFinalizeFunc) camel_gpg_context_finalise);