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