1 /*
2 * e-mail-folder-utils.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include "e-mail-folder-utils.h"
21
22 #include <glib/gi18n-lib.h>
23
24 #include <libedataserver/libedataserver.h>
25
26 #include <libemail-engine/e-mail-session.h>
27 #include <libemail-engine/mail-tools.h>
28
29 #include "e-mail-utils.h"
30
31 /* User-Agent header value */
32 #define USER_AGENT ("Evolution " VERSION VERSION_SUBSTRING " " VERSION_COMMENT)
33
34 typedef struct _AsyncContext AsyncContext;
35
36 struct _AsyncContext {
37 CamelMimeMessage *message;
38 CamelMessageInfo *info;
39 CamelMimePart *part;
40 GHashTable *hash_table;
41 GPtrArray *ptr_array;
42 GFile *destination;
43 gchar *fwd_subject;
44 gchar *message_uid;
45 };
46
47 static void
async_context_free(AsyncContext * context)48 async_context_free (AsyncContext *context)
49 {
50 if (context->hash_table != NULL)
51 g_hash_table_unref (context->hash_table);
52
53 if (context->ptr_array != NULL)
54 g_ptr_array_unref (context->ptr_array);
55
56 g_clear_object (&context->message);
57 g_clear_object (&context->info);
58 g_clear_object (&context->part);
59 g_clear_object (&context->destination);
60
61 g_free (context->fwd_subject);
62 g_free (context->message_uid);
63
64 g_slice_free (AsyncContext, context);
65 }
66
67 static void
mail_folder_append_message_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)68 mail_folder_append_message_thread (GSimpleAsyncResult *simple,
69 GObject *object,
70 GCancellable *cancellable)
71 {
72 AsyncContext *context;
73 GError *error = NULL;
74
75 context = g_simple_async_result_get_op_res_gpointer (simple);
76
77 e_mail_folder_append_message_sync (
78 CAMEL_FOLDER (object), context->message,
79 context->info, &context->message_uid,
80 cancellable, &error);
81
82 if (error != NULL)
83 g_simple_async_result_take_error (simple, error);
84 }
85
86 gboolean
e_mail_folder_append_message_sync(CamelFolder * folder,CamelMimeMessage * message,CamelMessageInfo * info,gchar ** appended_uid,GCancellable * cancellable,GError ** error)87 e_mail_folder_append_message_sync (CamelFolder *folder,
88 CamelMimeMessage *message,
89 CamelMessageInfo *info,
90 gchar **appended_uid,
91 GCancellable *cancellable,
92 GError **error)
93 {
94 CamelMedium *medium;
95 gchar *full_display_name;
96 gboolean success;
97
98 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
99 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
100
101 medium = CAMEL_MEDIUM (message);
102
103 full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
104 camel_operation_push_message (
105 cancellable,
106 _("Saving message to folder “%s”"),
107 full_display_name ? full_display_name : camel_folder_get_display_name (folder));
108 g_free (full_display_name);
109
110 if (!camel_medium_get_header (medium, "X-Evolution-Is-Redirect")) {
111 if (camel_medium_get_header (medium, "User-Agent") == NULL)
112 camel_medium_set_header (medium, "User-Agent", USER_AGENT);
113
114 camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
115 }
116
117 success = camel_folder_append_message_sync (
118 folder, message, info, appended_uid, cancellable, error);
119
120 camel_operation_pop_message (cancellable);
121
122 return success;
123 }
124
125 void
e_mail_folder_append_message(CamelFolder * folder,CamelMimeMessage * message,CamelMessageInfo * info,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)126 e_mail_folder_append_message (CamelFolder *folder,
127 CamelMimeMessage *message,
128 CamelMessageInfo *info,
129 gint io_priority,
130 GCancellable *cancellable,
131 GAsyncReadyCallback callback,
132 gpointer user_data)
133 {
134 GSimpleAsyncResult *simple;
135 AsyncContext *context;
136
137 g_return_if_fail (CAMEL_IS_FOLDER (folder));
138 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
139
140 context = g_slice_new0 (AsyncContext);
141 context->message = g_object_ref (message);
142
143 if (info != NULL)
144 context->info = g_object_ref (info);
145
146 simple = g_simple_async_result_new (
147 G_OBJECT (folder), callback, user_data,
148 e_mail_folder_append_message);
149
150 g_simple_async_result_set_check_cancellable (simple, cancellable);
151
152 g_simple_async_result_set_op_res_gpointer (
153 simple, context, (GDestroyNotify) async_context_free);
154
155 g_simple_async_result_run_in_thread (
156 simple, mail_folder_append_message_thread,
157 io_priority, cancellable);
158
159 g_object_unref (simple);
160 }
161
162 gboolean
e_mail_folder_append_message_finish(CamelFolder * folder,GAsyncResult * result,gchar ** appended_uid,GError ** error)163 e_mail_folder_append_message_finish (CamelFolder *folder,
164 GAsyncResult *result,
165 gchar **appended_uid,
166 GError **error)
167 {
168 GSimpleAsyncResult *simple;
169 AsyncContext *context;
170
171 g_return_val_if_fail (
172 g_simple_async_result_is_valid (
173 result, G_OBJECT (folder),
174 e_mail_folder_append_message), FALSE);
175
176 simple = G_SIMPLE_ASYNC_RESULT (result);
177 context = g_simple_async_result_get_op_res_gpointer (simple);
178
179 if (appended_uid != NULL) {
180 *appended_uid = context->message_uid;
181 context->message_uid = NULL;
182 }
183
184 /* Assume success unless a GError is set. */
185 return !g_simple_async_result_propagate_error (simple, error);
186 }
187
188 static void
mail_folder_expunge_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)189 mail_folder_expunge_thread (GSimpleAsyncResult *simple,
190 GObject *object,
191 GCancellable *cancellable)
192 {
193 GError *error = NULL;
194
195 e_mail_folder_expunge_sync (
196 CAMEL_FOLDER (object), cancellable, &error);
197
198 if (error != NULL)
199 g_simple_async_result_take_error (simple, error);
200 }
201
202 static gboolean
mail_folder_expunge_pop3_stores(CamelFolder * folder,GCancellable * cancellable,GError ** error)203 mail_folder_expunge_pop3_stores (CamelFolder *folder,
204 GCancellable *cancellable,
205 GError **error)
206 {
207 GHashTable *expunging_uids;
208 CamelStore *parent_store;
209 CamelService *service;
210 CamelSession *session;
211 ESourceRegistry *registry;
212 GPtrArray *uids;
213 GList *list, *link;
214 const gchar *extension_name;
215 gboolean success = TRUE;
216 guint ii;
217
218 parent_store = camel_folder_get_parent_store (folder);
219
220 service = CAMEL_SERVICE (parent_store);
221 session = camel_service_ref_session (service);
222 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
223
224 uids = camel_folder_get_uids (folder);
225
226 if (uids == NULL)
227 goto exit;
228
229 expunging_uids = g_hash_table_new_full (
230 (GHashFunc) g_str_hash,
231 (GEqualFunc) g_str_equal,
232 (GDestroyNotify) g_free,
233 (GDestroyNotify) g_free);
234
235 for (ii = 0; ii < uids->len; ii++) {
236 CamelMessageInfo *info;
237 CamelMessageFlags flags = 0;
238 CamelMimeMessage *message;
239 const gchar *pop3_uid;
240 const gchar *source_uid;
241
242 info = camel_folder_get_message_info (
243 folder, uids->pdata[ii]);
244
245 if (info != NULL) {
246 flags = camel_message_info_get_flags (info);
247 g_clear_object (&info);
248 }
249
250 /* Only interested in deleted messages. */
251 if ((flags & CAMEL_MESSAGE_DELETED) == 0)
252 continue;
253
254 /* because the UID in the local store doesn't
255 * match with the UID in the pop3 store */
256 message = camel_folder_get_message_sync (
257 folder, uids->pdata[ii], cancellable, NULL);
258
259 if (message == NULL)
260 continue;
261
262 pop3_uid = camel_medium_get_header (
263 CAMEL_MEDIUM (message), "X-Evolution-POP3-UID");
264 source_uid = camel_mime_message_get_source (message);
265
266 if (pop3_uid != NULL)
267 g_hash_table_insert (
268 expunging_uids,
269 g_strstrip (g_strdup (pop3_uid)),
270 g_strstrip (g_strdup (source_uid)));
271
272 g_object_unref (message);
273 }
274
275 camel_folder_free_uids (folder, uids);
276 uids = NULL;
277
278 if (g_hash_table_size (expunging_uids) == 0) {
279 g_hash_table_destroy (expunging_uids);
280 return TRUE;
281 }
282
283 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
284 list = e_source_registry_list_enabled (registry, extension_name);
285
286 for (link = list; link != NULL; link = g_list_next (link)) {
287 ESource *source = E_SOURCE (link->data);
288 ESourceBackend *extension;
289 CamelFolder *inbox_folder;
290 CamelService *service;
291 CamelSettings *settings;
292 const gchar *backend_name;
293 const gchar *service_uid;
294 const gchar *source_uid;
295 gboolean any_found = FALSE;
296 gboolean delete_expunged = FALSE;
297 gboolean keep_on_server = FALSE;
298
299 source_uid = e_source_get_uid (source);
300
301 extension = e_source_get_extension (source, extension_name);
302 backend_name = e_source_backend_get_backend_name (extension);
303
304 if (g_strcmp0 (backend_name, "pop") != 0)
305 continue;
306
307 service = camel_session_ref_service (
308 CAMEL_SESSION (session), source_uid);
309
310 service_uid = camel_service_get_uid (service);
311 settings = camel_service_ref_settings (service);
312
313 g_object_get (
314 settings,
315 "delete-expunged", &delete_expunged,
316 "keep-on-server", &keep_on_server,
317 NULL);
318
319 g_object_unref (settings);
320
321 if (!keep_on_server || !delete_expunged) {
322 g_object_unref (service);
323 continue;
324 }
325
326 inbox_folder = camel_store_get_inbox_folder_sync (
327 CAMEL_STORE (service), cancellable, error);
328
329 /* Abort the loop on error. */
330 if (inbox_folder == NULL) {
331 g_object_unref (service);
332 success = FALSE;
333 break;
334 }
335
336 uids = camel_folder_get_uids (inbox_folder);
337
338 if (uids == NULL) {
339 g_object_unref (service);
340 g_object_unref (inbox_folder);
341 continue;
342 }
343
344 for (ii = 0; ii < uids->len; ii++) {
345 const gchar *source_uid;
346
347 source_uid = g_hash_table_lookup (
348 expunging_uids, uids->pdata[ii]);
349 if (g_strcmp0 (source_uid, service_uid) == 0) {
350 any_found = TRUE;
351 camel_folder_delete_message (
352 inbox_folder, uids->pdata[ii]);
353 }
354 }
355
356 camel_folder_free_uids (inbox_folder, uids);
357
358 if (any_found)
359 success = camel_folder_synchronize_sync (
360 inbox_folder, TRUE, cancellable, error);
361
362 g_object_unref (inbox_folder);
363 g_object_unref (service);
364
365 /* Abort the loop on error. */
366 if (!success)
367 break;
368 }
369
370 g_list_free_full (list, (GDestroyNotify) g_object_unref);
371
372 g_hash_table_destroy (expunging_uids);
373
374 exit:
375 g_object_unref (session);
376
377 return success;
378 }
379
380 gboolean
e_mail_folder_expunge_sync(CamelFolder * folder,GCancellable * cancellable,GError ** error)381 e_mail_folder_expunge_sync (CamelFolder *folder,
382 GCancellable *cancellable,
383 GError **error)
384 {
385 CamelFolder *local_inbox;
386 CamelStore *parent_store;
387 CamelService *service;
388 CamelSession *session;
389 gboolean is_local_inbox;
390 gboolean is_local_trash;
391 gboolean store_is_local;
392 gboolean success = TRUE;
393 const gchar *uid;
394
395 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
396
397 parent_store = camel_folder_get_parent_store (folder);
398
399 service = CAMEL_SERVICE (parent_store);
400 session = camel_service_ref_session (service);
401
402 uid = camel_service_get_uid (service);
403 store_is_local = (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0);
404
405 local_inbox = e_mail_session_get_local_folder (
406 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_INBOX);
407 is_local_inbox = (folder == local_inbox);
408 is_local_trash = FALSE;
409
410 if (store_is_local && !is_local_inbox) {
411 CamelFolder *local_trash;
412
413 local_trash = camel_store_get_trash_folder_sync (
414 parent_store, cancellable, error);
415
416 if (local_trash != NULL) {
417 is_local_trash = (folder == local_trash);
418 g_object_unref (local_trash);
419 } else {
420 success = FALSE;
421 goto exit;
422 }
423 }
424
425 /* Expunge all POP3 accounts when expunging
426 * the local Inbox or Trash folder. */
427 if (is_local_inbox || is_local_trash)
428 success = mail_folder_expunge_pop3_stores (
429 folder, cancellable, error);
430
431 if (success)
432 success = camel_folder_expunge_sync (
433 folder, cancellable, error);
434
435 exit:
436 g_object_unref (session);
437
438 return success;
439 }
440
441 void
e_mail_folder_expunge(CamelFolder * folder,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)442 e_mail_folder_expunge (CamelFolder *folder,
443 gint io_priority,
444 GCancellable *cancellable,
445 GAsyncReadyCallback callback,
446 gpointer user_data)
447 {
448 GSimpleAsyncResult *simple;
449
450 g_return_if_fail (CAMEL_IS_FOLDER (folder));
451
452 simple = g_simple_async_result_new (
453 G_OBJECT (folder), callback,
454 user_data, e_mail_folder_expunge);
455
456 g_simple_async_result_set_check_cancellable (simple, cancellable);
457
458 g_simple_async_result_run_in_thread (
459 simple, mail_folder_expunge_thread,
460 io_priority, cancellable);
461
462 g_object_unref (simple);
463 }
464
465 gboolean
e_mail_folder_expunge_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)466 e_mail_folder_expunge_finish (CamelFolder *folder,
467 GAsyncResult *result,
468 GError **error)
469 {
470 GSimpleAsyncResult *simple;
471
472 g_return_val_if_fail (
473 g_simple_async_result_is_valid (
474 result, G_OBJECT (folder),
475 e_mail_folder_expunge), FALSE);
476
477 simple = G_SIMPLE_ASYNC_RESULT (result);
478
479 /* Assume success unless a GError is set. */
480 return !g_simple_async_result_propagate_error (simple, error);
481 }
482
483 static void
mail_folder_build_attachment_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)484 mail_folder_build_attachment_thread (GSimpleAsyncResult *simple,
485 GObject *object,
486 GCancellable *cancellable)
487 {
488 AsyncContext *context;
489 GError *error = NULL;
490
491 context = g_simple_async_result_get_op_res_gpointer (simple);
492
493 context->part = e_mail_folder_build_attachment_sync (
494 CAMEL_FOLDER (object), context->ptr_array,
495 &context->fwd_subject, cancellable, &error);
496
497 if (error != NULL)
498 g_simple_async_result_take_error (simple, error);
499 }
500
501 CamelMimePart *
e_mail_folder_build_attachment_sync(CamelFolder * folder,GPtrArray * message_uids,gchar ** fwd_subject,GCancellable * cancellable,GError ** error)502 e_mail_folder_build_attachment_sync (CamelFolder *folder,
503 GPtrArray *message_uids,
504 gchar **fwd_subject,
505 GCancellable *cancellable,
506 GError **error)
507 {
508 GHashTable *hash_table;
509 CamelMimeMessage *message;
510 CamelMimePart *part;
511 const gchar *uid;
512
513 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
514 g_return_val_if_fail (message_uids != NULL, NULL);
515
516 /* Need at least one message UID to make an attachment. */
517 g_return_val_if_fail (message_uids->len > 0, NULL);
518
519 hash_table = e_mail_folder_get_multiple_messages_sync (
520 folder, message_uids, cancellable, error);
521
522 if (hash_table == NULL)
523 return NULL;
524
525 /* Create the forward subject from the first message. */
526
527 uid = g_ptr_array_index (message_uids, 0);
528 g_return_val_if_fail (uid != NULL, NULL);
529
530 message = g_hash_table_lookup (hash_table, uid);
531 g_return_val_if_fail (message != NULL, NULL);
532
533 if (fwd_subject != NULL)
534 *fwd_subject = mail_tool_generate_forward_subject (message);
535
536 if (message_uids->len == 1) {
537 part = mail_tool_make_message_attachment (message);
538
539 } else {
540 CamelMultipart *multipart;
541 guint ii;
542
543 multipart = camel_multipart_new ();
544 camel_data_wrapper_set_mime_type (
545 CAMEL_DATA_WRAPPER (multipart), "multipart/digest");
546 camel_multipart_set_boundary (multipart, NULL);
547
548 for (ii = 0; ii < message_uids->len; ii++) {
549 uid = g_ptr_array_index (message_uids, ii);
550 g_return_val_if_fail (uid != NULL, NULL);
551
552 message = g_hash_table_lookup (hash_table, uid);
553 g_return_val_if_fail (message != NULL, NULL);
554
555 part = mail_tool_make_message_attachment (message);
556 camel_multipart_add_part (multipart, part);
557 g_object_unref (part);
558 }
559
560 part = camel_mime_part_new ();
561
562 camel_medium_set_content (
563 CAMEL_MEDIUM (part),
564 CAMEL_DATA_WRAPPER (multipart));
565
566 camel_mime_part_set_description (
567 part, _("Forwarded messages"));
568
569 g_object_unref (multipart);
570 }
571
572 g_hash_table_unref (hash_table);
573
574 return part;
575 }
576
577 void
e_mail_folder_build_attachment(CamelFolder * folder,GPtrArray * message_uids,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)578 e_mail_folder_build_attachment (CamelFolder *folder,
579 GPtrArray *message_uids,
580 gint io_priority,
581 GCancellable *cancellable,
582 GAsyncReadyCallback callback,
583 gpointer user_data)
584 {
585 GSimpleAsyncResult *simple;
586 AsyncContext *context;
587
588 g_return_if_fail (CAMEL_IS_FOLDER (folder));
589 g_return_if_fail (message_uids != NULL);
590
591 /* Need at least one message UID to make an attachment. */
592 g_return_if_fail (message_uids->len > 0);
593
594 context = g_slice_new0 (AsyncContext);
595 context->ptr_array = g_ptr_array_ref (message_uids);
596
597 simple = g_simple_async_result_new (
598 G_OBJECT (folder), callback, user_data,
599 e_mail_folder_build_attachment);
600
601 g_simple_async_result_set_check_cancellable (simple, cancellable);
602
603 g_simple_async_result_set_op_res_gpointer (
604 simple, context, (GDestroyNotify) async_context_free);
605
606 g_simple_async_result_run_in_thread (
607 simple, mail_folder_build_attachment_thread,
608 io_priority, cancellable);
609
610 g_object_unref (simple);
611 }
612
613 CamelMimePart *
e_mail_folder_build_attachment_finish(CamelFolder * folder,GAsyncResult * result,gchar ** fwd_subject,GError ** error)614 e_mail_folder_build_attachment_finish (CamelFolder *folder,
615 GAsyncResult *result,
616 gchar **fwd_subject,
617 GError **error)
618 {
619 GSimpleAsyncResult *simple;
620 AsyncContext *context;
621
622 g_return_val_if_fail (
623 g_simple_async_result_is_valid (
624 result, G_OBJECT (folder),
625 e_mail_folder_build_attachment), NULL);
626
627 simple = G_SIMPLE_ASYNC_RESULT (result);
628 context = g_simple_async_result_get_op_res_gpointer (simple);
629
630 if (g_simple_async_result_propagate_error (simple, error))
631 return NULL;
632
633 if (fwd_subject != NULL) {
634 *fwd_subject = context->fwd_subject;
635 context->fwd_subject = NULL;
636 }
637
638 g_return_val_if_fail (CAMEL_IS_MIME_PART (context->part), NULL);
639
640 return g_object_ref (context->part);
641 }
642
643 static void
mail_folder_find_duplicate_messages_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)644 mail_folder_find_duplicate_messages_thread (GSimpleAsyncResult *simple,
645 GObject *object,
646 GCancellable *cancellable)
647 {
648 AsyncContext *context;
649 GError *error = NULL;
650
651 context = g_simple_async_result_get_op_res_gpointer (simple);
652
653 context->hash_table = e_mail_folder_find_duplicate_messages_sync (
654 CAMEL_FOLDER (object), context->ptr_array,
655 cancellable, &error);
656
657 if (error != NULL)
658 g_simple_async_result_take_error (simple, error);
659 }
660
661 static GHashTable *
emfu_get_messages_hash_sync(CamelFolder * folder,GPtrArray * message_uids,GCancellable * cancellable,GError ** error)662 emfu_get_messages_hash_sync (CamelFolder *folder,
663 GPtrArray *message_uids,
664 GCancellable *cancellable,
665 GError **error)
666 {
667 GHashTable *hash_table;
668 CamelMimeMessage *message;
669 guint ii;
670
671 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
672 g_return_val_if_fail (message_uids != NULL, NULL);
673
674 camel_operation_push_message (
675 cancellable,
676 ngettext (
677 "Retrieving %d message",
678 "Retrieving %d messages",
679 message_uids->len),
680 message_uids->len);
681
682 hash_table = g_hash_table_new_full (
683 (GHashFunc) g_str_hash,
684 (GEqualFunc) g_str_equal,
685 (GDestroyNotify) g_free,
686 (GDestroyNotify) g_free);
687
688 /* This is an all or nothing operation. Destroy the
689 * hash table if we fail to retrieve any message. */
690
691 for (ii = 0; ii < message_uids->len; ii++) {
692 const gchar *uid;
693 gint percent;
694
695 uid = g_ptr_array_index (message_uids, ii);
696 percent = ((ii + 1) * 100) / message_uids->len;
697
698 message = camel_folder_get_message_sync (
699 folder, uid, cancellable, error);
700
701 camel_operation_progress (cancellable, percent);
702
703 if (CAMEL_IS_MIME_MESSAGE (message)) {
704 CamelDataWrapper *content;
705 gchar *digest = NULL;
706
707 /* Generate a digest string from the message's content. */
708 content = camel_medium_get_content (CAMEL_MEDIUM (message));
709
710 if (content != NULL) {
711 CamelStream *stream;
712 GByteArray *buffer;
713 gssize n_bytes;
714
715 stream = camel_stream_mem_new ();
716
717 n_bytes = camel_data_wrapper_decode_to_stream_sync (
718 content, stream, cancellable, error);
719
720 if (n_bytes >= 0) {
721 guint data_len;
722
723 /* The CamelStreamMem owns the buffer. */
724 buffer = camel_stream_mem_get_byte_array (
725 CAMEL_STREAM_MEM (stream));
726 g_return_val_if_fail (buffer != NULL, NULL);
727
728 data_len = buffer->len;
729
730 /* Strip trailing white-spaces and empty lines */
731 while (data_len > 0 && g_ascii_isspace (buffer->data[data_len - 1]))
732 data_len--;
733
734 if (data_len > 0)
735 digest = g_compute_checksum_for_data (G_CHECKSUM_SHA256, buffer->data, data_len);
736 }
737
738 g_object_unref (stream);
739 }
740
741 g_hash_table_insert (
742 hash_table, g_strdup (uid), digest);
743 g_object_unref (message);
744 } else {
745 g_hash_table_destroy (hash_table);
746 hash_table = NULL;
747 break;
748 }
749 }
750
751 camel_operation_pop_message (cancellable);
752
753 return hash_table;
754 }
755
756 GHashTable *
e_mail_folder_find_duplicate_messages_sync(CamelFolder * folder,GPtrArray * message_uids,GCancellable * cancellable,GError ** error)757 e_mail_folder_find_duplicate_messages_sync (CamelFolder *folder,
758 GPtrArray *message_uids,
759 GCancellable *cancellable,
760 GError **error)
761 {
762 GQueue trash = G_QUEUE_INIT;
763 GHashTable *hash_table;
764 GHashTable *unique_ids;
765 GHashTableIter iter;
766 gpointer key, value;
767
768 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
769 g_return_val_if_fail (message_uids != NULL, NULL);
770
771 /* hash_table = { MessageUID : digest-as-string } */
772 hash_table = emfu_get_messages_hash_sync (
773 folder, message_uids, cancellable, error);
774
775 if (hash_table == NULL)
776 return NULL;
777
778 camel_operation_push_message (
779 cancellable, _("Scanning messages for duplicates"));
780
781 unique_ids = g_hash_table_new_full (
782 (GHashFunc) g_int64_hash,
783 (GEqualFunc) g_int64_equal,
784 (GDestroyNotify) g_free,
785 (GDestroyNotify) g_free);
786
787 g_hash_table_iter_init (&iter, hash_table);
788
789 while (g_hash_table_iter_next (&iter, &key, &value)) {
790 CamelSummaryMessageID message_id;
791 CamelMessageFlags flags;
792 CamelMessageInfo *info;
793 gboolean duplicate;
794 const gchar *digest;
795
796 info = camel_folder_get_message_info (folder, key);
797 if (!info)
798 continue;
799
800 message_id.id.id = camel_message_info_get_message_id (info);
801 flags = camel_message_info_get_flags (info);
802
803 /* Skip messages marked for deletion. */
804 if (flags & CAMEL_MESSAGE_DELETED) {
805 g_queue_push_tail (&trash, key);
806 g_clear_object (&info);
807 continue;
808 }
809
810 digest = value;
811
812 if (digest == NULL) {
813 g_queue_push_tail (&trash, key);
814 g_clear_object (&info);
815 continue;
816 }
817
818 /* Determine if the message a duplicate. */
819
820 value = g_hash_table_lookup (unique_ids, &message_id.id.id);
821 duplicate = (value != NULL) && g_str_equal (digest, value);
822
823 if (!duplicate) {
824 gint64 *v_int64;
825
826 /* XXX Might be better to create a GArray
827 * of 64-bit integers and have the hash
828 * table keys point to array elements. */
829 v_int64 = g_new0 (gint64, 1);
830 *v_int64 = (gint64) message_id.id.id;
831
832 g_hash_table_insert (unique_ids, v_int64, g_strdup (digest));
833 g_queue_push_tail (&trash, key);
834 }
835
836 g_clear_object (&info);
837 }
838
839 /* Delete all non-duplicate messages from the hash table. */
840 while ((key = g_queue_pop_head (&trash)) != NULL)
841 g_hash_table_remove (hash_table, key);
842
843 camel_operation_pop_message (cancellable);
844
845 g_hash_table_destroy (unique_ids);
846
847 return hash_table;
848 }
849
850 void
e_mail_folder_find_duplicate_messages(CamelFolder * folder,GPtrArray * message_uids,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)851 e_mail_folder_find_duplicate_messages (CamelFolder *folder,
852 GPtrArray *message_uids,
853 gint io_priority,
854 GCancellable *cancellable,
855 GAsyncReadyCallback callback,
856 gpointer user_data)
857 {
858 GSimpleAsyncResult *simple;
859 AsyncContext *context;
860
861 g_return_if_fail (CAMEL_IS_FOLDER (folder));
862 g_return_if_fail (message_uids != NULL);
863
864 context = g_slice_new0 (AsyncContext);
865 context->ptr_array = g_ptr_array_ref (message_uids);
866
867 simple = g_simple_async_result_new (
868 G_OBJECT (folder), callback, user_data,
869 e_mail_folder_find_duplicate_messages);
870
871 g_simple_async_result_set_check_cancellable (simple, cancellable);
872
873 g_simple_async_result_set_op_res_gpointer (
874 simple, context, (GDestroyNotify) async_context_free);
875
876 g_simple_async_result_run_in_thread (
877 simple, mail_folder_find_duplicate_messages_thread,
878 io_priority, cancellable);
879
880 g_object_unref (simple);
881 }
882
883 GHashTable *
e_mail_folder_find_duplicate_messages_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)884 e_mail_folder_find_duplicate_messages_finish (CamelFolder *folder,
885 GAsyncResult *result,
886 GError **error)
887 {
888 GSimpleAsyncResult *simple;
889 AsyncContext *context;
890
891 g_return_val_if_fail (
892 g_simple_async_result_is_valid (
893 result, G_OBJECT (folder),
894 e_mail_folder_find_duplicate_messages), NULL);
895
896 simple = G_SIMPLE_ASYNC_RESULT (result);
897 context = g_simple_async_result_get_op_res_gpointer (simple);
898
899 if (g_simple_async_result_propagate_error (simple, error))
900 return NULL;
901
902 return g_hash_table_ref (context->hash_table);
903 }
904
905 static void
mail_folder_get_multiple_messages_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)906 mail_folder_get_multiple_messages_thread (GSimpleAsyncResult *simple,
907 GObject *object,
908 GCancellable *cancellable)
909 {
910 AsyncContext *context;
911 GError *error = NULL;
912
913 context = g_simple_async_result_get_op_res_gpointer (simple);
914
915 context->hash_table = e_mail_folder_get_multiple_messages_sync (
916 CAMEL_FOLDER (object), context->ptr_array,
917 cancellable, &error);
918
919 if (error != NULL)
920 g_simple_async_result_take_error (simple, error);
921 }
922
923 GHashTable *
e_mail_folder_get_multiple_messages_sync(CamelFolder * folder,GPtrArray * message_uids,GCancellable * cancellable,GError ** error)924 e_mail_folder_get_multiple_messages_sync (CamelFolder *folder,
925 GPtrArray *message_uids,
926 GCancellable *cancellable,
927 GError **error)
928 {
929 GHashTable *hash_table;
930 CamelMimeMessage *message;
931 guint ii;
932
933 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
934 g_return_val_if_fail (message_uids != NULL, NULL);
935
936 camel_operation_push_message (
937 cancellable,
938 ngettext (
939 "Retrieving %d message",
940 "Retrieving %d messages",
941 message_uids->len),
942 message_uids->len);
943
944 hash_table = g_hash_table_new_full (
945 (GHashFunc) g_str_hash,
946 (GEqualFunc) g_str_equal,
947 (GDestroyNotify) g_free,
948 (GDestroyNotify) g_object_unref);
949
950 /* This is an all or nothing operation. Destroy the
951 * hash table if we fail to retrieve any message. */
952
953 for (ii = 0; ii < message_uids->len; ii++) {
954 const gchar *uid;
955 gint percent;
956
957 uid = g_ptr_array_index (message_uids, ii);
958 percent = ((ii + 1) * 100) / message_uids->len;
959
960 message = camel_folder_get_message_sync (
961 folder, uid, cancellable, error);
962
963 camel_operation_progress (cancellable, percent);
964
965 if (CAMEL_IS_MIME_MESSAGE (message)) {
966 g_hash_table_insert (
967 hash_table, g_strdup (uid), message);
968 } else {
969 g_hash_table_destroy (hash_table);
970 hash_table = NULL;
971 break;
972 }
973 }
974
975 camel_operation_pop_message (cancellable);
976
977 return hash_table;
978 }
979
980 void
e_mail_folder_get_multiple_messages(CamelFolder * folder,GPtrArray * message_uids,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)981 e_mail_folder_get_multiple_messages (CamelFolder *folder,
982 GPtrArray *message_uids,
983 gint io_priority,
984 GCancellable *cancellable,
985 GAsyncReadyCallback callback,
986 gpointer user_data)
987 {
988 GSimpleAsyncResult *simple;
989 AsyncContext *context;
990
991 g_return_if_fail (CAMEL_IS_FOLDER (folder));
992 g_return_if_fail (message_uids != NULL);
993
994 context = g_slice_new0 (AsyncContext);
995 context->ptr_array = g_ptr_array_ref (message_uids);
996
997 simple = g_simple_async_result_new (
998 G_OBJECT (folder), callback, user_data,
999 e_mail_folder_get_multiple_messages);
1000
1001 g_simple_async_result_set_check_cancellable (simple, cancellable);
1002
1003 g_simple_async_result_set_op_res_gpointer (
1004 simple, context, (GDestroyNotify) async_context_free);
1005
1006 g_simple_async_result_run_in_thread (
1007 simple, mail_folder_get_multiple_messages_thread,
1008 io_priority, cancellable);
1009
1010 g_object_unref (simple);
1011 }
1012
1013 GHashTable *
e_mail_folder_get_multiple_messages_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)1014 e_mail_folder_get_multiple_messages_finish (CamelFolder *folder,
1015 GAsyncResult *result,
1016 GError **error)
1017 {
1018 GSimpleAsyncResult *simple;
1019 AsyncContext *context;
1020
1021 g_return_val_if_fail (
1022 g_simple_async_result_is_valid (
1023 result, G_OBJECT (folder),
1024 e_mail_folder_get_multiple_messages), NULL);
1025
1026 simple = G_SIMPLE_ASYNC_RESULT (result);
1027 context = g_simple_async_result_get_op_res_gpointer (simple);
1028
1029 if (g_simple_async_result_propagate_error (simple, error))
1030 return NULL;
1031
1032 return g_hash_table_ref (context->hash_table);
1033 }
1034
1035 static void
mail_folder_remove_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)1036 mail_folder_remove_thread (GSimpleAsyncResult *simple,
1037 GObject *object,
1038 GCancellable *cancellable)
1039 {
1040 GError *error = NULL;
1041
1042 e_mail_folder_remove_sync (
1043 CAMEL_FOLDER (object), cancellable, &error);
1044
1045 if (error != NULL)
1046 g_simple_async_result_take_error (simple, error);
1047 }
1048
1049 static gboolean
mail_folder_remove_recursive(CamelStore * store,CamelFolderInfo * folder_info,GCancellable * cancellable,GError ** error)1050 mail_folder_remove_recursive (CamelStore *store,
1051 CamelFolderInfo *folder_info,
1052 GCancellable *cancellable,
1053 GError **error)
1054 {
1055 gboolean success = TRUE;
1056
1057 while (folder_info != NULL) {
1058 CamelFolder *folder;
1059
1060 if (folder_info->child != NULL) {
1061 success = mail_folder_remove_recursive (
1062 store, folder_info->child, cancellable, error);
1063 if (!success)
1064 break;
1065 }
1066
1067 folder = camel_store_get_folder_sync (
1068 store, folder_info->full_name, 0, cancellable, error);
1069 if (folder == NULL) {
1070 success = FALSE;
1071 break;
1072 }
1073
1074 if (!CAMEL_IS_VEE_FOLDER (folder)) {
1075 GPtrArray *uids;
1076 guint ii;
1077
1078 /* Delete every message in this folder,
1079 * then expunge it. */
1080
1081 camel_folder_freeze (folder);
1082
1083 uids = camel_folder_get_uids (folder);
1084
1085 for (ii = 0; ii < uids->len; ii++)
1086 camel_folder_delete_message (
1087 folder, uids->pdata[ii]);
1088
1089 camel_folder_free_uids (folder, uids);
1090
1091 success = camel_folder_synchronize_sync (
1092 folder, TRUE, cancellable, error);
1093
1094 camel_folder_thaw (folder);
1095 }
1096
1097 g_object_unref (folder);
1098
1099 if (!success)
1100 break;
1101
1102 /* If the store supports subscriptions,
1103 * then unsubscribe from this folder. */
1104 if (CAMEL_IS_SUBSCRIBABLE (store)) {
1105 success = camel_subscribable_unsubscribe_folder_sync (
1106 CAMEL_SUBSCRIBABLE (store),
1107 folder_info->full_name,
1108 cancellable, error);
1109 if (!success)
1110 break;
1111 }
1112
1113 success = camel_store_delete_folder_sync (
1114 store, folder_info->full_name, cancellable, error);
1115 if (!success)
1116 break;
1117
1118 folder_info = folder_info->next;
1119 }
1120
1121 return success;
1122 }
1123
1124 static void
follow_cancel_cb(GCancellable * cancellable,GCancellable * transparent_cancellable)1125 follow_cancel_cb (GCancellable *cancellable,
1126 GCancellable *transparent_cancellable)
1127 {
1128 g_cancellable_cancel (transparent_cancellable);
1129 }
1130
1131 gboolean
e_mail_folder_remove_sync(CamelFolder * folder,GCancellable * cancellable,GError ** error)1132 e_mail_folder_remove_sync (CamelFolder *folder,
1133 GCancellable *cancellable,
1134 GError **error)
1135 {
1136 CamelFolderInfo *folder_info;
1137 CamelFolderInfo *to_remove;
1138 CamelFolderInfo *next = NULL;
1139 CamelStore *parent_store;
1140 const gchar *full_name;
1141 gchar *full_display_name;
1142 gboolean success = TRUE;
1143 GCancellable *transparent_cancellable = NULL;
1144 gulong cbid = 0;
1145
1146 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1147
1148 full_name = camel_folder_get_full_name (folder);
1149 parent_store = camel_folder_get_parent_store (folder);
1150
1151 full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
1152 camel_operation_push_message (cancellable, _("Removing folder “%s”"),
1153 full_display_name ? full_display_name : camel_folder_get_display_name (folder));
1154 g_free (full_display_name);
1155
1156 if (cancellable) {
1157 transparent_cancellable = g_cancellable_new ();
1158 cbid = g_cancellable_connect (
1159 cancellable, G_CALLBACK (follow_cancel_cb),
1160 transparent_cancellable, NULL);
1161 }
1162
1163 if ((camel_store_get_flags (parent_store) & CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE) != 0) {
1164 success = camel_store_delete_folder_sync (
1165 parent_store, full_name, transparent_cancellable, error);
1166 } else {
1167 folder_info = camel_store_get_folder_info_sync (
1168 parent_store, full_name,
1169 CAMEL_STORE_FOLDER_INFO_RECURSIVE |
1170 CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
1171 cancellable, error);
1172
1173 if (folder_info == NULL) {
1174 success = FALSE;
1175 goto exit;
1176 }
1177
1178 to_remove = folder_info;
1179
1180 /* For cases when the top-level folder_info contains siblings,
1181 * such as when full_name contains a wildcard letter, compare
1182 * the folder name against folder_info->full_name to avoid
1183 * removing more folders than requested. */
1184 if (folder_info->next != NULL) {
1185 while (to_remove != NULL) {
1186 if (g_strcmp0 (to_remove->full_name, full_name) == 0)
1187 break;
1188 to_remove = to_remove->next;
1189 }
1190
1191 /* XXX Should we set a GError and return FALSE here? */
1192 if (to_remove == NULL) {
1193 g_warning ("%s: Failed to find folder '%s'", G_STRFUNC, full_name);
1194 camel_folder_info_free (folder_info);
1195 success = TRUE;
1196 goto exit;
1197 }
1198
1199 /* Prevent iterating over siblings. */
1200 next = to_remove->next;
1201 to_remove->next = NULL;
1202 }
1203
1204 success = mail_folder_remove_recursive (
1205 parent_store, to_remove, transparent_cancellable, error);
1206
1207 /* Restore the folder_info tree to its original
1208 * state so we don't leak folder_info nodes. */
1209 to_remove->next = next;
1210
1211 camel_folder_info_free (folder_info);
1212 }
1213
1214 exit:
1215 if (transparent_cancellable) {
1216 g_cancellable_disconnect (cancellable, cbid);
1217 g_object_unref (transparent_cancellable);
1218 }
1219
1220 camel_operation_pop_message (cancellable);
1221
1222 return success;
1223 }
1224
1225 void
e_mail_folder_remove(CamelFolder * folder,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1226 e_mail_folder_remove (CamelFolder *folder,
1227 gint io_priority,
1228 GCancellable *cancellable,
1229 GAsyncReadyCallback callback,
1230 gpointer user_data)
1231 {
1232 GSimpleAsyncResult *simple;
1233
1234 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1235
1236 simple = g_simple_async_result_new (
1237 G_OBJECT (folder), callback,
1238 user_data, e_mail_folder_remove);
1239
1240 g_simple_async_result_set_check_cancellable (simple, cancellable);
1241
1242 g_simple_async_result_run_in_thread (
1243 simple, mail_folder_remove_thread,
1244 io_priority, cancellable);
1245
1246 g_object_unref (simple);
1247 }
1248
1249 gboolean
e_mail_folder_remove_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)1250 e_mail_folder_remove_finish (CamelFolder *folder,
1251 GAsyncResult *result,
1252 GError **error)
1253 {
1254 GSimpleAsyncResult *simple;
1255
1256 g_return_val_if_fail (
1257 g_simple_async_result_is_valid (
1258 result, G_OBJECT (folder),
1259 e_mail_folder_remove), FALSE);
1260
1261 simple = G_SIMPLE_ASYNC_RESULT (result);
1262
1263 /* Assume success unless a GError is set. */
1264 return !g_simple_async_result_propagate_error (simple, error);
1265 }
1266
1267 static void
mail_folder_remove_attachments_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)1268 mail_folder_remove_attachments_thread (GSimpleAsyncResult *simple,
1269 GObject *object,
1270 GCancellable *cancellable)
1271 {
1272 AsyncContext *context;
1273 GError *error = NULL;
1274
1275 context = g_simple_async_result_get_op_res_gpointer (simple);
1276
1277 e_mail_folder_remove_attachments_sync (
1278 CAMEL_FOLDER (object), context->ptr_array,
1279 cancellable, &error);
1280
1281 if (error != NULL)
1282 g_simple_async_result_take_error (simple, error);
1283 }
1284
1285 static gboolean
mail_folder_strip_message_level(CamelMimePart * in_part,GCancellable * cancellable)1286 mail_folder_strip_message_level (CamelMimePart *in_part,
1287 GCancellable *cancellable)
1288 {
1289 CamelDataWrapper *content;
1290 CamelMultipart *multipart;
1291 gboolean modified = FALSE;
1292 guint ii, n_parts;
1293
1294 g_return_val_if_fail (CAMEL_IS_MIME_PART (in_part), FALSE);
1295
1296 content = camel_medium_get_content (CAMEL_MEDIUM (in_part));
1297
1298 if (CAMEL_IS_MIME_MESSAGE (content)) {
1299 return mail_folder_strip_message_level (CAMEL_MIME_PART (content), cancellable);
1300 }
1301
1302 if (!CAMEL_IS_MULTIPART (content))
1303 return FALSE;
1304
1305 multipart = CAMEL_MULTIPART (content);
1306 n_parts = camel_multipart_get_number (multipart);
1307
1308 /* Replace MIME parts with "attachment" or "inline" dispositions
1309 * with a small "text/plain" part saying the file was removed. */
1310 for (ii = 0; ii < n_parts && !g_cancellable_is_cancelled (cancellable); ii++) {
1311 CamelMimePart *mime_part;
1312 const gchar *disposition;
1313 gboolean is_attachment;
1314
1315 mime_part = camel_multipart_get_part (multipart, ii);
1316 disposition = camel_mime_part_get_disposition (mime_part);
1317
1318 is_attachment =
1319 (g_strcmp0 (disposition, "attachment") == 0) ||
1320 (g_strcmp0 (disposition, "inline") == 0);
1321
1322 if (is_attachment) {
1323 const gchar *filename;
1324 const gchar *content_type;
1325 gchar *content;
1326
1327 disposition = "inline";
1328 content_type = "text/plain";
1329 filename = camel_mime_part_get_filename (mime_part);
1330
1331 if (filename != NULL && *filename != '\0')
1332 content = g_strdup_printf (
1333 _("File “%s” has been removed."),
1334 filename);
1335 else
1336 content = g_strdup (
1337 _("File has been removed."));
1338
1339 camel_mime_part_set_content (
1340 mime_part, content,
1341 strlen (content), content_type);
1342 camel_mime_part_set_content_type (
1343 mime_part, content_type);
1344 camel_mime_part_set_disposition (
1345 mime_part, disposition);
1346
1347 modified = TRUE;
1348 } else {
1349 modified = mail_folder_strip_message_level (mime_part, cancellable) || modified;
1350 }
1351 }
1352
1353 return modified;
1354 }
1355
1356 /* Helper for e_mail_folder_remove_attachments_sync() */
1357 static gboolean
mail_folder_strip_message(CamelFolder * folder,CamelMimeMessage * message,const gchar * message_uid,GCancellable * cancellable,GError ** error)1358 mail_folder_strip_message (CamelFolder *folder,
1359 CamelMimeMessage *message,
1360 const gchar *message_uid,
1361 GCancellable *cancellable,
1362 GError **error)
1363 {
1364 gboolean modified;
1365 gboolean success = TRUE;
1366
1367 modified = mail_folder_strip_message_level (CAMEL_MIME_PART (message), cancellable);
1368
1369 /* Append the modified message with removed attachments to
1370 * the folder and mark the original message for deletion. */
1371 if (modified) {
1372 CamelMessageInfo *orig_info;
1373 CamelMessageInfo *copy_info;
1374 CamelMessageFlags flags;
1375 const CamelNameValueArray *headers;
1376
1377 headers = camel_medium_get_headers (CAMEL_MEDIUM (message));
1378 orig_info = camel_folder_get_message_info (folder, message_uid);
1379 copy_info = camel_message_info_new_from_headers (NULL, headers);
1380
1381 flags = camel_folder_get_message_flags (folder, message_uid);
1382 camel_message_info_set_flags (copy_info, flags, flags);
1383
1384 success = camel_folder_append_message_sync (
1385 folder, message, copy_info, NULL, cancellable, error);
1386 if (success)
1387 camel_message_info_set_flags (
1388 orig_info,
1389 CAMEL_MESSAGE_DELETED,
1390 CAMEL_MESSAGE_DELETED);
1391
1392 g_clear_object (&orig_info);
1393 g_clear_object (©_info);
1394 }
1395
1396 return success;
1397 }
1398
1399 gboolean
e_mail_folder_remove_attachments_sync(CamelFolder * folder,GPtrArray * message_uids,GCancellable * cancellable,GError ** error)1400 e_mail_folder_remove_attachments_sync (CamelFolder *folder,
1401 GPtrArray *message_uids,
1402 GCancellable *cancellable,
1403 GError **error)
1404 {
1405 gboolean success = TRUE;
1406 guint ii;
1407
1408 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1409 g_return_val_if_fail (message_uids != NULL, FALSE);
1410
1411 camel_folder_freeze (folder);
1412
1413 camel_operation_push_message (cancellable, _("Removing attachments"));
1414
1415 for (ii = 0; success && ii < message_uids->len; ii++) {
1416 CamelMimeMessage *message;
1417 CamelFolder *real_folder = NULL, *use_folder;
1418 gchar *real_message_uid = NULL;
1419 const gchar *uid, *use_message_uid;
1420 gint percent;
1421
1422 uid = g_ptr_array_index (message_uids, ii);
1423
1424 em_utils_get_real_folder_and_message_uid (folder, uid, &real_folder, NULL, &real_message_uid);
1425
1426 use_folder = real_folder ? real_folder : folder;
1427 use_message_uid = real_message_uid ? real_message_uid : uid;
1428 message = camel_folder_get_message_sync (use_folder, use_message_uid, cancellable, error);
1429
1430 if (message == NULL) {
1431 g_clear_object (&real_folder);
1432 g_free (real_message_uid);
1433 success = FALSE;
1434 break;
1435 }
1436
1437 success = mail_folder_strip_message (use_folder, message, use_message_uid, cancellable, error);
1438
1439 percent = ((ii + 1) * 100) / message_uids->len;
1440 camel_operation_progress (cancellable, percent);
1441
1442 g_clear_object (&real_folder);
1443 g_clear_object (&message);
1444 g_free (real_message_uid);
1445 }
1446
1447 camel_operation_pop_message (cancellable);
1448
1449 if (success)
1450 camel_folder_synchronize_sync (
1451 folder, FALSE, cancellable, error);
1452
1453 camel_folder_thaw (folder);
1454
1455 return success;
1456 }
1457
1458 void
e_mail_folder_remove_attachments(CamelFolder * folder,GPtrArray * message_uids,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1459 e_mail_folder_remove_attachments (CamelFolder *folder,
1460 GPtrArray *message_uids,
1461 gint io_priority,
1462 GCancellable *cancellable,
1463 GAsyncReadyCallback callback,
1464 gpointer user_data)
1465 {
1466 GSimpleAsyncResult *simple;
1467 AsyncContext *context;
1468
1469 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1470 g_return_if_fail (message_uids != NULL);
1471
1472 context = g_slice_new0 (AsyncContext);
1473 context->ptr_array = g_ptr_array_ref (message_uids);
1474
1475 simple = g_simple_async_result_new (
1476 G_OBJECT (folder), callback, user_data,
1477 e_mail_folder_remove_attachments);
1478
1479 g_simple_async_result_set_check_cancellable (simple, cancellable);
1480
1481 g_simple_async_result_set_op_res_gpointer (
1482 simple, context, (GDestroyNotify) async_context_free);
1483
1484 g_simple_async_result_run_in_thread (
1485 simple, mail_folder_remove_attachments_thread,
1486 io_priority, cancellable);
1487
1488 g_object_unref (simple);
1489 }
1490
1491 gboolean
e_mail_folder_remove_attachments_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)1492 e_mail_folder_remove_attachments_finish (CamelFolder *folder,
1493 GAsyncResult *result,
1494 GError **error)
1495 {
1496 GSimpleAsyncResult *simple;
1497
1498 g_return_val_if_fail (
1499 g_simple_async_result_is_valid (
1500 result, G_OBJECT (folder),
1501 e_mail_folder_remove_attachments), FALSE);
1502
1503 simple = G_SIMPLE_ASYNC_RESULT (result);
1504
1505 /* Assume success unless a GError is set. */
1506 return !g_simple_async_result_propagate_error (simple, error);
1507 }
1508
1509 static void
mail_folder_save_messages_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)1510 mail_folder_save_messages_thread (GSimpleAsyncResult *simple,
1511 GObject *object,
1512 GCancellable *cancellable)
1513 {
1514 AsyncContext *context;
1515 GError *error = NULL;
1516
1517 context = g_simple_async_result_get_op_res_gpointer (simple);
1518
1519 e_mail_folder_save_messages_sync (
1520 CAMEL_FOLDER (object), context->ptr_array,
1521 context->destination, cancellable, &error);
1522
1523 if (error != NULL)
1524 g_simple_async_result_take_error (simple, error);
1525 }
1526
1527 /* Helper for e_mail_folder_save_messages_sync() */
1528 static void
mail_folder_save_prepare_part(CamelMimePart * mime_part)1529 mail_folder_save_prepare_part (CamelMimePart *mime_part)
1530 {
1531 CamelDataWrapper *content;
1532
1533 content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1534
1535 if (content == NULL)
1536 return;
1537
1538 if (CAMEL_IS_MULTIPART (content)) {
1539 guint n_parts, ii;
1540
1541 n_parts = camel_multipart_get_number (
1542 CAMEL_MULTIPART (content));
1543 for (ii = 0; ii < n_parts; ii++) {
1544 mime_part = camel_multipart_get_part (
1545 CAMEL_MULTIPART (content), ii);
1546 mail_folder_save_prepare_part (mime_part);
1547 }
1548
1549 } else if (CAMEL_IS_MIME_MESSAGE (content)) {
1550 mail_folder_save_prepare_part (CAMEL_MIME_PART (content));
1551
1552 } else {
1553 CamelContentType *type;
1554
1555 /* Save textual parts as 8-bit, not encoded. */
1556 type = camel_data_wrapper_get_mime_type_field (content);
1557 if (camel_content_type_is (type, "text", "*"))
1558 camel_mime_part_set_encoding (
1559 mime_part, CAMEL_TRANSFER_ENCODING_8BIT);
1560 }
1561 }
1562
1563 gboolean
e_mail_folder_save_messages_sync(CamelFolder * folder,GPtrArray * message_uids,GFile * destination,GCancellable * cancellable,GError ** error)1564 e_mail_folder_save_messages_sync (CamelFolder *folder,
1565 GPtrArray *message_uids,
1566 GFile *destination,
1567 GCancellable *cancellable,
1568 GError **error)
1569 {
1570 GFileOutputStream *file_output_stream;
1571 CamelStream *base_stream = NULL;
1572 GByteArray *byte_array;
1573 gboolean success = TRUE;
1574 guint ii;
1575
1576 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1577 g_return_val_if_fail (message_uids != NULL, FALSE);
1578 g_return_val_if_fail (G_IS_FILE (destination), FALSE);
1579
1580 /* Need at least one message UID to save. */
1581 g_return_val_if_fail (message_uids->len > 0, FALSE);
1582
1583 camel_operation_push_message (
1584 cancellable, ngettext (
1585 "Saving %d message",
1586 "Saving %d messages",
1587 message_uids->len),
1588 message_uids->len);
1589
1590 file_output_stream = g_file_replace (
1591 destination, NULL, FALSE,
1592 G_FILE_CREATE_PRIVATE |
1593 G_FILE_CREATE_REPLACE_DESTINATION,
1594 cancellable, error);
1595
1596 if (file_output_stream == NULL) {
1597 camel_operation_pop_message (cancellable);
1598 return FALSE;
1599 }
1600
1601 byte_array = g_byte_array_new ();
1602
1603 for (ii = 0; ii < message_uids->len; ii++) {
1604 CamelMimeMessage *message;
1605 CamelMimeFilter *filter;
1606 CamelStream *stream;
1607 const gchar *uid;
1608 gchar *from_line;
1609 gint percent;
1610 gint retval;
1611
1612 if (base_stream != NULL)
1613 g_object_unref (base_stream);
1614
1615 /* CamelStreamMem does NOT take ownership of the byte
1616 * array when set with camel_stream_mem_set_byte_array().
1617 * This allows us to reuse the same memory slab for each
1618 * message, which is slightly more efficient. */
1619 base_stream = camel_stream_mem_new ();
1620 camel_stream_mem_set_byte_array (
1621 CAMEL_STREAM_MEM (base_stream), byte_array);
1622
1623 uid = g_ptr_array_index (message_uids, ii);
1624
1625 message = camel_folder_get_message_sync (
1626 folder, uid, cancellable, error);
1627 if (message == NULL) {
1628 success = FALSE;
1629 goto exit;
1630 }
1631
1632 mail_folder_save_prepare_part (CAMEL_MIME_PART (message));
1633
1634 from_line = camel_mime_message_build_mbox_from (message);
1635 g_return_val_if_fail (from_line != NULL, FALSE);
1636
1637 success = g_output_stream_write_all (
1638 G_OUTPUT_STREAM (file_output_stream),
1639 from_line, strlen (from_line), NULL,
1640 cancellable, error);
1641
1642 g_free (from_line);
1643
1644 if (!success) {
1645 g_object_unref (message);
1646 goto exit;
1647 }
1648
1649 filter = camel_mime_filter_from_new ();
1650 stream = camel_stream_filter_new (base_stream);
1651 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
1652
1653 retval = camel_data_wrapper_write_to_stream_sync (
1654 CAMEL_DATA_WRAPPER (message),
1655 stream, cancellable, error);
1656
1657 g_object_unref (filter);
1658 g_object_unref (stream);
1659
1660 if (retval == -1) {
1661 g_object_unref (message);
1662 goto exit;
1663 }
1664
1665 g_byte_array_append (byte_array, (guint8 *) "\n", 1);
1666
1667 success = g_output_stream_write_all (
1668 G_OUTPUT_STREAM (file_output_stream),
1669 byte_array->data, byte_array->len,
1670 NULL, cancellable, error);
1671
1672 if (!success) {
1673 g_object_unref (message);
1674 goto exit;
1675 }
1676
1677 percent = ((ii + 1) * 100) / message_uids->len;
1678 camel_operation_progress (cancellable, percent);
1679
1680 /* Reset the byte array for the next message. */
1681 g_byte_array_set_size (byte_array, 0);
1682
1683 g_object_unref (message);
1684 }
1685
1686 exit:
1687 if (base_stream != NULL)
1688 g_object_unref (base_stream);
1689
1690 g_byte_array_free (byte_array, TRUE);
1691
1692 g_object_unref (file_output_stream);
1693
1694 camel_operation_pop_message (cancellable);
1695
1696 if (!success) {
1697 /* Try deleting the destination file. */
1698 g_file_delete (destination, NULL, NULL);
1699 }
1700
1701 return success;
1702 }
1703
1704 void
e_mail_folder_save_messages(CamelFolder * folder,GPtrArray * message_uids,GFile * destination,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1705 e_mail_folder_save_messages (CamelFolder *folder,
1706 GPtrArray *message_uids,
1707 GFile *destination,
1708 gint io_priority,
1709 GCancellable *cancellable,
1710 GAsyncReadyCallback callback,
1711 gpointer user_data)
1712 {
1713 GSimpleAsyncResult *simple;
1714 AsyncContext *context;
1715
1716 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1717 g_return_if_fail (message_uids != NULL);
1718 g_return_if_fail (G_IS_FILE (destination));
1719
1720 /* Need at least one message UID to save. */
1721 g_return_if_fail (message_uids->len > 0);
1722
1723 context = g_slice_new0 (AsyncContext);
1724 context->ptr_array = g_ptr_array_ref (message_uids);
1725 context->destination = g_object_ref (destination);
1726
1727 simple = g_simple_async_result_new (
1728 G_OBJECT (folder), callback, user_data,
1729 e_mail_folder_save_messages);
1730
1731 g_simple_async_result_set_check_cancellable (simple, cancellable);
1732
1733 g_simple_async_result_set_op_res_gpointer (
1734 simple, context, (GDestroyNotify) async_context_free);
1735
1736 g_simple_async_result_run_in_thread (
1737 simple, mail_folder_save_messages_thread,
1738 io_priority, cancellable);
1739
1740 g_object_unref (simple);
1741 }
1742
1743 gboolean
e_mail_folder_save_messages_finish(CamelFolder * folder,GAsyncResult * result,GError ** error)1744 e_mail_folder_save_messages_finish (CamelFolder *folder,
1745 GAsyncResult *result,
1746 GError **error)
1747 {
1748 GSimpleAsyncResult *simple;
1749
1750 g_return_val_if_fail (
1751 g_simple_async_result_is_valid (
1752 result, G_OBJECT (folder),
1753 e_mail_folder_save_messages), FALSE);
1754
1755 simple = G_SIMPLE_ASYNC_RESULT (result);
1756
1757 /* Assume success unless a GError is set. */
1758 return !g_simple_async_result_propagate_error (simple, error);
1759 }
1760
1761 /**
1762 * e_mail_folder_uri_build:
1763 * @store: a #CamelStore
1764 * @folder_name: a folder name
1765 *
1766 * Builds a folder URI string from @store and @folder_name.
1767 *
1768 * Returns: a newly-allocated folder URI string
1769 **/
1770 gchar *
e_mail_folder_uri_build(CamelStore * store,const gchar * folder_name)1771 e_mail_folder_uri_build (CamelStore *store,
1772 const gchar *folder_name)
1773 {
1774 const gchar *uid;
1775 gchar *encoded_name;
1776 gchar *encoded_uid;
1777 gchar *uri;
1778
1779 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1780 g_return_val_if_fail (folder_name != NULL, NULL);
1781
1782 /* Skip the leading slash, if present. */
1783 if (*folder_name == '/')
1784 folder_name++;
1785
1786 uid = camel_service_get_uid (CAMEL_SERVICE (store));
1787
1788 encoded_uid = camel_url_encode (uid, ":;@/");
1789 encoded_name = camel_url_encode (folder_name, ":;@?#");
1790
1791 uri = g_strdup_printf ("folder://%s/%s", encoded_uid, encoded_name);
1792
1793 g_free (encoded_uid);
1794 g_free (encoded_name);
1795
1796 return uri;
1797 }
1798
1799 /**
1800 * e_mail_folder_uri_parse:
1801 * @session: a #CamelSession
1802 * @folder_uri: a folder URI
1803 * @out_store: return location for a #CamelStore, or %NULL
1804 * @out_folder_name: return location for a folder name, or %NULL
1805 * @error: return location for a #GError, or %NULL
1806 *
1807 * Parses a folder URI generated by e_mail_folder_uri_build() and
1808 * returns the corresponding #CamelStore instance in @out_store and
1809 * folder name string in @out_folder_name. If the URI is malformed
1810 * or no corresponding store exists, the function sets @error and
1811 * returns %FALSE.
1812 *
1813 * If the function is able to parse the URI, the #CamelStore instance
1814 * set in @out_store should be unreferenced with g_object_unref() when
1815 * done with it, and the folder name string set in @out_folder_name
1816 * should be freed with g_free().
1817 *
1818 * The function also handles older style URIs, such as ones where the
1819 * #CamelStore's #CamelStore::uri string was embedded directly in the
1820 * folder URI, and account-based URIs that used an "email://" prefix.
1821 *
1822 * Returns: %TRUE if @folder_uri could be parsed, %FALSE otherwise
1823 **/
1824 gboolean
e_mail_folder_uri_parse(CamelSession * session,const gchar * folder_uri,CamelStore ** out_store,gchar ** out_folder_name,GError ** error)1825 e_mail_folder_uri_parse (CamelSession *session,
1826 const gchar *folder_uri,
1827 CamelStore **out_store,
1828 gchar **out_folder_name,
1829 GError **error)
1830 {
1831 CamelURL *url;
1832 CamelService *service = NULL;
1833 gchar *folder_name = NULL;
1834 gboolean success = FALSE;
1835
1836 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1837 g_return_val_if_fail (folder_uri != NULL, FALSE);
1838
1839 url = camel_url_new (folder_uri, error);
1840 if (url == NULL)
1841 return FALSE;
1842
1843 /* Current URI Format: 'folder://' STORE_UID '/' FOLDER_PATH */
1844 if (g_strcmp0 (url->protocol, "folder") == 0) {
1845
1846 if (url->host != NULL) {
1847 gchar *uid;
1848
1849 if (url->user == NULL || *url->user == '\0')
1850 uid = g_strdup (url->host);
1851 else
1852 uid = g_strconcat (
1853 url->user, "@", url->host, NULL);
1854
1855 service = camel_session_ref_service (session, uid);
1856 g_free (uid);
1857 }
1858
1859 if (url->path != NULL && *url->path == '/')
1860 folder_name = camel_url_decode_path (url->path + 1);
1861
1862 /* This style was used to reference accounts by UID before
1863 * CamelServices themselves had UIDs. Some examples are:
1864 *
1865 * Special cases:
1866 *
1867 * 'email://local@local/' FOLDER_PATH
1868 * 'email://vfolder@local/' FOLDER_PATH
1869 *
1870 * General case:
1871 *
1872 * 'email://' ACCOUNT_UID '/' FOLDER_PATH
1873 *
1874 * Note: ACCOUNT_UID is now equivalent to STORE_UID, and
1875 * the STORE_UIDs for the special cases are 'local'
1876 * and 'vfolder'.
1877 */
1878 } else if (g_strcmp0 (url->protocol, "email") == 0) {
1879 gchar *uid = NULL;
1880
1881 /* Handle the special cases. */
1882 if (g_strcmp0 (url->host, "local") == 0) {
1883 if (g_strcmp0 (url->user, "local") == 0)
1884 uid = g_strdup ("local");
1885 if (g_strcmp0 (url->user, "vfolder") == 0)
1886 uid = g_strdup ("vfolder");
1887 }
1888
1889 /* Handle the general case. */
1890 if (uid == NULL && url->host != NULL) {
1891 if (url->user == NULL)
1892 uid = g_strdup (url->host);
1893 else
1894 uid = g_strdup_printf (
1895 "%s@%s", url->user, url->host);
1896 }
1897
1898 if (uid != NULL) {
1899 service = camel_session_ref_service (session, uid);
1900 g_free (uid);
1901 }
1902
1903 if (url->path != NULL && *url->path == '/')
1904 folder_name = camel_url_decode_path (url->path + 1);
1905
1906 /* CamelFolderInfo URIs used to embed the store's URI, so the
1907 * folder name is appended as either a path part or a fragment
1908 * part, depending whether the store's URI used the path part.
1909 * To determine which it is, you have to check the provider
1910 * flags for CAMEL_URL_FRAGMENT_IS_PATH. */
1911 } else {
1912 gboolean local_mbox_folder;
1913
1914 /* In Evolution 2.x, the local mail store used mbox
1915 * format. camel_session_ref_service_by_url() won't
1916 * match "mbox:///.../mail/local" folder URIs, since
1917 * the local mail store is now Maildir format. Test
1918 * for this corner case and work around it.
1919 *
1920 * The folder path is kept in the fragment part of the
1921 * URL which makes it easy to test the filesystem path.
1922 * The suffix "evolution/mail/local" should match both
1923 * the current XDG-compliant location and the old "dot
1924 * folder" location (~/.evolution/mail/local). */
1925 local_mbox_folder =
1926 (g_strcmp0 (url->protocol, "mbox") == 0) &&
1927 (url->path != NULL) &&
1928 g_str_has_suffix (url->path, "evolution/mail/local");
1929
1930 if (local_mbox_folder) {
1931 service = camel_session_ref_service (session, "local");
1932 } else {
1933 service = camel_session_ref_service_by_url (
1934 session, url, CAMEL_PROVIDER_STORE);
1935 }
1936
1937 if (CAMEL_IS_STORE (service)) {
1938 CamelProvider *provider;
1939
1940 provider = camel_service_get_provider (service);
1941
1942 if (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
1943 folder_name = g_strdup (url->fragment);
1944 else if (url->path != NULL && *url->path == '/')
1945 folder_name = g_strdup (url->path + 1);
1946 }
1947 }
1948
1949 if (CAMEL_IS_STORE (service) && folder_name != NULL) {
1950 if (out_store != NULL)
1951 *out_store = CAMEL_STORE (g_object_ref (service));
1952
1953 if (out_folder_name != NULL) {
1954 *out_folder_name = folder_name;
1955 folder_name = NULL;
1956 }
1957
1958 success = TRUE;
1959 } else {
1960 g_set_error (
1961 error, CAMEL_FOLDER_ERROR,
1962 CAMEL_FOLDER_ERROR_INVALID,
1963 _("Invalid folder URI “%s”"),
1964 folder_uri);
1965 }
1966
1967 if (service != NULL)
1968 g_object_unref (service);
1969
1970 g_free (folder_name);
1971
1972 camel_url_free (url);
1973
1974 return success;
1975 }
1976
1977 /**
1978 * e_mail_folder_uri_equal:
1979 * @session: a #CamelSession
1980 * @folder_uri_a: a folder URI
1981 * @folder_uri_b: another folder URI
1982 *
1983 * Compares two folder URIs for equality. If either URI is invalid,
1984 * the function returns %FALSE.
1985 *
1986 * Returns: %TRUE if the URIs are equal, %FALSE if not
1987 **/
1988 gboolean
e_mail_folder_uri_equal(CamelSession * session,const gchar * folder_uri_a,const gchar * folder_uri_b)1989 e_mail_folder_uri_equal (CamelSession *session,
1990 const gchar *folder_uri_a,
1991 const gchar *folder_uri_b)
1992 {
1993 CamelStore *store_a;
1994 CamelStore *store_b;
1995 CamelStoreClass *class;
1996 gchar *folder_name_a;
1997 gchar *folder_name_b;
1998 gboolean success_a;
1999 gboolean success_b;
2000 gboolean equal = FALSE;
2001
2002 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
2003 g_return_val_if_fail (folder_uri_a != NULL, FALSE);
2004 g_return_val_if_fail (folder_uri_b != NULL, FALSE);
2005
2006 success_a = e_mail_folder_uri_parse (
2007 session, folder_uri_a, &store_a, &folder_name_a, NULL);
2008
2009 success_b = e_mail_folder_uri_parse (
2010 session, folder_uri_b, &store_b, &folder_name_b, NULL);
2011
2012 if (!success_a || !success_b)
2013 goto exit;
2014
2015 if (store_a != store_b)
2016 goto exit;
2017
2018 /* Doesn't matter which store we use since they're the same. */
2019 class = CAMEL_STORE_GET_CLASS (store_a);
2020 g_return_val_if_fail (class->equal_folder_name != NULL, FALSE);
2021
2022 equal = class->equal_folder_name (folder_name_a, folder_name_b);
2023
2024 exit:
2025 if (success_a) {
2026 g_object_unref (store_a);
2027 g_free (folder_name_a);
2028 }
2029
2030 if (success_b) {
2031 g_object_unref (store_b);
2032 g_free (folder_name_b);
2033 }
2034
2035 return equal;
2036 }
2037
2038 /**
2039 * e_mail_folder_uri_from_folder:
2040 * @folder: a #CamelFolder
2041 *
2042 * Convenience function for building a folder URI from a #CamelFolder.
2043 * Free the returned URI string with g_free().
2044 *
2045 * Returns: a newly-allocated folder URI string
2046 **/
2047 gchar *
e_mail_folder_uri_from_folder(CamelFolder * folder)2048 e_mail_folder_uri_from_folder (CamelFolder *folder)
2049 {
2050 CamelStore *store;
2051 const gchar *folder_name;
2052
2053 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2054
2055 store = camel_folder_get_parent_store (folder);
2056 folder_name = camel_folder_get_full_name (folder);
2057
2058 return e_mail_folder_uri_build (store, folder_name);
2059 }
2060
2061 /**
2062 * e_mail_folder_uri_to_markup:
2063 * @session: a #CamelSession
2064 * @folder_uri: a folder URI
2065 * @error: return location for a #GError, or %NULL
2066 *
2067 * Converts @folder_uri to a markup string suitable for displaying to users.
2068 * The string consists of the #CamelStore display name (in bold), followed
2069 * by the folder path. If the URI is malformed or no corresponding store
2070 * exists, the function sets @error and returns %NULL. Free the returned
2071 * string with g_free().
2072 *
2073 * Returns: a newly-allocated markup string, or %NULL
2074 **/
2075 gchar *
e_mail_folder_uri_to_markup(CamelSession * session,const gchar * folder_uri,GError ** error)2076 e_mail_folder_uri_to_markup (CamelSession *session,
2077 const gchar *folder_uri,
2078 GError **error)
2079 {
2080 CamelStore *store = NULL;
2081 const gchar *display_name;
2082 gchar *folder_name = NULL;
2083 gchar *markup;
2084 gboolean success;
2085
2086 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
2087 g_return_val_if_fail (folder_uri != NULL, NULL);
2088
2089 success = e_mail_folder_uri_parse (
2090 session, folder_uri, &store, &folder_name, error);
2091
2092 if (!success)
2093 return NULL;
2094
2095 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
2096 g_return_val_if_fail (folder_name != NULL, NULL);
2097
2098 display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
2099
2100 markup = g_markup_printf_escaped (
2101 "<b>%s</b> : %s", display_name, folder_name);
2102
2103 g_object_unref (store);
2104 g_free (folder_name);
2105
2106 return markup;
2107 }
2108
2109 /**
2110 * e_mail_folder_to_full_display_name:
2111 * @folder: a #CamelFolder
2112 * @error: return location for a #GError, or %NULL
2113 *
2114 * Returns similar description as e_mail_folder_uri_to_markup(), only without markup
2115 * and rather for a @folder, than for a folder URI. Returned pointer should be freed
2116 * with g_free() when no longer needed.
2117 *
2118 * Returns: a newly-allocated string, or %NULL
2119 *
2120 * Since: 3.18
2121 **/
2122 gchar *
e_mail_folder_to_full_display_name(CamelFolder * folder,GError ** error)2123 e_mail_folder_to_full_display_name (CamelFolder *folder,
2124 GError **error)
2125 {
2126 CamelSession *session;
2127 CamelStore *store;
2128 gchar *folder_uri, *full_display_name = NULL, *folder_name = NULL;
2129
2130 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2131
2132 folder_uri = e_mail_folder_uri_from_folder (folder);
2133 if (!folder_uri)
2134 return NULL;
2135
2136 store = camel_folder_get_parent_store (folder);
2137 if (!store) {
2138 g_warn_if_reached ();
2139 g_free (folder_uri);
2140
2141 return NULL;
2142 }
2143
2144 session = camel_service_ref_session (CAMEL_SERVICE (store));
2145 if (!session) {
2146 g_warn_if_reached ();
2147 g_free (folder_uri);
2148
2149 return NULL;
2150 }
2151
2152 if (e_mail_folder_uri_parse (session, folder_uri, NULL, &folder_name, error)) {
2153 const gchar *service_display_name;
2154
2155 service_display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
2156
2157 if (CAMEL_IS_VEE_FOLDER (folder) && (
2158 g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0 ||
2159 g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)) {
2160 full_display_name = g_strdup_printf ("%s : %s", service_display_name, camel_folder_get_display_name (folder));
2161 } else {
2162 full_display_name = g_strdup_printf ("%s : %s", service_display_name, folder_name);
2163 }
2164
2165 g_free (folder_name);
2166 }
2167
2168 g_clear_object (&session);
2169 g_free (folder_uri);
2170
2171 return full_display_name;
2172 }
2173