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 #include "evolution-data-server-config.h"
21 
22 #include <string.h>
23 
24 #include <glib/gi18n-lib.h>
25 
26 #include "camel-cipher-context.h"
27 #include "camel-debug.h"
28 #include "camel-session.h"
29 #include "camel-stream.h"
30 #include "camel-operation.h"
31 
32 #include "camel-mime-utils.h"
33 #include "camel-medium.h"
34 #include "camel-multipart.h"
35 #include "camel-multipart-encrypted.h"
36 #include "camel-multipart-signed.h"
37 #include "camel-mime-message.h"
38 #include "camel-mime-filter-canon.h"
39 #include "camel-stream-filter.h"
40 
41 #define CIPHER_LOCK(ctx) \
42 	g_mutex_lock (&((CamelCipherContext *) ctx)->priv->lock)
43 #define CIPHER_UNLOCK(ctx) \
44 	g_mutex_unlock (&((CamelCipherContext *) ctx)->priv->lock);
45 
46 #define d(x)
47 
48 typedef struct _AsyncContext AsyncContext;
49 
50 struct _CamelCipherContextPrivate {
51 	CamelSession *session;
52 	GMutex lock;
53 };
54 
55 struct _AsyncContext {
56 	/* arguments */
57 	CamelCipherHash hash;
58 	CamelMimePart *ipart;
59 	CamelMimePart *opart;
60 	CamelStream *stream;
61 	GPtrArray *strings;
62 	gchar *userid;
63 };
64 
65 enum {
66 	PROP_0,
67 	PROP_SESSION
68 };
69 
G_DEFINE_TYPE_WITH_PRIVATE(CamelCipherContext,camel_cipher_context,G_TYPE_OBJECT)70 G_DEFINE_TYPE_WITH_PRIVATE (CamelCipherContext, camel_cipher_context, G_TYPE_OBJECT)
71 
72 G_DEFINE_BOXED_TYPE (CamelCipherValidity,
73 		camel_cipher_validity,
74 		camel_cipher_validity_clone,
75 		camel_cipher_validity_free)
76 
77 static void
78 async_context_free (AsyncContext *async_context)
79 {
80 	if (async_context->ipart != NULL)
81 		g_object_unref (async_context->ipart);
82 
83 	if (async_context->opart != NULL)
84 		g_object_unref (async_context->opart);
85 
86 	if (async_context->stream != NULL)
87 		g_object_unref (async_context->stream);
88 
89 	if (async_context->strings != NULL) {
90 		g_ptr_array_foreach (
91 			async_context->strings, (GFunc) g_free, NULL);
92 		g_ptr_array_free (async_context->strings, TRUE);
93 	}
94 
95 	g_free (async_context->userid);
96 
97 	g_slice_free (AsyncContext, async_context);
98 }
99 
100 static void
cipher_context_set_session(CamelCipherContext * context,CamelSession * session)101 cipher_context_set_session (CamelCipherContext *context,
102                             CamelSession *session)
103 {
104 	g_return_if_fail (CAMEL_IS_SESSION (session));
105 	g_return_if_fail (context->priv->session == NULL);
106 
107 	context->priv->session = g_object_ref (session);
108 }
109 
110 static void
cipher_context_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)111 cipher_context_set_property (GObject *object,
112                              guint property_id,
113                              const GValue *value,
114                              GParamSpec *pspec)
115 {
116 	switch (property_id) {
117 		case PROP_SESSION:
118 			cipher_context_set_session (
119 				CAMEL_CIPHER_CONTEXT (object),
120 				g_value_get_object (value));
121 			return;
122 	}
123 
124 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
125 }
126 
127 static void
cipher_context_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)128 cipher_context_get_property (GObject *object,
129                              guint property_id,
130                              GValue *value,
131                              GParamSpec *pspec)
132 {
133 	switch (property_id) {
134 		case PROP_SESSION:
135 			g_value_set_object (
136 				value, camel_cipher_context_get_session (
137 				CAMEL_CIPHER_CONTEXT (object)));
138 			return;
139 	}
140 
141 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
142 }
143 
144 static void
cipher_context_dispose(GObject * object)145 cipher_context_dispose (GObject *object)
146 {
147 	CamelCipherContextPrivate *priv;
148 
149 	priv = CAMEL_CIPHER_CONTEXT (object)->priv;
150 	g_clear_object (&priv->session);
151 
152 	/* Chain up to parent's dispose () method. */
153 	G_OBJECT_CLASS (camel_cipher_context_parent_class)->dispose (object);
154 }
155 
156 static void
cipher_context_finalize(GObject * object)157 cipher_context_finalize (GObject *object)
158 {
159 	CamelCipherContextPrivate *priv;
160 
161 	priv = CAMEL_CIPHER_CONTEXT (object)->priv;
162 
163 	g_mutex_clear (&priv->lock);
164 
165 	/* Chain up to parent's finalize () method. */
166 	G_OBJECT_CLASS (camel_cipher_context_parent_class)->finalize (object);
167 }
168 
169 static const gchar *
cipher_context_hash_to_id(CamelCipherContext * context,CamelCipherHash hash)170 cipher_context_hash_to_id (CamelCipherContext *context,
171                            CamelCipherHash hash)
172 {
173 	return NULL;
174 }
175 
176 static CamelCipherHash
cipher_context_id_to_hash(CamelCipherContext * context,const gchar * id)177 cipher_context_id_to_hash (CamelCipherContext *context,
178                            const gchar *id)
179 {
180 	return CAMEL_CIPHER_HASH_DEFAULT;
181 }
182 
183 static gboolean
cipher_context_sign_sync(CamelCipherContext * ctx,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)184 cipher_context_sign_sync (CamelCipherContext *ctx,
185                           const gchar *userid,
186                           CamelCipherHash hash,
187                           CamelMimePart *ipart,
188                           CamelMimePart *opart,
189                           GCancellable *cancellable,
190                           GError **error)
191 {
192 	g_set_error (
193 		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
194 		_("Signing is not supported by this cipher"));
195 
196 	return FALSE;
197 }
198 
199 static CamelCipherValidity *
cipher_context_verify_sync(CamelCipherContext * context,CamelMimePart * sigpart,GCancellable * cancellable,GError ** error)200 cipher_context_verify_sync (CamelCipherContext *context,
201                             CamelMimePart *sigpart,
202                             GCancellable *cancellable,
203                             GError **error)
204 {
205 	g_set_error (
206 		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
207 		_("Verifying is not supported by this cipher"));
208 
209 	return NULL;
210 }
211 
212 static gboolean
cipher_context_encrypt_sync(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)213 cipher_context_encrypt_sync (CamelCipherContext *context,
214                              const gchar *userid,
215                              GPtrArray *recipients,
216                              CamelMimePart *ipart,
217                              CamelMimePart *opart,
218                              GCancellable *cancellable,
219                              GError **error)
220 {
221 	g_set_error (
222 		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
223 		_("Encryption is not supported by this cipher"));
224 
225 	return FALSE;
226 }
227 
228 static CamelCipherValidity *
cipher_context_decrypt_sync(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)229 cipher_context_decrypt_sync (CamelCipherContext *context,
230                              CamelMimePart *ipart,
231                              CamelMimePart *opart,
232                              GCancellable *cancellable,
233                              GError **error)
234 {
235 	g_set_error (
236 		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
237 		_("Decryption is not supported by this cipher"));
238 
239 	return NULL;
240 }
241 
242 static void
camel_cipher_context_class_init(CamelCipherContextClass * class)243 camel_cipher_context_class_init (CamelCipherContextClass *class)
244 {
245 	GObjectClass *object_class;
246 
247 	object_class = G_OBJECT_CLASS (class);
248 	object_class->set_property = cipher_context_set_property;
249 	object_class->get_property = cipher_context_get_property;
250 	object_class->dispose = cipher_context_dispose;
251 	object_class->finalize = cipher_context_finalize;
252 
253 	class->hash_to_id = cipher_context_hash_to_id;
254 	class->id_to_hash = cipher_context_id_to_hash;
255 
256 	class->sign_sync = cipher_context_sign_sync;
257 	class->verify_sync = cipher_context_verify_sync;
258 	class->encrypt_sync = cipher_context_encrypt_sync;
259 	class->decrypt_sync = cipher_context_decrypt_sync;
260 
261 	g_object_class_install_property (
262 		object_class,
263 		PROP_SESSION,
264 		g_param_spec_object (
265 			"session",
266 			"Session",
267 			NULL,
268 			CAMEL_TYPE_SESSION,
269 			G_PARAM_READWRITE |
270 			G_PARAM_CONSTRUCT_ONLY));
271 }
272 
273 static void
camel_cipher_context_init(CamelCipherContext * context)274 camel_cipher_context_init (CamelCipherContext *context)
275 {
276 	context->priv = camel_cipher_context_get_instance_private (context);
277 	g_mutex_init (&context->priv->lock);
278 }
279 
280 /* Helper for camel_cipher_context_sign() */
281 static void
cipher_context_sign_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)282 cipher_context_sign_thread (GTask *task,
283                             gpointer source_object,
284                             gpointer task_data,
285                             GCancellable *cancellable)
286 {
287 	gboolean success;
288 	AsyncContext *async_context;
289 	GError *local_error = NULL;
290 
291 	async_context = (AsyncContext *) task_data;
292 
293 	success = camel_cipher_context_sign_sync (
294 		CAMEL_CIPHER_CONTEXT (source_object),
295 		async_context->userid,
296 		async_context->hash,
297 		async_context->ipart,
298 		async_context->opart,
299 		cancellable, &local_error);
300 
301 	if (local_error != NULL) {
302 		g_task_return_error (task, local_error);
303 	} else {
304 		g_task_return_boolean (task, success);
305 	}
306 }
307 
308 /**
309  * camel_cipher_context_sign_sync:
310  * @context: a #CamelCipherContext
311  * @userid: a private key to use to sign the stream
312  * @hash: preferred Message-Integrity-Check hash algorithm
313  * @ipart: input #CamelMimePart
314  * @opart: output #CamelMimePart
315  * @cancellable: optional #GCancellable object, or %NULL
316  * @error: return location for a #GError, or %NULL
317  *
318  * Converts the (unsigned) part @ipart into a new self-contained MIME
319  * part @opart.  This may be a multipart/signed part, or a simple part
320  * for enveloped types.
321  *
322  * Returns: %TRUE on success, %FALSE on error
323  *
324  * Since: 3.0
325  **/
326 gboolean
camel_cipher_context_sign_sync(CamelCipherContext * context,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)327 camel_cipher_context_sign_sync (CamelCipherContext *context,
328                                 const gchar *userid,
329                                 CamelCipherHash hash,
330                                 CamelMimePart *ipart,
331                                 CamelMimePart *opart,
332                                 GCancellable *cancellable,
333                                 GError **error)
334 {
335 	CamelCipherContextClass *class;
336 	gboolean success;
337 
338 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), FALSE);
339 
340 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
341 	g_return_val_if_fail (class != NULL, FALSE);
342 	g_return_val_if_fail (class->sign_sync != NULL, FALSE);
343 
344 	CIPHER_LOCK (context);
345 
346 	/* Check for cancellation after locking. */
347 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
348 		CIPHER_UNLOCK (context);
349 		return FALSE;
350 	}
351 
352 	camel_operation_push_message (cancellable, _("Signing message"));
353 
354 	success = class->sign_sync (
355 		context, userid, hash, ipart, opart, cancellable, error);
356 	CAMEL_CHECK_GERROR (context, sign_sync, success, error);
357 
358 	camel_operation_pop_message (cancellable);
359 
360 	CIPHER_UNLOCK (context);
361 
362 	return success;
363 }
364 
365 /**
366  * camel_cipher_context_sign:
367  * @context: a #CamelCipherContext
368  * @userid: a private key to use to sign the stream
369  * @hash: preferred Message-Integrity-Check hash algorithm
370  * @ipart: input #CamelMimePart
371  * @opart: output #CamelMimePart
372  * @io_priority: the I/O priority of the request
373  * @cancellable: optional #GCancellable object, or %NULL
374  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
375  * @user_data: data to pass to the callback function
376  *
377  * Asynchronously converts the (unsigned) part @ipart into a new
378  * self-contained MIME part @opart.  This may be a multipart/signed part,
379  * or a simple part for enveloped types.
380  *
381  * When the operation is finished, @callback will be called.  You can then
382  * call camel_cipher_context_sign_finish() to get the result of the operation.
383  *
384  * Since: 3.0
385  **/
386 void
camel_cipher_context_sign(CamelCipherContext * context,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)387 camel_cipher_context_sign (CamelCipherContext *context,
388                            const gchar *userid,
389                            CamelCipherHash hash,
390                            CamelMimePart *ipart,
391                            CamelMimePart *opart,
392                            gint io_priority,
393                            GCancellable *cancellable,
394                            GAsyncReadyCallback callback,
395                            gpointer user_data)
396 {
397 	GTask *task;
398 	AsyncContext *async_context;
399 
400 	g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
401 	g_return_if_fail (CAMEL_IS_MIME_PART (ipart));
402 	g_return_if_fail (CAMEL_IS_MIME_PART (opart));
403 
404 	async_context = g_slice_new0 (AsyncContext);
405 	async_context->userid = g_strdup (userid);
406 	async_context->hash = hash;
407 	async_context->ipart = g_object_ref (ipart);
408 	async_context->opart = g_object_ref (opart);
409 
410 	task = g_task_new (context, cancellable, callback, user_data);
411 	g_task_set_source_tag (task, camel_cipher_context_sign);
412 	g_task_set_priority (task, io_priority);
413 
414 	g_task_set_task_data (
415 		task, async_context,
416 		(GDestroyNotify) async_context_free);
417 
418 	g_task_run_in_thread (task, cipher_context_sign_thread);
419 
420 	g_object_unref (task);
421 }
422 
423 /**
424  * camel_cipher_context_sign_finish:
425  * @context: a #CamelCipherContext
426  * @result: a #GAsyncResult
427  * @error: return location for a #GError, or %NULL
428  *
429  * Finishes the operation started with camel_cipher_context_sign().
430  *
431  * Returns: %TRUE on success, %FALSE on error
432  *
433  * Since: 3.0
434  **/
435 gboolean
camel_cipher_context_sign_finish(CamelCipherContext * context,GAsyncResult * result,GError ** error)436 camel_cipher_context_sign_finish (CamelCipherContext *context,
437                                   GAsyncResult *result,
438                                   GError **error)
439 {
440 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), FALSE);
441 	g_return_val_if_fail (g_task_is_valid (result, context), FALSE);
442 
443 	g_return_val_if_fail (
444 		g_async_result_is_tagged (
445 		result, camel_cipher_context_sign), FALSE);
446 
447 	return g_task_propagate_boolean (G_TASK (result), error);
448 }
449 
450 /**
451  * camel_cipher_context_verify_sync:
452  * @context: a #CamelCipherContext
453  * @ipart: the #CamelMimePart to verify
454  * @cancellable: optional #GCancellable object, or %NULL
455  * @error: return location for a #GError, or %NULL
456  *
457  * Verifies the signature.
458  *
459  * Returns: a #CamelCipherValidity structure containing information
460  * about the integrity of the input stream, or %NULL on failure to
461  * execute at all
462  **/
463 CamelCipherValidity *
camel_cipher_context_verify_sync(CamelCipherContext * context,CamelMimePart * ipart,GCancellable * cancellable,GError ** error)464 camel_cipher_context_verify_sync (CamelCipherContext *context,
465                                   CamelMimePart *ipart,
466                                   GCancellable *cancellable,
467                                   GError **error)
468 {
469 	CamelCipherContextClass *class;
470 	CamelCipherValidity *valid;
471 
472 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
473 	g_return_val_if_fail (CAMEL_IS_MIME_PART (ipart), NULL);
474 
475 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
476 	g_return_val_if_fail (class != NULL, NULL);
477 	g_return_val_if_fail (class->verify_sync != NULL, NULL);
478 
479 	CIPHER_LOCK (context);
480 
481 	/* Check for cancellation after locking. */
482 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
483 		CIPHER_UNLOCK (context);
484 		return NULL;
485 	}
486 
487 	valid = class->verify_sync (context, ipart, cancellable, error);
488 	CAMEL_CHECK_GERROR (context, verify_sync, valid != NULL, error);
489 
490 	CIPHER_UNLOCK (context);
491 
492 	return valid;
493 }
494 
495 /* Helper for camel_cipher_context_verify() */
496 static void
cipher_context_verify_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)497 cipher_context_verify_thread (GTask *task,
498                               gpointer source_object,
499                               gpointer task_data,
500                               GCancellable *cancellable)
501 {
502 	CamelCipherValidity *validity;
503 	AsyncContext *async_context;
504 	GError *local_error = NULL;
505 
506 	async_context = (AsyncContext *) task_data;
507 
508 	validity = camel_cipher_context_verify_sync (
509 		CAMEL_CIPHER_CONTEXT (source_object),
510 		async_context->ipart,
511 		cancellable, &local_error);
512 
513 	if (local_error != NULL) {
514 		g_warn_if_fail (validity == NULL);
515 		g_task_return_error (task, local_error);
516 	} else {
517 		g_task_return_pointer (
518 			task, validity,
519 			(GDestroyNotify) camel_cipher_validity_free);
520 	}
521 }
522 
523 /**
524  * camel_cipher_context_verify:
525  * @context: a #CamelCipherContext
526  * @ipart: the #CamelMimePart to verify
527  * @io_priority: the I/O priority of the request
528  * @cancellable: optional #GCancellable object, or %NULL
529  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
530  * @user_data: data to pass to the callback function
531  *
532  * Asynchronously verifies the signature.
533  *
534  * When the operation is finished, @callback will be called.  You can
535  * then call camel_cipher_context_verify_finish() to get the result of
536  * the operation.
537  *
538  * Since: 3.0
539  **/
540 void
camel_cipher_context_verify(CamelCipherContext * context,CamelMimePart * ipart,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)541 camel_cipher_context_verify (CamelCipherContext *context,
542                              CamelMimePart *ipart,
543                              gint io_priority,
544                              GCancellable *cancellable,
545                              GAsyncReadyCallback callback,
546                              gpointer user_data)
547 {
548 	GTask *task;
549 	AsyncContext *async_context;
550 
551 	g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
552 	g_return_if_fail (CAMEL_IS_MIME_PART (ipart));
553 
554 	async_context = g_slice_new0 (AsyncContext);
555 	async_context->ipart = g_object_ref (ipart);
556 
557 	task = g_task_new (context, cancellable, callback, user_data);
558 	g_task_set_source_tag (task, camel_cipher_context_verify);
559 	g_task_set_priority (task, io_priority);
560 
561 	g_task_set_task_data (
562 		task, async_context,
563 		(GDestroyNotify) async_context_free);
564 
565 	g_task_run_in_thread (task, cipher_context_verify_thread);
566 
567 	g_object_unref (task);
568 }
569 
570 /**
571  * camel_cipher_context_verify_finish:
572  * @context: a #CamelCipherContext
573  * @result: a #GAsyncResult
574  * @error: return location for a #GError, or %NULL
575  *
576  * Finishes the operation started with camel_cipher_context_verify().
577  *
578  * Returns: a #CamelCipherValidity structure containing information
579  * about the integrity of the input stream, or %NULL on failure to
580  * execute at all
581  *
582  * Since: 3.0
583  **/
584 CamelCipherValidity *
camel_cipher_context_verify_finish(CamelCipherContext * context,GAsyncResult * result,GError ** error)585 camel_cipher_context_verify_finish (CamelCipherContext *context,
586                                     GAsyncResult *result,
587                                     GError **error)
588 {
589 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
590 	g_return_val_if_fail (g_task_is_valid (result, context), NULL);
591 
592 	g_return_val_if_fail (
593 		g_async_result_is_tagged (
594 		result, camel_cipher_context_verify), NULL);
595 
596 	return g_task_propagate_pointer (G_TASK (result), error);
597 }
598 
599 /**
600  * camel_cipher_context_encrypt_sync:
601  * @context: a #CamelCipherContext
602  * @userid: key ID (or email address) to use when signing, or %NULL to not sign
603  * @recipients: (element-type utf8): an array of recipient key IDs and/or email addresses
604  * @ipart: clear-text #CamelMimePart
605  * @opart: cipher-text #CamelMimePart
606  * @cancellable: optional #GCancellable object, or %NULL
607  * @error: return location for a #GError, or %NULL
608  *
609  * Encrypts (and optionally signs) the clear-text @ipart and writes the
610  * resulting cipher-text to @opart.
611  *
612  * Returns: %TRUE on success, %FALSE on error
613  *
614  * Since: 3.0
615  **/
616 gboolean
camel_cipher_context_encrypt_sync(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)617 camel_cipher_context_encrypt_sync (CamelCipherContext *context,
618                                    const gchar *userid,
619                                    GPtrArray *recipients,
620                                    CamelMimePart *ipart,
621                                    CamelMimePart *opart,
622                                    GCancellable *cancellable,
623                                    GError **error)
624 {
625 	CamelCipherContextClass *class;
626 	gboolean success;
627 
628 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), FALSE);
629 	g_return_val_if_fail (CAMEL_IS_MIME_PART (ipart), FALSE);
630 	g_return_val_if_fail (CAMEL_IS_MIME_PART (opart), FALSE);
631 
632 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
633 	g_return_val_if_fail (class != NULL, FALSE);
634 	g_return_val_if_fail (class->encrypt_sync != NULL, FALSE);
635 
636 	CIPHER_LOCK (context);
637 
638 	/* Check for cancellation after locking. */
639 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
640 		CIPHER_UNLOCK (context);
641 		return FALSE;
642 	}
643 
644 	camel_operation_push_message (cancellable, _("Encrypting message"));
645 
646 	success = class->encrypt_sync (
647 		context, userid, recipients,
648 		ipart, opart, cancellable, error);
649 	CAMEL_CHECK_GERROR (context, encrypt_sync, success, error);
650 
651 	camel_operation_pop_message (cancellable);
652 
653 	CIPHER_UNLOCK (context);
654 
655 	return success;
656 }
657 
658 /* Helper for camel_cipher_context_encrypt_thread() */
659 static void
cipher_context_encrypt_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)660 cipher_context_encrypt_thread (GTask *task,
661                                gpointer source_object,
662                                gpointer task_data,
663                                GCancellable *cancellable)
664 {
665 	gboolean success;
666 	AsyncContext *async_context;
667 	GError *local_error = NULL;
668 
669 	async_context = (AsyncContext *) task_data;
670 
671 	success = camel_cipher_context_encrypt_sync (
672 		CAMEL_CIPHER_CONTEXT (source_object),
673 		async_context->userid,
674 		async_context->strings,
675 		async_context->ipart,
676 		async_context->opart,
677 		cancellable, &local_error);
678 
679 	if (local_error != NULL) {
680 		g_task_return_error (task, local_error);
681 	} else {
682 		g_task_return_boolean (task, success);
683 	}
684 }
685 
686 /**
687  * camel_cipher_context_encrypt:
688  * @context: a #CamelCipherContext
689  * @userid: key id (or email address) to use when signing, or %NULL to not sign
690  * @recipients: (element-type utf8): an array of recipient key IDs and/or email addresses
691  * @ipart: clear-text #CamelMimePart
692  * @opart: cipher-text #CamelMimePart
693  * @io_priority: the I/O priority of the request
694  * @cancellable: optional #GCancellable object, or %NULL
695  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
696  * @user_data: data to pass to the callback function
697  *
698  * Asynchronously encrypts (and optionally signs) the clear-text @ipart and
699  * writes the resulting cipher-text to @opart.
700  *
701  * When the operation is finished, @callback will be called.  You can
702  * then call camel_cipher_context_encrypt_finish() to get the result of
703  * the operation.
704  *
705  * Since: 3.0
706  **/
707 void
camel_cipher_context_encrypt(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)708 camel_cipher_context_encrypt (CamelCipherContext *context,
709                               const gchar *userid,
710                               GPtrArray *recipients,
711                               CamelMimePart *ipart,
712                               CamelMimePart *opart,
713                               gint io_priority,
714                               GCancellable *cancellable,
715                               GAsyncReadyCallback callback,
716                               gpointer user_data)
717 {
718 	GTask *task;
719 	AsyncContext *async_context;
720 	guint ii;
721 
722 	g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
723 	g_return_if_fail (CAMEL_IS_MIME_PART (ipart));
724 	g_return_if_fail (CAMEL_IS_MIME_PART (opart));
725 
726 	async_context = g_slice_new0 (AsyncContext);
727 	async_context->userid = g_strdup (userid);
728 	async_context->strings = g_ptr_array_new ();
729 	async_context->ipart = g_object_ref (ipart);
730 	async_context->opart = g_object_ref (opart);
731 
732 	for (ii = 0; ii < recipients->len; ii++)
733 		g_ptr_array_add (
734 			async_context->strings,
735 			g_strdup (recipients->pdata[ii]));
736 
737 	task = g_task_new (context, cancellable, callback, user_data);
738 	g_task_set_source_tag (task, camel_cipher_context_encrypt);
739 	g_task_set_priority (task, io_priority);
740 
741 	g_task_set_task_data (
742 		task, async_context,
743 		(GDestroyNotify) async_context_free);
744 
745 	g_task_run_in_thread (task, cipher_context_encrypt_thread);
746 
747 	g_object_unref (task);
748 }
749 
750 /**
751  * camel_cipher_context_encrypt_finish:
752  * @context: a #CamelCipherContext
753  * @result: a #GAsyncResult
754  * @error: return location for a #GError, or %NULL
755  *
756  * Finishes the operation started with camel_cipher_context_encrypt().
757  *
758  * Returns: %TRUE on success, %FALSE on error
759  *
760  * Since: 3.0
761  **/
762 gboolean
camel_cipher_context_encrypt_finish(CamelCipherContext * context,GAsyncResult * result,GError ** error)763 camel_cipher_context_encrypt_finish (CamelCipherContext *context,
764                                      GAsyncResult *result,
765                                      GError **error)
766 {
767 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), FALSE);
768 	g_return_val_if_fail (g_task_is_valid (result, context), FALSE);
769 
770 	g_return_val_if_fail (
771 		g_async_result_is_tagged (
772 		result, camel_cipher_context_encrypt), FALSE);
773 
774 	return g_task_propagate_boolean (G_TASK (result), error);
775 }
776 
777 /**
778  * camel_cipher_context_decrypt_sync:
779  * @context: a #CamelCipherContext
780  * @ipart: cipher-text #CamelMimePart
781  * @opart: clear-text #CamelMimePart
782  * @cancellable: optional #GCancellable object, or %NULL
783  * @error: return location for a #GError, or %NULL
784  *
785  * Decrypts @ipart into @opart.
786  *
787  * Returns: a validity/encryption status, or %NULL on error
788  *
789  * Since: 3.0
790  **/
791 CamelCipherValidity *
camel_cipher_context_decrypt_sync(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)792 camel_cipher_context_decrypt_sync (CamelCipherContext *context,
793                                    CamelMimePart *ipart,
794                                    CamelMimePart *opart,
795                                    GCancellable *cancellable,
796                                    GError **error)
797 {
798 	CamelCipherContextClass *class;
799 	CamelCipherValidity *valid;
800 
801 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
802 	g_return_val_if_fail (CAMEL_IS_MIME_PART (ipart), NULL);
803 	g_return_val_if_fail (CAMEL_IS_MIME_PART (opart), NULL);
804 
805 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
806 	g_return_val_if_fail (class != NULL, NULL);
807 	g_return_val_if_fail (class->decrypt_sync != NULL, NULL);
808 
809 	CIPHER_LOCK (context);
810 
811 	/* Check for cancellation after locking. */
812 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
813 		CIPHER_UNLOCK (context);
814 		return NULL;
815 	}
816 
817 	camel_operation_push_message (cancellable, _("Decrypting message"));
818 
819 	valid = class->decrypt_sync (
820 		context, ipart, opart, cancellable, error);
821 	CAMEL_CHECK_GERROR (context, decrypt_sync, valid != NULL, error);
822 
823 	camel_operation_pop_message (cancellable);
824 
825 	CIPHER_UNLOCK (context);
826 
827 	return valid;
828 }
829 
830 /* Helper for camel_cipher_context_decrypt() */
831 static void
cipher_context_decrypt_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)832 cipher_context_decrypt_thread (GTask *task,
833                                gpointer source_object,
834                                gpointer task_data,
835                                GCancellable *cancellable)
836 {
837 	CamelCipherValidity *validity;
838 	AsyncContext *async_context;
839 	GError *local_error = NULL;
840 
841 	async_context = (AsyncContext *) task_data;
842 
843 	validity = camel_cipher_context_decrypt_sync (
844 		CAMEL_CIPHER_CONTEXT (source_object),
845 		async_context->ipart,
846 		async_context->opart,
847 		cancellable, &local_error);
848 
849 	if (local_error != NULL) {
850 		g_warn_if_fail (validity == NULL);
851 		g_task_return_error (task, local_error);
852 	} else {
853 		g_task_return_pointer (
854 			task, validity,
855 			(GDestroyNotify) camel_cipher_validity_free);
856 	}
857 }
858 
859 /**
860  * camel_cipher_context_decrypt:
861  * @context: a #CamelCipherContext
862  * @ipart: cipher-text #CamelMimePart
863  * @opart: clear-text #CamelMimePart
864  * @io_priority: the I/O priority of the request
865  * @cancellable: optional #GCancellable object, or %NULL
866  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
867  * @user_data: data to pass to the callback function
868  *
869  * Asynchronously decrypts @ipart into @opart.
870  *
871  * When the operation is finished, @callback will be called.  You can
872  * then call camel_cipher_context_decrypt_finish() to get the result of
873  * the operation.
874  *
875  * Since: 3.0
876  **/
877 void
camel_cipher_context_decrypt(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)878 camel_cipher_context_decrypt (CamelCipherContext *context,
879                               CamelMimePart *ipart,
880                               CamelMimePart *opart,
881                               gint io_priority,
882                               GCancellable *cancellable,
883                               GAsyncReadyCallback callback,
884                               gpointer user_data)
885 {
886 	GTask *task;
887 	AsyncContext *async_context;
888 
889 	g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
890 	g_return_if_fail (CAMEL_IS_MIME_PART (ipart));
891 	g_return_if_fail (CAMEL_IS_MIME_PART (opart));
892 
893 	async_context = g_slice_new0 (AsyncContext);
894 	async_context->ipart = g_object_ref (ipart);
895 	async_context->opart = g_object_ref (opart);
896 
897 	task = g_task_new (context, cancellable, callback, user_data);
898 	g_task_set_source_tag (task, camel_cipher_context_decrypt);
899 	g_task_set_priority (task, io_priority);
900 
901 	g_task_set_task_data (
902 		task, async_context,
903 		(GDestroyNotify) async_context_free);
904 
905 	g_task_run_in_thread (task, cipher_context_decrypt_thread);
906 
907 	g_object_unref (task);
908 }
909 
910 /**
911  * camel_cipher_context_decrypt_finish:
912  * @context: a #CamelCipherContext
913  * @result: a #GAsyncResult
914  * @error: return location for a #GError, or %NULL
915  *
916  * Finishes the operation started with camel_cipher_context_decrypt().
917  *
918  * Returns: a validity/encryption status, or %NULL on error
919  *
920  * Since: 3.0
921  **/
922 CamelCipherValidity *
camel_cipher_context_decrypt_finish(CamelCipherContext * context,GAsyncResult * result,GError ** error)923 camel_cipher_context_decrypt_finish (CamelCipherContext *context,
924                                      GAsyncResult *result,
925                                      GError **error)
926 {
927 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
928 	g_return_val_if_fail (g_task_is_valid (result, context), NULL);
929 
930 	g_return_val_if_fail (
931 		g_async_result_is_tagged (
932 		result, camel_cipher_context_decrypt), NULL);
933 
934 	return g_task_propagate_pointer (G_TASK (result), error);
935 }
936 
937 /* a couple of util functions */
938 CamelCipherHash
camel_cipher_context_id_to_hash(CamelCipherContext * context,const gchar * id)939 camel_cipher_context_id_to_hash (CamelCipherContext *context,
940                                  const gchar *id)
941 {
942 	CamelCipherContextClass *class;
943 
944 	g_return_val_if_fail (
945 		CAMEL_IS_CIPHER_CONTEXT (context),
946 		CAMEL_CIPHER_HASH_DEFAULT);
947 
948 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
949 	g_return_val_if_fail (class != NULL, CAMEL_CIPHER_HASH_DEFAULT);
950 	g_return_val_if_fail (class->id_to_hash != NULL, CAMEL_CIPHER_HASH_DEFAULT);
951 
952 	return class->id_to_hash (context, id);
953 }
954 
955 const gchar *
camel_cipher_context_hash_to_id(CamelCipherContext * context,CamelCipherHash hash)956 camel_cipher_context_hash_to_id (CamelCipherContext *context,
957                                  CamelCipherHash hash)
958 {
959 	CamelCipherContextClass *class;
960 
961 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
962 
963 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
964 	g_return_val_if_fail (class != NULL, NULL);
965 	g_return_val_if_fail (class->hash_to_id != NULL, NULL);
966 
967 	return class->hash_to_id (context, hash);
968 }
969 
970 /* Cipher Validity stuff */
971 static void
ccv_certinfo_property_free(gpointer ptr)972 ccv_certinfo_property_free (gpointer ptr)
973 {
974 	CamelCipherCertInfoProperty *property = ptr;
975 
976 	if (property) {
977 		g_free (property->name);
978 		if (property->value_free)
979 			property->value_free (property->value);
980 		g_free (property);
981 	}
982 }
983 
984 static void
ccv_certinfo_free(CamelCipherCertInfo * info)985 ccv_certinfo_free (CamelCipherCertInfo *info)
986 {
987 	g_return_if_fail (info != NULL);
988 
989 	g_free (info->name);
990 	g_free (info->email);
991 
992 	if (info->cert_data && info->cert_data_free)
993 		info->cert_data_free (info->cert_data);
994 
995 	g_slist_free_full (info->properties, ccv_certinfo_property_free);
996 	g_free (info);
997 }
998 
999 CamelCipherValidity *
camel_cipher_validity_new(void)1000 camel_cipher_validity_new (void)
1001 {
1002 	CamelCipherValidity *validity;
1003 
1004 	validity = g_malloc (sizeof (*validity));
1005 	camel_cipher_validity_init (validity);
1006 
1007 	return validity;
1008 }
1009 
1010 void
camel_cipher_validity_init(CamelCipherValidity * validity)1011 camel_cipher_validity_init (CamelCipherValidity *validity)
1012 {
1013 	g_return_if_fail (validity != NULL);
1014 
1015 	memset (validity, 0, sizeof (*validity));
1016 	g_queue_init (&validity->children);
1017 	g_queue_init (&validity->sign.signers);
1018 	g_queue_init (&validity->encrypt.encrypters);
1019 }
1020 
1021 gboolean
camel_cipher_validity_get_valid(CamelCipherValidity * validity)1022 camel_cipher_validity_get_valid (CamelCipherValidity *validity)
1023 {
1024 	return validity != NULL
1025 		&& validity->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1026 }
1027 
1028 void
camel_cipher_validity_set_valid(CamelCipherValidity * validity,gboolean valid)1029 camel_cipher_validity_set_valid (CamelCipherValidity *validity,
1030                                  gboolean valid)
1031 {
1032 	g_return_if_fail (validity != NULL);
1033 
1034 	validity->sign.status = valid ? CAMEL_CIPHER_VALIDITY_SIGN_GOOD : CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1035 }
1036 
1037 gchar *
camel_cipher_validity_get_description(CamelCipherValidity * validity)1038 camel_cipher_validity_get_description (CamelCipherValidity *validity)
1039 {
1040 	g_return_val_if_fail (validity != NULL, NULL);
1041 
1042 	return validity->sign.description;
1043 }
1044 
1045 void
camel_cipher_validity_set_description(CamelCipherValidity * validity,const gchar * description)1046 camel_cipher_validity_set_description (CamelCipherValidity *validity,
1047                                        const gchar *description)
1048 {
1049 	g_return_if_fail (validity != NULL);
1050 
1051 	g_free (validity->sign.description);
1052 	validity->sign.description = g_strdup (description);
1053 }
1054 
1055 void
camel_cipher_validity_clear(CamelCipherValidity * validity)1056 camel_cipher_validity_clear (CamelCipherValidity *validity)
1057 {
1058 	g_return_if_fail (validity != NULL);
1059 
1060 	/* TODO: this doesn't free children/clear key lists */
1061 	g_free (validity->sign.description);
1062 	g_free (validity->encrypt.description);
1063 	camel_cipher_validity_init (validity);
1064 }
1065 
1066 CamelCipherValidity *
camel_cipher_validity_clone(CamelCipherValidity * vin)1067 camel_cipher_validity_clone (CamelCipherValidity *vin)
1068 {
1069 	CamelCipherValidity *vo;
1070 	GList *head, *link;
1071 
1072 	g_return_val_if_fail (vin != NULL, NULL);
1073 
1074 	vo = camel_cipher_validity_new ();
1075 	vo->sign.status = vin->sign.status;
1076 	vo->sign.description = g_strdup (vin->sign.description);
1077 	vo->encrypt.status = vin->encrypt.status;
1078 	vo->encrypt.description = g_strdup (vin->encrypt.description);
1079 
1080 	head = g_queue_peek_head_link (&vin->sign.signers);
1081 	for (link = head; link != NULL; link = g_list_next (link)) {
1082 		CamelCipherCertInfo *info = link->data;
1083 		gint index;
1084 
1085 		if (info->cert_data && info->cert_data_clone && info->cert_data_free)
1086 			index = camel_cipher_validity_add_certinfo_ex (
1087 				vo, CAMEL_CIPHER_VALIDITY_SIGN,
1088 				info->name,
1089 				info->email,
1090 				info->cert_data_clone (info->cert_data),
1091 				info->cert_data_free,
1092 				info->cert_data_clone);
1093 		else
1094 			index = camel_cipher_validity_add_certinfo (
1095 				vo, CAMEL_CIPHER_VALIDITY_SIGN,
1096 				info->name,
1097 				info->email);
1098 
1099 		if (index != -1 && info->properties) {
1100 			GSList *link;
1101 
1102 			for (link = info->properties; link; link = g_slist_next (link)) {
1103 				CamelCipherCertInfoProperty *property = link->data;
1104 				gpointer value;
1105 
1106 				if (!property)
1107 					continue;
1108 
1109 				value = property->value_clone ? property->value_clone (property->value) : property->value;
1110 				camel_cipher_validity_set_certinfo_property (vo, CAMEL_CIPHER_VALIDITY_SIGN, index,
1111 					property->name, value, property->value_free, property->value_clone);
1112 			}
1113 		}
1114 	}
1115 
1116 	head = g_queue_peek_head_link (&vin->encrypt.encrypters);
1117 	for (link = head; link != NULL; link = g_list_next (link)) {
1118 		CamelCipherCertInfo *info = link->data;
1119 		gint index;
1120 
1121 		if (info->cert_data && info->cert_data_clone && info->cert_data_free)
1122 			index = camel_cipher_validity_add_certinfo_ex (
1123 				vo, CAMEL_CIPHER_VALIDITY_SIGN,
1124 				info->name,
1125 				info->email,
1126 				info->cert_data_clone (info->cert_data),
1127 				info->cert_data_free,
1128 				info->cert_data_clone);
1129 		else
1130 			index = camel_cipher_validity_add_certinfo (
1131 				vo, CAMEL_CIPHER_VALIDITY_ENCRYPT,
1132 				info->name,
1133 				info->email);
1134 
1135 		if (index != -1 && info->properties) {
1136 			GSList *link;
1137 
1138 			for (link = info->properties; link; link = g_slist_next (link)) {
1139 				CamelCipherCertInfoProperty *property = link->data;
1140 				gpointer value;
1141 
1142 				if (!property)
1143 					continue;
1144 
1145 				value = property->value_clone ? property->value_clone (property->value) : property->value;
1146 				camel_cipher_validity_set_certinfo_property (vo, CAMEL_CIPHER_VALIDITY_ENCRYPT, index,
1147 					property->name, value, property->value_free, property->value_clone);
1148 			}
1149 		}
1150 	}
1151 
1152 	return vo;
1153 }
1154 
1155 /**
1156  * camel_cipher_validity_add_certinfo:
1157  * @vin: a #CamelCipherValidity
1158  * @mode: a #CamelCipherValidityMode, where to add the additional certificate information
1159  * @name: a name to add
1160  * @email: an e-mail address to add
1161  *
1162  * Add a cert info to the signer or encrypter info.
1163  *
1164  * Returns: Index of the added certinfo; -1 on error
1165  **/
1166 gint
camel_cipher_validity_add_certinfo(CamelCipherValidity * vin,CamelCipherValidityMode mode,const gchar * name,const gchar * email)1167 camel_cipher_validity_add_certinfo (CamelCipherValidity *vin,
1168                                     CamelCipherValidityMode mode,
1169                                     const gchar *name,
1170                                     const gchar *email)
1171 {
1172 	return camel_cipher_validity_add_certinfo_ex (vin, mode, name, email, NULL, NULL, NULL);
1173 }
1174 
1175 /**
1176  * camel_cipher_validity_add_certinfo_ex:
1177  * @vin: a #CamelCipherValidity
1178  * @mode: a #CamelCipherValidityMode, where to add the additional certificate information
1179  * @name: a name to add
1180  * @email: an e-mail address to add
1181  * @cert_data: (nullable) (destroy cert_data_free): a certificate data, or %NULL
1182  * @cert_data_free: (nullable): a destroy function for @cert_data; required, when @cert_data is not %NULL
1183  * @cert_data_clone: (nullable) (scope call): a copy function for @cert_data, to copy the data; required, when @cert_data is not %NULL
1184  *
1185  * Add a cert info to the signer or encrypter info, with extended data set.
1186  *
1187  * Returns: Index of the added certinfo; -1 on error
1188  *
1189  * Since: 2.30
1190  **/
1191 gint
camel_cipher_validity_add_certinfo_ex(CamelCipherValidity * vin,CamelCipherValidityMode mode,const gchar * name,const gchar * email,gpointer cert_data,GDestroyNotify cert_data_free,CamelCipherCloneFunc cert_data_clone)1192 camel_cipher_validity_add_certinfo_ex (CamelCipherValidity *vin,
1193                                        CamelCipherValidityMode mode,
1194                                        const gchar *name,
1195                                        const gchar *email,
1196                                        gpointer cert_data,
1197                                        GDestroyNotify cert_data_free,
1198                                        CamelCipherCloneFunc cert_data_clone)
1199 {
1200 	CamelCipherCertInfo *info;
1201 	GQueue *queue;
1202 
1203 	g_return_val_if_fail (vin != NULL, -1);
1204 	if (cert_data) {
1205 		g_return_val_if_fail (cert_data_free != NULL, -1);
1206 		g_return_val_if_fail (cert_data_clone != NULL, -1);
1207 	}
1208 
1209 	info = g_malloc0 (sizeof (*info));
1210 	info->name = g_strdup (name);
1211 	info->email = g_strdup (email);
1212 	if (cert_data) {
1213 		info->cert_data = cert_data;
1214 		info->cert_data_free = cert_data_free;
1215 		info->cert_data_clone = cert_data_clone;
1216 	}
1217 
1218 	if (mode == CAMEL_CIPHER_VALIDITY_SIGN)
1219 		queue = &vin->sign.signers;
1220 	else
1221 		queue = &vin->encrypt.encrypters;
1222 
1223 	g_queue_push_tail (queue, info);
1224 
1225 	return (gint) (g_queue_get_length (queue) - 1);
1226 }
1227 
1228 /**
1229  * camel_cipher_validity_get_certinfo_property:
1230  * @vin: a #CamelCipherValidity
1231  * @mode: which cipher validity part to use
1232  * @info_index: a 0-based index of the requested #CamelCipherCertInfo
1233  * @name: a property name
1234  *
1235  * Gets a named property @name value for the given @info_index of the @mode validity part.
1236  *
1237  * Returns: (transfer none) (nullable): Value of a named property of a #CamelCipherCertInfo, or %NULL when no such
1238  *    property exists. The returned value is owned by the associated #CamelCipherCertInfo
1239  *    and is valid until the cert info is freed.
1240  *
1241  * Since: 3.22
1242  **/
1243 gpointer
camel_cipher_validity_get_certinfo_property(CamelCipherValidity * vin,CamelCipherValidityMode mode,gint info_index,const gchar * name)1244 camel_cipher_validity_get_certinfo_property (CamelCipherValidity *vin,
1245 					     CamelCipherValidityMode mode,
1246 					     gint info_index,
1247 					     const gchar *name)
1248 {
1249 	GQueue *queue;
1250 	CamelCipherCertInfo *cert_info;
1251 
1252 	g_return_val_if_fail (vin != NULL, NULL);
1253 	g_return_val_if_fail (name != NULL, NULL);
1254 
1255 	if (mode == CAMEL_CIPHER_VALIDITY_SIGN)
1256 		queue = &vin->sign.signers;
1257 	else
1258 		queue = &vin->encrypt.encrypters;
1259 
1260 	g_return_val_if_fail (info_index >= 0 && info_index < g_queue_get_length (queue), NULL);
1261 
1262 	cert_info = g_queue_peek_nth (queue, info_index);
1263 
1264 	g_return_val_if_fail (cert_info != NULL, NULL);
1265 
1266 	return camel_cipher_certinfo_get_property (cert_info, name);
1267 }
1268 
1269 /**
1270  * camel_cipher_validity_set_certinfo_property:
1271  * @vin: a #CamelCipherValidity
1272  * @mode: which cipher validity part to use
1273  * @info_index: a 0-based index of the requested #CamelCipherCertInfo
1274  * @name: a property name
1275  * @value: (nullable) (destroy value_free): a property value, or %NULL
1276  * @value_free: (nullable): a free function for the @value
1277  * @value_clone: (nullable) (scope call): a clone function for the @value
1278  *
1279  * Sets a named property @name value @value for the given @info_index
1280  * of the @mode validity part. If the @value is %NULL, then the property
1281  * is removed. With a non-%NULL @value also @value_free and @value_clone
1282  * functions cannot be %NULL.
1283  *
1284  * Since: 3.22
1285  **/
1286 void
camel_cipher_validity_set_certinfo_property(CamelCipherValidity * vin,CamelCipherValidityMode mode,gint info_index,const gchar * name,gpointer value,GDestroyNotify value_free,CamelCipherCloneFunc value_clone)1287 camel_cipher_validity_set_certinfo_property (CamelCipherValidity *vin,
1288 					     CamelCipherValidityMode mode,
1289 					     gint info_index,
1290 					     const gchar *name,
1291 					     gpointer value,
1292 					     GDestroyNotify value_free,
1293 					     CamelCipherCloneFunc value_clone)
1294 {
1295 	GQueue *queue;
1296 	CamelCipherCertInfo *cert_info;
1297 
1298 	g_return_if_fail (vin != NULL);
1299 	g_return_if_fail (name != NULL);
1300 
1301 	if (mode == CAMEL_CIPHER_VALIDITY_SIGN)
1302 		queue = &vin->sign.signers;
1303 	else
1304 		queue = &vin->encrypt.encrypters;
1305 
1306 	g_return_if_fail (info_index >= 0 && info_index < g_queue_get_length (queue));
1307 
1308 	cert_info = g_queue_peek_nth (queue, info_index);
1309 
1310 	g_return_if_fail (cert_info != NULL);
1311 
1312 	camel_cipher_certinfo_set_property (cert_info, name, value, value_free, value_clone);
1313 }
1314 
1315 /**
1316  * camel_cipher_validity_envelope:
1317  * @parent: a #CamelCipherValidity
1318  * @valid: a new #CamelCipherValidity to conglomerate the @parent with
1319  *
1320  * Calculate a conglomerate validity based on wrapping one secure part inside
1321  * another one.
1322  **/
1323 void
camel_cipher_validity_envelope(CamelCipherValidity * parent,CamelCipherValidity * valid)1324 camel_cipher_validity_envelope (CamelCipherValidity *parent,
1325                                 CamelCipherValidity *valid)
1326 {
1327 
1328 	g_return_if_fail (parent != NULL);
1329 	g_return_if_fail (valid != NULL);
1330 
1331 	if (parent->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE
1332 	    && parent->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
1333 	    && valid->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE
1334 	    && valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
1335 		GList *head, *link;
1336 
1337 		/* case 1: only signed inside only encrypted -> merge both */
1338 		parent->encrypt.status = valid->encrypt.status;
1339 		parent->encrypt.description = g_strdup (valid->encrypt.description);
1340 
1341 		head = g_queue_peek_head_link (&valid->encrypt.encrypters);
1342 		for (link = head; link != NULL; link = g_list_next (link)) {
1343 			CamelCipherCertInfo *info = link->data;
1344 			camel_cipher_validity_add_certinfo (
1345 				parent, CAMEL_CIPHER_VALIDITY_ENCRYPT,
1346 				info->name, info->email);
1347 		}
1348 	} else if (parent->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE
1349 		   && parent->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
1350 		   && valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE
1351 		   && valid->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
1352 		GList *head, *link;
1353 
1354 		/* case 2: only encrypted inside only signed */
1355 		parent->sign.status = valid->sign.status;
1356 		parent->sign.description = g_strdup (valid->sign.description);
1357 
1358 		head = g_queue_peek_head_link (&valid->sign.signers);
1359 		for (link = head; link != NULL; link = g_list_next (link)) {
1360 			CamelCipherCertInfo *info = link->data;
1361 			camel_cipher_validity_add_certinfo (
1362 				parent, CAMEL_CIPHER_VALIDITY_SIGN,
1363 				info->name, info->email);
1364 		}
1365 	}
1366 	/* Otherwise, I dunno - what do you do? */
1367 }
1368 
1369 void
camel_cipher_validity_free(CamelCipherValidity * validity)1370 camel_cipher_validity_free (CamelCipherValidity *validity)
1371 {
1372 	CamelCipherValidity *child;
1373 	CamelCipherCertInfo *info;
1374 	GQueue *queue;
1375 
1376 	if (validity == NULL)
1377 		return;
1378 
1379 	queue = &validity->children;
1380 	while ((child = g_queue_pop_head (queue)) != NULL)
1381 		camel_cipher_validity_free (child);
1382 
1383 	queue = &validity->sign.signers;
1384 	while ((info = g_queue_pop_head (queue)) != NULL)
1385 		ccv_certinfo_free (info);
1386 
1387 	queue = &validity->encrypt.encrypters;
1388 	while ((info = g_queue_pop_head (queue)) != NULL)
1389 		ccv_certinfo_free (info);
1390 
1391 	camel_cipher_validity_clear (validity);
1392 	g_free (validity);
1393 }
1394 
1395 /* ********************************************************************** */
1396 
1397 /**
1398  * camel_cipher_certinfo_get_property:
1399  * @cert_info: a #CamelCipherCertInfo
1400  * @name: a property name
1401  *
1402  * Gets a named property @name value for the given @cert_info.
1403  *
1404  * Returns: (transfer none) (nullable): Value of a named property of the @cert_info,
1405  *    or %NULL when no such property exists. The returned value is owned by
1406  *    the @cert_info and is valid until the @cert_info is freed.
1407  *
1408  * Since: 3.22
1409  **/
1410 gpointer
camel_cipher_certinfo_get_property(CamelCipherCertInfo * cert_info,const gchar * name)1411 camel_cipher_certinfo_get_property (CamelCipherCertInfo *cert_info,
1412 				    const gchar *name)
1413 {
1414 	GSList *link;
1415 
1416 	g_return_val_if_fail (cert_info != NULL, NULL);
1417 	g_return_val_if_fail (name != NULL, NULL);
1418 
1419 	for (link = cert_info->properties; link; link = g_slist_next (link)) {
1420 		CamelCipherCertInfoProperty *property = link->data;
1421 
1422 		if (property && g_ascii_strcasecmp (property->name, name) == 0)
1423 			return property->value;
1424 	}
1425 
1426 	return NULL;
1427 }
1428 
1429 /**
1430  * camel_cipher_certinfo_set_property:
1431  * @cert_info: a #CamelCipherCertInfo
1432  * @name: a property name
1433  * @value: (nullable) (destroy value_free): a property value, or %NULL
1434  * @value_free: (nullable): a free function for the @value
1435  * @value_clone: (nullable) (scope call): a clone function for the @value
1436  *
1437  * Sets a named property @name value @value for the given @cert_info.
1438  * If the @value is %NULL, then the property is removed. With a non-%NULL
1439  * @value also @value_free and @value_clone functions cannot be %NULL.
1440  *
1441  * Since: 3.22
1442  **/
1443 void
camel_cipher_certinfo_set_property(CamelCipherCertInfo * cert_info,const gchar * name,gpointer value,GDestroyNotify value_free,CamelCipherCloneFunc value_clone)1444 camel_cipher_certinfo_set_property (CamelCipherCertInfo *cert_info,
1445 				    const gchar *name,
1446 				    gpointer value,
1447 				    GDestroyNotify value_free,
1448 				    CamelCipherCloneFunc value_clone)
1449 {
1450 	CamelCipherCertInfoProperty *property;
1451 	GSList *link;
1452 
1453 	g_return_if_fail (cert_info != NULL);
1454 	g_return_if_fail (name != NULL);
1455 
1456 	if (value) {
1457 		g_return_if_fail (value_free != NULL);
1458 		g_return_if_fail (value_clone != NULL);
1459 	}
1460 
1461 	for (link = cert_info->properties; link; link = g_slist_next (link)) {
1462 		property = link->data;
1463 
1464 		if (property && g_ascii_strcasecmp (property->name, name) == 0) {
1465 			if (value && property->value != value) {
1466 				/* Replace current value with the new value. */
1467 				property->value_free (property->value);
1468 
1469 				property->value = value;
1470 				property->value_free = value_free;
1471 				property->value_clone = value_clone;
1472 			} else if (!value) {
1473 				cert_info->properties = g_slist_remove (cert_info->properties, property);
1474 				ccv_certinfo_property_free (property);
1475 			}
1476 			break;
1477 		}
1478 	}
1479 
1480 	if (value && !link) {
1481 		property = g_new0 (CamelCipherCertInfoProperty, 1);
1482 		property->name = g_strdup (name);
1483 		property->value = value;
1484 		property->value_free = value_free;
1485 		property->value_clone = value_clone;
1486 
1487 		cert_info->properties = g_slist_prepend (cert_info->properties, property);
1488 	}
1489 }
1490 
1491 /* ********************************************************************** */
1492 
1493 /**
1494  * camel_cipher_context_new:
1495  * @session: a #CamelSession
1496  *
1497  * This creates a new CamelCipherContext object which is used to sign,
1498  * verify, encrypt and decrypt streams.
1499  *
1500  * Returns: the new CamelCipherContext
1501  **/
1502 CamelCipherContext *
camel_cipher_context_new(CamelSession * session)1503 camel_cipher_context_new (CamelSession *session)
1504 {
1505 	g_return_val_if_fail (session != NULL, NULL);
1506 
1507 	return g_object_new (
1508 		CAMEL_TYPE_CIPHER_CONTEXT,
1509 		"session", session, NULL);
1510 }
1511 
1512 /**
1513  * camel_cipher_context_get_session:
1514  * @context: a #CamelCipherContext
1515  *
1516  * Returns: (transfer none):
1517  *
1518  * Since: 2.32
1519  **/
1520 CamelSession *
camel_cipher_context_get_session(CamelCipherContext * context)1521 camel_cipher_context_get_session (CamelCipherContext *context)
1522 {
1523 	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
1524 
1525 	return context->priv->session;
1526 }
1527 
1528 /* See rfc3156, section 2 and others */
1529 /* We do this simply: Anything not base64 must be qp
1530  * This is so that we can safely translate any occurance of "From "
1531  * into the quoted-printable escaped version safely. */
1532 static void
cc_prepare_sign(CamelMimePart * part)1533 cc_prepare_sign (CamelMimePart *part)
1534 {
1535 	CamelDataWrapper *dw;
1536 	CamelTransferEncoding encoding;
1537 	gint parts, i;
1538 
1539 	dw = camel_medium_get_content ((CamelMedium *) part);
1540 	if (!dw)
1541 		return;
1542 
1543 	/* should not change encoding for these, they have the right encoding set already */
1544 	if (CAMEL_IS_MULTIPART_SIGNED (dw) || CAMEL_IS_MULTIPART_ENCRYPTED (dw))
1545 		return;
1546 
1547 	if (CAMEL_IS_MULTIPART (dw)) {
1548 		parts = camel_multipart_get_number ((CamelMultipart *) dw);
1549 		for (i = 0; i < parts; i++)
1550 			cc_prepare_sign (camel_multipart_get_part ((CamelMultipart *) dw, i));
1551 	} else if (CAMEL_IS_MIME_MESSAGE (dw)) {
1552 		cc_prepare_sign ((CamelMimePart *) dw);
1553 	} else {
1554 		encoding = camel_mime_part_get_encoding (part);
1555 
1556 		if (encoding != CAMEL_TRANSFER_ENCODING_BASE64
1557 		    && encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
1558 			camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
1559 		}
1560 	}
1561 }
1562 
1563 /**
1564  * camel_cipher_canonical_to_stream:
1565  * @part: Part to write.
1566  * @flags: flags for the canonicalisation filter (CamelMimeFilterCanon)
1567  * @ostream: stream to write canonicalised output to.
1568  * @cancellable: optional #GCancellable object, or %NULL
1569  * @error: return location for a #GError, or %NULL
1570  *
1571  * Writes a part to a stream in a canonicalised format, suitable for signing/encrypting.
1572  *
1573  * The transfer encoding paramaters for the part may be changed by this function.
1574  *
1575  * Returns: -1 on error;
1576  **/
1577 gint
camel_cipher_canonical_to_stream(CamelMimePart * part,guint32 flags,CamelStream * ostream,GCancellable * cancellable,GError ** error)1578 camel_cipher_canonical_to_stream (CamelMimePart *part,
1579                                   guint32 flags,
1580                                   CamelStream *ostream,
1581                                   GCancellable *cancellable,
1582                                   GError **error)
1583 {
1584 	CamelStream *filter;
1585 	CamelMimeFilter *canon;
1586 	gint res = -1;
1587 
1588 	g_return_val_if_fail (CAMEL_IS_MIME_PART (part), -1);
1589 	g_return_val_if_fail (CAMEL_IS_STREAM (ostream), -1);
1590 
1591 	if (flags & (CAMEL_MIME_FILTER_CANON_FROM | CAMEL_MIME_FILTER_CANON_STRIP))
1592 		cc_prepare_sign (part);
1593 
1594 	filter = camel_stream_filter_new (ostream);
1595 	canon = camel_mime_filter_canon_new (flags);
1596 	camel_stream_filter_add (CAMEL_STREAM_FILTER (filter), canon);
1597 	g_object_unref (canon);
1598 
1599 	if (camel_data_wrapper_write_to_stream_sync (
1600 		CAMEL_DATA_WRAPPER (part), filter, cancellable, error) != -1
1601 	    && camel_stream_flush (filter, cancellable, error) != -1)
1602 		res = 0;
1603 
1604 	g_object_unref (filter);
1605 
1606 	/* Reset stream position to beginning. */
1607 	if (G_IS_SEEKABLE (ostream))
1608 		g_seekable_seek (
1609 			G_SEEKABLE (ostream), 0,
1610 			G_SEEK_SET, NULL, NULL);
1611 
1612 	return res;
1613 }
1614 
1615 /**
1616  * camel_cipher_can_load_photos:
1617  *
1618  * Returns: Whether ciphers can load photos, as being setup by the user.
1619  *
1620  * Since: 3.22
1621  **/
1622 gboolean
camel_cipher_can_load_photos(void)1623 camel_cipher_can_load_photos (void)
1624 {
1625 	GSettings *settings;
1626 	gboolean load_photos;
1627 
1628 	settings = g_settings_new ("org.gnome.evolution-data-server");
1629 	load_photos = g_settings_get_boolean (settings, "camel-cipher-load-photos");
1630 	g_clear_object (&settings);
1631 
1632 	return load_photos;
1633 }
1634