goa: Add missing linker flag (for real).
[platform/upstream/evolution-data-server.git] / libedataserver / e-source-mail-signature.c
1 /*
2  * e-source-mail-signature.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
16  *
17  */
18
19 /**
20  * SECTION: e-source-mail-signature
21  * @include: libedataserver/libedataserver.h
22  * @short_description: #ESource extension for email signatures
23  *
24  * The #ESourceMailSignature extension refers to a personalized email
25  * signature.
26  *
27  * Access the extension as follows:
28  *
29  * |[
30  *   #include <libedataserver/libedataserver.h>
31  *
32  *   ESourceMailSignature *extension;
33  *
34  *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SIGNATURE);
35  * ]|
36  **/
37
38 #include "e-source-mail-signature.h"
39
40 #include <config.h>
41 #include <string.h>
42 #include <glib/gi18n-lib.h>
43
44 #include <libedataserver/e-data-server-util.h>
45
46 #define E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE(obj) \
47         (G_TYPE_INSTANCE_GET_PRIVATE \
48         ((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignaturePrivate))
49
50 typedef struct _AsyncContext AsyncContext;
51
52 struct _ESourceMailSignaturePrivate {
53         GMutex property_lock;
54         GFile *file;
55         gchar *mime_type;
56 };
57
58 struct _AsyncContext {
59         gchar *contents;
60         gchar *symlink_target;
61         gsize length;
62 };
63
64 enum {
65         PROP_0,
66         PROP_FILE,
67         PROP_MIME_TYPE
68 };
69
70 G_DEFINE_TYPE (
71         ESourceMailSignature,
72         e_source_mail_signature,
73         E_TYPE_SOURCE_EXTENSION)
74
75 static void
76 async_context_free (AsyncContext *async_context)
77 {
78         g_free (async_context->contents);
79         g_free (async_context->symlink_target);
80
81         g_slice_free (AsyncContext, async_context);
82 }
83
84 static void
85 source_mail_signature_set_property (GObject *object,
86                                     guint property_id,
87                                     const GValue *value,
88                                     GParamSpec *pspec)
89 {
90         switch (property_id) {
91                 case PROP_MIME_TYPE:
92                         e_source_mail_signature_set_mime_type (
93                                 E_SOURCE_MAIL_SIGNATURE (object),
94                                 g_value_get_string (value));
95                         return;
96         }
97
98         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
99 }
100
101 static void
102 source_mail_signature_get_property (GObject *object,
103                                     guint property_id,
104                                     GValue *value,
105                                     GParamSpec *pspec)
106 {
107         switch (property_id) {
108                 case PROP_FILE:
109                         g_value_set_object (
110                                 value,
111                                 e_source_mail_signature_get_file (
112                                 E_SOURCE_MAIL_SIGNATURE (object)));
113                         return;
114
115                 case PROP_MIME_TYPE:
116                         g_value_take_string (
117                                 value,
118                                 e_source_mail_signature_dup_mime_type (
119                                 E_SOURCE_MAIL_SIGNATURE (object)));
120                         return;
121         }
122
123         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
124 }
125
126 static void
127 source_mail_signature_dispose (GObject *object)
128 {
129         ESourceMailSignaturePrivate *priv;
130
131         priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
132
133         if (priv->file != NULL) {
134                 g_object_unref (priv->file);
135                 priv->file = NULL;
136         }
137
138         /* Chain up to parent's dispose() method. */
139         G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
140                 dispose (object);
141 }
142
143 static void
144 source_mail_signature_finalize (GObject *object)
145 {
146         ESourceMailSignaturePrivate *priv;
147
148         priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
149
150         g_mutex_clear (&priv->property_lock);
151
152         g_free (priv->mime_type);
153
154         /* Chain up to parent's finalize() method. */
155         G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
156                 finalize (object);
157 }
158
159 static void
160 source_mail_signature_constructed (GObject *object)
161 {
162         ESourceMailSignaturePrivate *priv;
163         ESourceExtension *extension;
164         ESource *source;
165         const gchar *config_dir;
166         const gchar *uid;
167         gchar *base_dir;
168         gchar *path;
169
170         priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
171
172         /* Chain up to parent's constructed() method. */
173         G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
174                 constructed (object);
175
176         extension = E_SOURCE_EXTENSION (object);
177         source = e_source_extension_ref_source (extension);
178         uid = e_source_get_uid (source);
179
180         config_dir = e_get_user_config_dir ();
181         base_dir = g_build_filename (config_dir, "signatures", NULL);
182         path = g_build_filename (base_dir, uid, NULL);
183         priv->file = g_file_new_for_path (path);
184         g_mkdir_with_parents (base_dir, 0700);
185         g_free (base_dir);
186         g_free (path);
187
188         g_object_unref (source);
189 }
190
191 static void
192 e_source_mail_signature_class_init (ESourceMailSignatureClass *class)
193 {
194         GObjectClass *object_class;
195         ESourceExtensionClass *extension_class;
196
197         g_type_class_add_private (
198                 class, sizeof (ESourceMailSignaturePrivate));
199
200         object_class = G_OBJECT_CLASS (class);
201         object_class->set_property = source_mail_signature_set_property;
202         object_class->get_property = source_mail_signature_get_property;
203         object_class->dispose = source_mail_signature_dispose;
204         object_class->finalize = source_mail_signature_finalize;
205         object_class->constructed = source_mail_signature_constructed;
206
207         extension_class = E_SOURCE_EXTENSION_CLASS (class);
208         extension_class->name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
209
210         g_object_class_install_property (
211                 object_class,
212                 PROP_FILE,
213                 g_param_spec_object (
214                         "file",
215                         "File",
216                         "File containing signature content",
217                         G_TYPE_FILE,
218                         G_PARAM_READABLE |
219                         G_PARAM_STATIC_STRINGS));
220
221         g_object_class_install_property (
222                 object_class,
223                 PROP_MIME_TYPE,
224                 g_param_spec_string (
225                         "mime-type",
226                         "MIME Type",
227                         "MIME type of the signature content",
228                         NULL,
229                         G_PARAM_READWRITE |
230                         G_PARAM_CONSTRUCT |
231                         G_PARAM_STATIC_STRINGS |
232                         E_SOURCE_PARAM_SETTING));
233 }
234
235 static void
236 e_source_mail_signature_init (ESourceMailSignature *extension)
237 {
238         extension->priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (extension);
239         g_mutex_init (&extension->priv->property_lock);
240 }
241
242 /**
243  * e_source_mail_signature_get_file:
244  * @extension: an #ESourceMailSignature
245  *
246  * Returns a #GFile instance pointing to the signature file for @extension.
247  * The signature file may be a regular file containing the static signature
248  * content, or it may be a symbolic link to an executable file that produces
249  * the signature content.
250  *
251  * e_source_mail_signature_load() uses this to load the signature content.
252  *
253  * Returns: (transfer none): a #GFile
254  *
255  * Since: 3.6
256  **/
257 GFile *
258 e_source_mail_signature_get_file (ESourceMailSignature *extension)
259 {
260         g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
261
262         return extension->priv->file;
263 }
264
265 /**
266  * e_source_mail_signature_get_mime_type:
267  * @extension: an #ESourceMailSignature
268  *
269  * Returns the MIME type of the signature content for @extension, or %NULL
270  * if it has not yet been determined.
271  *
272  * e_source_mail_signature_load() sets this automatically if the MIME type
273  * has not yet been determined.
274  *
275  * Returns: the MIME type of the signature content, or %NULL
276  *
277  * Since: 3.6
278  **/
279 const gchar *
280 e_source_mail_signature_get_mime_type (ESourceMailSignature *extension)
281 {
282         g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
283
284         return extension->priv->mime_type;
285 }
286
287 /**
288  * e_source_mail_signature_dup_mime_type:
289  * @extension: an #ESourceMailSignature
290  *
291  * Thread-safe variation of e_source_mail_signature_get_mime_type().
292  * Use this function when accessing @extension from multiple threads.
293  *
294  * The returned string should be freed with g_free() when no longer needed.
295  *
296  * Returns: a newly-allocated copy of #ESourceMailSignature:mime-type
297  *
298  * Since: 3.6
299  **/
300 gchar *
301 e_source_mail_signature_dup_mime_type (ESourceMailSignature *extension)
302 {
303         const gchar *protected;
304         gchar *duplicate;
305
306         g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
307
308         g_mutex_lock (&extension->priv->property_lock);
309
310         protected = e_source_mail_signature_get_mime_type (extension);
311         duplicate = g_strdup (protected);
312
313         g_mutex_unlock (&extension->priv->property_lock);
314
315         return duplicate;
316 }
317
318 /**
319  * e_source_mail_signature_set_mime_type:
320  * @extension: an #ESourceMailSignature
321  * @mime_type: (allow-none): a MIME type, or %NULL
322  *
323  * Sets the MIME type of the signature content for @extension.
324  *
325  * e_source_mail_signature_load() sets this automatically if the MIME type
326  * has not yet been determined.
327  *
328  * The internal copy of @mime_type is automatically stripped of leading
329  * and trailing whitespace.  If the resulting string is empty, %NULL is
330  * set instead.
331  *
332  * Since: 3.6
333  **/
334 void
335 e_source_mail_signature_set_mime_type (ESourceMailSignature *extension,
336                                        const gchar *mime_type)
337 {
338         g_return_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension));
339
340         g_mutex_lock (&extension->priv->property_lock);
341
342         if (g_strcmp0 (extension->priv->mime_type, mime_type) == 0) {
343                 g_mutex_unlock (&extension->priv->property_lock);
344                 return;
345         }
346
347         g_free (extension->priv->mime_type);
348         extension->priv->mime_type = e_util_strdup_strip (mime_type);
349
350         g_mutex_unlock (&extension->priv->property_lock);
351
352         g_object_notify (G_OBJECT (extension), "mime-type");
353 }
354
355 /********************** e_source_mail_signature_load() ***********************/
356
357 /* Helper for e_source_mail_signature_load() */
358 static void
359 source_mail_signature_load_thread (GSimpleAsyncResult *simple,
360                                    GObject *object,
361                                    GCancellable *cancellable)
362 {
363         AsyncContext *async_context;
364         GError *error = NULL;
365
366         async_context = g_simple_async_result_get_op_res_gpointer (simple);
367
368         e_source_mail_signature_load_sync (
369                 E_SOURCE (object),
370                 &async_context->contents,
371                 &async_context->length,
372                 cancellable, &error);
373
374         if (error != NULL)
375                 g_simple_async_result_take_error (simple, error);
376 }
377
378 /**
379  * e_source_mail_signature_load_sync:
380  * @source: an #ESource
381  * @contents: return location for the signature content
382  * @length: (allow-none): return location for the length of the signature
383  *          content, or %NULL if the length is not needed
384  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
385  * @error: return location for a #GError, or %NULL
386  *
387  * Loads a signature from the signature file for @source, which is
388  * given by e_source_mail_signature_get_file().  The signature contents
389  * are placed in @contents, and @length is set to the size of the @contents
390  * string.  The @contents string should be freed with g_free() when no
391  * longer needed.
392  *
393  * If the signature file is executable, it will be executed and its output
394  * captured as the email signature content.  If the signature file is not
395  * executable, the email signature content is read directly from the file.
396  *
397  * Returns: %TRUE on success, %FALSE on failure
398  *
399  * Since: 3.6
400  **/
401 gboolean
402 e_source_mail_signature_load_sync (ESource *source,
403                                    gchar **contents,
404                                    gsize *length,
405                                    GCancellable *cancellable,
406                                    GError **error)
407 {
408         ESourceMailSignature *extension;
409         GFileInfo *file_info;
410         GFile *file;
411         const gchar *content_type;
412         const gchar *extension_name;
413         gchar *local_contents = NULL;
414         gboolean can_execute;
415         gboolean success;
416         gchar *guessed_content_type;
417         gchar *command_line;
418         gchar *mime_type;
419         gchar *path;
420
421         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
422         g_return_val_if_fail (contents != NULL, FALSE);
423
424         extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
425         extension = e_source_get_extension (source, extension_name);
426         file = e_source_mail_signature_get_file (extension);
427
428         file_info = g_file_query_info (
429                 file,
430                 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
431                 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
432                 G_FILE_QUERY_INFO_NONE,
433                 cancellable, error);
434
435         if (file_info == NULL)
436                 return FALSE;
437
438         can_execute = g_file_info_get_attribute_boolean (
439                 file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
440
441         content_type = g_file_info_get_content_type (file_info);
442         mime_type = g_content_type_get_mime_type (content_type);
443
444         if (can_execute)
445                 goto execute;
446
447         /*** Load signature file contents ***/
448
449         success = g_file_load_contents (
450                 file, cancellable, &local_contents, NULL, NULL, error);
451
452         if (!success)
453                 goto exit;
454
455         g_return_val_if_fail (local_contents != NULL, FALSE);
456
457         /* Signatures are saved as UTF-8, but we still need to check that
458          * the signature is valid UTF-8 because the user may be opening a
459          * signature file this is in his/her locale character set.  If it
460          * is not UTF-8 then try converting from the current locale. */
461         if (!g_utf8_validate (local_contents, -1, NULL)) {
462                 gchar *utf8;
463
464                 utf8 = g_locale_to_utf8 (
465                         local_contents, -1, NULL, NULL, error);
466
467                 if (utf8 == NULL) {
468                         success = FALSE;
469                         goto exit;
470                 }
471
472                 g_free (local_contents);
473                 local_contents = utf8;
474         }
475
476         goto exit;
477
478 execute:
479
480         /*** Execute signature file and capture output ***/
481
482         path = g_file_get_path (file);
483
484         if (path == NULL) {
485                 g_set_error (
486                         error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
487                         _("Signature script must be a local file"));
488                 success = FALSE;
489                 goto exit;
490         }
491
492         /* Enclose the path in single-quotes for compatibility on Windows.
493          * (See g_spawn_command_line_sync() documentation for rationale.) */
494         command_line = g_strdup_printf ("'%s'", path);
495
496         success = g_spawn_command_line_sync (
497                 command_line, &local_contents, NULL, NULL, error);
498
499         g_free (command_line);
500         g_free (path);
501
502         /* Check if we failed to spawn the script. */
503         if (!success)
504                 goto exit;
505
506         /* Check if we were cancelled while the script was running. */
507         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
508                 success = FALSE;
509                 goto exit;
510         }
511
512         g_return_val_if_fail (local_contents != NULL, FALSE);
513
514         /* Signature scripts are supposed to generate UTF-8 content, but
515          * because users are known to never read the manual, we try to do
516          * our best if the content isn't valid UTF-8 by assuming that the
517          * content is in the user's locale character set. */
518         if (!g_utf8_validate (local_contents, -1, NULL)) {
519                 gchar *utf8;
520
521                 utf8 = g_locale_to_utf8 (
522                         local_contents, -1, NULL, NULL, error);
523
524                 if (utf8 == NULL) {
525                         success = FALSE;
526                         goto exit;
527                 }
528
529                 g_free (local_contents);
530                 local_contents = utf8;
531         }
532
533         g_free (mime_type);
534
535         /* Try and guess the content type of the script output
536          * so it can be applied correctly to the mail message. */
537         guessed_content_type = g_content_type_guess (
538                 NULL, (guchar *) local_contents,
539                 strlen (local_contents), NULL);
540         mime_type = g_content_type_get_mime_type (guessed_content_type);
541         g_free (guessed_content_type);
542
543 exit:
544         if (success) {
545                 const gchar *ext_mime_type;
546
547                 if (length != NULL)
548                         *length = strlen (local_contents);
549
550                 *contents = local_contents;
551                 local_contents = NULL;
552
553                 ext_mime_type =
554                         e_source_mail_signature_get_mime_type (extension);
555
556                 /* Don't override the MIME type if it's already set. */
557                 if (ext_mime_type == NULL || *ext_mime_type == '\0')
558                         e_source_mail_signature_set_mime_type (
559                                 extension, mime_type);
560         }
561
562         g_object_unref (file_info);
563         g_free (local_contents);
564         g_free (mime_type);
565
566         return success;
567 }
568
569 /**
570  * e_source_mail_signature_load:
571  * @source: an #ESource
572  * @io_priority: the I/O priority of the request
573  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
574  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
575  *            is satisfied
576  * @user_data: (closure): data to pass to the callback function
577  *
578  * Asynchronously loads a signature from the signature file for @source,
579  * which is given by e_source_mail_signature_get_file().
580  *
581  * If the signature file is executable, it will be executed and its output
582  * captured as the email signature content.  If the signature file is not
583  * executable, the email signature content is read directly from the file.
584  *
585  * When the operation is finished, @callback will be called.  You can
586  * then call e_source_mail_signature_load_finish() to get the result of
587  * the operation.
588  *
589  * Since: 3.6
590  **/
591 void
592 e_source_mail_signature_load (ESource *source,
593                               gint io_priority,
594                               GCancellable *cancellable,
595                               GAsyncReadyCallback callback,
596                               gpointer user_data)
597 {
598         GSimpleAsyncResult *simple;
599         AsyncContext *async_context;
600
601         g_return_if_fail (E_IS_SOURCE (source));
602
603         async_context = g_slice_new0 (AsyncContext);
604
605         simple = g_simple_async_result_new (
606                 G_OBJECT (source), callback, user_data,
607                 e_source_mail_signature_load);
608
609         g_simple_async_result_set_check_cancellable (simple, cancellable);
610
611         g_simple_async_result_set_op_res_gpointer (
612                 simple, async_context, (GDestroyNotify) async_context_free);
613
614         g_simple_async_result_run_in_thread (
615                 simple, source_mail_signature_load_thread,
616                 io_priority, cancellable);
617
618         g_object_unref (simple);
619 }
620
621 /**
622  * e_source_mail_signature_load_finish:
623  * @source: an #ESource
624  * @result: a #GAsyncResult
625  * @contents: return location for the signature content
626  * @length: (allow-none): return location for the length of the signature
627  *          content, or %NULL if the length is not needed
628  * @error: return location for a #GError, or %NULL
629  *
630  * Finishes an operation started with e_source_mail_signature_load().  The
631  * signature file contents are placed in @contents, and @length is set to
632  * the size of the @contents string.  The @contents string should be freed
633  * with g_free() when no longer needed.
634  *
635  * Returns: %TRUE on success, %FALSE on failure
636  *
637  * Since: 3.6
638  **/
639 gboolean
640 e_source_mail_signature_load_finish (ESource *source,
641                                      GAsyncResult *result,
642                                      gchar **contents,
643                                      gsize *length,
644                                      GError **error)
645 {
646         GSimpleAsyncResult *simple;
647         AsyncContext *async_context;
648
649         g_return_val_if_fail (
650                 g_simple_async_result_is_valid (
651                 result, G_OBJECT (source),
652                 e_source_mail_signature_load), FALSE);
653
654         g_return_val_if_fail (contents != NULL, FALSE);
655
656         simple = G_SIMPLE_ASYNC_RESULT (result);
657         async_context = g_simple_async_result_get_op_res_gpointer (simple);
658
659         if (g_simple_async_result_propagate_error (simple, error))
660                 return FALSE;
661
662         g_return_val_if_fail (async_context->contents != NULL, FALSE);
663
664         *contents = async_context->contents;
665         async_context->contents = NULL;
666
667         if (length != NULL)
668                 *length = async_context->length;
669
670         return TRUE;
671 }
672
673 /********************* e_source_mail_signature_replace() *********************/
674
675 /* Helper for e_source_mail_signature_replace() */
676 static void
677 source_mail_signature_replace_thread (GSimpleAsyncResult *simple,
678                                       GObject *object,
679                                       GCancellable *cancellable)
680 {
681         AsyncContext *async_context;
682         GError *error = NULL;
683
684         async_context = g_simple_async_result_get_op_res_gpointer (simple);
685
686         e_source_mail_signature_replace_sync (
687                 E_SOURCE (object), async_context->contents,
688                 async_context->length, cancellable, &error);
689
690         if (error != NULL)
691                 g_simple_async_result_take_error (simple, error);
692 }
693
694 /**
695  * e_source_mail_signature_replace_sync:
696  * @source: an #ESource
697  * @contents: the signature contents
698  * @length: the length of @contents in bytes
699  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
700  * @error: return location for a #GError, or %NULL
701  *
702  * Replaces the signature file for @source with the given @contents
703  * of @length bytes.  The signature file for @source is given by
704  * e_source_mail_signature_get_file().
705  *
706  * Returns: %TRUE on success, %FALSE on failure
707  *
708  * Since: 3.6
709  **/
710 gboolean
711 e_source_mail_signature_replace_sync (ESource *source,
712                                       const gchar *contents,
713                                       gsize length,
714                                       GCancellable *cancellable,
715                                       GError **error)
716 {
717         ESourceMailSignature *extension;
718         const gchar *extension_name;
719         GFile *file;
720
721         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
722         g_return_val_if_fail (contents != NULL, FALSE);
723
724         extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
725         extension = e_source_get_extension (source, extension_name);
726         file = e_source_mail_signature_get_file (extension);
727
728         return g_file_replace_contents (
729                 file, contents, length, NULL, FALSE,
730                 G_FILE_CREATE_REPLACE_DESTINATION,
731                 NULL, cancellable, error);
732 }
733
734 /**
735  * e_source_mail_signature_replace:
736  * @source: an #ESource
737  * @contents: the signature contents
738  * @length: the length of @contents in bytes
739  * @io_priority: the I/O priority of the request
740  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
741  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
742  *            is satisfied
743  * @user_data: (closure): data to pass to the callback function
744  *
745  * Asynchrously replaces the signature file for @source with the given
746  * @contents of @length bytes.  The signature file for @source is given
747  * by e_source_mail_signature_get_file().
748  *
749  * When the operation is finished, @callback will be called.  You can
750  * then call e_source_mail_signature_replace_finish() to get the result
751  * of the operation.
752  *
753  * Since: 3.6
754  **/
755 void
756 e_source_mail_signature_replace (ESource *source,
757                                  const gchar *contents,
758                                  gsize length,
759                                  gint io_priority,
760                                  GCancellable *cancellable,
761                                  GAsyncReadyCallback callback,
762                                  gpointer user_data)
763 {
764         GSimpleAsyncResult *simple;
765         AsyncContext *async_context;
766
767         g_return_if_fail (E_IS_SOURCE (source));
768         g_return_if_fail (contents != NULL);
769
770         async_context = g_slice_new0 (AsyncContext);
771         async_context->contents = g_strdup (contents);
772         async_context->length = length;
773
774         simple = g_simple_async_result_new (
775                 G_OBJECT (source), callback, user_data,
776                 e_source_mail_signature_replace);
777
778         g_simple_async_result_set_check_cancellable (simple, cancellable);
779
780         g_simple_async_result_set_op_res_gpointer (
781                 simple, async_context, (GDestroyNotify) async_context_free);
782
783         g_simple_async_result_run_in_thread (
784                 simple, source_mail_signature_replace_thread,
785                 io_priority, cancellable);
786
787         g_object_unref (simple);
788 }
789
790 /**
791  * e_source_mail_signature_replace_finish:
792  * @source: an #ESource
793  * @result: a #GAsyncResult
794  * @error: return location for a #GError, or %NULL
795  *
796  * Finishes an operation started with e_source_mail_signature_replace().
797  *
798  * Returns: %TRUE on success, %FALSE on failure
799  *
800  * Since: 3.6
801  **/
802 gboolean
803 e_source_mail_signature_replace_finish (ESource *source,
804                                         GAsyncResult *result,
805                                         GError **error)
806 {
807         GSimpleAsyncResult *simple;
808
809         g_return_val_if_fail (
810                 g_simple_async_result_is_valid (
811                 result, G_OBJECT (source),
812                 e_source_mail_signature_replace), FALSE);
813
814         simple = G_SIMPLE_ASYNC_RESULT (result);
815
816         /* Assume success unless a GError is set. */
817         return !g_simple_async_result_propagate_error (simple, error);
818 }
819
820 /********************* e_source_mail_signature_symlink() *********************/
821
822 /* Helper for e_source_mail_signature_symlink() */
823 static void
824 source_mail_signature_symlink_thread (GSimpleAsyncResult *simple,
825                                       GObject *object,
826                                       GCancellable *cancellable)
827 {
828         AsyncContext *async_context;
829         GError *error = NULL;
830
831         async_context = g_simple_async_result_get_op_res_gpointer (simple);
832
833         e_source_mail_signature_symlink_sync (
834                 E_SOURCE (object),
835                 async_context->symlink_target,
836                 cancellable, &error);
837
838         if (error != NULL)
839                 g_simple_async_result_take_error (simple, error);
840 }
841
842 /**
843  * e_source_mail_signature_symlink_sync:
844  * @source: an #ESource
845  * @symlink_target: executable filename to link to
846  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
847  * @error: return location for a #GError, or %NULL
848  *
849  * Replaces the signature file for @source with a symbolic link to
850  * @symlink_target, which should be an executable file that prints
851  * a mail signature to standard output.  The signature file for
852  * @source is given by e_source_mail_signature_get_file().
853  *
854  * Returns: %TRUE on success, %FALSE on failure
855  *
856  * Since: 3.6
857  **/
858 gboolean
859 e_source_mail_signature_symlink_sync (ESource *source,
860                                       const gchar *symlink_target,
861                                       GCancellable *cancellable,
862                                       GError **error)
863 {
864         ESourceMailSignature *extension;
865         const gchar *extension_name;
866         GFile *file;
867
868         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
869         g_return_val_if_fail (symlink_target != NULL, FALSE);
870
871         extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
872         extension = e_source_get_extension (source, extension_name);
873         file = e_source_mail_signature_get_file (extension);
874
875         /* The file may not exist, so we don't care if this fails.
876          * If it fails for a different reason than G_IO_ERROR_NOT_FOUND
877          * then the next step will probably also fail and we'll capture
878          * THAT error. */
879         g_file_delete (file, cancellable, NULL);
880
881         return g_file_make_symbolic_link (
882                 file, symlink_target, cancellable, error);
883 }
884
885 /**
886  * e_source_mail_signature_symlink:
887  * @source: an #ESource
888  * @symlink_target: executable filename to link to
889  * @io_priority: the I/O priority of the request
890  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
891  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
892  *            is satisfied
893  * @user_data: (closure): data to pass to the callback function
894  *
895  * Asynchronously replaces the signature file for @source with a symbolic
896  * link to @symlink_target, which should be an executable file that prints
897  * a mail signature to standard output.  The signature file for @source
898  * is given by e_source_mail_signature_get_file().
899  *
900  * When the operation is finished, @callback will be called.  You can
901  * then call e_source_mail_signature_symlink_finish() to get the result
902  * of the operation.
903  *
904  * Since: 3.6
905  **/
906 void
907 e_source_mail_signature_symlink (ESource *source,
908                                  const gchar *symlink_target,
909                                  gint io_priority,
910                                  GCancellable *cancellable,
911                                  GAsyncReadyCallback callback,
912                                  gpointer user_data)
913 {
914         GSimpleAsyncResult *simple;
915         AsyncContext *async_context;
916
917         g_return_if_fail (E_IS_SOURCE (source));
918         g_return_if_fail (symlink_target != NULL);
919
920         async_context = g_slice_new0 (AsyncContext);
921         async_context->symlink_target = g_strdup (symlink_target);
922
923         simple = g_simple_async_result_new (
924                 G_OBJECT (source), callback, user_data,
925                 e_source_mail_signature_symlink);
926
927         g_simple_async_result_set_check_cancellable (simple, cancellable);
928
929         g_simple_async_result_set_op_res_gpointer (
930                 simple, async_context, (GDestroyNotify) async_context_free);
931
932         g_simple_async_result_run_in_thread (
933                 simple, source_mail_signature_symlink_thread,
934                 io_priority, cancellable);
935
936         g_object_unref (simple);
937 }
938
939 /**
940  * e_source_mail_signature_symlink_finish:
941  * @source: an #ESource
942  * @result: a #GAsyncResult
943  * @error: return location for a #GError, or %NULL
944  *
945  * Finishes an operation started with e_source_mail_signature_symlink().
946  *
947  * Returns: %TRUE on success, %FALSE on failure
948  *
949  * Since: 3.6
950  **/
951 gboolean
952 e_source_mail_signature_symlink_finish (ESource *source,
953                                         GAsyncResult *result,
954                                         GError **error)
955 {
956         GSimpleAsyncResult *simple;
957
958         g_return_val_if_fail (
959                 g_simple_async_result_is_valid (
960                 result, G_OBJECT (source),
961                 e_source_mail_signature_symlink), FALSE);
962
963         simple = G_SIMPLE_ASYNC_RESULT (result);
964
965         /* Assume success unless a GError is set. */
966         return !g_simple_async_result_propagate_error (simple, error);
967 }
968