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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 (status, "[GNUPG:] ", 9) != 0) {
752 message = g_locale_to_utf8(status, -1, NULL, NULL, NULL);
753 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
754 _("Unexpected GnuPG status message encountered:\n\n%s"),
762 if (!strncmp ((char *) status, "USERID_HINT ", 12)) {
766 status = (const unsigned char *) next_token ((char *) status, &hint);
768 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
769 _("Failed to parse gpg userid hint."));
773 if (g_hash_table_lookup (gpg->userid_hint, hint)) {
774 /* we already have this userid hint... */
779 if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar *) status, -1, &nread, &nwritten, NULL)))
780 user = g_strdup ((gchar *) status);
784 g_hash_table_insert (gpg->userid_hint, hint, user);
785 } else if (!strncmp ((char *) status, "NEED_PASSPHRASE ", 16)) {
790 status = (const unsigned char *) next_token ((char *) status, &userid);
792 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
793 _("Failed to parse gpg passphrase request."));
797 g_free (gpg->need_id);
798 gpg->need_id = userid;
799 } else if (!strncmp ((char *) status, "NEED_PASSPHRASE_PIN ", 20)) {
804 status = (const unsigned char *) next_token ((char *) status, &userid);
806 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
807 _("Failed to parse gpg passphrase request."));
811 g_free (gpg->need_id);
812 gpg->need_id = userid;
813 } else if (!strncmp ((char *) status, "GET_HIDDEN ", 11)) {
814 const char *name = NULL;
815 char *prompt, *passwd;
820 if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
825 if (!strncmp ((char *) status, "passphrase.pin.ask", 18)) {
826 prompt = g_strdup_printf (_("You need a PIN to unlock the key for your\n"
827 "SmartCard: \"%s\""), name);
828 } else if (!strncmp ((char *) status, "passphrase.enter", 16)) {
829 prompt = g_strdup_printf (_("You need a passphrase to unlock the key for\n"
830 "user: \"%s\""), name);
832 next_token ((char *) status, &prompt);
833 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
834 _("Unexpected request from GnuPG for `%s'"), prompt);
839 flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
840 if ((passwd = camel_session_get_password (gpg->session, NULL, NULL, prompt, gpg->need_id, flags, ex))) {
842 char *opasswd = passwd;
844 if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
845 memset (opasswd, 0, strlen (opasswd));
852 gpg->passwd = g_strdup_printf ("%s\n", passwd);
853 memset (passwd, 0, strlen (passwd));
856 gpg->send_passwd = TRUE;
858 if (!camel_exception_is_set (ex))
859 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled."));
864 } else if (!strncmp ((char *) status, "GOOD_PASSPHRASE", 15)) {
865 gpg->bad_passwds = 0;
866 } else if (!strncmp ((char *) status, "BAD_PASSPHRASE", 14)) {
869 camel_session_forget_password (gpg->session, NULL, NULL, gpg->need_id, ex);
871 if (gpg->bad_passwds == 3) {
872 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
873 _("Failed to unlock secret key: 3 bad passphrases given."));
876 } else if (!strncmp (status, "UNEXPECTED ", 11)) {
877 /* this is an error */
879 message = g_locale_to_utf8(status+11, -1, NULL, NULL, NULL);
880 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
881 _("Unexpected response from GnuPG: %s"),
885 } else if (!strncmp (status, "NODATA", 6)) {
886 /* this is an error */
887 /* But we ignore it anyway, we should get other response codes to say why */
890 /* check to see if we are complete */
892 case GPG_CTX_MODE_SIGN:
893 if (!strncmp ((char *) status, "SIG_CREATED ", 12)) {
894 /* FIXME: save this state? */
897 case GPG_CTX_MODE_VERIFY:
898 if (!strncmp ((char *) status, "TRUST_", 6)) {
900 if (!strncmp ((char *) status, "NEVER", 5)) {
901 gpg->trust = GPG_TRUST_NEVER;
902 } else if (!strncmp ((char *) status, "MARGINAL", 8)) {
903 gpg->trust = GPG_TRUST_MARGINAL;
904 } else if (!strncmp ((char *) status, "FULLY", 5)) {
905 gpg->trust = GPG_TRUST_FULLY;
906 } else if (!strncmp ((char *) status, "ULTIMATE", 8)) {
907 gpg->trust = GPG_TRUST_ULTIMATE;
908 } else if (!strncmp ((char *) status, "UNDEFINED", 9)) {
909 gpg->trust = GPG_TRUST_UNDEFINED;
911 } else if (!strncmp ((char *) status, "GOODSIG ", 8)) {
914 } else if (!strncmp ((char *) status, "VALIDSIG ", 9)) {
915 gpg->validsig = TRUE;
916 } else if (!strncmp ((char *) status, "BADSIG ", 7)) {
919 } else if (!strncmp ((char *) status, "ERRSIG ", 7)) {
920 /* Note: NO_PUBKEY often comes after an ERRSIG */
923 } else if (!strncmp ((char *) status, "NO_PUBKEY ", 10)) {
924 gpg->nopubkey = TRUE;
927 case GPG_CTX_MODE_ENCRYPT:
928 if (!strncmp ((char *) status, "BEGIN_ENCRYPTION", 16)) {
929 /* nothing to do... but we know to expect data on stdout soon */
930 } else if (!strncmp ((char *) status, "END_ENCRYPTION", 14)) {
931 /* nothing to do, but we know the end is near? */
932 } else if (!strncmp ((char *) status, "NO_RECP", 7)) {
933 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
934 _("Failed to encrypt: No valid recipients specified."));
938 case GPG_CTX_MODE_DECRYPT:
939 if (!strncmp ((char *) status, "BEGIN_DECRYPTION", 16)) {
940 /* nothing to do... but we know to expect data on stdout soon */
941 } else if (!strncmp ((char *) status, "END_DECRYPTION", 14)) {
942 /* nothing to do, but we know the end is near? */
945 case GPG_CTX_MODE_IMPORT:
948 case GPG_CTX_MODE_EXPORT:
956 /* recycle our statusbuf by moving inptr to the beginning of statusbuf */
957 len = gpg->statusptr - inptr;
958 memmove (gpg->statusbuf, inptr, len);
960 len = inptr - gpg->statusbuf;
961 gpg->statusleft += len;
962 gpg->statusptr -= len;
964 /* if we have more data, try parsing the next line? */
965 if (gpg->statusptr > gpg->statusbuf)
973 #define status_backup(gpg, start, len) G_STMT_START { \
974 if (gpg->statusleft <= len) { \
975 unsigned int slen, soff; \
977 slen = soff = gpg->statusptr - gpg->statusbuf; \
978 slen = slen ? slen : 1; \
980 while (slen < soff + len) \
983 gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1); \
984 gpg->statusptr = gpg->statusbuf + soff; \
985 gpg->statusleft = slen - soff; \
988 memcpy (gpg->statusptr, start, len); \
989 gpg->statusptr += len; \
990 gpg->statusleft -= len; \
996 gpg_ctx_op_cancel (struct _GpgCtx *gpg)
1004 kill (gpg->pid, SIGTERM);
1006 retval = waitpid (gpg->pid, &status, WNOHANG);
1007 if (retval == (pid_t) 0) {
1008 /* no more mr nice guy... */
1009 kill (gpg->pid, SIGKILL);
1011 waitpid (gpg->pid, &status, WNOHANG);
1018 gpg_ctx_op_step (struct _GpgCtx *gpg, CamelException *ex)
1021 struct pollfd polls[6];
1022 int status, i, cancel_fd;
1026 polls[i].events = 0;
1029 if (!gpg->seen_eof1) {
1030 polls[0].fd = gpg->stdout_fd;
1031 polls[0].events = POLLIN;
1034 if (!gpg->seen_eof2) {
1035 polls[1].fd = gpg->stderr_fd;
1036 polls[1].events = POLLIN;
1039 if (!gpg->complete) {
1040 polls[2].fd = gpg->status_fd;
1041 polls[2].events = POLLIN;
1044 polls[3].fd = gpg->stdin_fd;
1045 polls[3].events = POLLOUT;
1046 polls[4].fd = gpg->passwd_fd;
1047 polls[4].events = POLLOUT;
1048 cancel_fd = camel_operation_cancel_fd(NULL);
1049 polls[5].fd = cancel_fd;
1050 polls[5].events = POLLIN;
1054 polls[i].revents = 0;
1055 status = poll(polls, 6, 30*1000);
1056 } while (status == -1 && errno == EINTR);
1059 return 0; /* timed out */
1060 else if (status == -1)
1063 if ((polls[5].revents & POLLIN) && camel_operation_cancel_check(NULL)) {
1064 camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled."));
1065 gpg_ctx_op_cancel(gpg);
1069 /* Test each and every file descriptor to see if it's 'ready',
1070 and if so - do what we can with it and then drop through to
1071 the next file descriptor and so on until we've done what we
1072 can to all of them. If one fails along the way, return
1075 if (polls[2].revents & (POLLIN|POLLHUP)) {
1076 /* read the status message and decide what to do... */
1080 d(printf ("reading from gpg's status-fd...\n"));
1083 nread = read (gpg->status_fd, buffer, sizeof (buffer));
1084 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1089 status_backup (gpg, buffer, nread);
1091 if (gpg_ctx_parse_status (gpg, ex) == -1)
1094 gpg->complete = TRUE;
1098 if ((polls[0].revents & (POLLIN|POLLHUP)) && gpg->ostream) {
1102 d(printf ("reading gpg's stdout...\n"));
1105 nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1106 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1111 if (camel_stream_write (gpg->ostream, buffer, (size_t) nread) == -1)
1114 gpg->seen_eof1 = TRUE;
1118 if (polls[1].revents & (POLLIN|POLLHUP)) {
1122 d(printf ("reading gpg's stderr...\n"));
1125 nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
1126 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1131 camel_stream_write (gpg->diagnostics, buffer, nread);
1133 gpg->seen_eof2 = TRUE;
1137 if ((polls[4].revents & (POLLOUT|POLLHUP)) && gpg->need_passwd && gpg->send_passwd) {
1138 ssize_t w, nwritten = 0;
1141 d(printf ("sending gpg our passphrase...\n"));
1143 /* send the passphrase to gpg */
1144 n = strlen (gpg->passwd);
1147 w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
1148 } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1152 } while (nwritten < n && w != -1);
1154 /* zero and free our passwd buffer */
1155 memset (gpg->passwd, 0, n);
1156 g_free (gpg->passwd);
1162 gpg->send_passwd = FALSE;
1165 if ((polls[3].revents & (POLLOUT|POLLHUP)) && gpg->istream) {
1169 d(printf ("writing to gpg's stdin...\n"));
1171 /* write our stream to gpg's stdin */
1172 nread = camel_stream_read (gpg->istream, buffer, sizeof (buffer));
1174 ssize_t w, nwritten = 0;
1178 w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
1179 } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1183 } while (nwritten < nread && w != -1);
1188 d(printf ("wrote %d (out of %d) bytes to gpg's stdin\n", nwritten, nread));
1191 if (camel_stream_eos (gpg->istream)) {
1192 d(printf ("closing gpg's stdin\n"));
1193 close (gpg->stdin_fd);
1201 /* always called on an i/o error */
1202 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg: %s"), g_strerror(errno));
1203 gpg_ctx_op_cancel(gpg);
1209 gpg_ctx_op_complete (struct _GpgCtx *gpg)
1211 return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
1216 gpg_ctx_op_exited (struct _GpgCtx *gpg)
1224 retval = waitpid (gpg->pid, &status, WNOHANG);
1225 if (retval == gpg->pid) {
1226 gpg->exit_status = status;
1236 gpg_ctx_op_wait (struct _GpgCtx *gpg)
1239 sigset_t mask, omask;
1244 sigemptyset (&mask);
1245 sigaddset (&mask, SIGALRM);
1246 sigprocmask (SIG_BLOCK, &mask, &omask);
1248 retval = waitpid (gpg->pid, &status, 0);
1250 sigprocmask (SIG_SETMASK, &omask, NULL);
1252 if (retval == (pid_t) -1 && errno == EINTR) {
1253 /* The child is hanging: send a friendly reminder. */
1254 kill (gpg->pid, SIGTERM);
1256 retval = waitpid (gpg->pid, &status, WNOHANG);
1257 if (retval == (pid_t) 0) {
1258 /* Still hanging; use brute force. */
1259 kill (gpg->pid, SIGKILL);
1261 retval = waitpid (gpg->pid, &status, WNOHANG);
1265 status = gpg->exit_status;
1269 if (retval != (pid_t) -1 && WIFEXITED (status))
1270 return WEXITSTATUS (status);
1281 gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1283 struct _GpgCtx *gpg = NULL;
1284 CamelStream *ostream = camel_stream_mem_new(), *istream;
1285 CamelDataWrapper *dw;
1286 CamelContentType *ct;
1288 CamelMimePart *sigpart;
1289 CamelMultipartSigned *mps;
1291 /* Note: see rfc2015 or rfc3156, section 5 */
1293 /* FIXME: stream this, we stream output at least */
1294 istream = camel_stream_mem_new();
1295 if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM,
1297 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1298 _("Could not generate signing data: %s"), g_strerror(errno));
1303 if (camel_debug_start("gpg:sign")) {
1307 name = g_strdup_printf("camel-gpg.%d.sign-data", logid++);
1308 out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1310 printf("Writing gpg signing data to '%s'\n", name);
1311 camel_stream_write_to_stream(istream, out);
1312 camel_stream_reset(istream);
1313 camel_object_unref(out);
1320 gpg = gpg_ctx_new (context->session);
1321 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
1322 gpg_ctx_set_hash (gpg, hash);
1323 gpg_ctx_set_armor (gpg, TRUE);
1324 gpg_ctx_set_userid (gpg, userid);
1325 gpg_ctx_set_istream (gpg, istream);
1326 gpg_ctx_set_ostream (gpg, ostream);
1328 if (gpg_ctx_op_start (gpg) == -1) {
1329 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1330 _("Failed to execute gpg: %s"), g_strerror (errno));
1334 while (!gpg_ctx_op_complete (gpg)) {
1335 if (gpg_ctx_op_step (gpg, ex) == -1)
1339 if (gpg_ctx_op_wait (gpg) != 0) {
1340 const char *diagnostics;
1342 diagnostics = gpg_ctx_get_diagnostics (gpg);
1343 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1344 diagnostics && *diagnostics ? diagnostics :
1345 _("Failed to execute gpg."));
1351 dw = camel_data_wrapper_new();
1352 camel_stream_reset(ostream);
1353 camel_data_wrapper_construct_from_stream(dw, ostream);
1355 sigpart = camel_mime_part_new();
1356 ct = camel_content_type_new("application", "pgp-signature");
1357 camel_content_type_set_param(ct, "name", "signature.asc");
1358 camel_data_wrapper_set_mime_type_field(dw, ct);
1359 camel_content_type_unref(ct);
1361 camel_medium_set_content_object((CamelMedium *)sigpart, dw);
1362 camel_object_unref(dw);
1364 camel_mime_part_set_description(sigpart, _("This is a digitally signed message part"));
1366 mps = camel_multipart_signed_new();
1367 ct = camel_content_type_new("multipart", "signed");
1368 camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash));
1369 camel_content_type_set_param(ct, "protocol", context->sign_protocol);
1370 camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct);
1371 camel_content_type_unref(ct);
1372 camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
1374 mps->signature = sigpart;
1375 mps->contentraw = istream;
1376 camel_stream_reset(istream);
1377 camel_object_ref(istream);
1379 camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps);
1381 camel_object_unref(ostream);
1391 swrite (CamelMimePart *sigpart)
1393 CamelStream *ostream;
1397 template = g_build_filename (g_get_tmp_dir (), "evolution-pgp.XXXXXX", NULL);
1398 if ((fd = g_mkstemp (template)) == -1) {
1403 /* TODO: This should probably just write the decoded message content out, not the part + headers */
1405 ostream = camel_stream_fs_new_with_fd (fd);
1406 ret = camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, ostream);
1408 ret = camel_stream_flush (ostream);
1410 ret = camel_stream_close (ostream);
1413 camel_object_unref(ostream);
1416 g_unlink (template);
1424 static CamelCipherValidity *
1425 gpg_verify (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex)
1427 CamelCipherValidity *validity;
1428 const char *diagnostics = NULL;
1429 struct _GpgCtx *gpg = NULL;
1430 char *sigfile = NULL;
1431 CamelContentType *ct;
1432 CamelMimePart *sigpart;
1433 CamelStream *istream = NULL;
1434 CamelMultipart *mps;
1436 mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart);
1437 ct = ((CamelDataWrapper *)mps)->mime_type;
1439 /* Inline signature (using our fake mime type) or PGP/Mime signature */
1440 if (camel_content_type_is(ct, "multipart", "signed")) {
1441 /* PGP/Mime Signature */
1444 tmp = camel_content_type_param(ct, "protocol");
1445 if (!CAMEL_IS_MULTIPART_SIGNED(mps)
1447 || g_ascii_strcasecmp(tmp, context->sign_protocol) != 0) {
1448 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1449 _("Cannot verify message signature: Incorrect message format"));
1453 if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
1454 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1455 _("Cannot verify message signature: Incorrect message format"));
1459 if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
1460 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1461 _("Cannot verify message signature: Incorrect message format"));
1462 camel_object_unref (istream);
1466 } else if (camel_content_type_is(ct, "application", "x-inlinepgp-signed")) {
1468 CamelDataWrapper *content;
1469 content = camel_medium_get_content_object ((CamelMedium *) ipart);
1470 istream = camel_stream_mem_new();
1471 camel_data_wrapper_decode_to_stream (content, istream);
1472 camel_stream_reset(istream);
1475 /* Invalid Mimetype */
1476 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1477 _("Cannot verify message signature: Incorrect message format"));
1481 /* Now start the real work of verifying the message */
1483 if (camel_debug_start("gpg:sign")) {
1487 name = g_strdup_printf("camel-gpg.%d.verify.data", logid);
1488 out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1490 printf("Writing gpg verify data to '%s'\n", name);
1491 camel_stream_write_to_stream(istream, out);
1492 camel_stream_reset(istream);
1493 camel_object_unref(out);
1497 name = g_strdup_printf("camel-gpg.%d.verify.signature", logid++);
1498 out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1500 printf("Writing gpg verify signature to '%s'\n", name);
1501 camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, out);
1502 camel_object_unref(out);
1511 sigfile = swrite (sigpart);
1512 if (sigfile == NULL) {
1513 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1514 _("Cannot verify message signature: could not create temp file: %s"),
1515 g_strerror (errno));
1520 camel_stream_reset(istream);
1521 gpg = gpg_ctx_new (context->session);
1522 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
1524 gpg_ctx_set_sigfile (gpg, sigfile);
1525 gpg_ctx_set_istream (gpg, istream);
1527 if (gpg_ctx_op_start (gpg) == -1) {
1528 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1529 _("Failed to execute gpg."));
1533 while (!gpg_ctx_op_complete (gpg)) {
1534 if (gpg_ctx_op_step (gpg, ex) == -1)
1538 gpg_ctx_op_wait (gpg);
1539 validity = camel_cipher_validity_new ();
1540 diagnostics = gpg_ctx_get_diagnostics (gpg);
1541 camel_cipher_validity_set_description (validity, diagnostics);
1542 if (gpg->validsig) {
1543 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1544 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1545 else if (gpg->trust != GPG_TRUST_NEVER)
1546 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1548 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1549 } else if (gpg->nopubkey) {
1550 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
1552 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1561 camel_object_unref(istream);
1571 camel_object_unref(istream);
1582 gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
1584 CamelGpgContext *ctx = (CamelGpgContext *) context;
1585 struct _GpgCtx *gpg;
1587 CamelStream *istream, *ostream, *vstream;
1588 CamelMimePart *encpart, *verpart;
1589 CamelDataWrapper *dw;
1590 CamelContentType *ct;
1591 CamelMultipartEncrypted *mpe;
1593 ostream = camel_stream_mem_new();
1594 istream = camel_stream_mem_new();
1595 if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream) == -1) {
1596 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1597 _("Could not generate encrypting data: %s"), g_strerror(errno));
1601 gpg = gpg_ctx_new (context->session);
1602 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
1603 gpg_ctx_set_armor (gpg, TRUE);
1604 gpg_ctx_set_userid (gpg, userid);
1605 gpg_ctx_set_istream (gpg, istream);
1606 gpg_ctx_set_ostream (gpg, ostream);
1607 gpg_ctx_set_always_trust (gpg, ctx->always_trust);
1609 for (i = 0; i < recipients->len; i++) {
1610 gpg_ctx_add_recipient (gpg, recipients->pdata[i]);
1613 if (gpg_ctx_op_start (gpg) == -1) {
1614 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg."));
1618 /* FIXME: move tihs to a common routine */
1619 while (!gpg_ctx_op_complete(gpg)) {
1620 if (gpg_ctx_op_step (gpg, ex) == -1)
1624 if (gpg_ctx_op_wait (gpg) != 0) {
1625 const char *diagnostics;
1627 diagnostics = gpg_ctx_get_diagnostics (gpg);
1628 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1629 diagnostics && *diagnostics ? diagnostics : _("Failed to execute gpg."));
1635 dw = camel_data_wrapper_new();
1636 camel_data_wrapper_construct_from_stream(dw, ostream);
1638 encpart = camel_mime_part_new();
1639 ct = camel_content_type_new("application", "octet-stream");
1640 camel_content_type_set_param(ct, "name", "encrypted.asc");
1641 camel_data_wrapper_set_mime_type_field(dw, ct);
1642 camel_content_type_unref(ct);
1644 camel_medium_set_content_object((CamelMedium *)encpart, dw);
1645 camel_object_unref(dw);
1647 camel_mime_part_set_description(encpart, _("This is a digitally encrypted message part"));
1649 vstream = camel_stream_mem_new();
1650 camel_stream_write(vstream, "Version: 1\n", strlen("Version: 1\n"));
1651 camel_stream_reset(vstream);
1653 verpart = camel_mime_part_new();
1654 dw = camel_data_wrapper_new();
1655 camel_data_wrapper_set_mime_type(dw, context->encrypt_protocol);
1656 camel_data_wrapper_construct_from_stream(dw, vstream);
1657 camel_object_unref(vstream);
1658 camel_medium_set_content_object((CamelMedium *)verpart, dw);
1659 camel_object_unref(dw);
1661 mpe = camel_multipart_encrypted_new();
1662 ct = camel_content_type_new("multipart", "encrypted");
1663 camel_content_type_set_param(ct, "protocol", context->encrypt_protocol);
1664 camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mpe, ct);
1665 camel_content_type_unref(ct);
1666 camel_multipart_set_boundary((CamelMultipart *)mpe, NULL);
1668 mpe->decrypted = ipart;
1669 camel_object_ref(ipart);
1671 camel_multipart_add_part((CamelMultipart *)mpe, verpart);
1672 camel_object_unref(verpart);
1673 camel_multipart_add_part((CamelMultipart *)mpe, encpart);
1674 camel_object_unref(encpart);
1676 camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mpe);
1680 camel_object_unref(istream);
1681 camel_object_unref(ostream);
1686 static CamelCipherValidity *
1687 gpg_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1689 struct _GpgCtx *gpg;
1690 CamelCipherValidity *valid = NULL;
1691 CamelStream *ostream, *istream;
1692 CamelDataWrapper *content;
1693 CamelMimePart *encrypted;
1695 CamelContentType *ct;
1699 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1700 _("Cannot decrypt message: Incorrect message format"));
1704 content = camel_medium_get_content_object((CamelMedium *)ipart);
1707 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1708 _("Cannot decrypt message: Incorrect message format"));
1712 ct = camel_mime_part_get_content_type((CamelMimePart *)content);
1713 /* Encrypted part (using our fake mime type) or PGP/Mime multipart */
1714 if (camel_content_type_is(ct, "multipart", "encrypted")) {
1715 mp = (CamelMultipart *) camel_medium_get_content_object ((CamelMedium *) ipart);
1716 if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
1717 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to decrypt MIME part: protocol error"));
1721 content = camel_medium_get_content_object ((CamelMedium *) encrypted);
1722 } else if (camel_content_type_is(ct, "application", "x-inlinepgp-encrypted")) {
1723 content = camel_medium_get_content_object ((CamelMedium *) ipart);
1726 /* Invalid Mimetype */
1727 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1728 _("Cannot decrypt message: Incorrect message format"));
1732 istream = camel_stream_mem_new();
1733 camel_data_wrapper_decode_to_stream (content, istream);
1734 camel_stream_reset(istream);
1736 ostream = camel_stream_mem_new();
1737 camel_stream_mem_set_secure((CamelStreamMem *)ostream);
1739 gpg = gpg_ctx_new (context->session);
1740 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
1741 gpg_ctx_set_istream (gpg, istream);
1742 gpg_ctx_set_ostream (gpg, ostream);
1744 if (gpg_ctx_op_start (gpg) == -1) {
1745 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1746 _("Failed to execute gpg."));
1750 while (!gpg_ctx_op_complete (gpg)) {
1751 if (gpg_ctx_op_step (gpg, ex) == -1)
1755 if (gpg_ctx_op_wait (gpg) != 0) {
1756 const char *diagnostics;
1758 diagnostics = gpg_ctx_get_diagnostics (gpg);
1759 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1760 diagnostics && *diagnostics ? diagnostics :
1761 _("Failed to execute gpg."));
1765 camel_stream_reset(ostream);
1766 if (camel_content_type_is(ct, "multipart", "encrypted")) {
1767 /* Multipart encrypted - parse a full mime part */
1768 rv = camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream);
1770 /* Inline signed - raw data (may not be a mime part) */
1771 CamelDataWrapper *dw;
1772 dw = camel_data_wrapper_new ();
1773 rv = camel_data_wrapper_construct_from_stream(dw, ostream);
1774 camel_data_wrapper_set_mime_type(dw, "application/octet-stream");
1775 camel_medium_set_content_object((CamelMedium *)opart, dw);
1776 camel_object_unref(dw);
1777 /* Set mime/type of this new part to application/octet-stream to force type snooping */
1778 camel_mime_part_set_content_type(opart, "application/octet-stream");
1781 valid = camel_cipher_validity_new();
1782 valid->encrypt.description = g_strdup(_("Encrypted content"));
1783 valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
1786 if (gpg->validsig) {
1787 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1788 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1789 else if (gpg->trust != GPG_TRUST_NEVER)
1790 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1792 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1793 } else if (gpg->nopubkey) {
1794 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
1796 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1800 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1801 _("Unable to parse message content"));
1805 camel_object_unref(ostream);
1806 camel_object_unref(istream);
1813 gpg_import_keys (CamelCipherContext *context, CamelStream *istream, CamelException *ex)
1815 struct _GpgCtx *gpg;
1818 gpg = gpg_ctx_new (context->session);
1819 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_IMPORT);
1820 gpg_ctx_set_istream (gpg, istream);
1822 if (gpg_ctx_op_start (gpg) == -1) {
1823 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1824 _("Failed to execute gpg: %s"),
1825 errno ? g_strerror (errno) : _("Unknown"));
1829 while (!gpg_ctx_op_complete (gpg)) {
1830 if (gpg_ctx_op_step (gpg, ex) == -1)
1834 if (gpg_ctx_op_wait (gpg) != 0) {
1835 const char *diagnostics;
1837 diagnostics = gpg_ctx_get_diagnostics (gpg);
1838 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1839 diagnostics && *diagnostics ? diagnostics :
1840 _("Failed to execute gpg."));
1852 gpg_export_keys (CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex)
1854 struct _GpgCtx *gpg;
1858 gpg = gpg_ctx_new (context->session);
1859 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_EXPORT);
1860 gpg_ctx_set_armor (gpg, TRUE);
1861 gpg_ctx_set_ostream (gpg, ostream);
1863 for (i = 0; i < keys->len; i++) {
1864 gpg_ctx_add_recipient (gpg, keys->pdata[i]);
1867 if (gpg_ctx_op_start (gpg) == -1) {
1868 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1869 _("Failed to execute gpg: %s"),
1870 errno ? g_strerror (errno) : _("Unknown"));
1874 while (!gpg_ctx_op_complete (gpg)) {
1875 if (gpg_ctx_op_step (gpg, ex) == -1)
1879 if (gpg_ctx_op_wait (gpg) != 0) {
1880 const char *diagnostics;
1882 diagnostics = gpg_ctx_get_diagnostics (gpg);
1883 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1884 diagnostics && *diagnostics ? diagnostics :
1885 _("Failed to execute gpg."));
1896 /* ********************************************************************** */
1899 camel_gpg_context_class_init (CamelGpgContextClass *klass)
1901 CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS (klass);
1903 parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ()));
1905 cipher_class->hash_to_id = gpg_hash_to_id;
1906 cipher_class->id_to_hash = gpg_id_to_hash;
1907 cipher_class->sign = gpg_sign;
1908 cipher_class->verify = gpg_verify;
1909 cipher_class->encrypt = gpg_encrypt;
1910 cipher_class->decrypt = gpg_decrypt;
1911 cipher_class->import_keys = gpg_import_keys;
1912 cipher_class->export_keys = gpg_export_keys;
1916 camel_gpg_context_init (CamelGpgContext *context)
1918 CamelCipherContext *cipher = (CamelCipherContext *) context;
1920 context->always_trust = FALSE;
1922 cipher->sign_protocol = "application/pgp-signature";
1923 cipher->encrypt_protocol = "application/pgp-encrypted";
1924 cipher->key_protocol = "application/pgp-keys";
1928 camel_gpg_context_finalise (CamelObject *object)
1934 camel_gpg_context_get_type (void)
1936 static CamelType type = CAMEL_INVALID_TYPE;
1938 if (type == CAMEL_INVALID_TYPE) {
1939 type = camel_type_register (camel_cipher_context_get_type (),
1941 sizeof (CamelGpgContext),
1942 sizeof (CamelGpgContextClass),
1943 (CamelObjectClassInitFunc) camel_gpg_context_class_init,
1945 (CamelObjectInitFunc) camel_gpg_context_init,
1946 (CamelObjectFinalizeFunc) camel_gpg_context_finalise);