1 /*
2 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3 * Copyright (C) 2008 - Diego Escalante Urrelo
4 * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors:
19 * Diego Escalante Urrelo <diegoe@gnome.org>
20 * Bharath Acharya <abharath@novell.com>
21 */
22
23 #include "evolution-config.h"
24
25 #include <string.h>
26
27 #include "e-util/e-util.h"
28 #include "em-format/e-mail-stripsig-filter.h"
29
30 #include "em-composer-utils.h"
31 #include "em-utils.h"
32
33 #include "e-mail-templates.h"
34
35 static void
replace_in_string(GString * text,const gchar * find,const gchar * replacement)36 replace_in_string (GString *text,
37 const gchar *find,
38 const gchar *replacement)
39 {
40 const gchar *p, *next;
41 GString *str;
42 gint find_len;
43
44 g_return_if_fail (text != NULL);
45 g_return_if_fail (find != NULL);
46
47 find_len = strlen (find);
48 str = g_string_new ("");
49 p = text->str;
50 while (next = e_util_strstrcase (p, find), next) {
51 if (p < next)
52 g_string_append_len (str, p, next - p);
53 if (replacement && *replacement)
54 g_string_append (str, replacement);
55 p = next + find_len;
56 }
57
58 /* Avoid unnecessary allocation when the 'text' doesn't contain the variable */
59 if (p != text->str) {
60 g_string_append (str, p);
61 g_string_assign (text, str->str);
62 }
63
64 g_string_free (str, TRUE);
65 }
66
67 /* Replaces $ORIG[variable] in given template by given replacement from the original message */
68 static void
replace_template_variable(GString * text,const gchar * variable,const gchar * replacement)69 replace_template_variable (GString *text,
70 const gchar *variable,
71 const gchar *replacement)
72 {
73 gchar *find;
74
75 g_return_if_fail (text != NULL);
76 g_return_if_fail (variable != NULL);
77 g_return_if_fail (*variable);
78
79 find = g_strconcat ("$ORIG[", variable, "]", NULL);
80
81 replace_in_string (text, find, replacement);
82
83 g_free (find);
84 }
85
86 static void
replace_user_variables(GString * text,CamelMimeMessage * source_message)87 replace_user_variables (GString *text,
88 CamelMimeMessage *source_message)
89 {
90 CamelInternetAddress *to;
91 const gchar *name, *addr;
92 GSettings *settings;
93 gchar **strv;
94 gint ii;
95
96 g_return_if_fail (text);
97 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (source_message));
98
99 settings = e_util_ref_settings ("org.gnome.evolution.plugin.templates");
100 strv = g_settings_get_strv (settings, "template-placeholders");
101 g_object_unref (settings);
102
103 for (ii = 0; strv && strv[ii]; ii++) {
104 gchar *equal_sign, *find, *var_name = strv[ii];
105 const gchar *var_value;
106
107 equal_sign = strchr (var_name, '=');
108 if (!equal_sign)
109 continue;
110
111 *equal_sign = '\0';
112 var_value = equal_sign + 1;
113
114 find = g_strconcat ("$", var_name, NULL);
115 replace_in_string (text, find, var_value);
116 g_free (find);
117
118 *equal_sign = '=';
119 }
120
121 g_strfreev (strv);
122
123 to = camel_mime_message_get_recipients (source_message, CAMEL_RECIPIENT_TYPE_TO);
124 if (to && camel_internet_address_get (to, 0, &name, &addr)) {
125 replace_in_string (text, "$sender_name", name);
126 replace_in_string (text, "$sender_email", addr);
127 }
128 }
129
130 static void
replace_email_addresses(GString * template,CamelInternetAddress * internet_address,const gchar * variable)131 replace_email_addresses (GString *template,
132 CamelInternetAddress *internet_address,
133 const gchar *variable)
134 {
135 gint address_index = 0;
136 GString *emails = g_string_new ("");
137 const gchar *address_name, *address_email;
138
139 g_return_if_fail (template);
140 g_return_if_fail (internet_address);
141 g_return_if_fail (variable);
142
143 while (camel_internet_address_get (internet_address, address_index, &address_name, &address_email)) {
144 gchar *address = camel_internet_address_format_address (address_name, address_email);
145
146 if (address_index > 0)
147 g_string_append_printf (emails, ", %s", address);
148 else
149 g_string_append_printf (emails, "%s", address);
150
151 address_index++;
152 g_free (address);
153 }
154 replace_template_variable (template, variable, emails->str);
155 g_string_free (emails, TRUE);
156 }
157
158 static ESource *
ref_identity_source_from_message_and_folder(CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid)159 ref_identity_source_from_message_and_folder (CamelMimeMessage *message,
160 CamelFolder *folder,
161 const gchar *message_uid)
162 {
163 EShell *shell;
164
165 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
166
167 shell = e_shell_get_default ();
168 if (!shell)
169 return NULL;
170
171 return em_composer_utils_guess_identity_source (shell, message, folder, message_uid, NULL, NULL);
172 }
173
174 static CamelMimePart *
fill_template(CamelMimeMessage * message,CamelFolder * source_folder,const gchar * source_message_uid,CamelFolder * templates_folder,CamelMimePart * template)175 fill_template (CamelMimeMessage *message,
176 CamelFolder *source_folder,
177 const gchar *source_message_uid,
178 CamelFolder *templates_folder,
179 CamelMimePart *template)
180 {
181 const CamelNameValueArray *headers;
182 CamelContentType *ct;
183 CamelStream *stream;
184 CamelMimePart *return_part;
185 CamelMimePart *message_part = NULL;
186 CamelDataWrapper *dw;
187 CamelInternetAddress *internet_address;
188 GString *template_body;
189 GByteArray *byte_array;
190 gint i;
191 guint jj, len;
192 gboolean message_html, template_html;
193 gboolean has_quoted_body;
194
195 ct = camel_mime_part_get_content_type (template);
196 template_html = ct && camel_content_type_is (ct, "text", "html");
197
198 message_html = FALSE;
199 /* When template is html, then prefer HTML part of the original message. Otherwise go for plaintext */
200 dw = camel_medium_get_content (CAMEL_MEDIUM (message));
201 if (CAMEL_IS_MULTIPART (dw)) {
202 CamelMultipart *multipart = CAMEL_MULTIPART (dw);
203
204 for (i = 0; i < camel_multipart_get_number (multipart); i++) {
205 CamelMimePart *part = camel_multipart_get_part (multipart, i);
206 CamelContentType *ct = camel_mime_part_get_content_type (part);
207
208 if (!ct)
209 continue;
210
211 if (camel_content_type_is (ct, "text", "html") && template_html) {
212 message_part = camel_multipart_get_part (multipart, i);
213 message_html = TRUE;
214 break;
215 } else if (camel_content_type_is (ct, "text", "plain") && message_html == FALSE) {
216 message_part = camel_multipart_get_part (multipart, i);
217 }
218 }
219 } else {
220 CamelContentType *mpct;
221
222 message_part = CAMEL_MIME_PART (message);
223
224 mpct = camel_mime_part_get_content_type (message_part);
225 message_html = mpct && camel_content_type_is (mpct, "text", "html");
226 }
227
228 /* Get content of the template */
229 stream = camel_stream_mem_new ();
230 camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (template)), stream, NULL, NULL);
231 camel_stream_flush (stream, NULL, NULL);
232 byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
233 template_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len);
234 g_object_unref (stream);
235
236 /* Replace all $ORIG[header_name] by respective values */
237 headers = camel_medium_get_headers (CAMEL_MEDIUM (message));
238 len = camel_name_value_array_get_length (headers);
239 for (jj = 0; jj < len; jj++) {
240 const gchar *header_name = NULL, *header_value = NULL;
241
242 if (!camel_name_value_array_get (headers, jj, &header_name, &header_value) ||
243 !header_name)
244 continue;
245
246 if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 &&
247 g_ascii_strcasecmp (header_name, "to") != 0 &&
248 g_ascii_strcasecmp (header_name, "cc") != 0 &&
249 g_ascii_strcasecmp (header_name, "bcc") != 0 &&
250 g_ascii_strcasecmp (header_name, "from") != 0 &&
251 g_ascii_strcasecmp (header_name, "subject") != 0)
252 replace_template_variable (template_body, header_name, header_value);
253 }
254
255 /* Now manually replace the *subject* header. The header->value for subject header could be
256 * base64 encoded, so let camel_mime_message to decode it for us if needed */
257 replace_template_variable (template_body, "subject", camel_mime_message_get_subject (message));
258
259 /* Replace TO and FROM modifiers. */
260 internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
261 replace_email_addresses (template_body, internet_address, "to");
262
263 internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
264 replace_email_addresses (template_body, internet_address, "cc");
265
266 internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
267 replace_email_addresses (template_body, internet_address, "bcc");
268
269 internet_address = camel_mime_message_get_from (message);
270 replace_email_addresses (template_body, internet_address, "from");
271
272 has_quoted_body = e_util_strstrcase (template_body->str, "$ORIG[quoted-body]") != NULL;
273 if (has_quoted_body && !template_html) {
274 gchar *html;
275
276 template_html = TRUE;
277
278 html = camel_text_to_html (
279 template_body->str,
280 CAMEL_MIME_FILTER_TOHTML_DIV |
281 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
282 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
283 CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
284 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
285 g_string_assign (template_body, html);
286 g_free (html);
287
288 g_string_append (template_body, "<!-- disable-format-prompt -->");
289 }
290
291 g_string_append (template_body, "<span id=\"x-evo-template-fix-paragraphs\"></span>");
292
293 /* Now extract body of the original message and replace the $ORIG[body] modifier in template */
294 if (message_part && (has_quoted_body || e_util_strstrcase (template_body->str, "$ORIG[body]"))) {
295 GString *message_body, *message_body_nosig = NULL;
296 CamelStream *mem_stream;
297
298 stream = camel_stream_mem_new ();
299 mem_stream = stream;
300
301 ct = camel_mime_part_get_content_type (message_part);
302 if (ct) {
303 const gchar *charset = camel_content_type_param (ct, "charset");
304 if (charset && *charset) {
305 CamelMimeFilter *filter = camel_mime_filter_charset_new (charset, "UTF-8");
306 if (filter) {
307 CamelStream *filtered = camel_stream_filter_new (stream);
308
309 if (filtered) {
310 camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter);
311 g_object_unref (stream);
312 stream = filtered;
313 }
314
315 g_object_unref (filter);
316 }
317 }
318 }
319
320 camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (message_part)), stream, NULL, NULL);
321 camel_stream_flush (stream, NULL, NULL);
322 byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream));
323 message_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len);
324 g_object_unref (stream);
325
326 if (has_quoted_body) {
327 CamelMimeFilter *filter;
328
329 stream = camel_stream_mem_new ();
330 mem_stream = stream;
331
332 ct = camel_mime_part_get_content_type (message_part);
333 if (ct) {
334 const gchar *charset = camel_content_type_param (ct, "charset");
335 if (charset && *charset) {
336 CamelMimeFilter *filter = camel_mime_filter_charset_new (charset, "UTF-8");
337 if (filter) {
338 CamelStream *filtered = camel_stream_filter_new (stream);
339
340 if (filtered) {
341 camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter);
342 g_object_unref (stream);
343 stream = filtered;
344 }
345
346 g_object_unref (filter);
347 }
348 }
349 }
350
351 filter = e_mail_stripsig_filter_new (!message_html);
352 if (filter) {
353 CamelStream *filtered = camel_stream_filter_new (stream);
354
355 if (filtered) {
356 camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter);
357 g_object_unref (stream);
358 stream = filtered;
359 }
360
361 g_object_unref (filter);
362 }
363
364 camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (message_part)), stream, NULL, NULL);
365 camel_stream_flush (stream, NULL, NULL);
366 byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream));
367 message_body_nosig = g_string_new_len ((gchar *) byte_array->data, byte_array->len);
368 g_object_unref (stream);
369 }
370
371 if (template_html && !message_html) {
372 gchar *html = camel_text_to_html (
373 message_body->str,
374 CAMEL_MIME_FILTER_TOHTML_PRE |
375 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
376 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
377 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
378 replace_template_variable (template_body, "body", html);
379 g_free (html);
380 } else if (!template_html && message_html) {
381 gchar *html;
382
383 g_string_prepend (message_body, "<pre>");
384 g_string_append (message_body, "</pre>");
385
386 template_html = TRUE;
387
388 html = camel_text_to_html (
389 template_body->str,
390 CAMEL_MIME_FILTER_TOHTML_DIV |
391 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
392 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
393 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
394 g_string_assign (template_body, html);
395 g_free (html);
396
397 replace_template_variable (template_body, "body", message_body->str);
398 } else { /* Other cases should not occur. And even if they happen to do, there's nothing we can really do about it */
399 replace_template_variable (template_body, "body", message_body->str);
400 }
401
402 if (has_quoted_body) {
403 if (!message_html) {
404 gchar *html = camel_text_to_html (
405 message_body_nosig->str,
406 CAMEL_MIME_FILTER_TOHTML_PRE |
407 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
408 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
409 CAMEL_MIME_FILTER_TOHTML_QUOTE_CITATION |
410 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
411 g_string_assign (message_body_nosig, html);
412 g_free (html);
413 }
414
415 g_string_prepend (message_body_nosig, "<blockquote type=\"cite\">");
416 g_string_append (message_body_nosig, "</blockquote>");
417
418 replace_template_variable (template_body, "quoted-body", message_body_nosig->str);
419 }
420
421 if (message_body_nosig)
422 g_string_free (message_body_nosig, TRUE);
423
424 g_string_free (message_body, TRUE);
425 } else {
426 replace_template_variable (template_body, "body", "");
427 replace_template_variable (template_body, "quoted-body", "");
428 }
429
430 if (e_util_strstrcase (template_body->str, "$ORIG[reply-credits]")) {
431 ESource *identity_source;
432 gchar *reply_credits;
433
434 identity_source = ref_identity_source_from_message_and_folder (message, source_folder, source_message_uid);
435
436 reply_credits = em_composer_utils_get_reply_credits (identity_source, message);
437
438 if (reply_credits && template_html) {
439 gchar *html = camel_text_to_html (
440 reply_credits,
441 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
442 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
443 g_free (reply_credits);
444 reply_credits = html;
445 }
446
447 replace_template_variable (template_body, "reply-credits", reply_credits ? reply_credits : "");
448
449 g_clear_object (&identity_source);
450 g_free (reply_credits);
451 }
452
453 replace_user_variables (template_body, message);
454
455 return_part = camel_mime_part_new ();
456
457 if (template_html)
458 camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/html");
459 else
460 camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/plain");
461
462 g_string_free (template_body, TRUE);
463
464 return return_part;
465 }
466
467 static CamelMimePart *
find_template_part_in_multipart(CamelMultipart * multipart,CamelMultipart * new_multipart)468 find_template_part_in_multipart (CamelMultipart *multipart,
469 CamelMultipart *new_multipart)
470 {
471 CamelMimePart *template_part = NULL;
472 gint ii;
473
474 for (ii = 0; ii < camel_multipart_get_number (multipart); ii++) {
475 CamelMimePart *part = camel_multipart_get_part (multipart, ii);
476 CamelContentType *ct = camel_mime_part_get_content_type (part);
477
478 if (!template_part && ct && camel_content_type_is (ct, "multipart", "*")) {
479 CamelDataWrapper *dw;
480
481 dw = camel_medium_get_content (CAMEL_MEDIUM (part));
482 template_part = (dw && CAMEL_IS_MULTIPART (dw)) ?
483 find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart) : NULL;
484
485 if (!template_part) {
486 /* Copy any other parts (attachments...) to the output message */
487 camel_mime_part_set_disposition (part, "attachment");
488 camel_multipart_add_part (new_multipart, part);
489 }
490 } else if (ct && camel_content_type_is (ct, "text", "html")) {
491 template_part = part;
492 } else if (ct && camel_content_type_is (ct, "text", "plain") && !template_part) {
493 template_part = part;
494 } else {
495 /* Copy any other parts (attachments...) to the output message */
496 camel_mime_part_set_disposition (part, "attachment");
497 camel_multipart_add_part (new_multipart, part);
498 }
499 }
500
501 return template_part;
502 }
503
504 CamelMimeMessage *
e_mail_templates_apply_sync(CamelMimeMessage * source_message,CamelFolder * source_folder,const gchar * source_message_uid,CamelFolder * templates_folder,const gchar * templates_message_uid,GCancellable * cancellable,GError ** error)505 e_mail_templates_apply_sync (CamelMimeMessage *source_message,
506 CamelFolder *source_folder,
507 const gchar *source_message_uid,
508 CamelFolder *templates_folder,
509 const gchar *templates_message_uid,
510 GCancellable *cancellable,
511 GError **error)
512 {
513 CamelMimeMessage *template_message, *result_message = NULL;
514 CamelMultipart *new_multipart;
515 CamelDataWrapper *dw;
516 const CamelNameValueArray *headers;
517 CamelMimePart *template_part = NULL;
518 gchar *references, *message_id;
519 guint ii, len;
520
521 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (source_message), NULL);
522 g_return_val_if_fail (CAMEL_IS_FOLDER (templates_folder), NULL);
523 g_return_val_if_fail (templates_message_uid != NULL, NULL);
524
525 if (g_cancellable_set_error_if_cancelled (cancellable, error))
526 return NULL;
527
528 template_message = camel_folder_get_message_sync (templates_folder, templates_message_uid, cancellable, error);
529 if (!template_message)
530 return NULL;
531
532 result_message = camel_mime_message_new ();
533 new_multipart = camel_multipart_new ();
534 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (new_multipart), "multipart/alternative");
535 camel_multipart_set_boundary (new_multipart, NULL);
536
537 dw = camel_medium_get_content (CAMEL_MEDIUM (template_message));
538 /* If template is a multipart, then try to use HTML. When no HTML part is available, use plaintext. Every other
539 * add as an attachment */
540 if (CAMEL_IS_MULTIPART (dw)) {
541 template_part = find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart);
542 } else {
543 CamelContentType *ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (template_message));
544
545 if (ct && (camel_content_type_is (ct, "text", "html") ||
546 camel_content_type_is (ct, "text", "plain"))) {
547 template_part = CAMEL_MIME_PART (template_message);
548 }
549 }
550
551 g_warn_if_fail (template_part != NULL);
552
553 if (template_part) {
554 CamelMimePart *out_part = NULL;
555
556 /* Here replace all the modifiers in template body by values
557 from message and return the newly created part */
558 out_part = fill_template (source_message, source_folder, source_message_uid, templates_folder, template_part);
559
560 /* Assigning part directly to mime_message causes problem with
561 "Content-type" header displaying in the HTML message (camel parsing bug?) */
562 camel_multipart_add_part (new_multipart, out_part);
563 g_object_unref (out_part);
564 }
565
566 camel_medium_set_content (CAMEL_MEDIUM (result_message), CAMEL_DATA_WRAPPER (new_multipart));
567
568 /* Add the headers from the message we are replying to, so CC and that
569 * stuff is preserved. Also replace any $ORIG[header-name] modifiers ignoring
570 * 'content-*' headers */
571 headers = camel_medium_get_headers (CAMEL_MEDIUM (source_message));
572 len = camel_name_value_array_get_length (headers);
573 for (ii = 0; ii < len; ii++) {
574 const gchar *header_name = NULL, *header_value = NULL;
575
576 if (!camel_name_value_array_get (headers, ii, &header_name, &header_value) ||
577 !header_name)
578 continue;
579
580 if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 &&
581 g_ascii_strcasecmp (header_name, "from") != 0 &&
582 g_ascii_strcasecmp (header_name, "Message-ID") != 0 &&
583 g_ascii_strcasecmp (header_name, "In-Reply-To") != 0 &&
584 g_ascii_strcasecmp (header_name, "References") != 0) {
585 gchar *new_header_value = NULL;
586
587 /* Some special handling of the 'subject' header */
588 if (g_ascii_strncasecmp (header_name, "subject", 7) == 0) {
589 GString *subject = g_string_new (camel_mime_message_get_subject (template_message));
590 guint jj;
591
592 /* Now replace all possible $ORIG[]s in the subject line by values from original message */
593 for (jj = 0; jj < len; jj++) {
594 const gchar *m_header_name = NULL, *m_header_value = NULL;
595
596 if (camel_name_value_array_get (headers, jj, &m_header_name, &m_header_value) &&
597 m_header_name &&
598 g_ascii_strncasecmp (m_header_name, "content-", 8) != 0 &&
599 g_ascii_strcasecmp (m_header_name, "subject") != 0)
600 replace_template_variable (subject, m_header_name, m_header_value);
601 }
602
603 /* Now replace $ORIG[subject] variable, handling possible base64 encryption */
604 replace_template_variable (
605 subject, "subject",
606 camel_mime_message_get_subject (source_message));
607
608 replace_user_variables (subject, source_message);
609
610 new_header_value = g_string_free (subject, FALSE);
611 }
612
613 camel_medium_add_header (CAMEL_MEDIUM (result_message), header_name, new_header_value ? new_header_value : header_value);
614
615 g_free (new_header_value);
616 }
617 }
618
619 /* Set the To: field to the same To: field of the message we are replying to. */
620 camel_mime_message_set_recipients (
621 result_message, CAMEL_RECIPIENT_TYPE_TO,
622 camel_mime_message_get_reply_to (source_message) ? camel_mime_message_get_reply_to (source_message) :
623 camel_mime_message_get_from (source_message));
624
625 /* Copy the recipients from the template. */
626 camel_mime_message_set_recipients (result_message, CAMEL_RECIPIENT_TYPE_CC,
627 camel_mime_message_get_recipients (template_message, CAMEL_RECIPIENT_TYPE_CC));
628
629 camel_mime_message_set_recipients (result_message, CAMEL_RECIPIENT_TYPE_BCC,
630 camel_mime_message_get_recipients (template_message, CAMEL_RECIPIENT_TYPE_BCC));
631
632 if (camel_mime_message_get_reply_to (template_message))
633 camel_mime_message_set_reply_to (result_message, camel_mime_message_get_reply_to (template_message));
634
635 /* Add In-Reply-To and References. */
636
637 message_id = camel_header_unfold (camel_medium_get_header (CAMEL_MEDIUM (source_message), "Message-ID"));
638 references = camel_header_unfold (camel_medium_get_header (CAMEL_MEDIUM (source_message), "References"));
639
640 if (message_id && *message_id) {
641 gchar *reply_refs;
642
643 camel_medium_add_header (CAMEL_MEDIUM (result_message), "In-Reply-To", message_id);
644
645 if (references)
646 reply_refs = g_strdup_printf ("%s %s", references, message_id);
647 else
648 reply_refs = NULL;
649
650 camel_medium_add_header (CAMEL_MEDIUM (result_message), "References", reply_refs ? reply_refs : message_id);
651
652 g_free (reply_refs);
653
654 } else if (references && *references) {
655 camel_medium_add_header (CAMEL_MEDIUM (result_message), "References", references);
656 }
657
658 g_free (message_id);
659 g_free (references);
660
661 g_clear_object (&template_message);
662 g_clear_object (&new_multipart);
663
664 return result_message;
665 }
666
667 typedef struct _AsyncContext {
668 CamelMimeMessage *source_message;
669 CamelFolder *source_folder;
670 CamelFolder *templates_folder;
671 gchar *source_message_uid;
672 gchar *templates_message_uid;
673 CamelMimeMessage *result_message;
674 } AsyncContext;
675
676 static void
async_context_free(gpointer ptr)677 async_context_free (gpointer ptr)
678 {
679 AsyncContext *context = ptr;
680
681 if (context) {
682 g_clear_object (&context->source_message);
683 g_clear_object (&context->source_folder);
684 g_clear_object (&context->templates_folder);
685 g_clear_object (&context->result_message);
686 g_free (context->source_message_uid);
687 g_free (context->templates_message_uid);
688 g_slice_free (AsyncContext, context);
689 }
690 }
691
692 static void
e_mail_templates_apply_thread(ESimpleAsyncResult * simple,gpointer source_object,GCancellable * cancellable)693 e_mail_templates_apply_thread (ESimpleAsyncResult *simple,
694 gpointer source_object,
695 GCancellable *cancellable)
696 {
697 AsyncContext *context;
698 GError *local_error = NULL;
699
700 context = e_simple_async_result_get_op_pointer (simple);
701 g_return_if_fail (context != NULL);
702
703 context->result_message = e_mail_templates_apply_sync (
704 context->source_message, context->source_folder, context->source_message_uid,
705 context->templates_folder, context->templates_message_uid,
706 cancellable, &local_error);
707
708 if (local_error)
709 e_simple_async_result_take_error (simple, local_error);
710 }
711
712 void
e_mail_templates_apply(CamelMimeMessage * source_message,CamelFolder * source_folder,const gchar * source_message_uid,CamelFolder * templates_folder,const gchar * templates_message_uid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)713 e_mail_templates_apply (CamelMimeMessage *source_message,
714 CamelFolder *source_folder,
715 const gchar *source_message_uid,
716 CamelFolder *templates_folder,
717 const gchar *templates_message_uid,
718 GCancellable *cancellable,
719 GAsyncReadyCallback callback,
720 gpointer user_data)
721 {
722 ESimpleAsyncResult *simple;
723 AsyncContext *context;
724
725 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (source_message));
726 g_return_if_fail (CAMEL_IS_FOLDER (templates_folder));
727 g_return_if_fail (templates_message_uid != NULL);
728 g_return_if_fail (callback != NULL);
729
730 context = g_slice_new0 (AsyncContext);
731 context->source_message = g_object_ref (source_message);
732 context->source_folder = source_folder ? g_object_ref (source_folder) : NULL;
733 context->source_message_uid = g_strdup (source_message_uid);
734 context->templates_folder = g_object_ref (templates_folder);
735 context->templates_message_uid = g_strdup (templates_message_uid);
736 context->result_message = NULL;
737
738 simple = e_simple_async_result_new (G_OBJECT (source_message), callback,
739 user_data, e_mail_templates_apply);
740
741 e_simple_async_result_set_op_pointer (simple, context, (GDestroyNotify) async_context_free);
742
743 e_simple_async_result_run_in_thread (simple, G_PRIORITY_DEFAULT, e_mail_templates_apply_thread, cancellable);
744
745 g_object_unref (simple);
746 }
747
748 CamelMimeMessage *
e_mail_templates_apply_finish(GObject * source_object,GAsyncResult * result,GError ** error)749 e_mail_templates_apply_finish (GObject *source_object,
750 GAsyncResult *result,
751 GError **error)
752 {
753 ESimpleAsyncResult *simple;
754 AsyncContext *context;
755
756 g_return_val_if_fail (e_simple_async_result_is_valid (result, source_object, e_mail_templates_apply), NULL);
757
758 simple = E_SIMPLE_ASYNC_RESULT (result);
759 context = e_simple_async_result_get_op_pointer (simple);
760
761 if (e_simple_async_result_propagate_error (simple, error))
762 return NULL;
763
764 return context->result_message ? g_object_ref (context->result_message) : NULL;
765 }
766