1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-folder.c : class for a imap folder
3 *
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * This library is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This library 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 Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Michael Zucchi <notzed@ximian.com>
19 */
20
21 #include "evolution-data-server-config.h"
22
23 #include <errno.h>
24 #include <glib/gi18n-lib.h>
25
26 #include "camel-imapx-folder.h"
27 #include "camel-imapx-search.h"
28 #include "camel-imapx-server.h"
29 #include "camel-imapx-settings.h"
30 #include "camel-imapx-store.h"
31 #include "camel-imapx-summary.h"
32 #include "camel-imapx-utils.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36
37 #define d(...) camel_imapx_debug(debug, '?', __VA_ARGS__)
38
39 struct _CamelIMAPXFolderPrivate {
40 GMutex property_lock;
41 GWeakRef mailbox;
42
43 GMutex move_to_hash_table_lock;
44 GHashTable *move_to_real_junk_uids;
45 GHashTable *move_to_real_trash_uids;
46 GHashTable *move_to_inbox_uids;
47
48 gboolean check_folder;
49 };
50
51 /* The custom property ID is a CamelArg artifact.
52 * It still identifies the property in state files. */
53 enum {
54 PROP_0,
55 PROP_MAILBOX,
56 PROP_APPLY_FILTERS = 0x2501,
57 PROP_CHECK_FOLDER = 0x2502
58 };
59
60 G_DEFINE_TYPE_WITH_PRIVATE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER)
61
62 static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder);
63
64 void
camel_imapx_folder_claim_move_to_real_junk_uids(CamelIMAPXFolder * folder,GPtrArray * out_uids_to_copy)65 camel_imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder,
66 GPtrArray *out_uids_to_copy)
67 {
68 GList *keys;
69
70 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
71
72 keys = g_hash_table_get_keys (folder->priv->move_to_real_junk_uids);
73 g_hash_table_steal_all (folder->priv->move_to_real_junk_uids);
74
75 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
76
77 while (keys != NULL) {
78 g_ptr_array_add (out_uids_to_copy, keys->data);
79 keys = g_list_delete_link (keys, keys);
80 }
81 }
82
83 void
camel_imapx_folder_claim_move_to_real_trash_uids(CamelIMAPXFolder * folder,GPtrArray * out_uids_to_copy)84 camel_imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder,
85 GPtrArray *out_uids_to_copy)
86 {
87 GList *keys;
88
89 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
90
91 keys = g_hash_table_get_keys (folder->priv->move_to_real_trash_uids);
92 g_hash_table_steal_all (folder->priv->move_to_real_trash_uids);
93
94 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
95
96 while (keys != NULL) {
97 g_ptr_array_add (out_uids_to_copy, keys->data);
98 keys = g_list_delete_link (keys, keys);
99 }
100 }
101
102 void
camel_imapx_folder_claim_move_to_inbox_uids(CamelIMAPXFolder * folder,GPtrArray * out_uids_to_copy)103 camel_imapx_folder_claim_move_to_inbox_uids (CamelIMAPXFolder *folder,
104 GPtrArray *out_uids_to_copy)
105 {
106 GList *keys;
107
108 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
109
110 keys = g_hash_table_get_keys (folder->priv->move_to_inbox_uids);
111 g_hash_table_steal_all (folder->priv->move_to_inbox_uids);
112
113 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
114
115 while (keys != NULL) {
116 g_ptr_array_add (out_uids_to_copy, keys->data);
117 keys = g_list_delete_link (keys, keys);
118 }
119 }
120
121 static gboolean
imapx_folder_get_apply_filters(CamelIMAPXFolder * folder)122 imapx_folder_get_apply_filters (CamelIMAPXFolder *folder)
123 {
124 g_return_val_if_fail (folder != NULL, FALSE);
125 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
126
127 return folder->apply_filters;
128 }
129
130 static void
imapx_folder_set_apply_filters(CamelIMAPXFolder * folder,gboolean apply_filters)131 imapx_folder_set_apply_filters (CamelIMAPXFolder *folder,
132 gboolean apply_filters)
133 {
134 g_return_if_fail (folder != NULL);
135 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
136
137 if (folder->apply_filters == apply_filters)
138 return;
139
140 folder->apply_filters = apply_filters;
141
142 g_object_notify (G_OBJECT (folder), "apply-filters");
143 }
144
145 static void
imapx_folder_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)146 imapx_folder_set_property (GObject *object,
147 guint property_id,
148 const GValue *value,
149 GParamSpec *pspec)
150 {
151 switch (property_id) {
152 case PROP_APPLY_FILTERS:
153 imapx_folder_set_apply_filters (
154 CAMEL_IMAPX_FOLDER (object),
155 g_value_get_boolean (value));
156 return;
157
158 case PROP_CHECK_FOLDER:
159 camel_imapx_folder_set_check_folder (
160 CAMEL_IMAPX_FOLDER (object),
161 g_value_get_boolean (value));
162 return;
163
164 case PROP_MAILBOX:
165 camel_imapx_folder_set_mailbox (
166 CAMEL_IMAPX_FOLDER (object),
167 g_value_get_object (value));
168 return;
169 }
170
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
172 }
173
174 static void
imapx_folder_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)175 imapx_folder_get_property (GObject *object,
176 guint property_id,
177 GValue *value,
178 GParamSpec *pspec)
179 {
180 switch (property_id) {
181 case PROP_APPLY_FILTERS:
182 g_value_set_boolean (
183 value,
184 imapx_folder_get_apply_filters (
185 CAMEL_IMAPX_FOLDER (object)));
186 return;
187
188 case PROP_CHECK_FOLDER:
189 g_value_set_boolean (
190 value,
191 camel_imapx_folder_get_check_folder (
192 CAMEL_IMAPX_FOLDER (object)));
193 return;
194
195 case PROP_MAILBOX:
196 g_value_take_object (
197 value,
198 camel_imapx_folder_ref_mailbox (
199 CAMEL_IMAPX_FOLDER (object)));
200 return;
201 }
202
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204 }
205
206 static void
imapx_folder_dispose(GObject * object)207 imapx_folder_dispose (GObject *object)
208 {
209 CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
210 CamelStore *store;
211
212 g_clear_object (&folder->cache);
213 g_clear_object (&folder->search);
214
215 store = camel_folder_get_parent_store (CAMEL_FOLDER (folder));
216 if (store != NULL) {
217 camel_store_summary_disconnect_folder_summary (
218 CAMEL_IMAPX_STORE (store)->summary,
219 camel_folder_get_folder_summary (CAMEL_FOLDER (folder)));
220 }
221
222 g_weak_ref_set (&folder->priv->mailbox, NULL);
223
224 /* Chain up to parent's dispose() method. */
225 G_OBJECT_CLASS (camel_imapx_folder_parent_class)->dispose (object);
226 }
227
228 static void
imapx_folder_finalize(GObject * object)229 imapx_folder_finalize (GObject *object)
230 {
231 CamelIMAPXFolder *folder = CAMEL_IMAPX_FOLDER (object);
232
233 g_mutex_clear (&folder->search_lock);
234 g_mutex_clear (&folder->stream_lock);
235
236 g_mutex_clear (&folder->priv->property_lock);
237
238 g_mutex_clear (&folder->priv->move_to_hash_table_lock);
239 g_hash_table_destroy (folder->priv->move_to_real_junk_uids);
240 g_hash_table_destroy (folder->priv->move_to_real_trash_uids);
241 g_hash_table_destroy (folder->priv->move_to_inbox_uids);
242
243 g_weak_ref_clear (&folder->priv->mailbox);
244
245 /* Chain up to parent's finalize() method. */
246 G_OBJECT_CLASS (camel_imapx_folder_parent_class)->finalize (object);
247 }
248
249 /* Algorithm for selecting a folder:
250 *
251 * - If uidvalidity == old uidvalidity
252 * and exsists == old exists
253 * and recent == old recent
254 * and unseen == old unseen
255 * Assume our summary is correct
256 * for each summary item
257 * mark the summary item as 'old/not updated'
258 * rof
259 * fetch flags from 1:*
260 * for each fetch response
261 * info = summary[index]
262 * if info.uid != uid
263 * info = summary_by_uid[uid]
264 * fi
265 * if info == NULL
266 * create new info @ index
267 * fi
268 * if got.flags
269 * update flags
270 * fi
271 * if got.header
272 * update based on header
273 * mark as retrieved
274 * else if got.body
275 * update based on imap body
276 * mark as retrieved
277 * fi
278 *
279 * Async fetch response:
280 * info = summary[index]
281 * if info == null
282 * if uid == null
283 * force resync/select?
284 * info = empty @ index
285 * else if uid && info.uid != uid
286 * force a resync?
287 * return
288 * fi
289 *
290 * if got.flags {
291 * info.flags = flags
292 * }
293 * if got.header {
294 * info.init (header)
295 * info.empty = false
296 * }
297 *
298 * info.state - 2 bit field in flags
299 * 0 = empty, nothing set
300 * 1 = uid & flags set
301 * 2 = update required
302 * 3 = up to date
303 */
304
305 static void
imapx_search_free(CamelFolder * folder,GPtrArray * uids)306 imapx_search_free (CamelFolder *folder,
307 GPtrArray *uids)
308 {
309 CamelIMAPXFolder *imapx_folder;
310
311 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
312
313 g_return_if_fail (imapx_folder->search);
314
315 g_mutex_lock (&imapx_folder->search_lock);
316
317 camel_folder_search_free_result (imapx_folder->search, uids);
318
319 g_mutex_unlock (&imapx_folder->search_lock);
320 }
321
322 static GPtrArray *
imapx_search_by_uids(CamelFolder * folder,const gchar * expression,GPtrArray * uids,GCancellable * cancellable,GError ** error)323 imapx_search_by_uids (CamelFolder *folder,
324 const gchar *expression,
325 GPtrArray *uids,
326 GCancellable *cancellable,
327 GError **error)
328 {
329 CamelIMAPXFolder *imapx_folder;
330 CamelIMAPXSearch *imapx_search;
331 GPtrArray *matches;
332
333 if (uids->len == 0)
334 return g_ptr_array_new ();
335
336 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
337
338 g_mutex_lock (&imapx_folder->search_lock);
339
340 imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
341
342 camel_folder_search_set_folder (imapx_folder->search, folder);
343 camel_imapx_search_clear_cached_results (imapx_search);
344 camel_imapx_search_set_cancellable_and_error (imapx_search, cancellable, error);
345
346 matches = camel_folder_search_search (
347 imapx_folder->search, expression, uids, cancellable, error);
348
349 camel_imapx_search_set_cancellable_and_error (imapx_search, NULL, NULL);
350 camel_imapx_search_clear_cached_results (imapx_search);
351
352 g_mutex_unlock (&imapx_folder->search_lock);
353
354 return matches;
355 }
356
357 static guint32
imapx_count_by_expression(CamelFolder * folder,const gchar * expression,GCancellable * cancellable,GError ** error)358 imapx_count_by_expression (CamelFolder *folder,
359 const gchar *expression,
360 GCancellable *cancellable,
361 GError **error)
362 {
363 CamelIMAPXFolder *imapx_folder;
364 CamelIMAPXSearch *imapx_search;
365 guint32 matches;
366
367 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
368
369 g_mutex_lock (&imapx_folder->search_lock);
370
371 imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
372
373 camel_folder_search_set_folder (imapx_folder->search, folder);
374 camel_imapx_search_clear_cached_results (imapx_search);
375 camel_imapx_search_set_cancellable_and_error (imapx_search, cancellable, error);
376
377 matches = camel_folder_search_count (
378 imapx_folder->search, expression, cancellable, error);
379
380 camel_imapx_search_set_cancellable_and_error (imapx_search, NULL, NULL);
381 camel_imapx_search_clear_cached_results (imapx_search);
382
383 g_mutex_unlock (&imapx_folder->search_lock);
384
385 return matches;
386 }
387
388 static GPtrArray *
imapx_search_by_expression(CamelFolder * folder,const gchar * expression,GCancellable * cancellable,GError ** error)389 imapx_search_by_expression (CamelFolder *folder,
390 const gchar *expression,
391 GCancellable *cancellable,
392 GError **error)
393 {
394 CamelIMAPXFolder *imapx_folder;
395 CamelIMAPXSearch *imapx_search;
396 GPtrArray *matches;
397
398 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
399
400 g_mutex_lock (&imapx_folder->search_lock);
401
402 imapx_search = CAMEL_IMAPX_SEARCH (imapx_folder->search);
403
404 camel_folder_search_set_folder (imapx_folder->search, folder);
405 camel_imapx_search_clear_cached_results (imapx_search);
406 camel_imapx_search_set_cancellable_and_error (imapx_search, cancellable, error);
407
408 matches = camel_folder_search_search (
409 imapx_folder->search, expression, NULL, cancellable, error);
410
411 camel_imapx_search_set_cancellable_and_error (imapx_search, NULL, NULL);
412 camel_imapx_search_clear_cached_results (imapx_search);
413
414 g_mutex_unlock (&imapx_folder->search_lock);
415
416 return matches;
417 }
418
419 static GPtrArray *
imapx_get_uncached_uids(CamelFolder * folder,GPtrArray * uids,GError ** error)420 imapx_get_uncached_uids (CamelFolder *folder,
421 GPtrArray *uids,
422 GError **error)
423 {
424 CamelIMAPXFolder *imapx_folder;
425 GPtrArray *result;
426 guint ii;
427
428 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
429 g_return_val_if_fail (uids != NULL, NULL);
430
431 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
432
433 result = g_ptr_array_sized_new (uids->len);
434
435 for (ii = 0; ii < uids->len; ii++) {
436 const gchar *uid = uids->pdata[ii];
437 GIOStream *io_stream;
438
439 /* Assume that UIDs with existing stream are valid;
440 the imapx_get_message_cached() can fail with broken files, but it's rather unlikely. */
441 io_stream = camel_data_cache_get (imapx_folder->cache, "cur", uid, NULL);
442 if (io_stream)
443 g_object_unref (io_stream);
444 else
445 g_ptr_array_add (result, (gpointer) camel_pstring_strdup (uid));
446 }
447
448 return result;
449 }
450
451 static gchar *
imapx_get_filename(CamelFolder * folder,const gchar * uid,GError ** error)452 imapx_get_filename (CamelFolder *folder,
453 const gchar *uid,
454 GError **error)
455 {
456 CamelIMAPXFolder *imapx_folder;
457
458 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
459
460 return camel_data_cache_get_filename (
461 imapx_folder->cache, "cache", uid);
462 }
463
464 static gboolean
imapx_append_message_sync(CamelFolder * folder,CamelMimeMessage * message,CamelMessageInfo * info,gchar ** appended_uid,GCancellable * cancellable,GError ** error)465 imapx_append_message_sync (CamelFolder *folder,
466 CamelMimeMessage *message,
467 CamelMessageInfo *info,
468 gchar **appended_uid,
469 GCancellable *cancellable,
470 GError **error)
471 {
472 CamelStore *store;
473 CamelIMAPXStore *imapx_store;
474 CamelIMAPXConnManager *conn_man;
475 CamelIMAPXMailbox *mailbox = NULL;
476 gboolean success = FALSE;
477
478 if (appended_uid != NULL)
479 *appended_uid = NULL;
480
481 store = camel_folder_get_parent_store (folder);
482
483 imapx_store = CAMEL_IMAPX_STORE (store);
484 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
485
486 mailbox = camel_imapx_folder_list_mailbox (
487 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
488
489 if (mailbox == NULL)
490 goto exit;
491
492 success = camel_imapx_conn_manager_append_message_sync (
493 conn_man, mailbox, camel_folder_get_folder_summary (folder),
494 CAMEL_IMAPX_FOLDER (folder)->cache, message,
495 info, appended_uid, cancellable, error);
496
497 exit:
498 g_clear_object (&mailbox);
499
500 return success;
501 }
502
503 static gboolean
imapx_expunge_sync(CamelFolder * folder,GCancellable * cancellable,GError ** error)504 imapx_expunge_sync (CamelFolder *folder,
505 GCancellable *cancellable,
506 GError **error)
507 {
508 CamelStore *store;
509 CamelIMAPXStore *imapx_store;
510 CamelIMAPXConnManager *conn_man;
511 CamelIMAPXMailbox *mailbox = NULL;
512 GError *local_error = NULL;
513 gboolean success = FALSE;
514
515 store = camel_folder_get_parent_store (folder);
516
517 imapx_store = CAMEL_IMAPX_STORE (store);
518 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
519
520 mailbox = camel_imapx_folder_list_mailbox (
521 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
522
523 if (mailbox == NULL)
524 goto exit;
525
526 if ((camel_store_get_flags (store) & CAMEL_STORE_VTRASH) == 0) {
527 CamelFolder *trash;
528 const gchar *full_name;
529
530 full_name = camel_folder_get_full_name (folder);
531
532 trash = camel_store_get_trash_folder_sync (store, cancellable, &local_error);
533
534 if (local_error == NULL && trash && (folder == trash || g_ascii_strcasecmp (full_name, camel_folder_get_full_name (trash)) == 0)) {
535 CamelFolderSummary *folder_summary;
536 CamelMessageInfo *info;
537 GPtrArray *known_uids;
538 gint ii;
539
540 folder_summary = camel_folder_get_folder_summary (folder);
541 camel_folder_summary_lock (folder_summary);
542
543 camel_folder_summary_prepare_fetch_all (folder_summary, NULL);
544 known_uids = camel_folder_summary_get_array (folder_summary);
545
546 /* it's a real trash folder, thus delete all mails from there */
547 for (ii = 0; known_uids && ii < known_uids->len; ii++) {
548 info = camel_folder_summary_get (camel_folder_get_folder_summary (folder), g_ptr_array_index (known_uids, ii));
549 if (info) {
550 camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
551 g_clear_object (&info);
552 }
553 }
554
555 camel_folder_summary_unlock (folder_summary);
556
557 camel_folder_summary_free_array (known_uids);
558 }
559
560 g_clear_object (&trash);
561 g_clear_error (&local_error);
562 }
563
564 success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error);
565
566 exit:
567 g_clear_object (&mailbox);
568
569 return success;
570 }
571
572 static CamelMimeMessage *
imapx_message_from_stream_sync(CamelIMAPXFolder * imapx_folder,CamelStream * stream,GCancellable * cancellable,GError ** error)573 imapx_message_from_stream_sync (CamelIMAPXFolder *imapx_folder,
574 CamelStream *stream,
575 GCancellable *cancellable,
576 GError **error)
577 {
578 CamelMimeMessage *msg;
579
580 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (imapx_folder), NULL);
581
582 if (!stream)
583 return NULL;
584
585 msg = camel_mime_message_new ();
586
587 g_mutex_lock (&imapx_folder->stream_lock);
588
589 /* Make sure the stream is at the beginning. It can be, when there are two
590 concurrent requests for a message, they both use the same underlying stream
591 from the local cache (encapsulated in the CamelStream), where one reads
592 it completely and lefts it at the end, thus the second caller reads
593 the stream from a wrong position. */
594 g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable, NULL);
595
596 if (!camel_data_wrapper_construct_from_stream_sync (CAMEL_DATA_WRAPPER (msg), stream, cancellable, error))
597 g_clear_object (&msg);
598
599 g_mutex_unlock (&imapx_folder->stream_lock);
600
601 return msg;
602 }
603
604 static CamelMimeMessage *
imapx_get_message_cached(CamelFolder * folder,const gchar * message_uid,GCancellable * cancellable)605 imapx_get_message_cached (CamelFolder *folder,
606 const gchar *message_uid,
607 GCancellable *cancellable)
608 {
609 CamelIMAPXFolder *imapx_folder;
610 CamelMimeMessage *msg = NULL;
611 CamelStream *stream = NULL;
612 GIOStream *base_stream;
613
614 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
615 g_return_val_if_fail (message_uid != NULL, NULL);
616
617 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
618
619 base_stream = camel_data_cache_get (imapx_folder->cache, "cur", message_uid, NULL);
620 if (base_stream != NULL) {
621 stream = camel_stream_new (base_stream);
622 g_object_unref (base_stream);
623 }
624
625 if (stream != NULL) {
626 msg = imapx_message_from_stream_sync (imapx_folder, stream, cancellable, NULL);
627
628 g_object_unref (stream);
629 }
630
631 return msg;
632 }
633
634 static CamelMimeMessage *
imapx_get_message_sync(CamelFolder * folder,const gchar * uid,GCancellable * cancellable,GError ** error)635 imapx_get_message_sync (CamelFolder *folder,
636 const gchar *uid,
637 GCancellable *cancellable,
638 GError **error)
639 {
640 CamelMimeMessage *msg = NULL;
641 CamelStream *stream;
642 CamelStore *store;
643 CamelIMAPXFolder *imapx_folder;
644 GIOStream *base_stream;
645 const gchar *path = NULL;
646 gboolean offline_message = FALSE;
647
648 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
649 store = camel_folder_get_parent_store (folder);
650
651 if (!strchr (uid, '-'))
652 path = "cur";
653 else {
654 path = "new";
655 offline_message = TRUE;
656 }
657
658 base_stream = camel_data_cache_get (
659 imapx_folder->cache, path, uid, NULL);
660 if (base_stream != NULL) {
661 stream = camel_stream_new (base_stream);
662 g_object_unref (base_stream);
663 } else {
664 CamelIMAPXConnManager *conn_man;
665 CamelIMAPXMailbox *mailbox;
666
667 if (offline_message) {
668 g_set_error (
669 error, CAMEL_FOLDER_ERROR,
670 CAMEL_FOLDER_ERROR_INVALID_UID,
671 "Offline message vanished from disk: %s", uid);
672 return NULL;
673 }
674
675 conn_man = camel_imapx_store_get_conn_manager (CAMEL_IMAPX_STORE (store));
676
677 mailbox = camel_imapx_folder_list_mailbox (
678 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
679
680 if (mailbox == NULL)
681 return NULL;
682
683 stream = camel_imapx_conn_manager_get_message_sync (
684 conn_man, mailbox, camel_folder_get_folder_summary (folder),
685 CAMEL_IMAPX_FOLDER (folder)->cache, uid,
686 cancellable, error);
687
688 g_clear_object (&mailbox);
689 }
690
691 if (stream != NULL) {
692 msg = imapx_message_from_stream_sync (imapx_folder, stream, cancellable, error);
693
694 g_object_unref (stream);
695 }
696
697 if (msg != NULL) {
698 CamelMessageInfo *mi;
699
700 mi = camel_folder_summary_get (camel_folder_get_folder_summary (folder), uid);
701 if (mi != NULL) {
702 CamelMessageFlags flags;
703 gboolean has_attachment;
704
705 flags = camel_message_info_get_flags (mi);
706 has_attachment = camel_mime_message_has_attachment (msg);
707 if (((flags & CAMEL_MESSAGE_ATTACHMENTS) && !has_attachment) ||
708 ((flags & CAMEL_MESSAGE_ATTACHMENTS) == 0 && has_attachment)) {
709 camel_message_info_set_flags (
710 mi, CAMEL_MESSAGE_ATTACHMENTS,
711 has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
712 }
713
714 g_clear_object (&mi);
715 }
716 }
717
718 return msg;
719 }
720
721 static CamelFolderQuotaInfo *
imapx_get_quota_info_sync(CamelFolder * folder,GCancellable * cancellable,GError ** error)722 imapx_get_quota_info_sync (CamelFolder *folder,
723 GCancellable *cancellable,
724 GError **error)
725 {
726 CamelStore *store;
727 CamelIMAPXStore *imapx_store;
728 CamelIMAPXConnManager *conn_man;
729 CamelIMAPXMailbox *mailbox = NULL;
730 CamelFolderQuotaInfo *quota_info = NULL;
731 gchar **quota_roots;
732 gboolean success = FALSE;
733
734 store = camel_folder_get_parent_store (folder);
735
736 imapx_store = CAMEL_IMAPX_STORE (store);
737 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
738
739 mailbox = camel_imapx_folder_list_mailbox (
740 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
741 if (mailbox == NULL)
742 goto exit;
743
744 success = camel_imapx_conn_manager_update_quota_info_sync (conn_man, mailbox, cancellable, error);
745
746 if (!success)
747 goto exit;
748
749 quota_roots = camel_imapx_mailbox_dup_quota_roots (mailbox);
750
751 /* XXX Just return info for the first quota root, I guess. */
752 if (quota_roots != NULL && quota_roots[0] != NULL) {
753 quota_info = camel_imapx_store_dup_quota_info (
754 CAMEL_IMAPX_STORE (store), quota_roots[0]);
755 }
756
757 g_strfreev (quota_roots);
758
759 if (quota_info == NULL)
760 g_set_error (
761 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
762 /* Translators: The first “%s” is replaced with an account name and the second “%s”
763 is replaced with a full path name. The spaces around “:” are intentional, as
764 the whole “%s : %s” is meant as an absolute identification of the folder. */
765 _("No quota information available for folder “%s : %s”"),
766 camel_service_get_display_name (CAMEL_SERVICE (store)),
767 camel_folder_get_full_name (folder));
768
769 exit:
770 g_clear_object (&mailbox);
771
772 return quota_info;
773 }
774
775 static gboolean
imapx_purge_message_cache_sync(CamelFolder * folder,gchar * start_uid,gchar * end_uid,GCancellable * cancellable,GError ** error)776 imapx_purge_message_cache_sync (CamelFolder *folder,
777 gchar *start_uid,
778 gchar *end_uid,
779 GCancellable *cancellable,
780 GError **error)
781 {
782 /* Not Implemented for now. */
783 return TRUE;
784 }
785
786 static gboolean
imapx_refresh_info_sync(CamelFolder * folder,GCancellable * cancellable,GError ** error)787 imapx_refresh_info_sync (CamelFolder *folder,
788 GCancellable *cancellable,
789 GError **error)
790 {
791 CamelStore *store;
792 CamelIMAPXStore *imapx_store;
793 CamelIMAPXConnManager *conn_man;
794 CamelIMAPXMailbox *mailbox = NULL;
795 gboolean success = FALSE;
796
797 store = camel_folder_get_parent_store (folder);
798
799 /* Not connected, thus skip the operation */
800 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
801 return TRUE;
802
803 imapx_store = CAMEL_IMAPX_STORE (store);
804 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
805
806 mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error);
807
808 if (mailbox) {
809 success = camel_imapx_conn_manager_refresh_info_sync (conn_man, mailbox, cancellable, error);
810 }
811
812 g_clear_object (&mailbox);
813
814 return success;
815 }
816
817 static gboolean
imapx_synchronize_sync(CamelFolder * folder,gboolean expunge,GCancellable * cancellable,GError ** error)818 imapx_synchronize_sync (CamelFolder *folder,
819 gboolean expunge,
820 GCancellable *cancellable,
821 GError **error)
822 {
823 CamelStore *store;
824 CamelIMAPXStore *imapx_store;
825 CamelIMAPXConnManager *conn_man;
826 CamelIMAPXMailbox *mailbox = NULL;
827 gboolean success = FALSE;
828
829 store = camel_folder_get_parent_store (folder);
830
831 /* Not connected, thus skip the operation */
832 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
833 return TRUE;
834
835 imapx_store = CAMEL_IMAPX_STORE (store);
836 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
837
838 mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error);
839
840 /* Do not update mailboxes on exit which were not entered yet */
841 if (mailbox == NULL || (camel_application_is_exiting &&
842 camel_imapx_mailbox_get_permanentflags (mailbox) == ~0)) {
843 success = mailbox != NULL;
844 } else {
845 success = camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error);
846 if (success && expunge && camel_folder_summary_get_deleted_count (camel_folder_get_folder_summary (folder)) > 0) {
847 success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error);
848 }
849 }
850
851 g_clear_object (&mailbox);
852
853 return success;
854 }
855
856 static gboolean
imapx_synchronize_message_sync(CamelFolder * folder,const gchar * uid,GCancellable * cancellable,GError ** error)857 imapx_synchronize_message_sync (CamelFolder *folder,
858 const gchar *uid,
859 GCancellable *cancellable,
860 GError **error)
861 {
862 CamelStore *store;
863 CamelIMAPXStore *imapx_store;
864 CamelIMAPXConnManager *conn_man;
865 CamelIMAPXMailbox *mailbox = NULL;
866 gboolean success = FALSE;
867
868 store = camel_folder_get_parent_store (folder);
869
870 imapx_store = CAMEL_IMAPX_STORE (store);
871 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
872
873 mailbox = camel_imapx_folder_list_mailbox (
874 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
875
876 if (mailbox == NULL)
877 goto exit;
878
879 success = camel_imapx_conn_manager_sync_message_sync (
880 conn_man, mailbox, camel_folder_get_folder_summary (folder),
881 CAMEL_IMAPX_FOLDER (folder)->cache, uid,
882 cancellable, error);
883
884 exit:
885 g_clear_object (&mailbox);
886
887 return success;
888 }
889
890 static gboolean
imapx_transfer_messages_to_sync(CamelFolder * source,GPtrArray * uids,CamelFolder * dest,gboolean delete_originals,GPtrArray ** transferred_uids,GCancellable * cancellable,GError ** error)891 imapx_transfer_messages_to_sync (CamelFolder *source,
892 GPtrArray *uids,
893 CamelFolder *dest,
894 gboolean delete_originals,
895 GPtrArray **transferred_uids,
896 GCancellable *cancellable,
897 GError **error)
898 {
899 CamelStore *store;
900 CamelIMAPXStore *imapx_store;
901 CamelIMAPXConnManager *conn_man;
902 CamelIMAPXMailbox *src_mailbox = NULL;
903 CamelIMAPXMailbox *dst_mailbox = NULL;
904 gboolean success = FALSE;
905
906 store = camel_folder_get_parent_store (source);
907
908 imapx_store = CAMEL_IMAPX_STORE (store);
909 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
910
911 src_mailbox = camel_imapx_folder_list_mailbox (
912 CAMEL_IMAPX_FOLDER (source), cancellable, error);
913
914 if (src_mailbox == NULL)
915 goto exit;
916
917 dst_mailbox = camel_imapx_folder_list_mailbox (
918 CAMEL_IMAPX_FOLDER (dest), cancellable, error);
919
920 if (dst_mailbox == NULL)
921 goto exit;
922
923 success = camel_imapx_conn_manager_copy_message_sync (
924 conn_man, src_mailbox, dst_mailbox, uids,
925 delete_originals, FALSE, cancellable, error);
926
927 exit:
928 g_clear_object (&src_mailbox);
929 g_clear_object (&dst_mailbox);
930
931 return success;
932 }
933
934 typedef struct _RemoveCacheFiles {
935 CamelIMAPXFolder *imapx_folder;
936 GSList *uids;
937 } RemoveCacheFiles;
938
939 static void
remove_cache_files_free(gpointer ptr)940 remove_cache_files_free (gpointer ptr)
941 {
942 RemoveCacheFiles *rcf = ptr;
943
944 if (rcf) {
945 g_clear_object (&rcf->imapx_folder);
946 g_slist_free_full (rcf->uids, (GDestroyNotify) camel_pstring_free);
947 g_slice_free (RemoveCacheFiles, rcf);
948 }
949 }
950
951 static void
imapx_folder_remove_cache_files_thread(CamelSession * session,GCancellable * cancellable,gpointer user_data,GError ** error)952 imapx_folder_remove_cache_files_thread (CamelSession *session,
953 GCancellable *cancellable,
954 gpointer user_data,
955 GError **error)
956 {
957 RemoveCacheFiles *rcf = user_data;
958 GSList *link;
959 guint len, index;
960
961 g_return_if_fail (rcf != NULL);
962 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (rcf->imapx_folder));
963 g_return_if_fail (rcf->uids != NULL);
964
965 len = g_slist_length (rcf->uids);
966
967 for (index = 1, link = rcf->uids;
968 link && !g_cancellable_set_error_if_cancelled (cancellable, error);
969 index++, link = g_slist_next (link)) {
970 const gchar *message_uid = link->data;
971
972 if (message_uid) {
973 camel_data_cache_remove (rcf->imapx_folder->cache, "tmp", message_uid, NULL);
974 camel_data_cache_remove (rcf->imapx_folder->cache, "cur", message_uid, NULL);
975
976 camel_operation_progress (cancellable, index * 100 / len);
977 }
978 }
979 }
980
981 static void
imapx_folder_changed(CamelFolder * folder,CamelFolderChangeInfo * info)982 imapx_folder_changed (CamelFolder *folder,
983 CamelFolderChangeInfo *info)
984 {
985 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
986
987 if (info && info->uid_removed && info->uid_removed->len) {
988 CamelIMAPXFolder *imapx_folder;
989 GSList *removed_uids = NULL;
990 guint ii;
991
992 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
993
994 g_mutex_lock (&imapx_folder->priv->move_to_hash_table_lock);
995
996 for (ii = 0; ii < info->uid_removed->len; ii++) {
997 const gchar *message_uid = info->uid_removed->pdata[ii];
998
999 if (!message_uid)
1000 continue;
1001
1002 g_hash_table_remove (imapx_folder->priv->move_to_real_trash_uids, message_uid);
1003 g_hash_table_remove (imapx_folder->priv->move_to_real_junk_uids, message_uid);
1004 g_hash_table_remove (imapx_folder->priv->move_to_inbox_uids, message_uid);
1005
1006 removed_uids = g_slist_prepend (removed_uids, (gpointer) camel_pstring_strdup (message_uid));
1007 }
1008
1009 g_mutex_unlock (&imapx_folder->priv->move_to_hash_table_lock);
1010
1011 if (removed_uids) {
1012 CamelSession *session = NULL;
1013 CamelStore *parent_store;
1014
1015 parent_store = camel_folder_get_parent_store (folder);
1016 if (parent_store)
1017 session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
1018
1019 if (session) {
1020 RemoveCacheFiles *rcf;
1021 gchar *description;
1022
1023 rcf = g_slice_new0 (RemoveCacheFiles);
1024 rcf->imapx_folder = g_object_ref (imapx_folder);
1025 rcf->uids = removed_uids;
1026
1027 removed_uids = NULL; /* transfer ownership to 'rcf' */
1028
1029 /* Translators: The first “%s” is replaced with an account name and the second “%s”
1030 is replaced with a full path name. The spaces around “:” are intentional, as
1031 the whole “%s : %s” is meant as an absolute identification of the folder. */
1032 description = g_strdup_printf (_("Removing stale cache files in folder “%s : %s”"),
1033 camel_service_get_display_name (CAMEL_SERVICE (parent_store)),
1034 camel_folder_get_full_name (CAMEL_FOLDER (imapx_folder)));
1035
1036 camel_session_submit_job (session, description,
1037 imapx_folder_remove_cache_files_thread, rcf, remove_cache_files_free);
1038
1039 g_free (description);
1040 }
1041
1042 g_slist_free_full (removed_uids, (GDestroyNotify) camel_pstring_free);
1043 }
1044 }
1045
1046 /* Chain up to parent's method. */
1047 CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->changed (folder, info);
1048 }
1049
1050 static guint32
imapx_get_permanent_flags(CamelFolder * folder)1051 imapx_get_permanent_flags (CamelFolder *folder)
1052 {
1053 return CAMEL_MESSAGE_ANSWERED |
1054 CAMEL_MESSAGE_DELETED |
1055 CAMEL_MESSAGE_DRAFT |
1056 CAMEL_MESSAGE_FLAGGED |
1057 CAMEL_MESSAGE_SEEN |
1058 CAMEL_MESSAGE_USER;
1059 }
1060
1061 static void
imapx_rename(CamelFolder * folder,const gchar * new_name)1062 imapx_rename (CamelFolder *folder,
1063 const gchar *new_name)
1064 {
1065 CamelStore *store;
1066 CamelIMAPXStore *imapx_store;
1067 const gchar *folder_name;
1068
1069 store = camel_folder_get_parent_store (folder);
1070 imapx_store = CAMEL_IMAPX_STORE (store);
1071
1072 camel_store_summary_disconnect_folder_summary (
1073 imapx_store->summary, camel_folder_get_folder_summary (folder));
1074
1075 /* Chain up to parent's rename() method. */
1076 CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->
1077 rename (folder, new_name);
1078
1079 folder_name = camel_folder_get_full_name (folder);
1080
1081 camel_store_summary_connect_folder_summary (
1082 imapx_store->summary, folder_name, camel_folder_get_folder_summary (folder));
1083 }
1084
1085 static void
camel_imapx_folder_class_init(CamelIMAPXFolderClass * class)1086 camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
1087 {
1088 GObjectClass *object_class;
1089 CamelFolderClass *folder_class;
1090
1091 object_class = G_OBJECT_CLASS (class);
1092 object_class->set_property = imapx_folder_set_property;
1093 object_class->get_property = imapx_folder_get_property;
1094 object_class->dispose = imapx_folder_dispose;
1095 object_class->finalize = imapx_folder_finalize;
1096
1097 folder_class = CAMEL_FOLDER_CLASS (class);
1098 folder_class->get_permanent_flags = imapx_get_permanent_flags;
1099 folder_class->rename = imapx_rename;
1100 folder_class->search_by_expression = imapx_search_by_expression;
1101 folder_class->search_by_uids = imapx_search_by_uids;
1102 folder_class->count_by_expression = imapx_count_by_expression;
1103 folder_class->get_uncached_uids = imapx_get_uncached_uids;
1104 folder_class->search_free = imapx_search_free;
1105 folder_class->get_filename = imapx_get_filename;
1106 folder_class->append_message_sync = imapx_append_message_sync;
1107 folder_class->expunge_sync = imapx_expunge_sync;
1108 folder_class->get_message_cached = imapx_get_message_cached;
1109 folder_class->get_message_sync = imapx_get_message_sync;
1110 folder_class->get_quota_info_sync = imapx_get_quota_info_sync;
1111 folder_class->purge_message_cache_sync = imapx_purge_message_cache_sync;
1112 folder_class->refresh_info_sync = imapx_refresh_info_sync;
1113 folder_class->synchronize_sync = imapx_synchronize_sync;
1114 folder_class->synchronize_message_sync = imapx_synchronize_message_sync;
1115 folder_class->transfer_messages_to_sync = imapx_transfer_messages_to_sync;
1116 folder_class->changed = imapx_folder_changed;
1117
1118 g_object_class_install_property (
1119 object_class,
1120 PROP_APPLY_FILTERS,
1121 g_param_spec_boolean (
1122 "apply-filters",
1123 "Apply Filters",
1124 _("Apply message _filters to this folder"),
1125 FALSE,
1126 G_PARAM_READWRITE |
1127 G_PARAM_EXPLICIT_NOTIFY |
1128 CAMEL_PARAM_PERSISTENT));
1129
1130 g_object_class_install_property (
1131 object_class,
1132 PROP_CHECK_FOLDER,
1133 g_param_spec_boolean (
1134 "check-folder",
1135 "Check Folder",
1136 _("Always check for _new mail in this folder"),
1137 FALSE,
1138 G_PARAM_READWRITE |
1139 G_PARAM_EXPLICIT_NOTIFY |
1140 CAMEL_PARAM_PERSISTENT));
1141
1142 g_object_class_install_property (
1143 object_class,
1144 PROP_MAILBOX,
1145 g_param_spec_object (
1146 "mailbox",
1147 "Mailbox",
1148 "IMAP mailbox for this folder",
1149 CAMEL_TYPE_IMAPX_MAILBOX,
1150 G_PARAM_READWRITE |
1151 G_PARAM_EXPLICIT_NOTIFY |
1152 G_PARAM_STATIC_STRINGS));
1153 }
1154
1155 static void
camel_imapx_folder_init(CamelIMAPXFolder * imapx_folder)1156 camel_imapx_folder_init (CamelIMAPXFolder *imapx_folder)
1157 {
1158 CamelFolder *folder = CAMEL_FOLDER (imapx_folder);
1159 GHashTable *move_to_real_junk_uids;
1160 GHashTable *move_to_real_trash_uids;
1161
1162 move_to_real_junk_uids = g_hash_table_new_full (
1163 (GHashFunc) g_str_hash,
1164 (GEqualFunc) g_str_equal,
1165 (GDestroyNotify) camel_pstring_free,
1166 (GDestroyNotify) NULL);
1167
1168 move_to_real_trash_uids = g_hash_table_new_full (
1169 (GHashFunc) g_str_hash,
1170 (GEqualFunc) g_str_equal,
1171 (GDestroyNotify) camel_pstring_free,
1172 (GDestroyNotify) NULL);
1173
1174 imapx_folder->priv = camel_imapx_folder_get_instance_private (imapx_folder);
1175
1176 camel_folder_set_flags (folder, camel_folder_get_flags (folder) | CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY);
1177
1178 camel_folder_set_lock_async (folder, TRUE);
1179
1180 g_mutex_init (&imapx_folder->priv->property_lock);
1181
1182 g_mutex_init (&imapx_folder->priv->move_to_hash_table_lock);
1183 imapx_folder->priv->move_to_real_junk_uids = move_to_real_junk_uids;
1184 imapx_folder->priv->move_to_real_trash_uids = move_to_real_trash_uids;
1185 imapx_folder->priv->move_to_inbox_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
1186
1187 g_mutex_init (&imapx_folder->search_lock);
1188 g_mutex_init (&imapx_folder->stream_lock);
1189
1190 g_weak_ref_init (&imapx_folder->priv->mailbox, NULL);
1191 }
1192
1193 CamelFolder *
camel_imapx_folder_new(CamelStore * store,const gchar * folder_dir,const gchar * folder_name,GError ** error)1194 camel_imapx_folder_new (CamelStore *store,
1195 const gchar *folder_dir,
1196 const gchar *folder_name,
1197 GError **error)
1198 {
1199 CamelFolder *folder;
1200 CamelFolderSummary *folder_summary;
1201 CamelService *service;
1202 CamelSettings *settings;
1203 CamelIMAPXFolder *imapx_folder;
1204 const gchar *short_name;
1205 gchar *state_file;
1206 gboolean filter_all;
1207 gboolean filter_inbox;
1208 gboolean filter_junk;
1209 gboolean filter_junk_inbox;
1210 gboolean offline_limit_by_age = FALSE;
1211 CamelTimeUnit offline_limit_unit;
1212 gint offline_limit_value;
1213 time_t when = (time_t) 0;
1214 guint32 add_folder_flags = 0;
1215
1216 d ("opening imap folder '%s'\n", folder_dir);
1217
1218 service = CAMEL_SERVICE (store);
1219
1220 settings = camel_service_ref_settings (service);
1221
1222 g_object_get (
1223 settings,
1224 "filter-all", &filter_all,
1225 "filter-inbox", &filter_inbox,
1226 "filter-junk", &filter_junk,
1227 "filter-junk-inbox", &filter_junk_inbox,
1228 "limit-by-age", &offline_limit_by_age,
1229 "limit-unit", &offline_limit_unit,
1230 "limit-value", &offline_limit_value,
1231 NULL);
1232
1233 g_object_unref (settings);
1234
1235 short_name = strrchr (folder_name, '/');
1236 if (short_name)
1237 short_name++;
1238 else
1239 short_name = folder_name;
1240
1241 folder = g_object_new (
1242 CAMEL_TYPE_IMAPX_FOLDER,
1243 "display-name", short_name,
1244 "full_name", folder_name,
1245 "parent-store", store, NULL);
1246
1247 folder_summary = camel_imapx_summary_new (folder);
1248 if (!folder_summary) {
1249 g_set_error (
1250 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1251 _("Could not create folder summary for %s"),
1252 short_name);
1253 g_object_unref (folder);
1254 return NULL;
1255 }
1256
1257 camel_folder_take_folder_summary (folder, folder_summary);
1258
1259 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1260 imapx_folder->cache = camel_data_cache_new (folder_dir, error);
1261 if (imapx_folder->cache == NULL) {
1262 g_prefix_error (
1263 error, _("Could not create cache for %s: "),
1264 short_name);
1265 g_object_unref (folder);
1266 return NULL;
1267 }
1268
1269 state_file = g_build_filename (folder_dir, "cmeta", NULL);
1270 camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
1271 g_free (state_file);
1272 camel_object_state_read (CAMEL_OBJECT (folder));
1273
1274 if (offline_limit_by_age)
1275 when = camel_time_value_apply (when, offline_limit_unit, offline_limit_value);
1276
1277 if (when <= (time_t) 0)
1278 when = (time_t) -1;
1279
1280 camel_imapx_folder_update_cache_expire (folder, when);
1281
1282 camel_binding_bind_property (store, "online",
1283 imapx_folder->cache, "expire-enabled",
1284 G_BINDING_SYNC_CREATE);
1285
1286 imapx_folder->search = camel_imapx_search_new (CAMEL_IMAPX_STORE (store));
1287
1288 if (filter_all)
1289 add_folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1290
1291 if (camel_imapx_mailbox_is_inbox (folder_name)) {
1292 if (filter_inbox)
1293 add_folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1294
1295 if (filter_junk)
1296 add_folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
1297 } else {
1298 if (filter_junk && !filter_junk_inbox)
1299 add_folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
1300
1301 if (imapx_folder_get_apply_filters (imapx_folder))
1302 add_folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
1303 }
1304
1305 camel_folder_set_flags (folder, camel_folder_get_flags (folder) | add_folder_flags);
1306
1307 camel_store_summary_connect_folder_summary (
1308 CAMEL_IMAPX_STORE (store)->summary,
1309 folder_name, camel_folder_get_folder_summary (folder));
1310
1311 return folder;
1312 }
1313
1314 /**
1315 * camel_imapx_folder_ref_mailbox:
1316 * @folder: a #CamelIMAPXFolder
1317 *
1318 * Returns the #CamelIMAPXMailbox for @folder from the current IMAP server
1319 * connection, or %NULL if @folder's #CamelFolder:parent-store is disconnected
1320 * from the IMAP server.
1321 *
1322 * The returned #CamelIMAPXMailbox is referenced for thread-safety and
1323 * should be unreferenced with g_object_unref() when finished with it.
1324 *
1325 * Returns: a #CamelIMAPXMailbox, or %NULL
1326 *
1327 * Since: 3.12
1328 **/
1329 CamelIMAPXMailbox *
camel_imapx_folder_ref_mailbox(CamelIMAPXFolder * folder)1330 camel_imapx_folder_ref_mailbox (CamelIMAPXFolder *folder)
1331 {
1332 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
1333
1334 return g_weak_ref_get (&folder->priv->mailbox);
1335 }
1336
1337 /**
1338 * camel_imapx_folder_set_mailbox:
1339 * @folder: a #CamelIMAPXFolder
1340 * @mailbox: a #CamelIMAPXMailbox
1341 *
1342 * Sets the #CamelIMAPXMailbox for @folder from the current IMAP server
1343 * connection. Note that #CamelIMAPXFolder only holds a weak reference
1344 * on its #CamelIMAPXMailbox so that when the IMAP server connection is
1345 * lost, all mailbox instances are automatically destroyed.
1346 *
1347 * Since: 3.12
1348 **/
1349 void
camel_imapx_folder_set_mailbox(CamelIMAPXFolder * folder,CamelIMAPXMailbox * mailbox)1350 camel_imapx_folder_set_mailbox (CamelIMAPXFolder *folder,
1351 CamelIMAPXMailbox *mailbox)
1352 {
1353 CamelIMAPXSummary *imapx_summary;
1354 guint32 uidvalidity;
1355
1356 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1357 g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
1358
1359 g_weak_ref_set (&folder->priv->mailbox, mailbox);
1360
1361 imapx_summary = CAMEL_IMAPX_SUMMARY (camel_folder_get_folder_summary (CAMEL_FOLDER (folder)));
1362 uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
1363
1364 if (uidvalidity > 0 && uidvalidity != imapx_summary->validity)
1365 camel_imapx_folder_invalidate_local_cache (folder, uidvalidity);
1366
1367 g_object_notify (G_OBJECT (folder), "mailbox");
1368 }
1369
1370 /**
1371 * camel_imapx_folder_list_mailbox:
1372 * @folder: a #CamelIMAPXFolder
1373 * @cancellable: optional #GCancellable object, or %NULL
1374 * @error: return location for a #GError, or %NULL
1375 *
1376 * Ensures that @folder's #CamelIMAPXFolder:mailbox property is set,
1377 * going so far as to issue a LIST command if necessary (but should
1378 * be a rarely needed last resort).
1379 *
1380 * If @folder's #CamelFolder:parent-store is disconnected from the IMAP
1381 * server or an error occurs during the LIST command, the function sets
1382 * @error and returns %NULL.
1383 *
1384 * The returned #CamelIMAPXMailbox is referenced for thread-safety and
1385 * should be unreferenced with g_object_unref() when finished with it.
1386 *
1387 * Returns: a #CamelIMAPXMailbox, or %NULL
1388 *
1389 * Since: 3.12
1390 **/
1391 CamelIMAPXMailbox *
camel_imapx_folder_list_mailbox(CamelIMAPXFolder * folder,GCancellable * cancellable,GError ** error)1392 camel_imapx_folder_list_mailbox (CamelIMAPXFolder *folder,
1393 GCancellable *cancellable,
1394 GError **error)
1395 {
1396 CamelIMAPXStore *imapx_store;
1397 CamelIMAPXConnManager *conn_man;
1398 CamelIMAPXMailbox *mailbox;
1399 CamelStore *parent_store;
1400 CamelStoreInfo *store_info;
1401 CamelIMAPXStoreInfo *imapx_store_info;
1402 gchar *folder_path = NULL;
1403 gchar *mailbox_name = NULL;
1404 gboolean success;
1405
1406 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
1407
1408 /* First check if we already have a mailbox. */
1409
1410 mailbox = camel_imapx_folder_ref_mailbox (folder);
1411 if (mailbox != NULL)
1412 goto exit;
1413
1414 /* Obtain the mailbox name from the store summary. */
1415
1416 folder_path = camel_folder_dup_full_name (CAMEL_FOLDER (folder));
1417 parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (folder));
1418
1419 imapx_store = CAMEL_IMAPX_STORE (parent_store);
1420
1421 store_info = camel_store_summary_path (
1422 imapx_store->summary, folder_path);
1423
1424 /* This should never fail. We needed the CamelStoreInfo
1425 * to instantiate the CamelIMAPXFolder in the first place. */
1426 g_return_val_if_fail (store_info != NULL, FALSE);
1427
1428 imapx_store_info = (CamelIMAPXStoreInfo *) store_info;
1429 mailbox_name = g_strdup (imapx_store_info->mailbox_name);
1430
1431 camel_store_summary_info_unref (imapx_store->summary, store_info);
1432
1433 /* See if the CamelIMAPXStore already has the mailbox. */
1434
1435 mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
1436 if (mailbox != NULL) {
1437 camel_imapx_folder_set_mailbox (folder, mailbox);
1438 goto exit;
1439 }
1440
1441 /* Last resort is to issue a LIST command. Maintainer should
1442 * monitor IMAP logs to make sure this is rarely if ever used. */
1443
1444 /* This creates a mailbox instance from the LIST response. */
1445 conn_man = camel_imapx_store_get_conn_manager (imapx_store);
1446 success = camel_imapx_conn_manager_list_sync (conn_man, mailbox_name, 0, cancellable, error);
1447
1448 if (!success)
1449 goto exit;
1450
1451 /* This might still return NULL if the mailbox has a
1452 * /NonExistent attribute. Otherwise this should work. */
1453 mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
1454 if (mailbox != NULL) {
1455 camel_imapx_folder_set_mailbox (folder, mailbox);
1456 } else {
1457 g_set_error (
1458 error, CAMEL_FOLDER_ERROR,
1459 CAMEL_FOLDER_ERROR_INVALID_STATE,
1460 /* Translators: The first “%s” is replaced with an account name and the second “%s”
1461 is replaced with a full path name. The spaces around “:” are intentional, as
1462 the whole “%s : %s” is meant as an absolute identification of the folder. */
1463 _("No IMAP mailbox available for folder “%s : %s”"),
1464 camel_service_get_display_name (CAMEL_SERVICE (parent_store)),
1465 camel_folder_get_full_name (CAMEL_FOLDER (folder)));
1466 }
1467
1468 exit:
1469 g_free (folder_path);
1470 g_free (mailbox_name);
1471
1472 return mailbox;
1473 }
1474
1475 /**
1476 * camel_imapx_folder_copy_message_map:
1477 * @folder: a #CamelIMAPXFolder
1478 *
1479 * Returns a #GSequence of 32-bit integers representing the locally cached
1480 * mapping of message sequence numbers to unique identifiers.
1481 *
1482 * Free the returns #GSequence with g_sequence_free().
1483 *
1484 * Returns: a #GSequence
1485 *
1486 * Since: 3.12
1487 **/
1488 GSequence *
camel_imapx_folder_copy_message_map(CamelIMAPXFolder * folder)1489 camel_imapx_folder_copy_message_map (CamelIMAPXFolder *folder)
1490 {
1491 CamelFolderSummary *summary;
1492 GSequence *message_map;
1493 GPtrArray *array;
1494 guint ii;
1495
1496 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
1497
1498 summary = camel_folder_get_folder_summary (CAMEL_FOLDER (folder));
1499 array = camel_folder_summary_get_array (summary);
1500 camel_folder_sort_uids (CAMEL_FOLDER (folder), array);
1501
1502 message_map = g_sequence_new (NULL);
1503
1504 for (ii = 0; ii < array->len; ii++) {
1505 guint32 uid = strtoul (array->pdata[ii], NULL, 10);
1506 g_sequence_append (message_map, GUINT_TO_POINTER (uid));
1507 }
1508
1509 camel_folder_summary_free_array (array);
1510
1511 return message_map;
1512 }
1513
1514 /**
1515 * camel_imapx_folder_add_move_to_real_junk:
1516 * @folder: a #CamelIMAPXFolder
1517 * @message_uid: a message UID
1518 *
1519 * Adds @message_uid to a pool of messages to be moved to a real junk
1520 * folder the next time @folder is explicitly synchronized by way of
1521 * camel_folder_synchronize() or camel_folder_synchronize_sync().
1522 *
1523 * This only applies when using a real folder to track junk messages,
1524 * as specified by the #CamelIMAPXSettings:use-real-junk-path setting.
1525 *
1526 * Since: 3.8
1527 **/
1528 void
camel_imapx_folder_add_move_to_real_junk(CamelIMAPXFolder * folder,const gchar * message_uid)1529 camel_imapx_folder_add_move_to_real_junk (CamelIMAPXFolder *folder,
1530 const gchar *message_uid)
1531 {
1532 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1533 g_return_if_fail (message_uid != NULL);
1534 g_return_if_fail (camel_folder_summary_check_uid (camel_folder_get_folder_summary (CAMEL_FOLDER (folder)), message_uid));
1535
1536 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1537
1538 g_hash_table_remove (folder->priv->move_to_real_trash_uids, message_uid);
1539 g_hash_table_remove (folder->priv->move_to_inbox_uids, message_uid);
1540
1541 g_hash_table_add (
1542 folder->priv->move_to_real_junk_uids,
1543 (gpointer) camel_pstring_strdup (message_uid));
1544
1545 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1546 }
1547
1548 /**
1549 * camel_imapx_folder_add_move_to_real_trash:
1550 * @folder: a #CamelIMAPXFolder
1551 * @message_uid: a message UID
1552 *
1553 * Adds @message_uid to a pool of messages to be moved to a real trash
1554 * folder the next time @folder is explicitly synchronized by way of
1555 * camel_folder_synchronize() or camel_folder_synchronize_sync().
1556 *
1557 * This only applies when using a real folder to track deleted messages,
1558 * as specified by the #CamelIMAPXSettings:use-real-trash-path setting.
1559 *
1560 * Since: 3.8
1561 **/
1562 void
camel_imapx_folder_add_move_to_real_trash(CamelIMAPXFolder * folder,const gchar * message_uid)1563 camel_imapx_folder_add_move_to_real_trash (CamelIMAPXFolder *folder,
1564 const gchar *message_uid)
1565 {
1566 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1567 g_return_if_fail (message_uid != NULL);
1568 g_return_if_fail (camel_folder_summary_check_uid (camel_folder_get_folder_summary (CAMEL_FOLDER (folder)), message_uid));
1569
1570 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1571
1572 g_hash_table_remove (folder->priv->move_to_real_junk_uids, message_uid);
1573 g_hash_table_remove (folder->priv->move_to_inbox_uids, message_uid);
1574
1575 g_hash_table_add (
1576 folder->priv->move_to_real_trash_uids,
1577 (gpointer) camel_pstring_strdup (message_uid));
1578
1579 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1580 }
1581
1582 /*
1583 * camel_imapx_folder_add_move_to_inbox:
1584 * @folder: a #CamelIMAPXFolder
1585 * @message_uid: a message UID
1586 *
1587 * Adds @message_uid to a pool of messages to be moved to the Inbox
1588 * folder the next time @folder is explicitly synchronized by way of
1589 * camel_folder_synchronize() or camel_folder_synchronize_sync().
1590 *
1591 * Since: 3.28
1592 */
1593 void
camel_imapx_folder_add_move_to_inbox(CamelIMAPXFolder * folder,const gchar * message_uid)1594 camel_imapx_folder_add_move_to_inbox (CamelIMAPXFolder *folder,
1595 const gchar *message_uid)
1596 {
1597 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1598 g_return_if_fail (message_uid != NULL);
1599 g_return_if_fail (camel_folder_summary_check_uid (camel_folder_get_folder_summary (CAMEL_FOLDER (folder)), message_uid));
1600
1601 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1602
1603 g_hash_table_remove (folder->priv->move_to_real_trash_uids, message_uid);
1604 g_hash_table_remove (folder->priv->move_to_real_junk_uids, message_uid);
1605
1606 g_hash_table_add (
1607 folder->priv->move_to_inbox_uids,
1608 (gpointer) camel_pstring_strdup (message_uid));
1609
1610 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1611 }
1612
1613 void
camel_imapx_folder_clear_move_to_real_trash_uids(CamelIMAPXFolder * folder)1614 camel_imapx_folder_clear_move_to_real_trash_uids (CamelIMAPXFolder *folder)
1615 {
1616 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1617
1618 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1619
1620 g_hash_table_remove_all (folder->priv->move_to_real_trash_uids);
1621
1622 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1623 }
1624
1625 void
camel_imapx_folder_clear_move_to_real_junk_uids(CamelIMAPXFolder * folder)1626 camel_imapx_folder_clear_move_to_real_junk_uids (CamelIMAPXFolder *folder)
1627 {
1628 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1629
1630 g_mutex_lock (&folder->priv->move_to_hash_table_lock);
1631
1632 g_hash_table_remove_all (folder->priv->move_to_real_junk_uids);
1633
1634 g_mutex_unlock (&folder->priv->move_to_hash_table_lock);
1635 }
1636
1637 /**
1638 * camel_imapx_folder_invalidate_local_cache:
1639 * @folder: a #CamelIMAPXFolder
1640 * @new_uidvalidity: the new UIDVALIDITY value
1641 *
1642 * Call this function when the IMAP server reports a different UIDVALIDITY
1643 * value than what is presently cached. This means all cached message UIDs
1644 * are now invalid and must be discarded.
1645 *
1646 * The local cache for @folder is reset and the @new_uidvalidity value is
1647 * recorded in the newly-reset cache.
1648 *
1649 * Since: 3.10
1650 **/
1651 void
camel_imapx_folder_invalidate_local_cache(CamelIMAPXFolder * folder,guint64 new_uidvalidity)1652 camel_imapx_folder_invalidate_local_cache (CamelIMAPXFolder *folder,
1653 guint64 new_uidvalidity)
1654 {
1655 CamelFolderSummary *summary;
1656 CamelFolderChangeInfo *changes;
1657 GPtrArray *array;
1658 guint ii;
1659
1660 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1661 g_return_if_fail (new_uidvalidity > 0);
1662
1663 summary = camel_folder_get_folder_summary (CAMEL_FOLDER (folder));
1664
1665 changes = camel_folder_change_info_new ();
1666 array = camel_folder_summary_get_array (summary);
1667
1668 for (ii = 0; ii < array->len; ii++) {
1669 const gchar *uid = array->pdata[ii];
1670 camel_folder_change_info_change_uid (changes, uid);
1671 }
1672
1673 CAMEL_IMAPX_SUMMARY (summary)->validity = new_uidvalidity;
1674 camel_folder_summary_touch (summary);
1675 camel_folder_summary_save (summary, NULL);
1676
1677 camel_data_cache_clear (folder->cache, "cache");
1678 camel_data_cache_clear (folder->cache, "cur");
1679
1680 camel_folder_changed (CAMEL_FOLDER (folder), changes);
1681
1682 camel_folder_change_info_free (changes);
1683 camel_folder_summary_free_array (array);
1684 }
1685
1686 gboolean
camel_imapx_folder_get_check_folder(CamelIMAPXFolder * folder)1687 camel_imapx_folder_get_check_folder (CamelIMAPXFolder *folder)
1688 {
1689 g_return_val_if_fail (folder != NULL, FALSE);
1690 g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
1691
1692 return folder->priv->check_folder;
1693 }
1694
1695 void
camel_imapx_folder_set_check_folder(CamelIMAPXFolder * folder,gboolean check_folder)1696 camel_imapx_folder_set_check_folder (CamelIMAPXFolder *folder,
1697 gboolean check_folder)
1698 {
1699 g_return_if_fail (folder != NULL);
1700 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1701
1702 if (folder->priv->check_folder == check_folder)
1703 return;
1704
1705 folder->priv->check_folder = check_folder;
1706
1707 g_object_notify (G_OBJECT (folder), "check-folder");
1708 }
1709
1710 void
camel_imapx_folder_update_cache_expire(CamelFolder * folder,time_t expire_when)1711 camel_imapx_folder_update_cache_expire (CamelFolder *folder,
1712 time_t expire_when)
1713 {
1714 CamelIMAPXFolder *imapx_folder;
1715
1716 g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
1717
1718 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1719
1720 if (camel_offline_folder_can_downsync (CAMEL_OFFLINE_FOLDER (folder))) {
1721 /* Ensure cache will expire when set up, otherwise
1722 * it causes redownload of messages too soon. */
1723 camel_data_cache_set_expire_age (imapx_folder->cache, expire_when);
1724 camel_data_cache_set_expire_access (imapx_folder->cache, expire_when);
1725 } else {
1726 /* Set cache expiration for one week. */
1727 camel_data_cache_set_expire_age (imapx_folder->cache, 60 * 60 * 24 * 7);
1728 camel_data_cache_set_expire_access (imapx_folder->cache, 60 * 60 * 24 * 7);
1729 }
1730 }
1731