1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Jeffrey Stedfast <fejj@ximian.com>
18  */
19 
20 /* Debug states:
21  * gpg:sign	dump canonicalised to-be-signed data to a file
22  * gpg:verify	dump canonicalised verification and signature data to file
23  * gpg:status	print gpg status-fd output to stdout
24  */
25 
26 #include "evolution-data-server-config.h"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <ctype.h>
39 
40 #include <glib.h>
41 #include <glib/gi18n-lib.h>
42 #include <glib/gstdio.h>
43 
44 #ifndef G_OS_WIN32
45 #include <sys/ioctl.h>
46 #include <sys/wait.h>
47 #include <termios.h>
48 #endif
49 
50 #include "camel-debug.h"
51 #include "camel-file-utils.h"
52 #include "camel-gpg-context.h"
53 #include "camel-iconv.h"
54 #include "camel-internet-address.h"
55 #include "camel-mime-filter-canon.h"
56 #include "camel-mime-filter-charset.h"
57 #include "camel-mime-part.h"
58 #include "camel-multipart-encrypted.h"
59 #include "camel-multipart-signed.h"
60 #include "camel-operation.h"
61 #include "camel-session.h"
62 #include "camel-stream-filter.h"
63 #include "camel-stream-fs.h"
64 #include "camel-stream-mem.h"
65 #include "camel-stream-null.h"
66 #include "camel-string-utils.h"
67 
68 #define d(x)
69 
70 #ifdef GPG_LOG
71 static gint logid;
72 #endif
73 
74 #define CHECK_CALL(x) G_STMT_START { \
75 	if ((x) == -1) { \
76 		g_debug ("%s: Call of '" #x "' failed: %s", G_STRFUNC, g_strerror (errno)); \
77 	} \
78 	} G_STMT_END
79 
80 struct _CamelGpgContextPrivate {
81 	gboolean always_trust;
82 	gboolean prefer_inline;
83 };
84 
85 enum {
86 	PROP_0,
87 	PROP_ALWAYS_TRUST,
88 	PROP_PREFER_INLINE
89 };
90 
91 G_DEFINE_TYPE_WITH_PRIVATE (CamelGpgContext, camel_gpg_context, CAMEL_TYPE_CIPHER_CONTEXT)
92 
93 static const gchar *gpg_ctx_get_executable_name (void);
94 
95 typedef struct _GpgRecipientsData {
96 	gchar *keyid;
97 	gchar *known_key_data;
98 } GpgRecipientsData;
99 
100 static void
gpg_recipients_data_free(gpointer ptr)101 gpg_recipients_data_free (gpointer ptr)
102 {
103 	GpgRecipientsData *rd = ptr;
104 
105 	if (rd) {
106 		g_free (rd->keyid);
107 		g_free (rd->known_key_data);
108 		g_slice_free (GpgRecipientsData, rd);
109 	}
110 }
111 
112 enum _GpgCtxMode {
113 	GPG_CTX_MODE_SIGN,
114 	GPG_CTX_MODE_VERIFY,
115 	GPG_CTX_MODE_ENCRYPT,
116 	GPG_CTX_MODE_DECRYPT
117 };
118 
119 enum _GpgTrustMetric {
120 	GPG_TRUST_NONE,
121 	GPG_TRUST_NEVER,
122 	GPG_TRUST_UNDEFINED,
123 	GPG_TRUST_MARGINAL,
124 	GPG_TRUST_FULLY,
125 	GPG_TRUST_ULTIMATE
126 };
127 
128 struct _GpgCtx {
129 	enum _GpgCtxMode mode;
130 	CamelSession *session;
131 	GCancellable *cancellable;
132 	GHashTable *userid_hint;
133 	pid_t pid;
134 
135 	GSList *userids;
136 	gchar *sigfile;
137 	GPtrArray *recipients; /* GpgRecipientsData * */
138 	GSList *recipient_key_files; /* gchar * with filenames of keys in the tmp directory */
139 	CamelCipherHash hash;
140 
141 	gint stdin_fd;
142 	gint stdout_fd;
143 	gint stderr_fd;
144 	gint status_fd;
145 	gint passwd_fd;  /* only needed for sign/decrypt */
146 
147 	/* status-fd buffer */
148 	guchar *statusbuf;
149 	guchar *statusptr;
150 	guint statusleft;
151 
152 	gchar *need_id;
153 	gchar *passwd;
154 
155 	CamelStream *istream;
156 	CamelStream *ostream;
157 
158 	GByteArray *diagbuf;
159 	CamelStream *diagnostics;
160 
161 	gchar *photos_filename;
162 	gchar *viewer_cmd;
163 
164 	GString *decrypt_extra_text; /* Text received during decryption, which is in the blob, but is not encrypted */
165 
166 	gint exit_status;
167 
168 	guint exited : 1;
169 	guint complete : 1;
170 	guint seen_eof1 : 1;
171 	guint seen_eof2 : 1;
172 	guint always_trust : 1;
173 	guint prefer_inline : 1;
174 	guint armor : 1;
175 	guint need_passwd : 1;
176 	guint send_passwd : 1;
177 	guint load_photos : 1;
178 
179 	guint bad_passwds : 2;
180 	guint anonymous_recipient : 1;
181 
182 	guint hadsig : 1;
183 	guint badsig : 1;
184 	guint errsig : 1;
185 	guint goodsig : 1;
186 	guint validsig : 1;
187 	guint nopubkey : 1;
188 	guint nodata : 1;
189 	guint trust : 3;
190 	guint processing : 1;
191 	guint bad_decrypt : 1;
192 	guint in_decrypt_stage : 1;
193 	guint noseckey : 1;
194 	GString *signers;
195 	GHashTable *signers_keyid;
196 
197 	guint diagflushed : 1;
198 
199 	guint utf8 : 1;
200 
201 	guint padding : 10;
202 };
203 
204 static struct _GpgCtx *
gpg_ctx_new(CamelCipherContext * context,GCancellable * cancellable)205 gpg_ctx_new (CamelCipherContext *context,
206 	     GCancellable *cancellable)
207 {
208 	struct _GpgCtx *gpg;
209 	const gchar *charset;
210 	CamelStream *stream;
211 	CamelSession *session;
212 
213 	session = camel_cipher_context_get_session (context);
214 
215 	gpg = g_slice_new0 (struct _GpgCtx);
216 	gpg->mode = GPG_CTX_MODE_SIGN;
217 	gpg->session = g_object_ref (session);
218 	gpg->cancellable = cancellable;
219 	gpg->userid_hint = g_hash_table_new (g_str_hash, g_str_equal);
220 	gpg->complete = FALSE;
221 	gpg->seen_eof1 = TRUE;
222 	gpg->seen_eof2 = FALSE;
223 	gpg->pid = (pid_t) -1;
224 	gpg->exit_status = 0;
225 	gpg->exited = FALSE;
226 
227 	gpg->userids = NULL;
228 	gpg->sigfile = NULL;
229 	gpg->recipients = NULL;
230 	gpg->recipient_key_files = NULL;
231 	gpg->hash = CAMEL_CIPHER_HASH_DEFAULT;
232 	gpg->always_trust = FALSE;
233 	gpg->prefer_inline = FALSE;
234 	gpg->armor = FALSE;
235 	gpg->load_photos = FALSE;
236 	gpg->photos_filename = NULL;
237 	gpg->viewer_cmd = NULL;
238 
239 	gpg->stdin_fd = -1;
240 	gpg->stdout_fd = -1;
241 	gpg->stderr_fd = -1;
242 	gpg->status_fd = -1;
243 	gpg->passwd_fd = -1;
244 
245 	gpg->statusbuf = g_malloc (128);
246 	gpg->statusptr = gpg->statusbuf;
247 	gpg->statusleft = 128;
248 
249 	gpg->bad_passwds = 0;
250 	gpg->anonymous_recipient = FALSE;
251 	gpg->need_passwd = FALSE;
252 	gpg->send_passwd = FALSE;
253 	gpg->need_id = NULL;
254 	gpg->passwd = NULL;
255 
256 	gpg->nodata = FALSE;
257 	gpg->hadsig = FALSE;
258 	gpg->badsig = FALSE;
259 	gpg->errsig = FALSE;
260 	gpg->goodsig = FALSE;
261 	gpg->validsig = FALSE;
262 	gpg->nopubkey = FALSE;
263 	gpg->trust = GPG_TRUST_NONE;
264 	gpg->processing = FALSE;
265 	gpg->bad_decrypt = FALSE;
266 	gpg->in_decrypt_stage = FALSE;
267 	gpg->noseckey = FALSE;
268 	gpg->signers = NULL;
269 	gpg->signers_keyid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
270 
271 	gpg->istream = NULL;
272 	gpg->ostream = NULL;
273 	gpg->decrypt_extra_text = NULL;
274 
275 	gpg->diagbuf = g_byte_array_new ();
276 	gpg->diagflushed = FALSE;
277 
278 	stream = camel_stream_mem_new_with_byte_array (gpg->diagbuf);
279 
280 	if ((charset = camel_iconv_locale_charset ()) && g_ascii_strcasecmp (charset, "UTF-8") != 0) {
281 		CamelMimeFilter *filter;
282 		CamelStream *fstream;
283 
284 		gpg->utf8 = FALSE;
285 
286 		if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
287 			fstream = camel_stream_filter_new (stream);
288 			camel_stream_filter_add (
289 				CAMEL_STREAM_FILTER (fstream), filter);
290 			g_object_unref (filter);
291 			g_object_unref (stream);
292 
293 			stream = (CamelStream *) fstream;
294 		}
295 	} else {
296 		gpg->utf8 = TRUE;
297 	}
298 
299 	gpg->diagnostics = stream;
300 
301 	return gpg;
302 }
303 
304 static void
gpg_ctx_set_mode(struct _GpgCtx * gpg,enum _GpgCtxMode mode)305 gpg_ctx_set_mode (struct _GpgCtx *gpg,
306                   enum _GpgCtxMode mode)
307 {
308 	gpg->mode = mode;
309 	gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT));
310 }
311 
312 static void
gpg_ctx_set_hash(struct _GpgCtx * gpg,CamelCipherHash hash)313 gpg_ctx_set_hash (struct _GpgCtx *gpg,
314                   CamelCipherHash hash)
315 {
316 	gpg->hash = hash;
317 }
318 
319 static void
gpg_ctx_set_always_trust(struct _GpgCtx * gpg,gboolean trust)320 gpg_ctx_set_always_trust (struct _GpgCtx *gpg,
321                           gboolean trust)
322 {
323 	gpg->always_trust = trust;
324 }
325 
326 static void
gpg_ctx_set_prefer_inline(struct _GpgCtx * gpg,gboolean prefer_inline)327 gpg_ctx_set_prefer_inline (struct _GpgCtx *gpg,
328 			   gboolean prefer_inline)
329 {
330 	gpg->prefer_inline = prefer_inline;
331 }
332 
333 static void
gpg_ctx_set_userid(struct _GpgCtx * gpg,const gchar * userid)334 gpg_ctx_set_userid (struct _GpgCtx *gpg,
335                     const gchar *userid)
336 {
337 	g_slist_free_full (gpg->userids, g_free);
338 	gpg->userids = NULL;
339 
340 	if (userid && *userid) {
341 		gchar **uids = g_strsplit (userid, " ", -1);
342 
343 		if (!uids) {
344 			gpg->userids = g_slist_append (gpg->userids, g_strdup (userid));
345 		} else {
346 			gint ii;
347 
348 			for (ii = 0; uids[ii]; ii++) {
349 				const gchar *uid = uids[ii];
350 
351 				if (*uid) {
352 					gpg->userids = g_slist_append (gpg->userids, g_strdup (uid));
353 				}
354 			}
355 
356 			g_strfreev (uids);
357 		}
358 	}
359 }
360 
361 static void
gpg_ctx_add_recipient(struct _GpgCtx * gpg,const gchar * keyid,const gchar * known_key_data)362 gpg_ctx_add_recipient (struct _GpgCtx *gpg,
363                        const gchar *keyid,
364 		       const gchar *known_key_data)
365 {
366 	GpgRecipientsData *rd;
367 	gchar *safe_keyid;
368 
369 	if (gpg->mode != GPG_CTX_MODE_ENCRYPT)
370 		return;
371 
372 	if (!gpg->recipients)
373 		gpg->recipients = g_ptr_array_new ();
374 
375 	g_return_if_fail (keyid != NULL);
376 
377 	/* If the recipient looks like an email address,
378 	 * enclose it in brackets to ensure an exact match. */
379 	if (strchr (keyid, '@') != NULL) {
380 		safe_keyid = g_strdup_printf ("<%s>", keyid);
381 	} else {
382 		safe_keyid = g_strdup (keyid);
383 	}
384 
385 	rd = g_slice_new0 (GpgRecipientsData);
386 	rd->keyid = safe_keyid;
387 	rd->known_key_data = g_strdup (known_key_data);
388 
389 	g_ptr_array_add (gpg->recipients, rd);
390 }
391 
392 static void
gpg_ctx_set_sigfile(struct _GpgCtx * gpg,const gchar * sigfile)393 gpg_ctx_set_sigfile (struct _GpgCtx *gpg,
394                      const gchar *sigfile)
395 {
396 	g_free (gpg->sigfile);
397 	gpg->sigfile = g_strdup (sigfile);
398 }
399 
400 static void
gpg_ctx_set_armor(struct _GpgCtx * gpg,gboolean armor)401 gpg_ctx_set_armor (struct _GpgCtx *gpg,
402                    gboolean armor)
403 {
404 	gpg->armor = armor;
405 }
406 
407 static void
gpg_ctx_set_load_photos(struct _GpgCtx * gpg,gboolean load_photos)408 gpg_ctx_set_load_photos (struct _GpgCtx *gpg,
409 			 gboolean load_photos)
410 {
411 	gpg->load_photos = load_photos;
412 }
413 
414 static void
gpg_ctx_set_istream(struct _GpgCtx * gpg,CamelStream * istream)415 gpg_ctx_set_istream (struct _GpgCtx *gpg,
416                      CamelStream *istream)
417 {
418 	g_object_ref (istream);
419 	if (gpg->istream)
420 		g_object_unref (gpg->istream);
421 	gpg->istream = istream;
422 }
423 
424 static void
gpg_ctx_set_ostream(struct _GpgCtx * gpg,CamelStream * ostream)425 gpg_ctx_set_ostream (struct _GpgCtx *gpg,
426                      CamelStream *ostream)
427 {
428 	g_object_ref (ostream);
429 	if (gpg->ostream)
430 		g_object_unref (gpg->ostream);
431 	gpg->ostream = ostream;
432 	gpg->seen_eof1 = FALSE;
433 }
434 
435 static const gchar *
gpg_ctx_get_diagnostics(struct _GpgCtx * gpg)436 gpg_ctx_get_diagnostics (struct _GpgCtx *gpg)
437 {
438 	if (!gpg->diagflushed) {
439 		gchar *prefix;
440 
441 		gpg->diagflushed = TRUE;
442 		camel_stream_flush (gpg->diagnostics, NULL, NULL);
443 		if (gpg->diagbuf->len == 0)
444 			return NULL;
445 
446 		/* Translators: The '%s' is replaced with the actual path and filename of the used gpg, like '/usr/bin/gpg2' */
447 		prefix = g_strdup_printf (_("Output from %s:"), gpg_ctx_get_executable_name ());
448 
449 		if (prefix && *prefix) {
450 			g_byte_array_prepend (gpg->diagbuf, (const guint8 *) "\n", 1);
451 			g_byte_array_prepend (gpg->diagbuf, (const guint8 *) prefix, strlen (prefix));
452 		}
453 
454 		g_byte_array_append (gpg->diagbuf, (guchar *) "", 1);
455 
456 		g_free (prefix);
457 	}
458 
459 	if (gpg->diagbuf->len == 0)
460 		return NULL;
461 
462 	return (const gchar *) gpg->diagbuf->data;
463 }
464 
465 static void
userid_hint_free(gpointer key,gpointer value,gpointer user_data)466 userid_hint_free (gpointer key,
467                   gpointer value,
468                   gpointer user_data)
469 {
470 	g_free (key);
471 	g_free (value);
472 }
473 
474 static void
gpg_ctx_free(struct _GpgCtx * gpg)475 gpg_ctx_free (struct _GpgCtx *gpg)
476 {
477 	gint i;
478 
479 	if (gpg == NULL)
480 		return;
481 
482 	if (gpg->session)
483 		g_object_unref (gpg->session);
484 
485 	g_hash_table_foreach (gpg->userid_hint, userid_hint_free, NULL);
486 	g_hash_table_destroy (gpg->userid_hint);
487 
488 	g_slist_free_full (gpg->userids, g_free);
489 
490 	g_free (gpg->sigfile);
491 
492 	if (gpg->recipients) {
493 		for (i = 0; i < gpg->recipients->len; i++)
494 			gpg_recipients_data_free (gpg->recipients->pdata[i]);
495 
496 		g_ptr_array_free (gpg->recipients, TRUE);
497 	}
498 
499 	if (gpg->recipient_key_files) {
500 		GSList *link;
501 
502 		if (!camel_debug ("gpg:data")) {
503 			for (link = gpg->recipient_key_files; link; link = g_slist_next (link)) {
504 				g_unlink (link->data);
505 			}
506 		}
507 
508 		g_slist_free_full (gpg->recipient_key_files, g_free);
509 		gpg->recipient_key_files = NULL;
510 	}
511 
512 	if (gpg->stdin_fd != -1)
513 		close (gpg->stdin_fd);
514 	if (gpg->stdout_fd != -1)
515 		close (gpg->stdout_fd);
516 	if (gpg->stderr_fd != -1)
517 		close (gpg->stderr_fd);
518 	if (gpg->status_fd != -1)
519 		close (gpg->status_fd);
520 	if (gpg->passwd_fd != -1)
521 		close (gpg->passwd_fd);
522 
523 	g_free (gpg->statusbuf);
524 
525 	g_free (gpg->need_id);
526 
527 	if (gpg->passwd) {
528 		memset (gpg->passwd, 0, strlen (gpg->passwd));
529 		g_free (gpg->passwd);
530 	}
531 
532 	if (gpg->istream)
533 		g_object_unref (gpg->istream);
534 
535 	if (gpg->ostream)
536 		g_object_unref (gpg->ostream);
537 
538 	g_object_unref (gpg->diagnostics);
539 
540 	if (gpg->signers)
541 		g_string_free (gpg->signers, TRUE);
542 
543 	g_hash_table_destroy (gpg->signers_keyid);
544 	if (gpg->photos_filename)
545 		g_unlink (gpg->photos_filename);
546 
547 	g_free (gpg->photos_filename);
548 	g_free (gpg->viewer_cmd);
549 
550 	if (gpg->decrypt_extra_text)
551 		g_string_free (gpg->decrypt_extra_text, TRUE);
552 
553 	g_slice_free (struct _GpgCtx, gpg);
554 }
555 
556 static const gchar *
gpg_ctx_get_executable_name(void)557 gpg_ctx_get_executable_name (void)
558 {
559 	static gint index = -1;
560 	static gchar preset_binary[512 + 1];
561 	const gchar *names[] = {
562 		"",
563 		"gpg2", /* Prefer gpg2, which the seahorse might use too */
564 		"gpg",
565 		NULL
566 	};
567 
568 	names[0] = preset_binary;
569 
570 	if (index == -1) {
571 		GSettings *settings;
572 		gchar *path;
573 
574 		settings = g_settings_new ("org.gnome.evolution-data-server");
575 		path = g_settings_get_string (settings, "camel-gpg-binary");
576 		g_clear_object (&settings);
577 
578 		preset_binary[0] = 0;
579 
580 		if (path && *path && g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
581 			if (strlen (path) > 512) {
582 				g_warning ("%s: Path is longer than expected (max 512), ignoring it; value:'%s'", G_STRFUNC, path);
583 			} else {
584 				strcpy (preset_binary, path);
585 			}
586 		}
587 
588 		g_free (path);
589 
590 		for (index = 0; names[index]; index++) {
591 			if (!*(names[index]))
592 				continue;
593 
594 			path = g_find_program_in_path (names[index]);
595 
596 			if (path) {
597 				g_free (path);
598 				break;
599 			}
600 		}
601 
602 		if (!names[index])
603 			index = 1;
604 	}
605 
606 	return names[index];
607 }
608 
609 #ifndef G_OS_WIN32
610 
611 static const gchar *
gpg_hash_str(CamelCipherHash hash)612 gpg_hash_str (CamelCipherHash hash)
613 {
614 	switch (hash) {
615 	case CAMEL_CIPHER_HASH_MD2:
616 		return "--digest-algo=MD2";
617 	case CAMEL_CIPHER_HASH_MD5:
618 		return "--digest-algo=MD5";
619 	case CAMEL_CIPHER_HASH_SHA1:
620 		return "--digest-algo=SHA1";
621 	case CAMEL_CIPHER_HASH_SHA256:
622 		return "--digest-algo=SHA256";
623 	case CAMEL_CIPHER_HASH_SHA384:
624 		return "--digest-algo=SHA384";
625 	case CAMEL_CIPHER_HASH_SHA512:
626 		return "--digest-algo=SHA512";
627 	case CAMEL_CIPHER_HASH_RIPEMD160:
628 		return "--digest-algo=RIPEMD160";
629 	default:
630 		return NULL;
631 	}
632 }
633 
634 static GPtrArray *
gpg_ctx_get_argv(struct _GpgCtx * gpg,gint status_fd,gchar ** sfd,gint passwd_fd,gchar ** pfd)635 gpg_ctx_get_argv (struct _GpgCtx *gpg,
636                   gint status_fd,
637                   gchar **sfd,
638                   gint passwd_fd,
639                   gchar **pfd)
640 {
641 	const gchar *hash_str;
642 	GPtrArray *argv;
643 	gchar *buf;
644 	gint i;
645 
646 	argv = g_ptr_array_new ();
647 	g_ptr_array_add (argv, (guint8 *) gpg_ctx_get_executable_name ());
648 
649 	g_ptr_array_add (argv, (guint8 *) "--verbose");
650 	g_ptr_array_add (argv, (guint8 *) "--no-secmem-warning");
651 	g_ptr_array_add (argv, (guint8 *) "--no-greeting");
652 	g_ptr_array_add (argv, (guint8 *) "--no-tty");
653 
654 	if (passwd_fd == -1) {
655 		/* only use batch mode if we don't intend on using the
656 		 * interactive --command-fd option */
657 		g_ptr_array_add (argv, (guint8 *) "--batch");
658 		g_ptr_array_add (argv, (guint8 *) "--yes");
659 	}
660 
661 	*sfd = buf = g_strdup_printf ("--status-fd=%d", status_fd);
662 	g_ptr_array_add (argv, buf);
663 
664 	if (passwd_fd != -1) {
665 		*pfd = buf = g_strdup_printf ("--command-fd=%d", passwd_fd);
666 		g_ptr_array_add (argv, buf);
667 	}
668 
669 	if (gpg->load_photos) {
670 		if (!gpg->viewer_cmd) {
671 			gint filefd;
672 
673 			filefd = g_file_open_tmp ("camel-gpg-photo-state-XXXXXX", &gpg->photos_filename, NULL);
674 			if (filefd) {
675 				gchar *viewer_filename;
676 
677 				close (filefd);
678 
679 				viewer_filename = g_build_filename (CAMEL_LIBEXECDIR, "camel-gpg-photo-saver", NULL);
680 				gpg->viewer_cmd = g_strdup_printf ("%s --state \"%s\" --photo \"%%i\" --keyid \"%%K\" --type \"%%t\"", viewer_filename, gpg->photos_filename);
681 				g_free (viewer_filename);
682 			}
683 		}
684 
685 		if (gpg->viewer_cmd) {
686 			g_ptr_array_add (argv, (guint8 *) "--verify-options");
687 			g_ptr_array_add (argv, (guint8 *) "show-photos");
688 
689 			g_ptr_array_add (argv, (guint8 *) "--photo-viewer");
690 			g_ptr_array_add (argv, (guint8 *) gpg->viewer_cmd);
691 		}
692 	}
693 
694 	switch (gpg->mode) {
695 	case GPG_CTX_MODE_SIGN:
696 		if (gpg->prefer_inline) {
697 			g_ptr_array_add (argv, (guint8 *) "--clearsign");
698 		} else {
699 			g_ptr_array_add (argv, (guint8 *) "--sign");
700 			g_ptr_array_add (argv, (guint8 *) "--detach");
701 			if (gpg->armor)
702 				g_ptr_array_add (argv, (guint8 *) "--armor");
703 		}
704 		hash_str = gpg_hash_str (gpg->hash);
705 		if (hash_str)
706 			g_ptr_array_add (argv, (guint8 *) hash_str);
707 		if (gpg->userids) {
708 			GSList *uiter;
709 
710 			for (uiter = gpg->userids; uiter; uiter = uiter->next) {
711 				g_ptr_array_add (argv, (guint8 *) "-u");
712 				g_ptr_array_add (argv, (guint8 *) uiter->data);
713 			}
714 		}
715 		g_ptr_array_add (argv, (guint8 *) "--output");
716 		g_ptr_array_add (argv, (guint8 *) "-");
717 		break;
718 	case GPG_CTX_MODE_VERIFY:
719 		if (!camel_session_get_online (gpg->session)) {
720 			/* this is a deprecated flag to gpg since 1.0.7 */
721 			/*g_ptr_array_add (argv, "--no-auto-key-retrieve");*/
722 			g_ptr_array_add (argv, (guint8 *) "--keyserver-options");
723 			g_ptr_array_add (argv, (guint8 *) "no-auto-key-retrieve");
724 		}
725 		g_ptr_array_add (argv, (guint8 *) "--verify");
726 		if (gpg->sigfile)
727 			g_ptr_array_add (argv, gpg->sigfile);
728 		g_ptr_array_add (argv, (guint8 *) "-");
729 		break;
730 	case GPG_CTX_MODE_ENCRYPT:
731 		g_ptr_array_add (argv, (guint8 *) "--encrypt");
732 		if (gpg->armor || gpg->prefer_inline)
733 			g_ptr_array_add (argv, (guint8 *) "--armor");
734 		if (gpg->always_trust)
735 			g_ptr_array_add (argv, (guint8 *) "--always-trust");
736 		if (gpg->userids) {
737 			GSList *uiter;
738 
739 			for (uiter = gpg->userids; uiter; uiter = uiter->next) {
740 				g_ptr_array_add (argv, (guint8 *) "-u");
741 				g_ptr_array_add (argv, (guint8 *) uiter->data);
742 			}
743 		}
744 		if (gpg->recipients) {
745 			for (i = 0; i < gpg->recipients->len; i++) {
746 				const GpgRecipientsData *rd = gpg->recipients->pdata[i];
747 				gboolean add_with_keyid = TRUE;
748 
749 				if (!rd)
750 					continue;
751 
752 				if (rd->known_key_data && *(rd->known_key_data)) {
753 					gsize len = 0;
754 					guchar *data;
755 
756 					data = g_base64_decode (rd->known_key_data, &len);
757 					if (data && len) {
758 						gchar *filename = NULL;
759 						gint filefd;
760 
761 						filefd = g_file_open_tmp ("camel-gpg-key-XXXXXX", &filename, NULL);
762 						if (filefd) {
763 							gpg->recipient_key_files = g_slist_prepend (gpg->recipient_key_files, filename);
764 
765 							if (camel_write (filefd, (const gchar *) data, len, gpg->cancellable, NULL) == len) {
766 								add_with_keyid = FALSE;
767 								g_ptr_array_add (argv, (guint8 *) "--recipient-file");
768 								g_ptr_array_add (argv, (guint8 *) filename);
769 							}
770 
771 							close (filefd);
772 						}
773 					}
774 
775 					g_free (data);
776 				}
777 
778 				if (add_with_keyid) {
779 					g_ptr_array_add (argv, (guint8 *) "-r");
780 					g_ptr_array_add (argv, rd->keyid);
781 				}
782 			}
783 		}
784 		g_ptr_array_add (argv, (guint8 *) "--output");
785 		g_ptr_array_add (argv, (guint8 *) "-");
786 		break;
787 	case GPG_CTX_MODE_DECRYPT:
788 		g_ptr_array_add (argv, (guint8 *) "--decrypt");
789 		g_ptr_array_add (argv, (guint8 *) "--output");
790 		g_ptr_array_add (argv, (guint8 *) "-");
791 		break;
792 	}
793 
794 	g_ptr_array_add (argv, NULL);
795 
796 	return argv;
797 }
798 
799 #endif
800 
801 static gboolean
gpg_ctx_op_start(struct _GpgCtx * gpg,GError ** error)802 gpg_ctx_op_start (struct _GpgCtx *gpg,
803                   GError **error)
804 {
805 #ifndef G_OS_WIN32
806 	gchar *status_fd = NULL, *passwd_fd = NULL;
807 	gint i, maxfd, errnosave, fds[10];
808 	GPtrArray *argv;
809 	gint flags;
810 
811 	for (i = 0; i < 10; i++)
812 		fds[i] = -1;
813 
814 	maxfd = gpg->need_passwd ? 10 : 8;
815 	for (i = 0; i < maxfd; i += 2) {
816 		if (pipe (fds + i) == -1)
817 			goto exception;
818 	}
819 
820 	argv = gpg_ctx_get_argv (gpg, fds[7], &status_fd, fds[8], &passwd_fd);
821 
822 	if (camel_debug_start ("gpg")) {
823 		guint ii;
824 
825 		printf ("[GPG] going to execute:");
826 		for (ii = 0; ii < argv->len; ii++) {
827 			if (!argv->pdata[ii])
828 				break;
829 
830 			printf (" %s", (const gchar *) argv->pdata[ii]);
831 		}
832 		printf ("\n");
833 		camel_debug_end ();
834 	}
835 
836 	if (!(gpg->pid = fork ())) {
837 		/* child process */
838 
839 		if ((dup2 (fds[0], STDIN_FILENO) < 0 ) ||
840 		    (dup2 (fds[3], STDOUT_FILENO) < 0 ) ||
841 		    (dup2 (fds[5], STDERR_FILENO) < 0 )) {
842 			_exit (255);
843 		}
844 
845 		/* Dissociate from camel's controlling terminal so
846 		 * that gpg won't be able to read from it.
847 		 */
848 		setsid ();
849 
850 		maxfd = sysconf (_SC_OPEN_MAX);
851 		/* Loop over all fds. */
852 		for (i = 3; i < maxfd; i++) {
853 			/* don't close the status-fd or passwd-fd */
854 			if (i != fds[7] && i != fds[8]) {
855 				if (fcntl (i, F_SETFD, FD_CLOEXEC) == -1) {
856 					/* Do nothing here. Cannot use CHECK_CALL() macro here, because
857 					   it makes the process stuck, possibly due to the debug print. */
858 				}
859 			}
860 		}
861 
862 		/* run gpg */
863 		execvp (gpg_ctx_get_executable_name (), (gchar **) argv->pdata);
864 		_exit (255);
865 	} else if (gpg->pid < 0) {
866 		g_ptr_array_free (argv, TRUE);
867 		g_free (status_fd);
868 		g_free (passwd_fd);
869 		goto exception;
870 	}
871 
872 	g_ptr_array_free (argv, TRUE);
873 	g_free (status_fd);
874 	g_free (passwd_fd);
875 
876 	/* Parent */
877 	close (fds[0]);
878 	gpg->stdin_fd = fds[1];
879 	gpg->stdout_fd = fds[2];
880 	close (fds[3]);
881 	gpg->stderr_fd = fds[4];
882 	close (fds[5]);
883 	gpg->status_fd = fds[6];
884 	close (fds[7]);
885 
886 	if (gpg->need_passwd) {
887 		close (fds[8]);
888 		gpg->passwd_fd = fds[9];
889 		flags = fcntl (gpg->passwd_fd, F_GETFL);
890 		CHECK_CALL (fcntl (gpg->passwd_fd, F_SETFL, flags | O_NONBLOCK));
891 	}
892 
893 	flags = fcntl (gpg->stdin_fd, F_GETFL);
894 	CHECK_CALL (fcntl (gpg->stdin_fd, F_SETFL, flags | O_NONBLOCK));
895 
896 	flags = fcntl (gpg->stdout_fd, F_GETFL);
897 	CHECK_CALL (fcntl (gpg->stdout_fd, F_SETFL, flags | O_NONBLOCK));
898 
899 	flags = fcntl (gpg->stderr_fd, F_GETFL);
900 	CHECK_CALL (fcntl (gpg->stderr_fd, F_SETFL, flags | O_NONBLOCK));
901 
902 	flags = fcntl (gpg->status_fd, F_GETFL);
903 	CHECK_CALL (fcntl (gpg->status_fd, F_SETFL, flags | O_NONBLOCK));
904 
905 	return TRUE;
906 
907 exception:
908 
909 	errnosave = errno;
910 
911 	for (i = 0; i < 10; i++) {
912 		if (fds[i] != -1)
913 			close (fds[i]);
914 	}
915 
916 	errno = errnosave;
917 #else
918 	/* FIXME: Port me */
919 	g_warning ("%s: Not implemented", G_STRFUNC);
920 
921 	errno = EINVAL;
922 #endif
923 
924 	if (errno != 0)
925 		g_set_error (
926 			error, G_IO_ERROR,
927 			g_io_error_from_errno (errno),
928 			_("Failed to execute gpg: %s"),
929 			g_strerror (errno));
930 	else
931 		g_set_error (
932 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
933 			_("Failed to execute gpg: %s"), _("Unknown"));
934 
935 	return FALSE;
936 }
937 
938 #ifndef G_OS_WIN32
939 
940 static const gchar *
next_token(const gchar * in,gchar ** token)941 next_token (const gchar *in,
942             gchar **token)
943 {
944 	const gchar *start, *inptr = in;
945 
946 	while (*inptr == ' ')
947 		inptr++;
948 
949 	if (*inptr == '\0' || *inptr == '\n') {
950 		if (token)
951 			*token = NULL;
952 		return inptr;
953 	}
954 
955 	start = inptr;
956 	while (*inptr && *inptr != ' ' && *inptr != '\n')
957 		inptr++;
958 
959 	if (token)
960 		*token = g_strndup (start, inptr - start);
961 
962 	return inptr;
963 }
964 
965 static void
gpg_ctx_extract_signer_from_status(struct _GpgCtx * gpg,const gchar * status)966 gpg_ctx_extract_signer_from_status (struct _GpgCtx *gpg,
967 				    const gchar *status)
968 {
969 	const gchar *tmp;
970 
971 	g_return_if_fail (gpg != NULL);
972 	g_return_if_fail (status != NULL);
973 
974 	/* there's a key ID, then the email address */
975 	tmp = status;
976 
977 	status = strchr (status, ' ');
978 	if (status) {
979 		gchar *keyid;
980 		const gchar *str = status + 1;
981 		const gchar *eml = strchr (str, '<');
982 
983 		keyid = g_strndup (tmp, status - tmp);
984 
985 		if (eml && eml > str) {
986 			eml--;
987 			if (strchr (str, ' ') >= eml)
988 				eml = NULL;
989 		} else {
990 			eml = NULL;
991 		}
992 
993 		if (gpg->signers) {
994 			g_string_append (gpg->signers, ", ");
995 		} else {
996 			gpg->signers = g_string_new ("");
997 		}
998 
999 		if (eml) {
1000 			g_string_append_c (gpg->signers, '\"');
1001 			g_string_append_len (gpg->signers, str, eml - str);
1002 			g_string_append_c (gpg->signers, '\"');
1003 			g_string_append (gpg->signers, eml);
1004 		} else {
1005 			g_string_append (gpg->signers, str);
1006 		}
1007 
1008 		g_hash_table_insert (gpg->signers_keyid, g_strdup (str), keyid);
1009 	}
1010 }
1011 
1012 static gint
gpg_ctx_parse_status(struct _GpgCtx * gpg,GError ** error)1013 gpg_ctx_parse_status (struct _GpgCtx *gpg,
1014                       GError **error)
1015 {
1016 	register guchar *inptr;
1017 	const guchar *status;
1018 	gsize nread, nwritten;
1019 	gint len;
1020 
1021  parse:
1022 
1023 	inptr = gpg->statusbuf;
1024 	while (inptr < gpg->statusptr && *inptr != '\n')
1025 		inptr++;
1026 
1027 	if (*inptr != '\n') {
1028 		/* we don't have enough data buffered to parse this status line */
1029 		return 0;
1030 	}
1031 
1032 	*inptr++ = '\0';
1033 	status = gpg->statusbuf;
1034 
1035 	if (camel_debug ("gpg:status"))
1036 		printf ("status: %s\n", status);
1037 
1038 	if (strncmp ((const gchar *) status, "[GNUPG:] ", 9) != 0) {
1039 		gchar *message;
1040 
1041 		message = g_locale_to_utf8 (
1042 			(const gchar *) status, -1, NULL, NULL, NULL);
1043 		g_set_error (
1044 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1045 			_("Unexpected GnuPG status message encountered:\n\n%s"),
1046 			message);
1047 		g_free (message);
1048 
1049 		return -1;
1050 	}
1051 
1052 	status += 9;
1053 
1054 	if (!strncmp ((gchar *) status, "ENC_TO ", 7)) {
1055 		gchar *key = NULL;
1056 
1057 		status += 7;
1058 
1059 		next_token ((gchar *) status, &key);
1060 		if (key) {
1061 			gboolean all_zero = *key == '0';
1062 			gint i = 0;
1063 
1064 			while (key[i] && all_zero) {
1065 				all_zero = key[i] == '0';
1066 				i++;
1067 			}
1068 
1069 			gpg->anonymous_recipient = all_zero;
1070 
1071 			g_free (key);
1072 		}
1073 	} else if (!strncmp ((gchar *) status, "USERID_HINT ", 12)) {
1074 		gchar *hint, *user;
1075 
1076 		status += 12;
1077 		status = (const guchar *) next_token ((gchar *) status, &hint);
1078 		if (!hint) {
1079 			g_set_error (
1080 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1081 				_("Failed to parse gpg userid hint."));
1082 			return -1;
1083 		}
1084 
1085 		if (g_hash_table_lookup (gpg->userid_hint, hint)) {
1086 			/* we already have this userid hint... */
1087 			g_free (hint);
1088 			goto recycle;
1089 		}
1090 
1091 		if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar *) status, -1, &nread, &nwritten, NULL)))
1092 			user = g_strdup ((gchar *) status);
1093 
1094 		g_strstrip (user);
1095 
1096 		g_hash_table_insert (gpg->userid_hint, hint, user);
1097 	} else if (!strncmp ((gchar *) status, "NEED_PASSPHRASE ", 16)) {
1098 		gchar *userid;
1099 
1100 		status += 16;
1101 
1102 		next_token ((gchar *) status, &userid);
1103 		if (!userid) {
1104 			g_set_error (
1105 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1106 				_("Failed to parse gpg passphrase request."));
1107 			return -1;
1108 		}
1109 
1110 		g_free (gpg->need_id);
1111 		gpg->need_id = userid;
1112 	} else if (!strncmp ((gchar *) status, "NEED_PASSPHRASE_PIN ", 20)) {
1113 		gchar *userid;
1114 
1115 		status += 20;
1116 
1117 		next_token ((gchar *) status, &userid);
1118 		if (!userid) {
1119 			g_set_error (
1120 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1121 				_("Failed to parse gpg passphrase request."));
1122 			return -1;
1123 		}
1124 
1125 		g_free (gpg->need_id);
1126 		gpg->need_id = userid;
1127 	} else if (!strncmp ((gchar *) status, "GET_HIDDEN ", 11)) {
1128 		const gchar *name = NULL;
1129 		gchar *prompt, *passwd;
1130 		guint32 flags;
1131 		GError *local_error = NULL;
1132 
1133 		status += 11;
1134 
1135 		if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
1136 			name = gpg->need_id;
1137 		else if (!name)
1138 			name = "";
1139 
1140 		if (!strncmp ((gchar *) status, "passphrase.pin.ask", 18)) {
1141 			prompt = g_markup_printf_escaped (
1142 				_("You need a PIN to unlock the key for your\n"
1143 				"SmartCard: “%s”"), name);
1144 		} else if (!strncmp ((gchar *) status, "passphrase.enter", 16)) {
1145 			prompt = g_markup_printf_escaped (
1146 				_("You need a passphrase to unlock the key for\n"
1147 				"user: “%s”"), name);
1148 		} else {
1149 			next_token ((gchar *) status, &prompt);
1150 			g_set_error (
1151 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1152 				_("Unexpected request from GnuPG for “%s”"),
1153 				prompt);
1154 			g_free (prompt);
1155 			return -1;
1156 		}
1157 
1158 		if (gpg->anonymous_recipient) {
1159 			gchar *tmp = prompt;
1160 
1161 			/* FIXME Reword prompt message. */
1162 			prompt = g_strconcat (
1163 				tmp, "\n",
1164 				_("Note the encrypted content doesn’t contain "
1165 				"information about a recipient, thus there "
1166 				"will be a password prompt for each of stored "
1167 				"private key."), NULL);
1168 
1169 			g_free (tmp);
1170 		}
1171 
1172 		flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
1173 		if ((passwd = camel_session_get_password (gpg->session, NULL, prompt, gpg->need_id, flags, &local_error))) {
1174 			if (!gpg->utf8) {
1175 				gchar *opasswd = passwd;
1176 
1177 				if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
1178 					memset (opasswd, 0, strlen (opasswd));
1179 					g_free (opasswd);
1180 				} else {
1181 					passwd = opasswd;
1182 				}
1183 			}
1184 
1185 			gpg->passwd = g_strdup_printf ("%s\n", passwd);
1186 			memset (passwd, 0, strlen (passwd));
1187 			g_free (passwd);
1188 
1189 			gpg->send_passwd = TRUE;
1190 		} else {
1191 			if (local_error == NULL)
1192 				g_set_error (
1193 					error, G_IO_ERROR,
1194 					G_IO_ERROR_CANCELLED,
1195 					_("Cancelled"));
1196 			g_propagate_error (error, local_error);
1197 
1198 			return -1;
1199 		}
1200 
1201 		g_free (prompt);
1202 	} else if (!strncmp ((gchar *) status, "GOOD_PASSPHRASE", 15)) {
1203 		gpg->bad_passwds = 0;
1204 	} else if (!strncmp ((gchar *) status, "BAD_PASSPHRASE", 14)) {
1205 		/* with anonymous recipient is user asked for his/her password for each stored key,
1206 		 * thus here cannot be counted wrong passwords */
1207 		if (!gpg->anonymous_recipient) {
1208 			gpg->bad_passwds++;
1209 
1210 			camel_session_forget_password (gpg->session, NULL, gpg->need_id, error);
1211 
1212 			if (gpg->bad_passwds == 3) {
1213 				g_set_error (
1214 					error, CAMEL_SERVICE_ERROR,
1215 					CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1216 					_("Failed to unlock secret key: "
1217 					"3 bad passphrases given."));
1218 				return -1;
1219 			}
1220 		}
1221 	} else if (!strncmp ((const gchar *) status, "UNEXPECTED ", 11)) {
1222 		/* this is an error */
1223 		gchar *message;
1224 
1225 		message = g_locale_to_utf8 (
1226 			(const gchar *) status + 11, -1, NULL, NULL, NULL);
1227 		g_set_error (
1228 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1229 			_("Unexpected response from GnuPG: %s"), message);
1230 		g_free (message);
1231 
1232 		return -1;
1233 	} else if (!strncmp ((gchar *) status, "NODATA", 6)) {
1234 		/* this is an error */
1235 		/* But we ignore it anyway, we should get other response codes to say why */
1236 		gpg->nodata = TRUE;
1237 	} else {
1238 		if (!strncmp ((gchar *) status, "BEGIN_", 6)) {
1239 			gpg->processing = TRUE;
1240 		} else if (!strncmp ((gchar *) status, "END_", 4)) {
1241 			gpg->processing = FALSE;
1242 		}
1243 
1244 		/* check to see if we are complete */
1245 		switch (gpg->mode) {
1246 		case GPG_CTX_MODE_SIGN:
1247 			if (!strncmp ((gchar *) status, "SIG_CREATED ", 12)) {
1248 				/* SIG_CREATED <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr> */
1249 				const gchar *str, *p;
1250 				gint i = 0;
1251 
1252 				str = (const gchar *) status + 12;
1253 				while (p = strchr (str, ' '), i < 2 && p) {
1254 					str = p + 1;
1255 					i++;
1256 				}
1257 
1258 				if (*str && i == 2) {
1259 					struct {
1260 						gint gpg_hash_algo;
1261 						CamelCipherHash camel_hash_algo;
1262 					} hash_algos[] = {
1263 						/* the rest are deprecated/not supported by gpg any more */
1264 						{  2, CAMEL_CIPHER_HASH_SHA1 },
1265 						{  3, CAMEL_CIPHER_HASH_RIPEMD160 },
1266 						{  8, CAMEL_CIPHER_HASH_SHA256 },
1267 						{  9, CAMEL_CIPHER_HASH_SHA384 },
1268 						{ 10, CAMEL_CIPHER_HASH_SHA512 }
1269 					};
1270 
1271 					gint gpg_hash = strtoul (str, NULL, 10);
1272 
1273 					for (i = 0; i < G_N_ELEMENTS (hash_algos); i++) {
1274 						if (hash_algos[i].gpg_hash_algo == gpg_hash) {
1275 							gpg->hash = hash_algos[i].camel_hash_algo;
1276 							break;
1277 						}
1278 					}
1279 				}
1280 			}
1281 			break;
1282 		case GPG_CTX_MODE_DECRYPT:
1283 			if (!strncmp ((gchar *) status, "BEGIN_DECRYPTION", 16)) {
1284 				gpg->bad_decrypt = FALSE;
1285 				/* Mark it's expected to get decrypted data on stdout */
1286 				gpg->in_decrypt_stage = TRUE;
1287 				break;
1288 			} else if (!strncmp ((gchar *) status, "END_DECRYPTION", 14)) {
1289 				/* Mark to no longer expect decrypted data */
1290 				gpg->in_decrypt_stage = FALSE;
1291 				break;
1292 			} else if (!strncmp ((gchar *) status, "NO_SECKEY ", 10)) {
1293 				gpg->noseckey = TRUE;
1294 				break;
1295 			} else if (!strncmp ((gchar *) status, "DECRYPTION_FAILED", 17)) {
1296 				gpg->bad_decrypt = TRUE;
1297 				break;
1298 			}
1299 			/* let if fall through to verify possible signatures too */
1300 			/* break; */
1301 			/* falls through */
1302 		case GPG_CTX_MODE_VERIFY:
1303 			if (!strncmp ((gchar *) status, "TRUST_", 6)) {
1304 				status += 6;
1305 				if (!strncmp ((gchar *) status, "NEVER", 5)) {
1306 					gpg->trust = GPG_TRUST_NEVER;
1307 				} else if (!strncmp ((gchar *) status, "MARGINAL", 8)) {
1308 					gpg->trust = GPG_TRUST_MARGINAL;
1309 				} else if (!strncmp ((gchar *) status, "FULLY", 5)) {
1310 					gpg->trust = GPG_TRUST_FULLY;
1311 				} else if (!strncmp ((gchar *) status, "ULTIMATE", 8)) {
1312 					gpg->trust = GPG_TRUST_ULTIMATE;
1313 				} else if (!strncmp ((gchar *) status, "UNDEFINED", 9)) {
1314 					gpg->trust = GPG_TRUST_UNDEFINED;
1315 				}
1316 			} else if (!strncmp ((gchar *) status, "GOODSIG ", 8)) {
1317 				gpg->goodsig = TRUE;
1318 				gpg->hadsig = TRUE;
1319 
1320 				gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 8);
1321 			} else if (!strncmp ((gchar *) status, "EXPKEYSIG ", 10)) {
1322 				gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 10);
1323 			} else if (!strncmp ((gchar *) status, "VALIDSIG ", 9)) {
1324 				gpg->validsig = TRUE;
1325 			} else if (!strncmp ((gchar *) status, "BADSIG ", 7)) {
1326 				gpg->badsig = FALSE;
1327 				gpg->hadsig = TRUE;
1328 
1329 				gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 7);
1330 			} else if (!strncmp ((gchar *) status, "ERRSIG ", 7)) {
1331 				/* Note: NO_PUBKEY often comes after an ERRSIG */
1332 				gpg->errsig = FALSE;
1333 				gpg->hadsig = TRUE;
1334 			} else if (!strncmp ((gchar *) status, "NO_PUBKEY ", 10)) {
1335 				gpg->nopubkey = TRUE;
1336 			}
1337 			break;
1338 		case GPG_CTX_MODE_ENCRYPT:
1339 			if (!strncmp ((gchar *) status, "BEGIN_ENCRYPTION", 16)) {
1340 				/* nothing to do... but we know to expect data on stdout soon */
1341 			} else if (!strncmp ((gchar *) status, "END_ENCRYPTION", 14)) {
1342 				/* nothing to do, but we know the end is near? */
1343 			} else if (!strncmp ((gchar *) status, "NO_RECP", 7)) {
1344 				g_set_error (
1345 					error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1346 					_("Failed to encrypt: No valid recipients specified."));
1347 				return -1;
1348 			} else if (!strncmp ((gchar *) status, "INV_RECP ", 9)) {
1349 				const gchar *addr;
1350 
1351 				addr = strchr ((gchar *) status, '<');
1352 				if (!addr)
1353 					addr = ((gchar *) status) + 10;
1354 
1355 				g_set_error (
1356 					error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1357 					/* Translators: The first '%s' is replaced with the e-mail address, like '<user@example.com>';
1358 					   the second '%s' is replaced with the actual path and filename of the used gpg, like '/usr/bin/gpg2' */
1359 					_("Failed to encrypt: Invalid recipient %s specified. A common issue is that the %s doesn’t have imported public key for this recipient."),
1360 					addr, gpg_ctx_get_executable_name ());
1361 				return -1;
1362 			}
1363 			break;
1364 		}
1365 	}
1366 
1367  recycle:
1368 
1369 	/* recycle our statusbuf by moving inptr to the beginning of statusbuf */
1370 	len = gpg->statusptr - inptr;
1371 	memmove (gpg->statusbuf, inptr, len);
1372 
1373 	len = inptr - gpg->statusbuf;
1374 	gpg->statusleft += len;
1375 	gpg->statusptr -= len;
1376 
1377 	/* if we have more data, try parsing the next line? */
1378 	if (gpg->statusptr > gpg->statusbuf)
1379 		goto parse;
1380 
1381 	return 0;
1382 }
1383 
1384 #endif
1385 
1386 #define status_backup(gpg, start, len) G_STMT_START { \
1387 	if (gpg->statusleft <= len) { \
1388 		guint slen, soff; \
1389  \
1390 		slen = soff = gpg->statusptr - gpg->statusbuf; \
1391 		slen = slen ? slen : 1; \
1392  \
1393 		while (slen < soff + len) \
1394 			slen <<= 1; \
1395  \
1396 		gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1); \
1397 		gpg->statusptr = gpg->statusbuf + soff; \
1398 		gpg->statusleft = slen - soff; \
1399 	} \
1400  \
1401 	memcpy (gpg->statusptr, start, len); \
1402 	gpg->statusptr += len; \
1403 	gpg->statusleft -= len; \
1404 } G_STMT_END
1405 
1406 static void
gpg_ctx_op_cancel(struct _GpgCtx * gpg)1407 gpg_ctx_op_cancel (struct _GpgCtx *gpg)
1408 {
1409 #ifndef G_OS_WIN32
1410 	pid_t retval;
1411 	gint status;
1412 
1413 	if (gpg->exited)
1414 		return;
1415 
1416 	kill (gpg->pid, SIGTERM);
1417 	sleep (1);
1418 	retval = waitpid (gpg->pid, &status, WNOHANG);
1419 
1420 	if (retval == (pid_t) 0) {
1421 		/* no more mr nice guy... */
1422 		kill (gpg->pid, SIGKILL);
1423 		sleep (1);
1424 		waitpid (gpg->pid, &status, WNOHANG);
1425 	}
1426 #endif
1427 }
1428 
1429 static gint
gpg_ctx_op_step(struct _GpgCtx * gpg,GCancellable * cancellable,GError ** error)1430 gpg_ctx_op_step (struct _GpgCtx *gpg,
1431                  GCancellable *cancellable,
1432                  GError **error)
1433 {
1434 #ifndef G_OS_WIN32
1435 	GPollFD polls[6];
1436 	gint status, i;
1437 	gboolean read_data = FALSE, wrote_data = FALSE;
1438 	gboolean was_in_decrypt_stage;
1439 
1440 	for (i = 0; i < 6; i++) {
1441 		polls[i].fd = -1;
1442 		polls[i].events = 0;
1443 	}
1444 
1445 	if (!gpg->seen_eof1) {
1446 		polls[0].fd = gpg->stdout_fd;
1447 		polls[0].events = G_IO_IN;
1448 	}
1449 
1450 	if (!gpg->seen_eof2) {
1451 		polls[1].fd = gpg->stderr_fd;
1452 		polls[1].events = G_IO_IN;
1453 	}
1454 
1455 	if (!gpg->complete) {
1456 		polls[2].fd = gpg->status_fd;
1457 		polls[2].events = G_IO_IN;
1458 	}
1459 
1460 	polls[3].fd = gpg->stdin_fd;
1461 	polls[3].events = G_IO_OUT;
1462 	polls[4].fd = gpg->passwd_fd;
1463 	polls[4].events = G_IO_OUT;
1464 	polls[5].fd = g_cancellable_get_fd (cancellable);
1465 	polls[5].events = G_IO_IN;
1466 
1467 	do {
1468 		for (i = 0; i < 6; i++)
1469 			polls[i].revents = 0;
1470 		status = g_poll (polls, 6, 30 * 1000);
1471 	} while (status == -1 && errno == EINTR);
1472 
1473 	if (status == 0)
1474 		return 0;	/* timed out */
1475 	else if (status == -1)
1476 		goto exception;
1477 
1478 	if ((polls[5].revents & G_IO_IN) &&
1479 		g_cancellable_set_error_if_cancelled (cancellable, error)) {
1480 
1481 		gpg_ctx_op_cancel (gpg);
1482 		return -1;
1483 	}
1484 
1485 	/* Test each and every file descriptor to see if it's 'ready',
1486 	 * and if so - do what we can with it and then drop through to
1487 	 * the next file descriptor and so on until we've done what we
1488 	 * can to all of them. If one fails along the way, return
1489 	 * -1. */
1490 
1491 	was_in_decrypt_stage = gpg->in_decrypt_stage;
1492 
1493 	if (polls[2].revents & (G_IO_IN | G_IO_HUP)) {
1494 		/* read the status message and decide what to do... */
1495 		gchar buffer[4096];
1496 		gssize nread;
1497 
1498 		d (printf ("reading from gpg's status-fd...\n"));
1499 
1500 		do {
1501 			nread = read (gpg->status_fd, buffer, sizeof (buffer));
1502 			d (printf ("   read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1503 		} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1504 		if (nread == -1)
1505 			goto exception;
1506 
1507 		if (nread > 0) {
1508 			status_backup (gpg, buffer, nread);
1509 
1510 			if (gpg_ctx_parse_status (gpg, error) == -1)
1511 				return -1;
1512 		} else {
1513 			gpg->complete = TRUE;
1514 		}
1515 	}
1516 
1517 	if ((polls[0].revents & (G_IO_IN | G_IO_HUP)) && gpg->ostream) {
1518 		gchar buffer[4096];
1519 		gssize nread;
1520 
1521 		d (printf ("reading gpg's stdout...\n"));
1522 
1523 		do {
1524 			nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1525 			d (printf ("   read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1526 		} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1527 		if (nread == -1)
1528 			goto exception;
1529 
1530 		if (nread > 0) {
1531 			if (gpg->mode != GPG_CTX_MODE_DECRYPT ||
1532 			    gpg->in_decrypt_stage || was_in_decrypt_stage) {
1533 				gboolean done = FALSE;
1534 
1535 				while (!done) {
1536 					gsize written = camel_stream_write (
1537 						gpg->ostream, buffer, (gsize)
1538 						nread, cancellable, error);
1539 					if (written != nread)
1540 						return -1;
1541 
1542 					done = TRUE;
1543 
1544 					/* Read everything cached */
1545 					do {
1546 						polls[0].revents = 0;
1547 						status = g_poll (polls, 1, 5);
1548 					} while (status == -1 && errno == EINTR);
1549 
1550 					if (status != -1 && status != 0 && (polls[0].revents & (G_IO_IN | G_IO_HUP))) {
1551 						do {
1552 							nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1553 							d (printf ("   cached read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1554 						} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1555 
1556 						if (nread == -1)
1557 							goto exception;
1558 
1559 						done = !nread;
1560 					}
1561 				}
1562 			} else {
1563 				if (!gpg->decrypt_extra_text)
1564 					gpg->decrypt_extra_text = g_string_new ("");
1565 				g_string_append_len (gpg->decrypt_extra_text, buffer, nread);
1566 			}
1567 		} else {
1568 			gpg->seen_eof1 = TRUE;
1569 		}
1570 
1571 		read_data = TRUE;
1572 	}
1573 
1574 	if (polls[1].revents & (G_IO_IN | G_IO_HUP)) {
1575 		gchar buffer[4096];
1576 		gssize nread;
1577 
1578 		d (printf ("reading gpg's stderr...\n"));
1579 
1580 		do {
1581 			nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
1582 			d (printf ("   read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1583 		} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1584 		if (nread == -1)
1585 			goto exception;
1586 
1587 		if (nread > 0) {
1588 			camel_stream_write (
1589 				gpg->diagnostics, buffer,
1590 				nread, cancellable, error);
1591 		} else {
1592 			gpg->seen_eof2 = TRUE;
1593 		}
1594 	}
1595 
1596 	if ((polls[4].revents & (G_IO_OUT | G_IO_HUP)) && gpg->need_passwd && gpg->send_passwd) {
1597 		gssize w, nwritten = 0;
1598 		gsize n;
1599 
1600 		d (printf ("sending gpg our passphrase...\n"));
1601 
1602 		/* send the passphrase to gpg */
1603 		n = strlen (gpg->passwd);
1604 		do {
1605 			do {
1606 				w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
1607 			} while (w == -1 && (errno == EINTR || errno == EAGAIN));
1608 
1609 			if (w > 0)
1610 				nwritten += w;
1611 		} while (nwritten < n && w != -1);
1612 
1613 		/* zero and free our passwd buffer */
1614 		memset (gpg->passwd, 0, n);
1615 		g_free (gpg->passwd);
1616 		gpg->passwd = NULL;
1617 
1618 		if (w == -1)
1619 			goto exception;
1620 
1621 		gpg->send_passwd = FALSE;
1622 	}
1623 
1624 	if ((polls[3].revents & (G_IO_OUT | G_IO_HUP)) && gpg->istream) {
1625 		gchar buffer[4096];
1626 		gssize nread;
1627 
1628 		d (printf ("writing to gpg's stdin...\n"));
1629 
1630 		/* write our stream to gpg's stdin */
1631 		nread = camel_stream_read (
1632 			gpg->istream, buffer,
1633 			sizeof (buffer), cancellable, NULL);
1634 		if (nread > 0) {
1635 			gssize w, nwritten = 0;
1636 
1637 			do {
1638 				do {
1639 					w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
1640 				} while (w == -1 && (errno == EINTR || errno == EAGAIN));
1641 
1642 				if (w > 0)
1643 					nwritten += w;
1644 			} while (nwritten < nread && w != -1);
1645 
1646 			if (w == -1)
1647 				goto exception;
1648 
1649 			d (printf ("wrote %d (out of %d) bytes to gpg's stdin\n", (gint) nwritten, (gint) nread));
1650 			wrote_data = TRUE;
1651 		}
1652 
1653 		if (camel_stream_eos (gpg->istream)) {
1654 			d (printf ("closing gpg's stdin\n"));
1655 			close (gpg->stdin_fd);
1656 			gpg->stdin_fd = -1;
1657 		}
1658 	}
1659 
1660 	if (gpg->need_id && !gpg->processing && !read_data && !wrote_data) {
1661 		/* do not ask more than hundred times per second when looking for a pass phrase,
1662 		 * in case user has the use-agent set, it'll not use the all CPU when
1663 		 * agent is asking for a pass phrase, instead of us */
1664 		g_usleep (G_USEC_PER_SEC / 100);
1665 	}
1666 
1667 	return 0;
1668 
1669  exception:
1670 	/* always called on an i/o error */
1671 	g_set_error (
1672 		error, G_IO_ERROR,
1673 		g_io_error_from_errno (errno),
1674 		_("Failed to execute gpg: %s"), g_strerror (errno));
1675 	gpg_ctx_op_cancel (gpg);
1676 #endif
1677 	return -1;
1678 }
1679 
1680 static gboolean
gpg_ctx_op_complete(struct _GpgCtx * gpg)1681 gpg_ctx_op_complete (struct _GpgCtx *gpg)
1682 {
1683 	return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
1684 
1685 #if 0
1686 static gboolean
1687 gpg_ctx_op_exited (struct _GpgCtx *gpg)
1688 {
1689 	pid_t retval;
1690 	gint status;
1691 
1692 	if (gpg->exited)
1693 		return TRUE;
1694 
1695 	retval = waitpid (gpg->pid, &status, WNOHANG);
1696 	if (retval == gpg->pid) {
1697 		gpg->exit_status = status;
1698 		gpg->exited = TRUE;
1699 		return TRUE;
1700 	}
1701 
1702 	return FALSE;
1703 }
1704 #endif
1705 
1706 static gint
gpg_ctx_op_wait(struct _GpgCtx * gpg)1707 gpg_ctx_op_wait (struct _GpgCtx *gpg)
1708 {
1709 #ifndef G_OS_WIN32
1710 	sigset_t mask, omask;
1711 	pid_t retval;
1712 	gint status;
1713 
1714 	if (!gpg->exited) {
1715 		sigemptyset (&mask);
1716 		sigaddset (&mask, SIGALRM);
1717 		sigprocmask (SIG_BLOCK, &mask, &omask);
1718 		alarm (1);
1719 		retval = waitpid (gpg->pid, &status, 0);
1720 		alarm (0);
1721 		sigprocmask (SIG_SETMASK, &omask, NULL);
1722 
1723 		if (retval == (pid_t) -1 && errno == EINTR) {
1724 			/* The child is hanging: send a friendly reminder. */
1725 			kill (gpg->pid, SIGTERM);
1726 			sleep (1);
1727 			retval = waitpid (gpg->pid, &status, WNOHANG);
1728 			if (retval == (pid_t) 0) {
1729 				/* Still hanging; use brute force. */
1730 				kill (gpg->pid, SIGKILL);
1731 				sleep (1);
1732 				retval = waitpid (gpg->pid, &status, WNOHANG);
1733 			}
1734 		}
1735 	} else {
1736 		status = gpg->exit_status;
1737 		retval = gpg->pid;
1738 	}
1739 
1740 	if (retval != (pid_t) -1 && WIFEXITED (status))
1741 		return WEXITSTATUS (status);
1742 	else
1743 		return -1;
1744 #else
1745 	return -1;
1746 #endif
1747 }
1748 
1749 static gchar *
swrite(CamelMimePart * sigpart,GCancellable * cancellable,GError ** error)1750 swrite (CamelMimePart *sigpart,
1751         GCancellable *cancellable,
1752         GError **error)
1753 {
1754 	GFile *file;
1755 	GFileIOStream *base_stream = NULL;
1756 	CamelStream *stream = NULL;
1757 	CamelDataWrapper *wrapper;
1758 	gchar *path = NULL;
1759 	gint ret;
1760 
1761 	file = g_file_new_tmp ("evolution-pgp.XXXXXX", &base_stream, error);
1762 
1763 	/* Sanity check. */
1764 	g_return_val_if_fail (
1765 		((file != NULL) && (base_stream != NULL)) ||
1766 		((file == NULL) && (base_stream == NULL)), NULL);
1767 
1768 	if (base_stream != NULL) {
1769 		stream = camel_stream_new (G_IO_STREAM (base_stream));
1770 		g_object_unref (base_stream);
1771 	}
1772 
1773 	if (stream == NULL)
1774 		return NULL;
1775 
1776 	wrapper = camel_medium_get_content (CAMEL_MEDIUM (sigpart));
1777 	if (wrapper == NULL)
1778 		wrapper = CAMEL_DATA_WRAPPER (sigpart);
1779 
1780 	ret = camel_data_wrapper_decode_to_stream_sync (
1781 		wrapper, stream, cancellable, error);
1782 	if (ret != -1) {
1783 		ret = camel_stream_flush (stream, cancellable, error);
1784 		if (ret != -1)
1785 			ret = camel_stream_close (stream, cancellable, error);
1786 	}
1787 
1788 	if (ret != -1)
1789 		path = g_file_get_path (file);
1790 
1791 	g_object_unref (file);
1792 	g_object_unref (stream);
1793 
1794 	return path;
1795 }
1796 
1797 static const gchar *
gpg_context_find_photo(GHashTable * photos,GHashTable * signers_keyid,const gchar * name,const gchar * email)1798 gpg_context_find_photo (GHashTable *photos, /* keyid ~> filename in tmp */
1799 			GHashTable *signers_keyid, /* signer ~> keyid */
1800 			const gchar *name,
1801 			const gchar *email)
1802 {
1803 	GHashTableIter iter;
1804 	gpointer key, value;
1805 	const gchar *keyid = NULL;
1806 
1807 	if (!photos || !signers_keyid || ((!name || !*name) && (!email || !*email)))
1808 		return NULL;
1809 
1810 	g_hash_table_iter_init (&iter, signers_keyid);
1811 	while (g_hash_table_iter_next (&iter, &key, &value)) {
1812 		if ((email && *email && strstr (key, email)) ||
1813 		    (name && *name && strstr (key, name))) {
1814 			keyid = value;
1815 			break;
1816 		}
1817 	}
1818 
1819 	if (keyid) {
1820 		const gchar *filename;
1821 
1822 		filename = g_hash_table_lookup (photos, keyid);
1823 		if (filename)
1824 			return camel_pstring_strdup (filename);
1825 	}
1826 
1827 	return NULL;
1828 }
1829 
1830 static void
camel_gpg_context_free_photo_filename(gpointer ptr)1831 camel_gpg_context_free_photo_filename (gpointer ptr)
1832 {
1833 	gchar *tmp_filename = g_strdup (ptr);
1834 
1835 	camel_pstring_free (ptr);
1836 
1837 	if (!camel_pstring_contains (tmp_filename) &&
1838 	    g_file_test (tmp_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1839 		g_unlink (tmp_filename);
1840 	}
1841 
1842 	g_free (tmp_filename);
1843 }
1844 
1845 static gpointer
camel_gpg_context_clone_photo_filename(gpointer ptr)1846 camel_gpg_context_clone_photo_filename (gpointer ptr)
1847 {
1848 	return (gpointer) camel_pstring_strdup (ptr);
1849 }
1850 
1851 static CamelInternetAddress *
gpg_ctx_extract_email_addresses_from_text(const gchar * text)1852 gpg_ctx_extract_email_addresses_from_text (const gchar *text)
1853 {
1854 	CamelInternetAddress *signers_alt_emails = NULL;
1855 	const gchar *lt = NULL, *gt = NULL, *bs = NULL, *be = NULL;
1856 	gssize ii;
1857 
1858 	for (ii = 0; text && text[ii]; ii++) {
1859 		switch (text[ii]) {
1860 		case '<':
1861 			lt = text + ii;
1862 			gt = NULL;
1863 			bs = NULL;
1864 			be = NULL;
1865 			break;
1866 		case '>':
1867 			if (lt) {
1868 				gt = text + ii;
1869 				bs = NULL;
1870 				be = NULL;
1871 			}
1872 			break;
1873 		case '[':
1874 			if (lt && gt) {
1875 				bs = text + ii;
1876 				be = NULL;
1877 			}
1878 			break;
1879 		case ']':
1880 			if (lt && gt && bs) {
1881 				be = text + ii;
1882 			}
1883 			break;
1884 		case '\n':
1885 			if (lt && lt < gt && gt < bs && bs < be) {
1886 				gchar *email = g_strndup (lt + 1, gt - lt - 1);
1887 
1888 				if (!signers_alt_emails)
1889 					signers_alt_emails = camel_internet_address_new ();
1890 
1891 				camel_internet_address_add (signers_alt_emails, NULL, email);
1892 				g_free (email);
1893 			}
1894 			lt = NULL;
1895 			gt = NULL;
1896 			bs = NULL;
1897 			be = NULL;
1898 			break;
1899 		default:
1900 			break;
1901 		}
1902 	}
1903 
1904 	if (lt && lt < gt && gt < bs && bs < be) {
1905 		gchar *email = g_strndup (lt + 1, gt - lt - 1);
1906 
1907 		if (!signers_alt_emails)
1908 			signers_alt_emails = camel_internet_address_new ();
1909 
1910 		camel_internet_address_add (signers_alt_emails, NULL, email);
1911 		g_free (email);
1912 	}
1913 
1914 	return signers_alt_emails;
1915 }
1916 
1917 static void
add_signers(CamelCipherValidity * validity,const gchar * diagnostics,const GString * signers,GHashTable * signers_keyid,const gchar * photos_filename)1918 add_signers (CamelCipherValidity *validity,
1919 	     const gchar *diagnostics,
1920 	     const GString *signers,
1921 	     GHashTable *signers_keyid,
1922 	     const gchar *photos_filename)
1923 {
1924 	CamelInternetAddress *address;
1925 	GHashTable *photos = NULL;
1926 	gint i, count;
1927 
1928 	g_return_if_fail (validity != NULL);
1929 
1930 	if (!signers || !signers->str || !*signers->str)
1931 		return;
1932 
1933 	address = camel_internet_address_new ();
1934 	g_return_if_fail (address != NULL);
1935 
1936 	if (photos_filename) {
1937 		/* A short file is expected */
1938 		gchar *content = NULL;
1939 		GError *error = NULL;
1940 
1941 		if (g_file_get_contents (photos_filename, &content, NULL, &error)) {
1942 			gchar **lines;
1943 			gint ii;
1944 
1945 			/* Each line is encoded as: KeyID\tPhotoFilename */
1946 			lines = g_strsplit (content, "\n", -1);
1947 
1948 			for (ii = 0; lines && lines[ii]; ii++) {
1949 				gchar *line, *filename;
1950 
1951 				line = lines[ii];
1952 				filename = strchr (line, '\t');
1953 
1954 				if (filename) {
1955 					*filename = '\0';
1956 					filename++;
1957 				}
1958 
1959 				if (filename && g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1960 					if (!photos)
1961 						photos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, camel_gpg_context_free_photo_filename);
1962 
1963 					g_hash_table_insert (photos, g_strdup (line), (gpointer) camel_pstring_strdup (filename));
1964 				}
1965 			}
1966 
1967 			g_strfreev (lines);
1968 		} else {
1969 			g_warning ("CamelGPGContext: Failed to open photos file '%s': %s", photos_filename, error ? error->message : "Unknown error");
1970 		}
1971 
1972 		g_free (content);
1973 		g_clear_error (&error);
1974 	}
1975 
1976 	count = camel_address_decode (CAMEL_ADDRESS (address), signers->str);
1977 	for (i = 0; i < count; i++) {
1978 		const gchar *name = NULL, *email = NULL;
1979 		const gchar *photo_filename; /* allocated on the string pool */
1980 		gint index;
1981 
1982 		if (!camel_internet_address_get (address, i, &name, &email))
1983 			break;
1984 
1985 		photo_filename = gpg_context_find_photo (photos, signers_keyid, name, email);
1986 		index = camel_cipher_validity_add_certinfo (validity, CAMEL_CIPHER_VALIDITY_SIGN, name, email);
1987 
1988 		if (index != -1 && photo_filename) {
1989 			camel_cipher_validity_set_certinfo_property (validity, CAMEL_CIPHER_VALIDITY_SIGN, index,
1990 				CAMEL_CIPHER_CERT_INFO_PROPERTY_PHOTO_FILENAME, (gpointer) photo_filename,
1991 				camel_gpg_context_free_photo_filename, camel_gpg_context_clone_photo_filename);
1992 		} else if (photo_filename) {
1993 			camel_gpg_context_free_photo_filename ((gpointer) photo_filename);
1994 		}
1995 	}
1996 
1997 	if (photos)
1998 		g_hash_table_destroy (photos);
1999 	g_object_unref (address);
2000 
2001 	if (diagnostics && !g_queue_is_empty (&validity->sign.signers)) {
2002 		CamelInternetAddress *signers_alt_emails;
2003 
2004 		signers_alt_emails = gpg_ctx_extract_email_addresses_from_text (diagnostics);
2005 		if (signers_alt_emails && camel_address_length (CAMEL_ADDRESS (signers_alt_emails)) > 1) {
2006 			gchar *addresses = camel_address_format (CAMEL_ADDRESS (signers_alt_emails));
2007 
2008 			camel_cipher_validity_set_certinfo_property (validity, CAMEL_CIPHER_VALIDITY_SIGN, 0,
2009 				CAMEL_CIPHER_CERT_INFO_PROPERTY_SIGNERS_ALT_EMAILS, addresses,
2010 				g_free, (CamelCipherCloneFunc) g_strdup);
2011 		}
2012 
2013 		g_clear_object (&signers_alt_emails);
2014 	}
2015 }
2016 
2017 /* ********************************************************************** */
2018 
2019 static void
gpg_context_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)2020 gpg_context_set_property (GObject *object,
2021                           guint property_id,
2022                           const GValue *value,
2023                           GParamSpec *pspec)
2024 {
2025 	switch (property_id) {
2026 		case PROP_ALWAYS_TRUST:
2027 			camel_gpg_context_set_always_trust (
2028 				CAMEL_GPG_CONTEXT (object),
2029 				g_value_get_boolean (value));
2030 			return;
2031 
2032 		case PROP_PREFER_INLINE:
2033 			camel_gpg_context_set_prefer_inline (
2034 				CAMEL_GPG_CONTEXT (object),
2035 				g_value_get_boolean (value));
2036 			return;
2037 	}
2038 
2039 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2040 }
2041 
2042 static void
gpg_context_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)2043 gpg_context_get_property (GObject *object,
2044                           guint property_id,
2045                           GValue *value,
2046                           GParamSpec *pspec)
2047 {
2048 	switch (property_id) {
2049 		case PROP_ALWAYS_TRUST:
2050 			g_value_set_boolean (
2051 				value,
2052 				camel_gpg_context_get_always_trust (
2053 				CAMEL_GPG_CONTEXT (object)));
2054 			return;
2055 
2056 		case PROP_PREFER_INLINE:
2057 			g_value_set_boolean (
2058 				value,
2059 				camel_gpg_context_get_prefer_inline (
2060 				CAMEL_GPG_CONTEXT (object)));
2061 			return;
2062 	}
2063 
2064 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2065 }
2066 
2067 static const gchar *
gpg_hash_to_id(CamelCipherContext * context,CamelCipherHash hash)2068 gpg_hash_to_id (CamelCipherContext *context,
2069                 CamelCipherHash hash)
2070 {
2071 	switch (hash) {
2072 	case CAMEL_CIPHER_HASH_MD2:
2073 		return "pgp-md2";
2074 	case CAMEL_CIPHER_HASH_MD5:
2075 		return "pgp-md5";
2076 	case CAMEL_CIPHER_HASH_SHA1:
2077 		return "pgp-sha1";
2078 	case CAMEL_CIPHER_HASH_DEFAULT:
2079 	case CAMEL_CIPHER_HASH_SHA256:
2080 		return "pgp-sha256";
2081 	case CAMEL_CIPHER_HASH_SHA384:
2082 		return "pgp-sha384";
2083 	case CAMEL_CIPHER_HASH_SHA512:
2084 		return "pgp-sha512";
2085 	case CAMEL_CIPHER_HASH_RIPEMD160:
2086 		return "pgp-ripemd160";
2087 	case CAMEL_CIPHER_HASH_TIGER192:
2088 		return "pgp-tiger192";
2089 	case CAMEL_CIPHER_HASH_HAVAL5160:
2090 		return "pgp-haval-5-160";
2091 	}
2092 
2093 	return NULL;
2094 }
2095 
2096 static CamelCipherHash
gpg_id_to_hash(CamelCipherContext * context,const gchar * id)2097 gpg_id_to_hash (CamelCipherContext *context,
2098                 const gchar *id)
2099 {
2100 	if (id) {
2101 		if (!strcmp (id, "pgp-md2"))
2102 			return CAMEL_CIPHER_HASH_MD2;
2103 		else if (!strcmp (id, "pgp-md5"))
2104 			return CAMEL_CIPHER_HASH_MD5;
2105 		else if (!strcmp (id, "pgp-sha1"))
2106 			return CAMEL_CIPHER_HASH_SHA1;
2107 		else if (!strcmp (id, "pgp-sha256"))
2108 			return CAMEL_CIPHER_HASH_SHA256;
2109 		else if (!strcmp (id, "pgp-sha384"))
2110 			return CAMEL_CIPHER_HASH_SHA384;
2111 		else if (!strcmp (id, "pgp-sha512"))
2112 			return CAMEL_CIPHER_HASH_SHA512;
2113 		else if (!strcmp (id, "pgp-ripemd160"))
2114 			return CAMEL_CIPHER_HASH_RIPEMD160;
2115 		else if (!strcmp (id, "tiger192"))
2116 			return CAMEL_CIPHER_HASH_TIGER192;
2117 		else if (!strcmp (id, "haval-5-160"))
2118 			return CAMEL_CIPHER_HASH_HAVAL5160;
2119 	}
2120 
2121 	return CAMEL_CIPHER_HASH_DEFAULT;
2122 }
2123 
2124 static gboolean
gpg_context_decode_to_stream(CamelMimePart * part,CamelStream * stream,GCancellable * cancellable,GError ** error)2125 gpg_context_decode_to_stream (CamelMimePart *part,
2126 			      CamelStream *stream,
2127 			      GCancellable *cancellable,
2128 			      GError **error)
2129 {
2130 	CamelDataWrapper *wrapper;
2131 
2132 	wrapper = camel_medium_get_content (CAMEL_MEDIUM (part));
2133 
2134 	/* This is when encrypting already signed part */
2135 	if (CAMEL_IS_MIME_PART (wrapper))
2136 		wrapper = camel_medium_get_content (CAMEL_MEDIUM (wrapper));
2137 
2138 	if (camel_data_wrapper_decode_to_stream_sync (wrapper, stream, cancellable, error) == -1 ||
2139 	    camel_stream_flush (stream, cancellable, error) == -1)
2140 		return FALSE;
2141 
2142 	/* Reset stream position to beginning. */
2143 	if (G_IS_SEEKABLE (stream))
2144 		g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
2145 
2146 	return TRUE;
2147 }
2148 
2149 static gboolean
gpg_sign_sync(CamelCipherContext * context,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2150 gpg_sign_sync (CamelCipherContext *context,
2151                const gchar *userid,
2152                CamelCipherHash hash,
2153                CamelMimePart *ipart,
2154                CamelMimePart *opart,
2155                GCancellable *cancellable,
2156                GError **error)
2157 {
2158 	struct _GpgCtx *gpg = NULL;
2159 	CamelCipherContextClass *class;
2160 	CamelGpgContext *ctx = (CamelGpgContext *) context;
2161 	CamelStream *ostream = camel_stream_mem_new (), *istream;
2162 	CamelDataWrapper *dw;
2163 	CamelContentType *ct;
2164 	CamelMimePart *sigpart;
2165 	CamelMultipartSigned *mps;
2166 	gboolean success = FALSE;
2167 	gboolean prefer_inline;
2168 
2169 	/* Note: see rfc2015 or rfc3156, section 5 */
2170 
2171 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2172 
2173 	prefer_inline = ctx->priv->prefer_inline &&
2174 		camel_content_type_is (camel_mime_part_get_content_type (ipart), "text", "plain");
2175 
2176 	/* FIXME: stream this, we stream output at least */
2177 	istream = camel_stream_mem_new ();
2178 	if ((prefer_inline && !gpg_context_decode_to_stream (ipart, istream, cancellable, error)) ||
2179 	    (!prefer_inline && camel_cipher_canonical_to_stream (
2180 		ipart, CAMEL_MIME_FILTER_CANON_STRIP |
2181 		CAMEL_MIME_FILTER_CANON_CRLF |
2182 		CAMEL_MIME_FILTER_CANON_FROM,
2183 		istream, NULL, error) == -1)) {
2184 		g_prefix_error (
2185 			error, _("Could not generate signing data: "));
2186 		goto fail;
2187 	}
2188 
2189 #ifdef GPG_LOG
2190 	if (camel_debug_start ("gpg:sign")) {
2191 		gchar *name;
2192 		CamelStream *out;
2193 
2194 		name = g_strdup_printf ("camel-gpg.%d.sign-data", logid++);
2195 		out = camel_stream_fs_new_with_name (
2196 			name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2197 		if (out) {
2198 			printf ("Writing gpg signing data to '%s'\n", name);
2199 			camel_stream_write_to_stream (istream, out, NULL, NULL);
2200 			g_seekable_seek (
2201 				G_SEEKABLE (istream), 0,
2202 				G_SEEK_SET, NULL, NULL);
2203 			g_object_unref (out);
2204 		}
2205 		g_free (name);
2206 		camel_debug_end ();
2207 	}
2208 #endif
2209 
2210 	gpg = gpg_ctx_new (context, cancellable);
2211 	gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
2212 	gpg_ctx_set_hash (gpg, hash);
2213 	gpg_ctx_set_armor (gpg, TRUE);
2214 	gpg_ctx_set_userid (gpg, userid);
2215 	gpg_ctx_set_istream (gpg, istream);
2216 	gpg_ctx_set_ostream (gpg, ostream);
2217 	gpg_ctx_set_prefer_inline (gpg, prefer_inline);
2218 
2219 	if (!gpg_ctx_op_start (gpg, error))
2220 		goto fail;
2221 
2222 	while (!gpg_ctx_op_complete (gpg)) {
2223 		if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2224 			gpg_ctx_op_cancel (gpg);
2225 			goto fail;
2226 		}
2227 	}
2228 
2229 	if (gpg_ctx_op_wait (gpg) != 0) {
2230 		const gchar *diagnostics;
2231 
2232 		diagnostics = gpg_ctx_get_diagnostics (gpg);
2233 		g_set_error (
2234 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2235 			(diagnostics != NULL && *diagnostics != '\0') ?
2236 			diagnostics : _("Failed to execute gpg."));
2237 
2238 		goto fail;
2239 	}
2240 
2241 	success = TRUE;
2242 
2243 	dw = camel_data_wrapper_new ();
2244 	g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2245 	camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
2246 
2247 	if (gpg->prefer_inline) {
2248 		CamelTransferEncoding encoding;
2249 
2250 		encoding = camel_mime_part_get_encoding (ipart);
2251 
2252 		if (encoding != CAMEL_TRANSFER_ENCODING_BASE64 &&
2253 		    encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
2254 			encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
2255 
2256 		sigpart = camel_mime_part_new ();
2257 		ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (ipart));
2258 		camel_data_wrapper_set_mime_type_field (dw, ct);
2259 
2260 		camel_mime_part_set_encoding (sigpart, encoding);
2261 		camel_medium_set_content ((CamelMedium *) sigpart, dw);
2262 		g_object_unref (dw);
2263 
2264 		camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) sigpart);
2265 
2266 		g_object_unref (sigpart);
2267 	} else {
2268 		sigpart = camel_mime_part_new ();
2269 		ct = camel_content_type_new ("application", "pgp-signature");
2270 		camel_content_type_set_param (ct, "name", "signature.asc");
2271 		camel_data_wrapper_set_mime_type_field (dw, ct);
2272 		camel_content_type_unref (ct);
2273 
2274 		camel_medium_set_content ((CamelMedium *) sigpart, dw);
2275 		g_object_unref (dw);
2276 
2277 		camel_mime_part_set_description (sigpart, "This is a digitally signed message part");
2278 
2279 		mps = camel_multipart_signed_new ();
2280 		ct = camel_content_type_new ("multipart", "signed");
2281 		camel_content_type_set_param (ct, "micalg", camel_cipher_context_hash_to_id (context, hash == CAMEL_CIPHER_HASH_DEFAULT ? gpg->hash : hash));
2282 		camel_content_type_set_param (ct, "protocol", class->sign_protocol);
2283 		camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) mps, ct);
2284 		camel_content_type_unref (ct);
2285 		camel_multipart_set_boundary ((CamelMultipart *) mps, NULL);
2286 
2287 		camel_multipart_signed_set_signature (mps, sigpart);
2288 		camel_multipart_signed_set_content_stream (mps, istream);
2289 
2290 		g_object_unref (sigpart);
2291 
2292 		camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) mps);
2293 
2294 		g_object_unref (mps);
2295 	}
2296 
2297 	g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2298 
2299 fail:
2300 	g_object_unref (ostream);
2301 
2302 	if (gpg)
2303 		gpg_ctx_free (gpg);
2304 
2305 	return success;
2306 }
2307 
2308 static CamelCipherValidity *
gpg_verify_sync(CamelCipherContext * context,CamelMimePart * ipart,GCancellable * cancellable,GError ** error)2309 gpg_verify_sync (CamelCipherContext *context,
2310                  CamelMimePart *ipart,
2311                  GCancellable *cancellable,
2312                  GError **error)
2313 {
2314 	CamelCipherContextClass *class;
2315 	CamelCipherValidity *validity;
2316 	const gchar *diagnostics = NULL;
2317 	struct _GpgCtx *gpg = NULL;
2318 	gchar *sigfile = NULL;
2319 	CamelContentType *ct;
2320 	CamelMimePart *sigpart;
2321 	CamelStream *istream = NULL, *canon_stream;
2322 	CamelMultipart *mps;
2323 	CamelStream *filter;
2324 	CamelMimeFilter *canon;
2325 	gboolean is_retry = FALSE;
2326 
2327 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2328 
2329 	mps = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) ipart);
2330 	ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mps));
2331 
2332 	/* Inline signature (using our fake mime type) or PGP/Mime signature */
2333 	if (camel_content_type_is (ct, "multipart", "signed")) {
2334 		/* PGP/Mime Signature */
2335 		const gchar *tmp;
2336 
2337 		tmp = camel_content_type_param (ct, "protocol");
2338 		if (!CAMEL_IS_MULTIPART_SIGNED (mps)
2339 		    || tmp == NULL
2340 		    || g_ascii_strcasecmp (tmp, class->sign_protocol) != 0) {
2341 			g_set_error (
2342 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2343 				_("Cannot verify message signature: "
2344 				"Incorrect message format"));
2345 			return NULL;
2346 		}
2347 
2348 		if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
2349 			g_set_error (
2350 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2351 				_("Cannot verify message signature: "
2352 				"Incorrect message format"));
2353 			return NULL;
2354 		}
2355 
2356 		if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
2357 			g_set_error (
2358 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2359 				_("Cannot verify message signature: "
2360 				"Incorrect message format"));
2361 			g_object_unref (istream);
2362 			return NULL;
2363 		}
2364 	} else if (camel_content_type_is (ct, "application", "x-inlinepgp-signed")) {
2365 		/* Inline Signed */
2366 		CamelDataWrapper *content;
2367 		content = camel_medium_get_content ((CamelMedium *) ipart);
2368 		istream = camel_stream_mem_new ();
2369 		if (!camel_data_wrapper_decode_to_stream_sync (
2370 			content, istream, cancellable, error))
2371 			goto exception;
2372 		g_seekable_seek (
2373 			G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2374 		sigpart = NULL;
2375 	} else {
2376 		/* Invalid Mimetype */
2377 		g_set_error (
2378 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2379 			_("Cannot verify message signature: "
2380 			"Incorrect message format"));
2381 		return NULL;
2382 	}
2383 
2384 	/* Now start the real work of verifying the message */
2385 #ifdef GPG_LOG
2386 	if (camel_debug_start ("gpg:sign")) {
2387 		gchar *name;
2388 		CamelStream *out;
2389 
2390 		name = g_strdup_printf ("camel-gpg.%d.verify.data", logid);
2391 		out = camel_stream_fs_new_with_name (
2392 			name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2393 		if (out) {
2394 			printf ("Writing gpg verify data to '%s'\n", name);
2395 			camel_stream_write_to_stream (istream, out, NULL, NULL);
2396 			g_seekable_seek (
2397 				G_SEEKABLE (istream),
2398 				0, G_SEEK_SET, NULL, NULL);
2399 			g_object_unref (out);
2400 		}
2401 
2402 		g_free (name);
2403 
2404 		if (sigpart) {
2405 			name = g_strdup_printf ("camel-gpg.%d.verify.signature", logid++);
2406 			out = camel_stream_fs_new_with_name (
2407 				name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2408 			if (out) {
2409 				printf ("Writing gpg verify signature to '%s'\n", name);
2410 				camel_data_wrapper_write_to_stream_sync (
2411 					CAMEL_DATA_WRAPPER (sigpart),
2412 					out, NULL, NULL);
2413 				g_object_unref (out);
2414 			}
2415 			g_free (name);
2416 		}
2417 		camel_debug_end ();
2418 	}
2419 #endif
2420 
2421 	if (sigpart) {
2422 		sigfile = swrite (sigpart, cancellable, error);
2423 		if (sigfile == NULL) {
2424 			g_prefix_error (
2425 				error, _("Cannot verify message signature: "));
2426 			goto exception;
2427 		}
2428 	}
2429 
2430  retry:
2431 	g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2432 
2433 	canon_stream = camel_stream_mem_new ();
2434 
2435 	/* strip trailing white-spaces */
2436 	filter = camel_stream_filter_new (istream);
2437 	canon = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF | (is_retry ? 0 : CAMEL_MIME_FILTER_CANON_STRIP));
2438 	camel_stream_filter_add (CAMEL_STREAM_FILTER (filter), canon);
2439 	g_object_unref (canon);
2440 
2441 	camel_stream_write_to_stream (filter, canon_stream, NULL, NULL);
2442 
2443 	g_object_unref (filter);
2444 
2445 	g_seekable_seek (G_SEEKABLE (canon_stream), 0, G_SEEK_SET, NULL, NULL);
2446 
2447 	gpg = gpg_ctx_new (context, cancellable);
2448 	gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
2449 	gpg_ctx_set_load_photos (gpg, camel_cipher_can_load_photos ());
2450 	if (sigfile)
2451 		gpg_ctx_set_sigfile (gpg, sigfile);
2452 	gpg_ctx_set_istream (gpg, canon_stream);
2453 
2454 	if (!gpg_ctx_op_start (gpg, error)) {
2455 		g_object_unref (canon_stream);
2456 		goto exception;
2457 	}
2458 
2459 	while (!gpg_ctx_op_complete (gpg)) {
2460 		if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2461 			gpg_ctx_op_cancel (gpg);
2462 			g_object_unref (canon_stream);
2463 			goto exception;
2464 		}
2465 	}
2466 
2467 	/* report error only when no data or didn't found signature */
2468 	if (gpg_ctx_op_wait (gpg) != 0 && (gpg->nodata || !gpg->hadsig)) {
2469 		const gchar *diagnostics;
2470 
2471 		diagnostics = gpg_ctx_get_diagnostics (gpg);
2472 		g_set_error (
2473 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2474 			(diagnostics != NULL && *diagnostics != '\0') ?
2475 			diagnostics : _("Failed to execute gpg."));
2476 
2477 		g_object_unref (canon_stream);
2478 
2479 		goto exception;
2480 	}
2481 
2482 	if (!is_retry && !gpg->validsig && !gpg->nopubkey) {
2483 		/* Retry without stripping trailing spaces */
2484 		is_retry = TRUE;
2485 		gpg_ctx_free (gpg);
2486 		g_object_unref (canon_stream);
2487 		goto retry;
2488 	}
2489 
2490 	validity = camel_cipher_validity_new ();
2491 	diagnostics = gpg_ctx_get_diagnostics (gpg);
2492 	camel_cipher_validity_set_description (validity, diagnostics);
2493 	if (gpg->validsig) {
2494 		if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE || gpg->trust == GPG_TRUST_MARGINAL)
2495 			validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
2496 		else if (gpg->trust != GPG_TRUST_NEVER)
2497 			validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
2498 		else
2499 			validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2500 	} else if (gpg->nopubkey) {
2501 		validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
2502 	} else {
2503 		validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2504 	}
2505 
2506 	add_signers (validity, diagnostics, gpg->signers, gpg->signers_keyid, gpg->photos_filename);
2507 
2508 	gpg_ctx_free (gpg);
2509 
2510 	if (sigfile) {
2511 		g_unlink (sigfile);
2512 		g_free (sigfile);
2513 	}
2514 
2515 	g_object_unref (canon_stream);
2516 	g_clear_object (&istream);
2517 
2518 	return validity;
2519 
2520  exception:
2521 
2522 	if (gpg != NULL)
2523 		gpg_ctx_free (gpg);
2524 
2525 	if (istream)
2526 		g_object_unref (istream);
2527 
2528 	if (sigfile) {
2529 		g_unlink (sigfile);
2530 		g_free (sigfile);
2531 	}
2532 
2533 	return NULL;
2534 }
2535 
2536 static gboolean
gpg_encrypt_sync(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2537 gpg_encrypt_sync (CamelCipherContext *context,
2538                   const gchar *userid,
2539                   GPtrArray *recipients,
2540                   CamelMimePart *ipart,
2541                   CamelMimePart *opart,
2542                   GCancellable *cancellable,
2543                   GError **error)
2544 {
2545 	CamelCipherContextClass *class;
2546 	CamelGpgContext *ctx = (CamelGpgContext *) context;
2547 	struct _GpgCtx *gpg;
2548 	CamelStream *istream, *ostream, *vstream;
2549 	CamelMimePart *encpart, *verpart;
2550 	CamelDataWrapper *dw;
2551 	CamelContentType *ct;
2552 	CamelMultipartEncrypted *mpe;
2553 	gboolean success = FALSE;
2554 	gboolean prefer_inline;
2555 	GSList *gathered_keys = NULL, *link;
2556 	gint i;
2557 
2558 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2559 
2560 	if (!camel_session_get_recipient_certificates_sync (camel_cipher_context_get_session (context),
2561 		CAMEL_RECIPIENT_CERTIFICATE_PGP, recipients, &gathered_keys, cancellable, error))
2562 		return FALSE;
2563 
2564 	prefer_inline = ctx->priv->prefer_inline &&
2565 		camel_content_type_is (camel_mime_part_get_content_type (ipart), "text", "plain");
2566 
2567 	ostream = camel_stream_mem_new ();
2568 	istream = camel_stream_mem_new ();
2569 	if ((prefer_inline && !gpg_context_decode_to_stream (ipart, istream, cancellable, error)) ||
2570 	    (!prefer_inline && camel_cipher_canonical_to_stream (
2571 		ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream, NULL, error) == -1)) {
2572 		g_prefix_error (
2573 			error, _("Could not generate encrypting data: "));
2574 		goto fail1;
2575 	}
2576 
2577 	gpg = gpg_ctx_new (context, cancellable);
2578 	gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
2579 	gpg_ctx_set_armor (gpg, TRUE);
2580 	gpg_ctx_set_userid (gpg, userid);
2581 	gpg_ctx_set_istream (gpg, istream);
2582 	gpg_ctx_set_ostream (gpg, ostream);
2583 	gpg_ctx_set_always_trust (gpg, ctx->priv->always_trust);
2584 	gpg_ctx_set_prefer_inline (gpg, prefer_inline);
2585 
2586 	if (gathered_keys && g_slist_length (gathered_keys) != recipients->len) {
2587 		g_slist_free_full (gathered_keys, g_free);
2588 		gathered_keys = NULL;
2589 	}
2590 
2591 	for (link = gathered_keys, i = 0; i < recipients->len; i++) {
2592 		gpg_ctx_add_recipient (gpg, recipients->pdata[i], link ? link->data : NULL);
2593 		link = g_slist_next (link);
2594 	}
2595 
2596 	if (!gpg_ctx_op_start (gpg, error))
2597 		goto fail;
2598 
2599 	/* FIXME: move this to a common routine */
2600 	while (!gpg_ctx_op_complete (gpg)) {
2601 		if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2602 			gpg_ctx_op_cancel (gpg);
2603 			goto fail;
2604 		}
2605 	}
2606 
2607 	if (gpg_ctx_op_wait (gpg) != 0) {
2608 		const gchar *diagnostics;
2609 
2610 		diagnostics = gpg_ctx_get_diagnostics (gpg);
2611 		g_set_error (
2612 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2613 			(diagnostics != NULL && *diagnostics != '\0') ?
2614 			diagnostics : _("Failed to execute gpg."));
2615 		goto fail;
2616 	}
2617 
2618 	success = TRUE;
2619 
2620 	dw = camel_data_wrapper_new ();
2621 	g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2622 	camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
2623 
2624 	if (gpg->prefer_inline) {
2625 		CamelTransferEncoding encoding;
2626 
2627 		encoding = camel_mime_part_get_encoding (ipart);
2628 
2629 		if (encoding != CAMEL_TRANSFER_ENCODING_BASE64 &&
2630 		    encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
2631 			encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
2632 
2633 		encpart = camel_mime_part_new ();
2634 		ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (ipart));
2635 		camel_data_wrapper_set_mime_type_field (dw, ct);
2636 
2637 		camel_mime_part_set_encoding (encpart, encoding);
2638 		camel_medium_set_content ((CamelMedium *) encpart, dw);
2639 		g_object_unref (dw);
2640 
2641 		camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) encpart);
2642 
2643 		g_object_unref (encpart);
2644 	} else {
2645 		encpart = camel_mime_part_new ();
2646 		ct = camel_content_type_new ("application", "octet-stream");
2647 		camel_content_type_set_param (ct, "name", "encrypted.asc");
2648 		camel_data_wrapper_set_mime_type_field (dw, ct);
2649 		camel_content_type_unref (ct);
2650 
2651 		camel_medium_set_content ((CamelMedium *) encpart, dw);
2652 		g_object_unref (dw);
2653 
2654 		camel_mime_part_set_description (encpart, _("This is a digitally encrypted message part"));
2655 
2656 		vstream = camel_stream_mem_new ();
2657 		camel_stream_write_string (vstream, "Version: 1\n", NULL, NULL);
2658 		g_seekable_seek (G_SEEKABLE (vstream), 0, G_SEEK_SET, NULL, NULL);
2659 
2660 		verpart = camel_mime_part_new ();
2661 		dw = camel_data_wrapper_new ();
2662 		camel_data_wrapper_set_mime_type (dw, class->encrypt_protocol);
2663 		camel_data_wrapper_construct_from_stream_sync (
2664 			dw, vstream, NULL, NULL);
2665 		g_object_unref (vstream);
2666 		camel_medium_set_content ((CamelMedium *) verpart, dw);
2667 		g_object_unref (dw);
2668 
2669 		mpe = camel_multipart_encrypted_new ();
2670 		ct = camel_content_type_new ("multipart", "encrypted");
2671 		camel_content_type_set_param (ct, "protocol", class->encrypt_protocol);
2672 		camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) mpe, ct);
2673 		camel_content_type_unref (ct);
2674 		camel_multipart_set_boundary ((CamelMultipart *) mpe, NULL);
2675 
2676 		camel_multipart_add_part ((CamelMultipart *) mpe, verpart);
2677 		g_object_unref (verpart);
2678 		camel_multipart_add_part ((CamelMultipart *) mpe, encpart);
2679 		g_object_unref (encpart);
2680 
2681 		camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) mpe);
2682 
2683 		g_object_unref (mpe);
2684 	}
2685 fail:
2686 	gpg_ctx_free (gpg);
2687 fail1:
2688 	g_slist_free_full (gathered_keys, g_free);
2689 	g_object_unref (istream);
2690 	g_object_unref (ostream);
2691 
2692 	return success;
2693 }
2694 
2695 static CamelCipherValidity *
gpg_decrypt_sync(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2696 gpg_decrypt_sync (CamelCipherContext *context,
2697                   CamelMimePart *ipart,
2698                   CamelMimePart *opart,
2699                   GCancellable *cancellable,
2700                   GError **error)
2701 {
2702 	struct _GpgCtx *gpg = NULL;
2703 	CamelCipherValidity *valid = NULL;
2704 	CamelStream *ostream, *istream;
2705 	CamelDataWrapper *content;
2706 	CamelMimePart *encrypted;
2707 	CamelMultipart *mp;
2708 	CamelContentType *ct;
2709 	gboolean success;
2710 
2711 	if (!ipart) {
2712 		g_set_error (
2713 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2714 			_("Cannot decrypt message: Incorrect message format"));
2715 		return NULL;
2716 	}
2717 
2718 	content = camel_medium_get_content ((CamelMedium *) ipart);
2719 
2720 	if (!content) {
2721 		g_set_error (
2722 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2723 			_("Cannot decrypt message: Incorrect message format"));
2724 		return NULL;
2725 	}
2726 
2727 	ct = camel_data_wrapper_get_mime_type_field (content);
2728 	/* Encrypted part (using our fake mime type) or PGP/Mime multipart */
2729 	if (camel_content_type_is (ct, "multipart", "encrypted")) {
2730 		mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) ipart);
2731 		if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
2732 			g_set_error (
2733 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2734 				_("Failed to decrypt MIME part: "
2735 				"protocol error"));
2736 			return NULL;
2737 		}
2738 
2739 		content = camel_medium_get_content ((CamelMedium *) encrypted);
2740 	} else if (camel_content_type_is (ct, "application", "x-inlinepgp-encrypted")) {
2741 		content = camel_medium_get_content ((CamelMedium *) ipart);
2742 	} else {
2743 		/* Invalid Mimetype */
2744 		g_set_error (
2745 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2746 			_("Cannot decrypt message: Incorrect message format"));
2747 		return NULL;
2748 	}
2749 
2750 	istream = camel_stream_mem_new ();
2751 	if (!camel_data_wrapper_decode_to_stream_sync (
2752 		content, istream, cancellable, error)) {
2753 		g_object_unref (istream);
2754 		return NULL;
2755 	}
2756 
2757 	g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2758 
2759 	ostream = camel_stream_mem_new ();
2760 	camel_stream_mem_set_secure ((CamelStreamMem *) ostream);
2761 
2762 	gpg = gpg_ctx_new (context, cancellable);
2763 	gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
2764 	gpg_ctx_set_load_photos (gpg, camel_cipher_can_load_photos ());
2765 	gpg_ctx_set_istream (gpg, istream);
2766 	gpg_ctx_set_ostream (gpg, ostream);
2767 
2768 	gpg->bad_decrypt = TRUE;
2769 
2770 	if (!gpg_ctx_op_start (gpg, error))
2771 		goto fail;
2772 
2773 	while (!gpg_ctx_op_complete (gpg)) {
2774 		if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2775 			gpg_ctx_op_cancel (gpg);
2776 			goto fail;
2777 		}
2778 	}
2779 
2780 	/* Report errors only if nothing was decrypted; missing sender's key used
2781 	 * for signature of a signed and encrypted messages causes GPG to return
2782 	 * failure, thus count with it.
2783 	 */
2784 	if (gpg_ctx_op_wait (gpg) != 0 && (gpg->nodata || (gpg->bad_decrypt && !gpg->noseckey))) {
2785 		const gchar *diagnostics;
2786 
2787 		diagnostics = gpg_ctx_get_diagnostics (gpg);
2788 		g_set_error (
2789 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2790 			(diagnostics != NULL && *diagnostics != '\0') ?
2791 			diagnostics : _("Failed to execute gpg."));
2792 		goto fail;
2793 	}
2794 
2795 	/* Decrypted nothing, write at least CRLF */
2796 	if (!g_seekable_tell (G_SEEKABLE (ostream))) {
2797 		g_warn_if_fail (2 == camel_stream_write (ostream, "\r\n", 2, cancellable, NULL));
2798 	}
2799 
2800 	g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2801 
2802 	if (gpg->bad_decrypt && gpg->noseckey) {
2803 		success = FALSE;
2804 		g_set_error (
2805 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2806 			_("Failed to decrypt MIME part: Secret key not found"));
2807 	} else if (camel_content_type_is (ct, "multipart", "encrypted")) {
2808 		CamelDataWrapper *dw;
2809 		CamelStream *null = camel_stream_null_new ();
2810 
2811 		/* Multipart encrypted - parse a full mime part */
2812 		success = camel_data_wrapper_construct_from_stream_sync (
2813 			CAMEL_DATA_WRAPPER (opart),
2814 			ostream, NULL, error);
2815 
2816 		dw = camel_medium_get_content ((CamelMedium *) opart);
2817 		if (!camel_data_wrapper_decode_to_stream_sync (
2818 			dw, null, cancellable, NULL)) {
2819 			/* nothing had been decoded from the stream, it doesn't
2820 			 * contain any header, like Content-Type or such, thus
2821 			 * write it as a message body */
2822 			success = camel_data_wrapper_construct_from_stream_sync (
2823 				dw, ostream, cancellable, error);
2824 		}
2825 
2826 		g_object_unref (null);
2827 	} else {
2828 		/* Inline signed - raw data (may not be a mime part) */
2829 		CamelDataWrapper *dw;
2830 		dw = camel_data_wrapper_new ();
2831 		success = camel_data_wrapper_construct_from_stream_sync (
2832 			dw, ostream, NULL, error);
2833 		camel_data_wrapper_set_mime_type (dw, "application/octet-stream");
2834 		camel_medium_set_content ((CamelMedium *) opart, dw);
2835 		g_object_unref (dw);
2836 		/* Set mime/type of this new part to application/octet-stream to force type snooping */
2837 		camel_mime_part_set_content_type (opart, "application/octet-stream");
2838 	}
2839 
2840 	if (success) {
2841 		valid = camel_cipher_validity_new ();
2842 		if (gpg->decrypt_extra_text)
2843 			valid->encrypt.description = g_strdup_printf (_("GPG blob contains unencrypted text: %s"), gpg->decrypt_extra_text->str);
2844 		else
2845 			valid->encrypt.description = g_strdup (_("Encrypted content"));
2846 		valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
2847 
2848 		if (gpg->hadsig) {
2849 			if (gpg->validsig) {
2850 				if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
2851 					valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
2852 				else if (gpg->trust != GPG_TRUST_NEVER)
2853 					valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
2854 				else
2855 					valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2856 			} else if (gpg->nopubkey) {
2857 				valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
2858 			} else {
2859 				valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2860 			}
2861 
2862 			add_signers (valid, gpg_ctx_get_diagnostics (gpg), gpg->signers, gpg->signers_keyid, gpg->photos_filename);
2863 		}
2864 	}
2865 
2866  fail:
2867 	g_object_unref (ostream);
2868 	g_object_unref (istream);
2869 	gpg_ctx_free (gpg);
2870 
2871 	return valid;
2872 }
2873 
2874 static void
camel_gpg_context_class_init(CamelGpgContextClass * class)2875 camel_gpg_context_class_init (CamelGpgContextClass *class)
2876 {
2877 	GObjectClass *object_class;
2878 	CamelCipherContextClass *cipher_context_class;
2879 
2880 	object_class = G_OBJECT_CLASS (class);
2881 	object_class->set_property = gpg_context_set_property;
2882 	object_class->get_property = gpg_context_get_property;
2883 
2884 	cipher_context_class = CAMEL_CIPHER_CONTEXT_CLASS (class);
2885 	cipher_context_class->sign_protocol = "application/pgp-signature";
2886 	cipher_context_class->encrypt_protocol = "application/pgp-encrypted";
2887 	cipher_context_class->key_protocol = "application/pgp-keys";
2888 	cipher_context_class->hash_to_id = gpg_hash_to_id;
2889 	cipher_context_class->id_to_hash = gpg_id_to_hash;
2890 	cipher_context_class->sign_sync = gpg_sign_sync;
2891 	cipher_context_class->verify_sync = gpg_verify_sync;
2892 	cipher_context_class->encrypt_sync = gpg_encrypt_sync;
2893 	cipher_context_class->decrypt_sync = gpg_decrypt_sync;
2894 
2895 	g_object_class_install_property (
2896 		object_class,
2897 		PROP_ALWAYS_TRUST,
2898 		g_param_spec_boolean (
2899 			"always-trust",
2900 			"Always Trust",
2901 			NULL,
2902 			FALSE,
2903 			G_PARAM_READWRITE |
2904 			G_PARAM_CONSTRUCT |
2905 			G_PARAM_EXPLICIT_NOTIFY));
2906 
2907 	g_object_class_install_property (
2908 		object_class,
2909 		PROP_PREFER_INLINE,
2910 		g_param_spec_boolean (
2911 			"prefer-inline",
2912 			"Prefer Inline",
2913 			NULL,
2914 			FALSE,
2915 			G_PARAM_READWRITE |
2916 			G_PARAM_CONSTRUCT |
2917 			G_PARAM_EXPLICIT_NOTIFY));
2918 }
2919 
2920 static void
camel_gpg_context_init(CamelGpgContext * context)2921 camel_gpg_context_init (CamelGpgContext *context)
2922 {
2923 	context->priv = camel_gpg_context_get_instance_private (context);
2924 }
2925 
2926 /**
2927  * camel_gpg_context_new:
2928  * @session: session
2929  *
2930  * Creates a new gpg cipher context object.
2931  *
2932  * Returns: a new gpg cipher context object.
2933  **/
2934 CamelCipherContext *
camel_gpg_context_new(CamelSession * session)2935 camel_gpg_context_new (CamelSession *session)
2936 {
2937 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
2938 
2939 	return g_object_new (
2940 		CAMEL_TYPE_GPG_CONTEXT,
2941 		"session", session, NULL);
2942 }
2943 
2944 /**
2945  * camel_gpg_context_get_always_trust:
2946  * @context: a #CamelGpgContext
2947  *
2948  * Since: 2.32
2949  **/
2950 gboolean
camel_gpg_context_get_always_trust(CamelGpgContext * context)2951 camel_gpg_context_get_always_trust (CamelGpgContext *context)
2952 {
2953 	g_return_val_if_fail (CAMEL_IS_GPG_CONTEXT (context), FALSE);
2954 
2955 	return context->priv->always_trust;
2956 }
2957 
2958 /**
2959  * camel_gpg_context_set_always_trust:
2960  * @context: gpg context
2961  * @always_trust: always trust flag
2962  *
2963  * Sets the @always_trust flag on the gpg context which is used for
2964  * encryption.
2965  **/
2966 void
camel_gpg_context_set_always_trust(CamelGpgContext * context,gboolean always_trust)2967 camel_gpg_context_set_always_trust (CamelGpgContext *context,
2968                                     gboolean always_trust)
2969 {
2970 	g_return_if_fail (CAMEL_IS_GPG_CONTEXT (context));
2971 
2972 	if (context->priv->always_trust == always_trust)
2973 		return;
2974 
2975 	context->priv->always_trust = always_trust;
2976 
2977 	g_object_notify (G_OBJECT (context), "always-trust");
2978 }
2979 
2980 /**
2981  * camel_gpg_context_get_prefer_inline:
2982  * @context: a #CamelGpgContext
2983  *
2984  * Returns: Whether prefer inline sign/encrypt (%TRUE), or as multiparts (%FALSE)
2985  *
2986  * Since: 3.20
2987  **/
2988 gboolean
camel_gpg_context_get_prefer_inline(CamelGpgContext * context)2989 camel_gpg_context_get_prefer_inline (CamelGpgContext *context)
2990 {
2991 	g_return_val_if_fail (CAMEL_IS_GPG_CONTEXT (context), FALSE);
2992 
2993 	return context->priv->prefer_inline;
2994 }
2995 
2996 /**
2997  * camel_gpg_context_set_prefer_inline:
2998  * @context: gpg context
2999  * @prefer_inline: whether prefer inline sign/encrypt
3000  *
3001  * Sets the @prefer_inline flag on the gpg context.
3002  *
3003  * Since: 3.20
3004  **/
3005 void
camel_gpg_context_set_prefer_inline(CamelGpgContext * context,gboolean prefer_inline)3006 camel_gpg_context_set_prefer_inline (CamelGpgContext *context,
3007 				     gboolean prefer_inline)
3008 {
3009 	g_return_if_fail (CAMEL_IS_GPG_CONTEXT (context));
3010 
3011 	if (context->priv->prefer_inline == prefer_inline)
3012 		return;
3013 
3014 	context->priv->prefer_inline = prefer_inline;
3015 
3016 	g_object_notify (G_OBJECT (context), "prefer-inline");
3017 }
3018