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