1 /*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-conn-manager.h
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: Chenthill Palanisamy <pchenthill@novell.com>
19  */
20 
21 #include "evolution-data-server-config.h"
22 
23 #include <glib.h>
24 #include <glib/gi18n-lib.h>
25 
26 #include "camel-imapx-conn-manager.h"
27 #include "camel-imapx-folder.h"
28 #include "camel-imapx-job.h"
29 #include "camel-imapx-settings.h"
30 #include "camel-imapx-store.h"
31 #include "camel-imapx-utils.h"
32 
33 #define c(...) camel_imapx_debug(conman, __VA_ARGS__)
34 
35 #define CON_READ_LOCK(x) \
36 	(g_rw_lock_reader_lock (&(x)->priv->rw_lock))
37 #define CON_READ_UNLOCK(x) \
38 	(g_rw_lock_reader_unlock (&(x)->priv->rw_lock))
39 #define CON_WRITE_LOCK(x) \
40 	(g_rw_lock_writer_lock (&(x)->priv->rw_lock))
41 #define CON_WRITE_UNLOCK(x) \
42 	(g_rw_lock_writer_unlock (&(x)->priv->rw_lock))
43 
44 #define JOB_QUEUE_LOCK(x) g_rec_mutex_lock (&(x)->priv->job_queue_lock)
45 #define JOB_QUEUE_UNLOCK(x) g_rec_mutex_unlock (&(x)->priv->job_queue_lock)
46 
47 typedef struct _ConnectionInfo ConnectionInfo;
48 
49 struct _CamelIMAPXConnManagerPrivate {
50 	GList *connections; /* ConnectionInfo * */
51 	GWeakRef store;
52 	GRWLock rw_lock;
53 	guint limit_max_connections;
54 
55 	GMutex pending_connections_lock;
56 	GSList *pending_connections; /* GCancellable * */
57 
58 	gchar last_tagprefix;
59 
60 	GRecMutex job_queue_lock;
61 	GSList *job_queue; /* CamelIMAPXJob * */
62 
63 	GMutex busy_connections_lock;
64 	GCond busy_connections_cond;
65 
66 	GMutex busy_mailboxes_lock; /* used for both busy_mailboxes and idle_mailboxes */
67 	GHashTable *busy_mailboxes; /* CamelIMAPXMailbox ~> gint */
68 	GHashTable *idle_mailboxes; /* CamelIMAPXMailbox ~> gint */
69 
70 	GMutex idle_refresh_lock;
71 	GHashTable *idle_refresh_mailboxes; /* not-referenced CamelIMAPXMailbox, just to use for pointer comparison ~> NULL */
72 };
73 
74 struct _ConnectionInfo {
75 	GMutex lock;
76 	CamelIMAPXServer *is;
77 	gboolean busy;
78 	gulong refresh_mailbox_handler_id;
79 	volatile gint ref_count;
80 };
81 
82 enum {
83 	PROP_0,
84 	PROP_STORE
85 };
86 
87 enum {
88 	CONNECTION_CREATED,
89 	LAST_SIGNAL
90 };
91 
92 static guint signals[LAST_SIGNAL];
93 
94 G_DEFINE_TYPE_WITH_PRIVATE (
95 	CamelIMAPXConnManager,
96 	camel_imapx_conn_manager,
97 	G_TYPE_OBJECT)
98 
99 static gboolean
100 imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
101 				      CamelIMAPXMailbox *mailbox,
102 				      CamelIMAPXMailbox *destination,
103 				      GPtrArray *uids,
104 				      gboolean delete_originals,
105 				      gboolean remove_deleted_flags,
106 				      gboolean skip_sync_changes,
107 				      GCancellable *cancellable,
108 				      GError **error);
109 
110 typedef struct _MailboxRefreshData {
111 	CamelIMAPXConnManager *conn_man;
112 	CamelIMAPXMailbox *mailbox;
113 } MailboxRefreshData;
114 
115 static void
mailbox_refresh_data_free(MailboxRefreshData * data)116 mailbox_refresh_data_free (MailboxRefreshData *data)
117 {
118 	if (data) {
119 		g_clear_object (&data->conn_man);
120 		g_clear_object (&data->mailbox);
121 		g_slice_free (MailboxRefreshData, data);
122 	}
123 }
124 
125 static gpointer
imapx_conn_manager_idle_mailbox_refresh_thread(gpointer user_data)126 imapx_conn_manager_idle_mailbox_refresh_thread (gpointer user_data)
127 {
128 	MailboxRefreshData *data = user_data;
129 	GError *local_error = NULL;
130 
131 	g_return_val_if_fail (data != NULL, NULL);
132 
133 	/* passing NULL cancellable means to use only the job's abort cancellable */
134 	if (!camel_imapx_conn_manager_refresh_info_sync (data->conn_man, data->mailbox, NULL, &local_error)) {
135 		c ('*', "%s: Failed to refresh mailbox '%s': %s\n", G_STRFUNC,
136 			camel_imapx_mailbox_get_name (data->mailbox),
137 			local_error ? local_error->message : "Unknown error");
138 	}
139 
140 	g_mutex_lock (&data->conn_man->priv->idle_refresh_lock);
141 	g_hash_table_remove (data->conn_man->priv->idle_refresh_mailboxes, data->mailbox);
142 	g_mutex_unlock (&data->conn_man->priv->idle_refresh_lock);
143 
144 	mailbox_refresh_data_free (data);
145 	g_clear_error (&local_error);
146 
147 	return NULL;
148 }
149 
150 static void
imapx_conn_manager_refresh_mailbox_cb(CamelIMAPXServer * is,CamelIMAPXMailbox * mailbox,CamelIMAPXConnManager * conn_man)151 imapx_conn_manager_refresh_mailbox_cb (CamelIMAPXServer *is,
152 				       CamelIMAPXMailbox *mailbox,
153 				       CamelIMAPXConnManager *conn_man)
154 {
155 	MailboxRefreshData *data;
156 	GThread *thread;
157 	GError *local_error = NULL;
158 
159 	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
160 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
161 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
162 
163 	g_mutex_lock (&conn_man->priv->idle_refresh_lock);
164 	if (!g_hash_table_insert (conn_man->priv->idle_refresh_mailboxes, mailbox, NULL)) {
165 		g_mutex_unlock (&conn_man->priv->idle_refresh_lock);
166 		return;
167 	}
168 	g_mutex_unlock (&conn_man->priv->idle_refresh_lock);
169 
170 	data = g_slice_new0 (MailboxRefreshData);
171 	data->conn_man = g_object_ref (conn_man);
172 	data->mailbox = g_object_ref (mailbox);
173 
174 	thread = g_thread_try_new (NULL, imapx_conn_manager_idle_mailbox_refresh_thread, data, &local_error);
175 	if (!thread) {
176 		g_warning ("%s: Failed to create IDLE mailbox refresh thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
177 		mailbox_refresh_data_free (data);
178 	} else {
179 		g_thread_unref (thread);
180 	}
181 
182 	g_clear_error (&local_error);
183 }
184 
185 static ConnectionInfo *
connection_info_new(CamelIMAPXServer * is)186 connection_info_new (CamelIMAPXServer *is)
187 {
188 	ConnectionInfo *cinfo;
189 
190 	cinfo = g_slice_new0 (ConnectionInfo);
191 	g_mutex_init (&cinfo->lock);
192 	cinfo->is = g_object_ref (is);
193 	cinfo->ref_count = 1;
194 
195 	return cinfo;
196 }
197 
198 static ConnectionInfo *
connection_info_ref(ConnectionInfo * cinfo)199 connection_info_ref (ConnectionInfo *cinfo)
200 {
201 	g_return_val_if_fail (cinfo != NULL, NULL);
202 	g_return_val_if_fail (cinfo->ref_count > 0, NULL);
203 
204 	g_atomic_int_inc (&cinfo->ref_count);
205 
206 	return cinfo;
207 }
208 
209 static void
connection_info_unref(ConnectionInfo * cinfo)210 connection_info_unref (ConnectionInfo *cinfo)
211 {
212 	g_return_if_fail (cinfo != NULL);
213 	g_return_if_fail (cinfo->ref_count > 0);
214 
215 	if (g_atomic_int_dec_and_test (&cinfo->ref_count)) {
216 		if (cinfo->refresh_mailbox_handler_id)
217 			g_signal_handler_disconnect (cinfo->is, cinfo->refresh_mailbox_handler_id);
218 
219 		g_mutex_clear (&cinfo->lock);
220 		g_object_unref (cinfo->is);
221 
222 		g_slice_free (ConnectionInfo, cinfo);
223 	}
224 }
225 
226 static gboolean
connection_info_try_reserve(ConnectionInfo * cinfo)227 connection_info_try_reserve (ConnectionInfo *cinfo)
228 {
229 	gboolean reserved = FALSE;
230 
231 	g_return_val_if_fail (cinfo != NULL, FALSE);
232 
233 	g_mutex_lock (&cinfo->lock);
234 
235 	if (!cinfo->busy) {
236 		cinfo->busy = TRUE;
237 		reserved = TRUE;
238 	}
239 
240 	g_mutex_unlock (&cinfo->lock);
241 
242 	return reserved;
243 }
244 
245 static gboolean
connection_info_get_busy(ConnectionInfo * cinfo)246 connection_info_get_busy (ConnectionInfo *cinfo)
247 {
248 	gboolean busy;
249 
250 	g_return_val_if_fail (cinfo != NULL, FALSE);
251 
252 	g_mutex_lock (&cinfo->lock);
253 
254 	busy = cinfo->busy;
255 
256 	g_mutex_unlock (&cinfo->lock);
257 
258 	return busy;
259 }
260 
261 static void
connection_info_set_busy(ConnectionInfo * cinfo,gboolean busy)262 connection_info_set_busy (ConnectionInfo *cinfo,
263 			  gboolean busy)
264 {
265 	g_return_if_fail (cinfo != NULL);
266 
267 	g_mutex_lock (&cinfo->lock);
268 
269 	cinfo->busy = busy;
270 
271 	g_mutex_unlock (&cinfo->lock);
272 }
273 
274 static void
imapx_conn_manager_signal_busy_connections(CamelIMAPXConnManager * conn_man)275 imapx_conn_manager_signal_busy_connections (CamelIMAPXConnManager *conn_man)
276 {
277 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
278 
279 	g_mutex_lock (&conn_man->priv->busy_connections_lock);
280 	g_cond_broadcast (&conn_man->priv->busy_connections_cond);
281 	g_mutex_unlock (&conn_man->priv->busy_connections_lock);
282 }
283 
284 static void
imapx_conn_manager_unmark_busy(CamelIMAPXConnManager * conn_man,ConnectionInfo * cinfo)285 imapx_conn_manager_unmark_busy (CamelIMAPXConnManager *conn_man,
286 				ConnectionInfo *cinfo)
287 {
288 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
289 	g_return_if_fail (cinfo != NULL);
290 	g_return_if_fail (connection_info_get_busy (cinfo));
291 
292 	connection_info_set_busy (cinfo, FALSE);
293 
294 	imapx_conn_manager_signal_busy_connections (conn_man);
295 }
296 
297 static gboolean
imapx_conn_manager_remove_info(CamelIMAPXConnManager * conn_man,ConnectionInfo * cinfo)298 imapx_conn_manager_remove_info (CamelIMAPXConnManager *conn_man,
299                                 ConnectionInfo *cinfo)
300 {
301 	GList *list, *link;
302 	gboolean removed = FALSE;
303 
304 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
305 	g_return_val_if_fail (cinfo != NULL, FALSE);
306 
307 	CON_WRITE_LOCK (conn_man);
308 
309 	list = conn_man->priv->connections;
310 	link = g_list_find (list, cinfo);
311 
312 	if (link != NULL) {
313 		list = g_list_delete_link (list, link);
314 		connection_info_unref (cinfo);
315 		removed = TRUE;
316 	}
317 
318 	conn_man->priv->connections = list;
319 
320 	CON_WRITE_UNLOCK (conn_man);
321 
322 	if (removed)
323 		imapx_conn_manager_signal_busy_connections (conn_man);
324 
325 	return removed;
326 }
327 
328 static void
imapx_conn_manager_cancel_pending_connections(CamelIMAPXConnManager * conn_man)329 imapx_conn_manager_cancel_pending_connections (CamelIMAPXConnManager *conn_man)
330 {
331 	GSList *link;
332 
333 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
334 
335 	g_mutex_lock (&conn_man->priv->pending_connections_lock);
336 	for (link = conn_man->priv->pending_connections; link; link = g_slist_next (link)) {
337 		GCancellable *cancellable = link->data;
338 
339 		if (cancellable)
340 			g_cancellable_cancel (cancellable);
341 	}
342 	g_mutex_unlock (&conn_man->priv->pending_connections_lock);
343 }
344 
345 static void
imapx_conn_manager_abort_jobs(CamelIMAPXConnManager * conn_man)346 imapx_conn_manager_abort_jobs (CamelIMAPXConnManager *conn_man)
347 {
348 	GSList *link;
349 
350 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
351 
352 	JOB_QUEUE_LOCK (conn_man);
353 
354 	for (link = conn_man->priv->job_queue; link; link = g_slist_next (link)) {
355 		CamelIMAPXJob *job = link->data;
356 
357 		if (job)
358 			camel_imapx_job_abort (job);
359 	}
360 
361 	JOB_QUEUE_UNLOCK (conn_man);
362 }
363 
364 static CamelFolder *
imapx_conn_manager_ref_folder_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)365 imapx_conn_manager_ref_folder_sync (CamelIMAPXConnManager *conn_man,
366 				    CamelIMAPXMailbox *mailbox,
367 				    GCancellable *cancellable,
368 				    GError **error)
369 {
370 	CamelIMAPXStore *store;
371 	CamelFolder *folder;
372 	gchar *folder_path;
373 
374 	store = camel_imapx_conn_manager_ref_store (conn_man);
375 	folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
376 
377 	folder = camel_store_get_folder_sync (CAMEL_STORE (store), folder_path, 0, cancellable, NULL);
378 	if (folder)
379 		camel_imapx_folder_set_mailbox (CAMEL_IMAPX_FOLDER (folder), mailbox);
380 
381 	g_free (folder_path);
382 	g_clear_object (&store);
383 
384 	return folder;
385 }
386 
387 static void
imapx_conn_manager_inc_mailbox_hash(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GHashTable * mailboxes_hash)388 imapx_conn_manager_inc_mailbox_hash (CamelIMAPXConnManager *conn_man,
389 				     CamelIMAPXMailbox *mailbox,
390 				     GHashTable *mailboxes_hash)
391 {
392 	gint count;
393 
394 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
395 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
396 	g_return_if_fail (mailboxes_hash != NULL);
397 
398 	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
399 
400 	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
401 	count++;
402 
403 	g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count));
404 
405 	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
406 }
407 
408 static void
imapx_conn_manager_dec_mailbox_hash(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GHashTable * mailboxes_hash)409 imapx_conn_manager_dec_mailbox_hash (CamelIMAPXConnManager *conn_man,
410 				     CamelIMAPXMailbox *mailbox,
411 				     GHashTable *mailboxes_hash)
412 {
413 	gint count;
414 
415 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
416 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
417 	g_return_if_fail (mailboxes_hash != NULL);
418 
419 	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
420 
421 	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
422 	if (!count) {
423 		g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
424 		return;
425 	}
426 
427 	count--;
428 
429 	if (count)
430 		g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count));
431 	else
432 		g_hash_table_remove (mailboxes_hash, mailbox);
433 
434 	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
435 }
436 
437 static gboolean
imapx_conn_manager_is_mailbox_hash(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GHashTable * mailboxes_hash)438 imapx_conn_manager_is_mailbox_hash (CamelIMAPXConnManager *conn_man,
439 				    CamelIMAPXMailbox *mailbox,
440 				    GHashTable *mailboxes_hash)
441 {
442 	gint count;
443 
444 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
445 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
446 	g_return_val_if_fail (mailboxes_hash != NULL, FALSE);
447 
448 	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
449 
450 	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
451 
452 	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
453 
454 	return count > 0;
455 }
456 
457 static void
imapx_conn_manager_clear_mailboxes_hashes(CamelIMAPXConnManager * conn_man)458 imapx_conn_manager_clear_mailboxes_hashes (CamelIMAPXConnManager *conn_man)
459 {
460 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
461 
462 	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
463 
464 	g_hash_table_remove_all (conn_man->priv->busy_mailboxes);
465 	g_hash_table_remove_all (conn_man->priv->idle_mailboxes);
466 
467 	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
468 }
469 
470 static void
imapx_conn_manager_inc_mailbox_busy(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)471 imapx_conn_manager_inc_mailbox_busy (CamelIMAPXConnManager *conn_man,
472 				     CamelIMAPXMailbox *mailbox)
473 {
474 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
475 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
476 
477 	imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
478 }
479 
480 static void
imapx_conn_manager_dec_mailbox_busy(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)481 imapx_conn_manager_dec_mailbox_busy (CamelIMAPXConnManager *conn_man,
482 				     CamelIMAPXMailbox *mailbox)
483 {
484 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
485 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
486 
487 	imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
488 }
489 
490 static gboolean
imapx_conn_manager_is_mailbox_busy(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)491 imapx_conn_manager_is_mailbox_busy (CamelIMAPXConnManager *conn_man,
492 				    CamelIMAPXMailbox *mailbox)
493 {
494 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
495 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
496 
497 	return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
498 }
499 
500 static void
imapx_conn_manager_inc_mailbox_idle(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)501 imapx_conn_manager_inc_mailbox_idle (CamelIMAPXConnManager *conn_man,
502 				     CamelIMAPXMailbox *mailbox)
503 {
504 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
505 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
506 
507 	imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
508 }
509 
510 static void
imapx_conn_manager_dec_mailbox_idle(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)511 imapx_conn_manager_dec_mailbox_idle (CamelIMAPXConnManager *conn_man,
512 				     CamelIMAPXMailbox *mailbox)
513 {
514 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
515 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
516 
517 	imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
518 }
519 
520 static gboolean
imapx_conn_manager_is_mailbox_idle(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox)521 imapx_conn_manager_is_mailbox_idle (CamelIMAPXConnManager *conn_man,
522 				    CamelIMAPXMailbox *mailbox)
523 {
524 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
525 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
526 
527 	return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
528 }
529 
530 static gboolean
imapx_conn_manager_has_inbox_idle(CamelIMAPXConnManager * conn_man)531 imapx_conn_manager_has_inbox_idle (CamelIMAPXConnManager *conn_man)
532 {
533 	CamelIMAPXStore *imapx_store;
534 	CamelIMAPXMailbox *inbox_mailbox;
535 	gboolean is_idle;
536 
537 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
538 
539 	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
540 	inbox_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL;
541 
542 	g_clear_object (&imapx_store);
543 
544 	is_idle = inbox_mailbox && imapx_conn_manager_is_mailbox_idle (conn_man, inbox_mailbox);
545 
546 	g_clear_object (&inbox_mailbox);
547 
548 	return is_idle;
549 }
550 
551 static void
imapx_conn_manager_set_store(CamelIMAPXConnManager * conn_man,CamelStore * store)552 imapx_conn_manager_set_store (CamelIMAPXConnManager *conn_man,
553                               CamelStore *store)
554 {
555 	g_return_if_fail (CAMEL_IS_STORE (store));
556 
557 	g_weak_ref_set (&conn_man->priv->store, store);
558 }
559 
560 static void
imapx_conn_manager_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)561 imapx_conn_manager_set_property (GObject *object,
562                                  guint property_id,
563                                  const GValue *value,
564                                  GParamSpec *pspec)
565 {
566 	switch (property_id) {
567 		case PROP_STORE:
568 			imapx_conn_manager_set_store (
569 				CAMEL_IMAPX_CONN_MANAGER (object),
570 				g_value_get_object (value));
571 			return;
572 	}
573 
574 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
575 }
576 
577 static void
imapx_conn_manager_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)578 imapx_conn_manager_get_property (GObject *object,
579                                  guint property_id,
580                                  GValue *value,
581                                  GParamSpec *pspec)
582 {
583 	switch (property_id) {
584 		case PROP_STORE:
585 			g_value_take_object (
586 				value,
587 				camel_imapx_conn_manager_ref_store (
588 				CAMEL_IMAPX_CONN_MANAGER (object)));
589 			return;
590 	}
591 
592 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
593 }
594 
595 static void
imapx_conn_manager_dispose(GObject * object)596 imapx_conn_manager_dispose (GObject *object)
597 {
598 	CamelIMAPXConnManager *conn_man;
599 
600 	conn_man = CAMEL_IMAPX_CONN_MANAGER (object);
601 
602 	imapx_conn_manager_cancel_pending_connections (conn_man);
603 	imapx_conn_manager_abort_jobs (conn_man);
604 
605 	g_list_free_full (
606 		conn_man->priv->connections,
607 		(GDestroyNotify) connection_info_unref);
608 	conn_man->priv->connections = NULL;
609 
610 	g_weak_ref_set (&conn_man->priv->store, NULL);
611 
612 	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
613 	g_hash_table_remove_all (conn_man->priv->busy_mailboxes);
614 	g_hash_table_remove_all (conn_man->priv->idle_mailboxes);
615 	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
616 
617 	/* Chain up to parent's dispose() method. */
618 	G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->dispose (object);
619 }
620 
621 static void
imapx_conn_manager_finalize(GObject * object)622 imapx_conn_manager_finalize (GObject *object)
623 {
624 	CamelIMAPXConnManagerPrivate *priv;
625 
626 	priv = CAMEL_IMAPX_CONN_MANAGER (object)->priv;
627 
628 	g_warn_if_fail (priv->pending_connections == NULL);
629 	g_warn_if_fail (priv->job_queue == NULL);
630 
631 	g_rw_lock_clear (&priv->rw_lock);
632 	g_rec_mutex_clear (&priv->job_queue_lock);
633 	g_mutex_clear (&priv->pending_connections_lock);
634 	g_mutex_clear (&priv->busy_connections_lock);
635 	g_cond_clear (&priv->busy_connections_cond);
636 	g_weak_ref_clear (&priv->store);
637 	g_mutex_clear (&priv->busy_mailboxes_lock);
638 	g_hash_table_destroy (priv->busy_mailboxes);
639 	g_hash_table_destroy (priv->idle_mailboxes);
640 	g_mutex_clear (&priv->idle_refresh_lock);
641 	g_hash_table_destroy (priv->idle_refresh_mailboxes);
642 
643 	/* Chain up to parent's finalize() method. */
644 	G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->finalize (object);
645 }
646 
647 static void
camel_imapx_conn_manager_class_init(CamelIMAPXConnManagerClass * class)648 camel_imapx_conn_manager_class_init (CamelIMAPXConnManagerClass *class)
649 {
650 	GObjectClass *object_class;
651 
652 	object_class = G_OBJECT_CLASS (class);
653 	object_class->set_property = imapx_conn_manager_set_property;
654 	object_class->get_property = imapx_conn_manager_get_property;
655 	object_class->dispose = imapx_conn_manager_dispose;
656 	object_class->finalize = imapx_conn_manager_finalize;
657 
658 	g_object_class_install_property (
659 		object_class,
660 		PROP_STORE,
661 		g_param_spec_object (
662 			"store",
663 			"Store",
664 			"The CamelIMAPXStore to which we belong",
665 			CAMEL_TYPE_IMAPX_STORE,
666 			G_PARAM_READWRITE |
667 			G_PARAM_CONSTRUCT_ONLY |
668 			G_PARAM_STATIC_STRINGS));
669 
670 	signals[CONNECTION_CREATED] = g_signal_new (
671 		"connection-created",
672 		G_OBJECT_CLASS_TYPE (class),
673 		G_SIGNAL_RUN_FIRST,
674 		G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, connection_created),
675 		NULL, NULL, NULL,
676 		G_TYPE_NONE, 1,
677 		CAMEL_TYPE_IMAPX_SERVER);
678 }
679 
680 static void
camel_imapx_conn_manager_init(CamelIMAPXConnManager * conn_man)681 camel_imapx_conn_manager_init (CamelIMAPXConnManager *conn_man)
682 {
683 	conn_man->priv = camel_imapx_conn_manager_get_instance_private (conn_man);
684 
685 	g_rw_lock_init (&conn_man->priv->rw_lock);
686 	g_rec_mutex_init (&conn_man->priv->job_queue_lock);
687 	g_mutex_init (&conn_man->priv->pending_connections_lock);
688 	g_mutex_init (&conn_man->priv->busy_connections_lock);
689 	g_cond_init (&conn_man->priv->busy_connections_cond);
690 	g_weak_ref_init (&conn_man->priv->store, NULL);
691 	g_mutex_init (&conn_man->priv->busy_mailboxes_lock);
692 	g_mutex_init (&conn_man->priv->idle_refresh_lock);
693 
694 	conn_man->priv->last_tagprefix = 'A' - 1;
695 	conn_man->priv->busy_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
696 	conn_man->priv->idle_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
697 	conn_man->priv->idle_refresh_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
698 }
699 
700 static gchar
imapx_conn_manager_get_next_free_tagprefix_unlocked(CamelIMAPXConnManager * conn_man)701 imapx_conn_manager_get_next_free_tagprefix_unlocked (CamelIMAPXConnManager *conn_man)
702 {
703 	gchar adept;
704 	gint ii;
705 	GList *iter;
706 
707 	adept = conn_man->priv->last_tagprefix + 1;
708 
709 	/* the 'Z' is dedicated to auth types query */
710 	if (adept >= 'Z')
711 		adept = 'A';
712 	else if (adept < 'A')
713 		adept = 'A';
714 
715 	for (ii = 0; ii < 26; ii++) {
716 		for (iter = conn_man->priv->connections; iter; iter = g_list_next (iter)) {
717 			ConnectionInfo *cinfo = iter->data;
718 
719 			if (!cinfo || !cinfo->is)
720 				continue;
721 
722 			if (camel_imapx_server_get_tagprefix (cinfo->is) == adept)
723 				break;
724 		}
725 
726 		/* Read all current active connections and none has the same tag prefix */
727 		if (!iter)
728 			break;
729 
730 		adept++;
731 		if (adept >= 'Z')
732 			adept = 'A';
733 	}
734 
735 	g_return_val_if_fail (adept >= 'A' && adept < 'Z', 'Z');
736 
737 	conn_man->priv->last_tagprefix = adept;
738 
739 	return adept;
740 }
741 
742 static ConnectionInfo *
imapx_create_new_connection_unlocked(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)743 imapx_create_new_connection_unlocked (CamelIMAPXConnManager *conn_man,
744                                       CamelIMAPXMailbox *mailbox,
745                                       GCancellable *cancellable,
746                                       GError **error)
747 {
748 	CamelIMAPXServer *is = NULL;
749 	CamelIMAPXStore *imapx_store;
750 	ConnectionInfo *cinfo = NULL;
751 	gboolean success;
752 
753 	/* Caller must be holding CON_WRITE_LOCK. */
754 
755 	/* Check if we got cancelled while we were waiting. */
756 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
757 		return NULL;
758 
759 	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
760 	g_return_val_if_fail (imapx_store != NULL, NULL);
761 
762 	is = camel_imapx_server_new (imapx_store);
763 	camel_imapx_server_set_tagprefix (is, imapx_conn_manager_get_next_free_tagprefix_unlocked (conn_man));
764 
765 	g_signal_emit (conn_man, signals[CONNECTION_CREATED], 0, is);
766 
767 	/* XXX As part of the connect operation the CamelIMAPXServer will
768 	 *     have to call camel_session_authenticate_sync(), but it has
769 	 *     no way to pass itself through in that call so the service
770 	 *     knows which CamelIMAPXServer is trying to authenticate.
771 	 *
772 	 *     IMAPX is the only provider that does multiple connections
773 	 *     like this, so I didn't want to pollute the CamelSession and
774 	 *     CamelService authentication APIs with an extra argument.
775 	 *     Instead we do this little hack so the service knows which
776 	 *     CamelIMAPXServer to act on in its authenticate_sync() method.
777 	 *
778 	 *     Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK
779 	 *     we should not have multiple IMAPX connections trying to
780 	 *     authenticate at once, so this should be thread-safe.
781 	 */
782 	camel_imapx_store_set_connecting_server (imapx_store, is, conn_man->priv->connections != NULL);
783 	success = camel_imapx_server_connect_sync (is, cancellable, error);
784 	camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE);
785 
786 	if (!success)
787 		goto exit;
788 
789 	cinfo = connection_info_new (is);
790 
791 	cinfo->refresh_mailbox_handler_id = g_signal_connect (
792 		is, "refresh-mailbox", G_CALLBACK (imapx_conn_manager_refresh_mailbox_cb), conn_man);
793 
794 	/* Takes ownership of the ConnectionInfo. */
795 	conn_man->priv->connections = g_list_append (conn_man->priv->connections, cinfo);
796 
797 	c (camel_imapx_server_get_tagprefix (is), "Created new connection %p (server:%p) for %s; total connections %d\n",
798 		cinfo, cinfo->is,
799 		mailbox ? camel_imapx_mailbox_get_name (mailbox) : "[null]",
800 		g_list_length (conn_man->priv->connections));
801 
802 exit:
803 	g_object_unref (imapx_store);
804 	g_clear_object (&is);
805 
806 	return cinfo;
807 }
808 
809 static gint
imapx_conn_manager_get_max_connections(CamelIMAPXConnManager * conn_man)810 imapx_conn_manager_get_max_connections (CamelIMAPXConnManager *conn_man)
811 {
812 	CamelIMAPXStore *imapx_store;
813 	CamelSettings *settings;
814 	gint max_connections;
815 
816 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), -1);
817 
818 	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
819 	if (!imapx_store)
820 		return -1;
821 
822 	settings = camel_service_ref_settings (CAMEL_SERVICE (imapx_store));
823 
824 	max_connections = camel_imapx_settings_get_concurrent_connections (CAMEL_IMAPX_SETTINGS (settings));
825 
826 	if (conn_man->priv->limit_max_connections > 0 &&
827 	    conn_man->priv->limit_max_connections < max_connections)
828 		max_connections = conn_man->priv->limit_max_connections;
829 
830 	g_object_unref (settings);
831 	g_object_unref (imapx_store);
832 
833 	return max_connections > 0 ? max_connections : 1;
834 }
835 
836 static void
imapx_conn_manager_connection_wait_cancelled_cb(GCancellable * cancellable,CamelIMAPXConnManager * conn_man)837 imapx_conn_manager_connection_wait_cancelled_cb (GCancellable *cancellable,
838 						 CamelIMAPXConnManager *conn_man)
839 {
840 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
841 
842 	imapx_conn_manager_signal_busy_connections (conn_man);
843 }
844 
845 static ConnectionInfo *
camel_imapx_conn_manager_ref_connection(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,gboolean * out_is_new_connection,GCancellable * cancellable,GError ** error)846 camel_imapx_conn_manager_ref_connection (CamelIMAPXConnManager *conn_man,
847 					 CamelIMAPXMailbox *mailbox,
848 					 gboolean *out_is_new_connection,
849 					 GCancellable *cancellable,
850 					 GError **error)
851 {
852 	ConnectionInfo *cinfo = NULL;
853 	CamelIMAPXStore *imapx_store;
854 	CamelSession *session;
855 	GError *local_error = NULL;
856 
857 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
858 
859 	if (out_is_new_connection)
860 		*out_is_new_connection = FALSE;
861 
862 	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
863 	if (!imapx_store)
864 		return NULL;
865 
866 	session = camel_service_ref_session (CAMEL_SERVICE (imapx_store));
867 
868 	if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (imapx_store)) &&
869 	    session && camel_session_get_online (session)) {
870 
871 		g_mutex_lock (&conn_man->priv->pending_connections_lock);
872 		cancellable = camel_operation_new_proxy (cancellable);
873 		conn_man->priv->pending_connections = g_slist_prepend (conn_man->priv->pending_connections, cancellable);
874 		g_mutex_unlock (&conn_man->priv->pending_connections_lock);
875 
876 		/* Hold the writer lock while we requisition a CamelIMAPXServer
877 		 * to prevent other threads from adding or removing connections. */
878 		CON_READ_LOCK (conn_man);
879 
880 		/* Check if we've got cancelled while waiting for the lock. */
881 		while (!cinfo && !g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
882 			gint opened_connections, max_connections;
883 			GList *link;
884 
885 			for (link = conn_man->priv->connections; link; link = g_list_next (link)) {
886 				ConnectionInfo *candidate = link->data;
887 
888 				if (candidate && connection_info_try_reserve (candidate)) {
889 					cinfo = connection_info_ref (candidate);
890 					break;
891 				}
892 			}
893 
894 			if (cinfo)
895 				break;
896 
897 			opened_connections = g_list_length (conn_man->priv->connections);
898 			max_connections = imapx_conn_manager_get_max_connections (conn_man);
899 
900 			if (max_connections <= 0)
901 				break;
902 
903 			if (!cinfo && opened_connections < max_connections) {
904 				GError *local_error_2 = NULL;
905 
906 				CON_READ_UNLOCK (conn_man);
907 				CON_WRITE_LOCK (conn_man);
908 				cinfo = imapx_create_new_connection_unlocked (conn_man, mailbox, cancellable, &local_error_2);
909 				if (cinfo)
910 					connection_info_set_busy (cinfo, TRUE);
911 				CON_WRITE_UNLOCK (conn_man);
912 				CON_READ_LOCK (conn_man);
913 
914 				if (!cinfo) {
915 					gboolean limit_connections =
916 						g_error_matches (local_error_2, CAMEL_IMAPX_SERVER_ERROR,
917 						CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) &&
918 						conn_man->priv->connections;
919 
920 					c ('*', "Failed to open a new connection, while having %d opened, with error: %s; will limit connections: %s\n",
921 						g_list_length (conn_man->priv->connections),
922 						local_error_2 ? local_error_2->message : "Unknown error",
923 						limit_connections ? "yes" : "no");
924 
925 					if (limit_connections) {
926 						/* limit to one-less than current connection count - be nice to the server */
927 						conn_man->priv->limit_max_connections = g_list_length (conn_man->priv->connections) - 1;
928 						if (!conn_man->priv->limit_max_connections)
929 							conn_man->priv->limit_max_connections = 1;
930 
931 						g_clear_error (&local_error_2);
932 					} else {
933 						if (local_error_2)
934 							g_propagate_error (&local_error, local_error_2);
935 						break;
936 					}
937 				} else {
938 					connection_info_ref (cinfo);
939 
940 					if (out_is_new_connection)
941 						*out_is_new_connection = TRUE;
942 				}
943 			}
944 
945 			if (!cinfo) {
946 				gulong handler_id;
947 
948 				CON_READ_UNLOCK (conn_man);
949 
950 				handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_conn_manager_connection_wait_cancelled_cb), conn_man, NULL);
951 
952 				g_mutex_lock (&conn_man->priv->busy_connections_lock);
953 				g_cond_wait (&conn_man->priv->busy_connections_cond, &conn_man->priv->busy_connections_lock);
954 				g_mutex_unlock (&conn_man->priv->busy_connections_lock);
955 
956 				if (handler_id)
957 					g_cancellable_disconnect (cancellable, handler_id);
958 
959 				CON_READ_LOCK (conn_man);
960 			}
961 		}
962 
963 		CON_READ_UNLOCK (conn_man);
964 
965 		g_mutex_lock (&conn_man->priv->pending_connections_lock);
966 		conn_man->priv->pending_connections = g_slist_remove (conn_man->priv->pending_connections, cancellable);
967 		g_object_unref (cancellable);
968 		g_mutex_unlock (&conn_man->priv->pending_connections_lock);
969 	}
970 
971 	g_clear_object (&imapx_store);
972 	g_clear_object (&session);
973 
974 	if (!cinfo && (!local_error || local_error->domain == G_RESOLVER_ERROR)) {
975 		if (local_error) {
976 			g_set_error (
977 				error, CAMEL_SERVICE_ERROR,
978 				CAMEL_SERVICE_ERROR_UNAVAILABLE,
979 				_("You must be working online to complete this operation (%s)"),
980 				local_error->message);
981 
982 			g_clear_error (&local_error);
983 		} else {
984 			g_set_error_literal (
985 				&local_error, CAMEL_SERVICE_ERROR,
986 				CAMEL_SERVICE_ERROR_UNAVAILABLE,
987 				_("You must be working online to complete this operation"));
988 		}
989 	}
990 
991 	if (local_error)
992 		g_propagate_error (error, local_error);
993 
994 	return cinfo;
995 }
996 
997 /****************************/
998 
999 CamelIMAPXConnManager *
camel_imapx_conn_manager_new(CamelStore * store)1000 camel_imapx_conn_manager_new (CamelStore *store)
1001 {
1002 	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1003 
1004 	return g_object_new (
1005 		CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL);
1006 }
1007 
1008 CamelIMAPXStore *
camel_imapx_conn_manager_ref_store(CamelIMAPXConnManager * conn_man)1009 camel_imapx_conn_manager_ref_store (CamelIMAPXConnManager *conn_man)
1010 {
1011 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
1012 
1013 	return g_weak_ref_get (&conn_man->priv->store);
1014 }
1015 
1016 gboolean
camel_imapx_conn_manager_connect_sync(CamelIMAPXConnManager * conn_man,GCancellable * cancellable,GError ** error)1017 camel_imapx_conn_manager_connect_sync (CamelIMAPXConnManager *conn_man,
1018 				       GCancellable *cancellable,
1019 				       GError **error)
1020 {
1021 	ConnectionInfo *cinfo;
1022 
1023 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1024 
1025 	CON_READ_LOCK (conn_man);
1026 	if (conn_man->priv->connections) {
1027 		CON_READ_UNLOCK (conn_man);
1028 		return TRUE;
1029 	}
1030 	CON_READ_UNLOCK (conn_man);
1031 
1032 	imapx_conn_manager_clear_mailboxes_hashes (conn_man);
1033 
1034 	cinfo = camel_imapx_conn_manager_ref_connection (conn_man, NULL, NULL, cancellable, error);
1035 	if (cinfo) {
1036 		imapx_conn_manager_unmark_busy (conn_man, cinfo);
1037 		connection_info_unref (cinfo);
1038 	}
1039 
1040 	return cinfo != NULL;
1041 }
1042 
1043 gboolean
camel_imapx_conn_manager_disconnect_sync(CamelIMAPXConnManager * conn_man,GCancellable * cancellable,GError ** error)1044 camel_imapx_conn_manager_disconnect_sync (CamelIMAPXConnManager *conn_man,
1045 					  GCancellable *cancellable,
1046 					  GError **error)
1047 {
1048 	GList *link, *connections;
1049 
1050 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1051 
1052 	/* Do this before acquiring the write lock, because any pending
1053 	   connection holds the write lock, thus makes this request starve. */
1054 	imapx_conn_manager_cancel_pending_connections (conn_man);
1055 	imapx_conn_manager_abort_jobs (conn_man);
1056 
1057 	CON_WRITE_LOCK (conn_man);
1058 
1059 	c ('*', "Disconnecting all %d connections\n", g_list_length (conn_man->priv->connections));
1060 
1061 	connections = conn_man->priv->connections;
1062 	conn_man->priv->connections = NULL;
1063 
1064 	CON_WRITE_UNLOCK (conn_man);
1065 
1066 	for (link = connections; link; link = g_list_next (link)) {
1067 		ConnectionInfo *cinfo = link->data;
1068 		GError *local_error = NULL;
1069 
1070 		if (!cinfo)
1071 			continue;
1072 
1073 		if (!camel_imapx_server_disconnect_sync (cinfo->is, cancellable, &local_error)) {
1074 			c (camel_imapx_server_get_tagprefix (cinfo->is), "   Failed to disconnect from the server: %s\n",
1075 				local_error ? local_error->message : "Unknown error");
1076 		}
1077 
1078 		connection_info_unref (cinfo);
1079 		g_clear_error (&local_error);
1080 	}
1081 
1082 	g_list_free (connections);
1083 
1084 	imapx_conn_manager_clear_mailboxes_hashes (conn_man);
1085 
1086 	return TRUE;
1087 }
1088 
1089 static gboolean
imapx_conn_manager_should_wait_for(CamelIMAPXConnManager * conn_man,CamelIMAPXJob * new_job,CamelIMAPXJob * queued_job)1090 imapx_conn_manager_should_wait_for (CamelIMAPXConnManager *conn_man,
1091 				    CamelIMAPXJob *new_job,
1092 				    CamelIMAPXJob *queued_job)
1093 {
1094 	guint32 job_kind;
1095 
1096 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1097 	g_return_val_if_fail (queued_job != NULL, FALSE);
1098 
1099 	if (camel_imapx_job_get_kind (new_job) == CAMEL_IMAPX_JOB_GET_MESSAGE)
1100 		return FALSE;
1101 
1102 	job_kind = camel_imapx_job_get_kind (queued_job);
1103 
1104 	/* List jobs with high priority. */
1105 	return job_kind == CAMEL_IMAPX_JOB_GET_MESSAGE ||
1106 	       job_kind == CAMEL_IMAPX_JOB_COPY_MESSAGE ||
1107 	       job_kind == CAMEL_IMAPX_JOB_MOVE_MESSAGE ||
1108 	       job_kind == CAMEL_IMAPX_JOB_EXPUNGE;
1109 }
1110 
1111 gboolean
camel_imapx_conn_manager_run_job_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXJob * job,CamelIMAPXJobMatchesFunc finish_before_job,GCancellable * cancellable,GError ** error)1112 camel_imapx_conn_manager_run_job_sync (CamelIMAPXConnManager *conn_man,
1113 				       CamelIMAPXJob *job,
1114 				       CamelIMAPXJobMatchesFunc finish_before_job,
1115 				       GCancellable *cancellable,
1116 				       GError **error)
1117 {
1118 	GSList *link;
1119 	ConnectionInfo *cinfo;
1120 	gboolean success = FALSE, is_new_connection = FALSE;
1121 	GError *local_error = NULL;
1122 
1123 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1124 	g_return_val_if_fail (job != NULL, FALSE);
1125 
1126 	JOB_QUEUE_LOCK (conn_man);
1127 
1128 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
1129 		JOB_QUEUE_UNLOCK (conn_man);
1130 		return FALSE;
1131 	}
1132 
1133 	link = conn_man->priv->job_queue;
1134 	while (link) {
1135 		CamelIMAPXJob *queued_job = link->data;
1136 		gboolean matches;
1137 
1138 		g_warn_if_fail (queued_job != NULL);
1139 		g_warn_if_fail (queued_job != job);
1140 
1141 		if (!queued_job) {
1142 			link = g_slist_next (link);
1143 			continue;
1144 		}
1145 
1146 		matches = camel_imapx_job_matches (job, queued_job);
1147 		if (matches || (finish_before_job && finish_before_job (job, queued_job)) ||
1148 		    imapx_conn_manager_should_wait_for (conn_man, job, queued_job)) {
1149 			camel_imapx_job_ref (queued_job);
1150 
1151 			JOB_QUEUE_UNLOCK (conn_man);
1152 
1153 			camel_imapx_job_wait_sync (queued_job, cancellable);
1154 
1155 			if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
1156 				camel_imapx_job_unref (queued_job);
1157 				return FALSE;
1158 			}
1159 
1160 			if (matches) {
1161 				gpointer result = NULL;
1162 				GDestroyNotify destroy_result = NULL;
1163 
1164 				/* Do not inherit cancelled errors, just try again */
1165 				if (!camel_imapx_job_was_cancelled (queued_job) &&
1166 				    camel_imapx_job_copy_result (queued_job, &success, &result, &local_error, &destroy_result)) {
1167 					camel_imapx_job_set_result (job, success, result, local_error, destroy_result);
1168 					camel_imapx_job_unref (queued_job);
1169 
1170 					if (local_error)
1171 						g_propagate_error (error, local_error);
1172 
1173 					return success;
1174 				}
1175 			}
1176 
1177 			JOB_QUEUE_LOCK (conn_man);
1178 
1179 			camel_imapx_job_unref (queued_job);
1180 
1181 			/* The queue could change, start from the beginning. */
1182 			link = conn_man->priv->job_queue;
1183 		} else {
1184 			link = g_slist_next (link);
1185 		}
1186 	}
1187 
1188 	conn_man->priv->job_queue = g_slist_prepend (conn_man->priv->job_queue, job);
1189 
1190 	JOB_QUEUE_UNLOCK (conn_man);
1191 
1192 	do {
1193 		g_clear_error (&local_error);
1194 
1195 		cinfo = camel_imapx_conn_manager_ref_connection (conn_man, camel_imapx_job_get_mailbox (job), &is_new_connection, cancellable, error);
1196 		if (cinfo) {
1197 			CamelIMAPXMailbox *job_mailbox;
1198 			CamelIMAPXMailbox *idle_mailbox;
1199 
1200 			job_mailbox = camel_imapx_job_get_mailbox (job);
1201 
1202 			if (job_mailbox)
1203 				imapx_conn_manager_inc_mailbox_busy (conn_man, job_mailbox);
1204 
1205 			idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is);
1206 
1207 			if (idle_mailbox)
1208 				imapx_conn_manager_dec_mailbox_idle (conn_man, idle_mailbox);
1209 
1210 			g_clear_object (&idle_mailbox);
1211 
1212 			success = camel_imapx_server_stop_idle_sync (cinfo->is, cancellable, &local_error);
1213 
1214 			if (success && camel_imapx_server_can_use_idle (cinfo->is)) {
1215 				GList *link, *connection_infos, *disconnected_infos = NULL;
1216 
1217 				CON_READ_LOCK (conn_man);
1218 				connection_infos = g_list_copy (conn_man->priv->connections);
1219 				g_list_foreach (connection_infos, (GFunc) connection_info_ref, NULL);
1220 				CON_READ_UNLOCK (conn_man);
1221 
1222 				/* Stop IDLE on all connections serving the same mailbox,
1223 				   to avoid notifications for changes done by itself */
1224 				for (link = connection_infos; link && !g_cancellable_is_cancelled (cancellable); link = g_list_next (link)) {
1225 					ConnectionInfo *other_cinfo = link->data;
1226 					CamelIMAPXMailbox *other_mailbox;
1227 
1228 					if (!other_cinfo || other_cinfo == cinfo || connection_info_get_busy (other_cinfo) ||
1229 					    !camel_imapx_server_is_in_idle (other_cinfo->is))
1230 						continue;
1231 
1232 					other_mailbox = camel_imapx_server_ref_idle_mailbox (other_cinfo->is);
1233 					if (job_mailbox == other_mailbox) {
1234 						if (!camel_imapx_server_stop_idle_sync (other_cinfo->is, cancellable, &local_error)) {
1235 							c (camel_imapx_server_get_tagprefix (other_cinfo->is),
1236 								"Failed to stop IDLE call (will be removed) on connection %p (server:%p) due to error: %s\n",
1237 								other_cinfo, other_cinfo->is, local_error ? local_error->message : "Unknown error");
1238 
1239 							camel_imapx_server_disconnect_sync (other_cinfo->is, cancellable, NULL);
1240 
1241 							disconnected_infos = g_list_prepend (disconnected_infos, connection_info_ref (other_cinfo));
1242 						} else {
1243 							imapx_conn_manager_dec_mailbox_idle (conn_man, other_mailbox);
1244 						}
1245 
1246 						g_clear_error (&local_error);
1247 					}
1248 
1249 					g_clear_object (&other_mailbox);
1250 				}
1251 
1252 				for (link = disconnected_infos; link; link = g_list_next (link)) {
1253 					ConnectionInfo *other_cinfo = link->data;
1254 
1255 					imapx_conn_manager_remove_info (conn_man, other_cinfo);
1256 				}
1257 
1258 				g_list_free_full (disconnected_infos, (GDestroyNotify) connection_info_unref);
1259 				g_list_free_full (connection_infos, (GDestroyNotify) connection_info_unref);
1260 			}
1261 
1262 			if (success)
1263 				success = camel_imapx_job_run_sync (job, cinfo->is, cancellable, &local_error);
1264 
1265 			if (job_mailbox)
1266 				imapx_conn_manager_dec_mailbox_busy (conn_man, job_mailbox);
1267 
1268 			if (success) {
1269 				if (!imapx_conn_manager_has_inbox_idle (conn_man)) {
1270 					CamelIMAPXStore *imapx_store;
1271 
1272 					imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
1273 					idle_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL;
1274 
1275 					g_clear_object (&imapx_store);
1276 				}
1277 
1278 				if (!idle_mailbox)
1279 					idle_mailbox = camel_imapx_server_ref_selected (cinfo->is);
1280 
1281 				/* Can start IDLE on the connection only if the IDLE folder is not busy
1282 				   and not in IDLE already, to avoid multiple IDLE notifications on the same mailbox */
1283 				if (idle_mailbox && camel_imapx_server_can_use_idle (cinfo->is) &&
1284 				    !imapx_conn_manager_is_mailbox_busy (conn_man, idle_mailbox) &&
1285 				    !imapx_conn_manager_is_mailbox_idle (conn_man, idle_mailbox)) {
1286 					camel_imapx_server_schedule_idle_sync (cinfo->is, idle_mailbox, cancellable, NULL);
1287 
1288 					if (camel_imapx_server_is_in_idle (cinfo->is)) {
1289 						g_clear_object (&idle_mailbox);
1290 
1291 						idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is);
1292 						if (idle_mailbox)
1293 							imapx_conn_manager_inc_mailbox_idle (conn_man, idle_mailbox);
1294 					}
1295 				}
1296 
1297 				g_clear_object (&idle_mailbox);
1298 
1299 				imapx_conn_manager_unmark_busy (conn_man, cinfo);
1300 			} else if (!local_error || ((local_error->domain == G_IO_ERROR || local_error->domain == G_TLS_ERROR || local_error->domain == CAMEL_IMAPX_ERROR ||
1301 				   g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) &&
1302 				   !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))) {
1303 				c (camel_imapx_server_get_tagprefix (cinfo->is), "Removed connection %p (server:%p) due to error: %s\n",
1304 					cinfo, cinfo->is, local_error ? local_error->message : "Unknown error");
1305 
1306 				camel_imapx_server_disconnect_sync (cinfo->is, cancellable, NULL);
1307 				imapx_conn_manager_remove_info (conn_man, cinfo);
1308 
1309 				if (!local_error ||
1310 				    g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_MISC) ||
1311 				    g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_EOF) ||
1312 				    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CLOSED)) {
1313 					GError *tmp = local_error;
1314 
1315 					local_error = NULL;
1316 
1317 					/* This message won't get into UI. */
1318 					g_set_error (&local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
1319 						"Reconnect after failure: %s", tmp ? tmp->message : "Unknown error");
1320 
1321 					g_clear_error (&tmp);
1322 				}
1323 			} else {
1324 				c (camel_imapx_server_get_tagprefix (cinfo->is), "Unmark connection %p (server:%p) busy after failure, error: %s\n",
1325 					cinfo, cinfo->is, local_error ? local_error->message : "Unknown error");
1326 
1327 				imapx_conn_manager_unmark_busy (conn_man, cinfo);
1328 			}
1329 
1330 			connection_info_unref (cinfo);
1331 		}
1332 
1333 		/* If there's a reconnect required for a new connection, then there happened
1334 		   something really wrong, thus rather give up. */
1335 	} while (!success && !is_new_connection && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT));
1336 
1337 	if (local_error)
1338 		g_propagate_error (error, local_error);
1339 
1340 	JOB_QUEUE_LOCK (conn_man);
1341 	conn_man->priv->job_queue = g_slist_remove (conn_man->priv->job_queue, job);
1342 	JOB_QUEUE_UNLOCK (conn_man);
1343 
1344 	camel_imapx_job_done (job);
1345 
1346 	return success;
1347 }
1348 
1349 static gboolean
imapx_conn_manager_noop_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)1350 imapx_conn_manager_noop_run_sync (CamelIMAPXJob *job,
1351 				  CamelIMAPXServer *server,
1352 				  GCancellable *cancellable,
1353 				  GError **error)
1354 {
1355 	CamelIMAPXMailbox *mailbox;
1356 	gboolean success;
1357 	GError *local_error = NULL;
1358 
1359 	g_return_val_if_fail (job != NULL, FALSE);
1360 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
1361 
1362 	mailbox = camel_imapx_job_get_mailbox (job);
1363 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
1364 
1365 	success = camel_imapx_server_noop_sync (server, mailbox, cancellable, &local_error);
1366 
1367 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
1368 
1369 	if (local_error)
1370 		g_propagate_error (error, local_error);
1371 
1372 	return success;
1373 }
1374 
1375 gboolean
camel_imapx_conn_manager_noop_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)1376 camel_imapx_conn_manager_noop_sync (CamelIMAPXConnManager *conn_man,
1377 				    CamelIMAPXMailbox *mailbox,
1378 				    GCancellable *cancellable,
1379 				    GError **error)
1380 {
1381 	CamelIMAPXJob *job;
1382 	gboolean success;
1383 
1384 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1385 
1386 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_NOOP, mailbox,
1387 		imapx_conn_manager_noop_run_sync, NULL, NULL);
1388 
1389 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
1390 
1391 	camel_imapx_job_unref (job);
1392 
1393 	return success;
1394 }
1395 
1396 static gboolean
imapx_conn_manager_nothing_matches(CamelIMAPXJob * job,CamelIMAPXJob * other_job)1397 imapx_conn_manager_nothing_matches (CamelIMAPXJob *job,
1398 				    CamelIMAPXJob *other_job)
1399 {
1400 	/* For jobs where none can match. */
1401 	return FALSE;
1402 }
1403 
1404 static gboolean
imapx_conn_manager_matches_sync_changes_or_refresh_info(CamelIMAPXJob * job,CamelIMAPXJob * other_job)1405 imapx_conn_manager_matches_sync_changes_or_refresh_info (CamelIMAPXJob *job,
1406 							 CamelIMAPXJob *other_job)
1407 {
1408 	CamelIMAPXJobKind other_job_kind;
1409 
1410 	g_return_val_if_fail (job != NULL, FALSE);
1411 	g_return_val_if_fail (other_job != NULL, FALSE);
1412 	g_return_val_if_fail (job != other_job, FALSE);
1413 
1414 	if (camel_imapx_job_get_mailbox (job) != camel_imapx_job_get_mailbox (other_job))
1415 		return FALSE;
1416 
1417 	other_job_kind = camel_imapx_job_get_kind (other_job);
1418 
1419 	return other_job_kind == CAMEL_IMAPX_JOB_SYNC_CHANGES ||
1420 	       other_job_kind == CAMEL_IMAPX_JOB_REFRESH_INFO;
1421 }
1422 
1423 struct ListJobData {
1424 	gchar *pattern;
1425 	CamelStoreGetFolderInfoFlags flags;
1426 };
1427 
1428 static void
list_job_data_free(gpointer ptr)1429 list_job_data_free (gpointer ptr)
1430 {
1431 	struct ListJobData *job_data = ptr;
1432 
1433 	if (job_data) {
1434 		g_free (job_data->pattern);
1435 		g_slice_free (struct ListJobData, job_data);
1436 	}
1437 }
1438 
1439 static gboolean
imapx_conn_manager_list_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)1440 imapx_conn_manager_list_run_sync (CamelIMAPXJob *job,
1441 				  CamelIMAPXServer *server,
1442 				  GCancellable *cancellable,
1443 				  GError **error)
1444 {
1445 	struct ListJobData *job_data;
1446 
1447 	g_return_val_if_fail (job != NULL, FALSE);
1448 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
1449 
1450 	job_data = camel_imapx_job_get_user_data (job);
1451 	g_return_val_if_fail (job_data != NULL, FALSE);
1452 
1453 	return camel_imapx_server_list_sync (server, job_data->pattern, job_data->flags, cancellable, error);
1454 }
1455 
1456 static gboolean
imapx_conn_manager_list_matches(CamelIMAPXJob * job,CamelIMAPXJob * other_job)1457 imapx_conn_manager_list_matches (CamelIMAPXJob *job,
1458 				 CamelIMAPXJob *other_job)
1459 {
1460 	struct ListJobData *job_data, *other_job_data;
1461 
1462 	g_return_val_if_fail (job != NULL, FALSE);
1463 	g_return_val_if_fail (other_job != NULL, FALSE);
1464 
1465 	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_LIST ||
1466 	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
1467 		return FALSE;
1468 
1469 	job_data = camel_imapx_job_get_user_data (job);
1470 	other_job_data = camel_imapx_job_get_user_data (other_job);
1471 
1472 	if (!job_data || !other_job_data)
1473 		return FALSE;
1474 
1475 	return job_data->flags == other_job_data->flags &&
1476 	       g_strcmp0 (job_data->pattern, other_job_data->pattern) == 0;
1477 }
1478 
1479 gboolean
camel_imapx_conn_manager_list_sync(CamelIMAPXConnManager * conn_man,const gchar * pattern,CamelStoreGetFolderInfoFlags flags,GCancellable * cancellable,GError ** error)1480 camel_imapx_conn_manager_list_sync (CamelIMAPXConnManager *conn_man,
1481 				    const gchar *pattern,
1482 				    CamelStoreGetFolderInfoFlags flags,
1483 				    GCancellable *cancellable,
1484 				    GError **error)
1485 {
1486 	CamelIMAPXJob *job;
1487 	struct ListJobData *job_data;
1488 	gboolean success = FALSE;
1489 
1490 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1491 
1492 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_LIST, NULL,
1493 		imapx_conn_manager_list_run_sync,
1494 		imapx_conn_manager_list_matches,
1495 		NULL);
1496 
1497 	job_data = g_slice_new0 (struct ListJobData);
1498 	job_data->pattern = g_strdup (pattern);
1499 	job_data->flags = flags;
1500 
1501 	camel_imapx_job_set_user_data (job, job_data, list_job_data_free);
1502 
1503 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
1504 	if (success)
1505 		camel_imapx_job_copy_result (job, &success, NULL, error, NULL);
1506 
1507 	camel_imapx_job_unref (job);
1508 
1509 	return success;
1510 }
1511 
1512 static gboolean
imapx_conn_manager_refresh_info_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)1513 imapx_conn_manager_refresh_info_run_sync (CamelIMAPXJob *job,
1514 					  CamelIMAPXServer *server,
1515 					  GCancellable *cancellable,
1516 					  GError **error)
1517 {
1518 	CamelIMAPXMailbox *mailbox;
1519 	gboolean success;
1520 	GError *local_error = NULL;
1521 
1522 	g_return_val_if_fail (job != NULL, FALSE);
1523 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
1524 
1525 	mailbox = camel_imapx_job_get_mailbox (job);
1526 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
1527 
1528 	success = camel_imapx_server_refresh_info_sync (server, mailbox, cancellable, &local_error);
1529 
1530 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
1531 
1532 	if (local_error)
1533 		g_propagate_error (error, local_error);
1534 
1535 	return success;
1536 }
1537 
1538 gboolean
camel_imapx_conn_manager_refresh_info_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)1539 camel_imapx_conn_manager_refresh_info_sync (CamelIMAPXConnManager *conn_man,
1540 					    CamelIMAPXMailbox *mailbox,
1541 					    GCancellable *cancellable,
1542 					    GError **error)
1543 {
1544 	CamelIMAPXJob *job;
1545 	gboolean success;
1546 
1547 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1548 
1549 	if (!camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
1550 		return FALSE;
1551 
1552 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_REFRESH_INFO, mailbox,
1553 		imapx_conn_manager_refresh_info_run_sync, NULL, NULL);
1554 
1555 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
1556 		imapx_conn_manager_matches_sync_changes_or_refresh_info,
1557 		cancellable, error);
1558 
1559 	camel_imapx_job_unref (job);
1560 
1561 	return success;
1562 }
1563 
1564 static gboolean
imapx_conn_manager_move_to_real_junk_sync(CamelIMAPXConnManager * conn_man,CamelFolder * folder,GCancellable * cancellable,gboolean * out_need_to_expunge,GError ** error)1565 imapx_conn_manager_move_to_real_junk_sync (CamelIMAPXConnManager *conn_man,
1566 					   CamelFolder *folder,
1567 					   GCancellable *cancellable,
1568 					   gboolean *out_need_to_expunge,
1569 					   GError **error)
1570 {
1571 	CamelIMAPXFolder *imapx_folder;
1572 	CamelIMAPXMailbox *mailbox;
1573 	CamelIMAPXSettings *settings;
1574 	GPtrArray *uids_to_copy;
1575 	gchar *real_junk_path = NULL;
1576 	gboolean success = TRUE;
1577 
1578 	*out_need_to_expunge = FALSE;
1579 
1580 	/* Caller already obtained the mailbox from the folder,
1581 	 * so the folder should still have it readily available. */
1582 	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1583 	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
1584 	g_return_val_if_fail (mailbox != NULL, FALSE);
1585 
1586 	uids_to_copy = g_ptr_array_new_with_free_func (
1587 		(GDestroyNotify) camel_pstring_free);
1588 
1589 	settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder))));
1590 	if (camel_imapx_settings_get_use_real_junk_path (settings)) {
1591 		real_junk_path = camel_imapx_settings_dup_real_junk_path (settings);
1592 		camel_imapx_folder_claim_move_to_real_junk_uids (imapx_folder, uids_to_copy);
1593 	}
1594 	g_object_unref (settings);
1595 
1596 	if (uids_to_copy->len > 0) {
1597 		CamelIMAPXStore *imapx_store;
1598 		CamelIMAPXMailbox *destination = NULL;
1599 
1600 		imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
1601 
1602 		if (real_junk_path != NULL) {
1603 			folder = camel_store_get_folder_sync (
1604 				CAMEL_STORE (imapx_store),
1605 				real_junk_path, 0,
1606 				cancellable, error);
1607 		} else {
1608 			g_set_error (
1609 				error, CAMEL_FOLDER_ERROR,
1610 				CAMEL_FOLDER_ERROR_INVALID_PATH,
1611 				_("No destination folder specified"));
1612 			folder = NULL;
1613 		}
1614 
1615 		if (folder != NULL) {
1616 			destination = camel_imapx_folder_list_mailbox (
1617 				CAMEL_IMAPX_FOLDER (folder),
1618 				cancellable, error);
1619 			g_object_unref (folder);
1620 		}
1621 
1622 		/* Avoid duplicating messages in the Junk folder. */
1623 		if (destination == mailbox) {
1624 			success = TRUE;
1625 		} else if (destination != NULL) {
1626 			success = imapx_conn_manager_copy_message_sync (
1627 				conn_man, mailbox, destination,
1628 				uids_to_copy, TRUE, FALSE, TRUE,
1629 				cancellable, error);
1630 			*out_need_to_expunge = success;
1631 		} else {
1632 			success = FALSE;
1633 		}
1634 
1635 		if (!success) {
1636 			g_prefix_error (
1637 				error, "%s: ",
1638 				_("Unable to move junk messages"));
1639 		}
1640 
1641 		g_clear_object (&destination);
1642 		g_clear_object (&imapx_store);
1643 	}
1644 
1645 	g_ptr_array_unref (uids_to_copy);
1646 	g_free (real_junk_path);
1647 
1648 	g_clear_object (&mailbox);
1649 
1650 	return success;
1651 }
1652 
1653 static gboolean
imapx_conn_manager_move_to_real_trash_sync(CamelIMAPXConnManager * conn_man,CamelFolder * folder,GCancellable * cancellable,gboolean * out_need_to_expunge,GError ** error)1654 imapx_conn_manager_move_to_real_trash_sync (CamelIMAPXConnManager *conn_man,
1655 					    CamelFolder *folder,
1656 					    GCancellable *cancellable,
1657 					    gboolean *out_need_to_expunge,
1658 					    GError **error)
1659 {
1660 	CamelIMAPXFolder *imapx_folder;
1661 	CamelIMAPXMailbox *mailbox, *destination = NULL;
1662 	CamelIMAPXSettings *settings;
1663 	CamelIMAPXStore *imapx_store;
1664 	GPtrArray *uids_to_copy;
1665 	gchar *real_trash_path = NULL;
1666 	guint32 folder_deleted_count = 0;
1667 	gboolean success = TRUE;
1668 
1669 	*out_need_to_expunge = FALSE;
1670 
1671 	/* Caller already obtained the mailbox from the folder,
1672 	 * so the folder should still have it readily available. */
1673 	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1674 	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
1675 	g_return_val_if_fail (mailbox != NULL, FALSE);
1676 
1677 	uids_to_copy = g_ptr_array_new_with_free_func (
1678 		(GDestroyNotify) camel_pstring_free);
1679 
1680 	settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder))));
1681 	if (camel_imapx_settings_get_use_real_trash_path (settings)) {
1682 		real_trash_path = camel_imapx_settings_dup_real_trash_path (settings);
1683 		camel_imapx_folder_claim_move_to_real_trash_uids (CAMEL_IMAPX_FOLDER (folder), uids_to_copy);
1684 	}
1685 	g_object_unref (settings);
1686 
1687 	if (!uids_to_copy->len) {
1688 		g_ptr_array_unref (uids_to_copy);
1689 		g_clear_object (&mailbox);
1690 		g_free (real_trash_path);
1691 
1692 		return TRUE;
1693 	}
1694 
1695 	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
1696 
1697 	if (real_trash_path != NULL) {
1698 		folder = camel_store_get_folder_sync (
1699 			CAMEL_STORE (imapx_store),
1700 			real_trash_path, 0,
1701 			cancellable, error);
1702 	} else {
1703 		if (uids_to_copy->len > 0) {
1704 			g_set_error (
1705 				error, CAMEL_FOLDER_ERROR,
1706 				CAMEL_FOLDER_ERROR_INVALID_PATH,
1707 				_("No destination folder specified"));
1708 		}
1709 
1710 		folder = NULL;
1711 	}
1712 
1713 	if (folder != NULL) {
1714 		destination = camel_imapx_folder_list_mailbox (
1715 			CAMEL_IMAPX_FOLDER (folder),
1716 			cancellable, error);
1717 		folder_deleted_count = camel_folder_summary_get_deleted_count (camel_folder_get_folder_summary (folder));
1718 		g_object_unref (folder);
1719 	}
1720 
1721 	/* Avoid duplicating messages in the Trash folder. */
1722 	if (destination == mailbox) {
1723 		success = TRUE;
1724 		/* Deleted messages in the real Trash folder will be permanently deleted immediately. */
1725 		*out_need_to_expunge = folder_deleted_count > 0 || uids_to_copy->len > 0;
1726 	} else if (destination != NULL) {
1727 		if (uids_to_copy->len > 0) {
1728 			success = imapx_conn_manager_copy_message_sync (
1729 				conn_man, mailbox, destination,
1730 				uids_to_copy, TRUE, TRUE, TRUE,
1731 				cancellable, error);
1732 			*out_need_to_expunge = success;
1733 		}
1734 	} else if (uids_to_copy->len > 0) {
1735 		success = FALSE;
1736 	}
1737 
1738 	if (!success) {
1739 		g_prefix_error (
1740 			error, "%s: ",
1741 			_("Unable to move deleted messages"));
1742 	}
1743 
1744 	g_ptr_array_unref (uids_to_copy);
1745 	g_free (real_trash_path);
1746 
1747 	g_clear_object (&imapx_store);
1748 	g_clear_object (&destination);
1749 	g_clear_object (&mailbox);
1750 
1751 	return success;
1752 }
1753 
1754 static gboolean
imapx_conn_manager_move_to_inbox_sync(CamelIMAPXConnManager * conn_man,CamelFolder * folder,GCancellable * cancellable,gboolean * out_need_to_expunge,GError ** error)1755 imapx_conn_manager_move_to_inbox_sync (CamelIMAPXConnManager *conn_man,
1756 				       CamelFolder *folder,
1757 				       GCancellable *cancellable,
1758 				       gboolean *out_need_to_expunge,
1759 				       GError **error)
1760 {
1761 	CamelIMAPXFolder *imapx_folder;
1762 	CamelIMAPXMailbox *mailbox;
1763 	GPtrArray *uids_to_copy;
1764 	gboolean success = TRUE;
1765 
1766 	*out_need_to_expunge = FALSE;
1767 
1768 	/* Caller already obtained the mailbox from the folder,
1769 	 * so the folder should still have it readily available. */
1770 	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1771 	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
1772 	g_return_val_if_fail (mailbox != NULL, FALSE);
1773 
1774 	uids_to_copy = g_ptr_array_new_with_free_func ((GDestroyNotify) camel_pstring_free);
1775 
1776 	camel_imapx_folder_claim_move_to_inbox_uids (CAMEL_IMAPX_FOLDER (folder), uids_to_copy);
1777 
1778 	if (uids_to_copy->len > 0) {
1779 		CamelIMAPXStore *imapx_store;
1780 		CamelIMAPXMailbox *destination = NULL;
1781 
1782 		imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
1783 
1784 		folder = camel_store_get_inbox_folder_sync (CAMEL_STORE (imapx_store), cancellable, error);
1785 
1786 		if (folder != NULL) {
1787 			destination = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1788 			g_object_unref (folder);
1789 		}
1790 
1791 		/* Avoid duplicating messages in the Inbox folder. */
1792 		if (destination == mailbox) {
1793 			success = TRUE;
1794 		} else if (destination != NULL) {
1795 			if (uids_to_copy->len > 0) {
1796 				success = imapx_conn_manager_copy_message_sync (
1797 					conn_man, mailbox, destination,
1798 					uids_to_copy, TRUE, TRUE, TRUE,
1799 					cancellable, error);
1800 				*out_need_to_expunge = success;
1801 			}
1802 		} else if (uids_to_copy->len > 0) {
1803 			success = FALSE;
1804 		}
1805 
1806 		if (!success) {
1807 			g_prefix_error (
1808 				error, "%s: ",
1809 				_("Unable to move messages to Inbox"));
1810 		}
1811 
1812 		g_clear_object (&imapx_store);
1813 		g_clear_object (&destination);
1814 	}
1815 
1816 	g_ptr_array_unref (uids_to_copy);
1817 	g_clear_object (&mailbox);
1818 
1819 	return success;
1820 }
1821 
1822 static gboolean
1823 imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
1824 				 CamelIMAPXMailbox *mailbox,
1825 				 gboolean skip_sync_changes,
1826 				 GCancellable *cancellable,
1827 				 GError **error);
1828 
1829 static gboolean
imapx_conn_manager_sync_changes_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)1830 imapx_conn_manager_sync_changes_run_sync (CamelIMAPXJob *job,
1831 					  CamelIMAPXServer *server,
1832 					  GCancellable *cancellable,
1833 					  GError **error)
1834 {
1835 	CamelIMAPXMailbox *mailbox;
1836 	GError *local_error = NULL;
1837 	gboolean can_influence_flags, success;
1838 
1839 	g_return_val_if_fail (job != NULL, FALSE);
1840 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
1841 
1842 	mailbox = camel_imapx_job_get_mailbox (job);
1843 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
1844 
1845 	can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1;
1846 
1847 	success = camel_imapx_server_sync_changes_sync (server, mailbox, can_influence_flags, cancellable, &local_error);
1848 
1849 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
1850 
1851 	if (local_error)
1852 		g_propagate_error (error, local_error);
1853 
1854 	return success;
1855 }
1856 
1857 static gboolean
imapx_conn_manager_sync_changes_matches(CamelIMAPXJob * job,CamelIMAPXJob * other_job)1858 imapx_conn_manager_sync_changes_matches (CamelIMAPXJob *job,
1859 					 CamelIMAPXJob *other_job)
1860 {
1861 	gboolean job_can_influence_flags, other_job_can_influence_flags;
1862 
1863 	g_return_val_if_fail (job != NULL, FALSE);
1864 	g_return_val_if_fail (other_job != NULL, FALSE);
1865 
1866 	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_SYNC_CHANGES ||
1867 	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
1868 		return FALSE;
1869 
1870 	job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1;
1871 	other_job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (other_job)) == 1;
1872 
1873 	return job_can_influence_flags == other_job_can_influence_flags;
1874 }
1875 
1876 gboolean
camel_imapx_conn_manager_sync_changes_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)1877 camel_imapx_conn_manager_sync_changes_sync (CamelIMAPXConnManager *conn_man,
1878 					    CamelIMAPXMailbox *mailbox,
1879 					    GCancellable *cancellable,
1880 					    GError **error)
1881 {
1882 	CamelIMAPXJob *job;
1883 	CamelFolder *folder = NULL;
1884 	gboolean need_to_expunge = FALSE, expunge = FALSE;
1885 	gboolean success, is_virtual_mailbox;
1886 
1887 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1888 
1889 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox,
1890 		imapx_conn_manager_sync_changes_run_sync,
1891 		imapx_conn_manager_sync_changes_matches, NULL);
1892 
1893 	/* Skip store of the \Deleted flag */
1894 	camel_imapx_job_set_user_data (job, GINT_TO_POINTER (1), NULL);
1895 
1896 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
1897 		imapx_conn_manager_matches_sync_changes_or_refresh_info,
1898 		cancellable, error);
1899 
1900 	camel_imapx_job_unref (job);
1901 
1902 	if (success) {
1903 		folder = imapx_conn_manager_ref_folder_sync (conn_man, mailbox, cancellable, error);
1904 		if (!folder)
1905 			success = FALSE;
1906 	}
1907 
1908 	is_virtual_mailbox = camel_imapx_mailbox_has_attribute (mailbox, CAMEL_IMAPX_LIST_ATTR_ALL) ||
1909 			     camel_imapx_mailbox_has_attribute (mailbox, CAMEL_IMAPX_LIST_ATTR_FLAGGED);
1910 
1911 	if (success && is_virtual_mailbox) {
1912 		CamelIMAPXFolder *imapx_folder = CAMEL_IMAPX_FOLDER (folder);
1913 
1914 		camel_imapx_folder_clear_move_to_real_trash_uids (imapx_folder);
1915 		camel_imapx_folder_clear_move_to_real_junk_uids (imapx_folder);
1916 	}
1917 
1918 	if (success && !is_virtual_mailbox) {
1919 		success = imapx_conn_manager_move_to_real_junk_sync (
1920 			conn_man, folder, cancellable,
1921 			&need_to_expunge, error);
1922 		expunge |= need_to_expunge;
1923 	}
1924 
1925 	if (success && !is_virtual_mailbox) {
1926 		success = imapx_conn_manager_move_to_real_trash_sync (
1927 			conn_man, folder, cancellable,
1928 			&need_to_expunge, error);
1929 		expunge |= need_to_expunge;
1930 	}
1931 
1932 	if (success) {
1933 		success = imapx_conn_manager_move_to_inbox_sync (
1934 			conn_man, folder, cancellable,
1935 			&need_to_expunge, error);
1936 		expunge |= need_to_expunge;
1937 	}
1938 
1939 	if (success && expunge) {
1940 		job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox,
1941 			imapx_conn_manager_sync_changes_run_sync,
1942 			imapx_conn_manager_sync_changes_matches, NULL);
1943 
1944 		/* Store also the \Deleted flag */
1945 		camel_imapx_job_set_user_data (job, GINT_TO_POINTER (0), NULL);
1946 
1947 		success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
1948 			imapx_conn_manager_matches_sync_changes_or_refresh_info,
1949 			cancellable, error);
1950 
1951 		camel_imapx_job_unref (job);
1952 
1953 		if (success)
1954 			success = imapx_conn_manager_expunge_sync (conn_man, mailbox, TRUE, cancellable, error);
1955 	}
1956 
1957 	g_clear_object (&folder);
1958 
1959 	return success;
1960 }
1961 
1962 static gboolean
imapx_conn_manager_expunge_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)1963 imapx_conn_manager_expunge_run_sync (CamelIMAPXJob *job,
1964 				     CamelIMAPXServer *server,
1965 				     GCancellable *cancellable,
1966 				     GError **error)
1967 {
1968 	CamelIMAPXMailbox *mailbox;
1969 	GError *local_error = NULL;
1970 	gboolean success;
1971 
1972 	g_return_val_if_fail (job != NULL, FALSE);
1973 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
1974 
1975 	mailbox = camel_imapx_job_get_mailbox (job);
1976 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
1977 
1978 	success = camel_imapx_server_expunge_sync (server, mailbox, cancellable, &local_error);
1979 
1980 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
1981 
1982 	if (local_error)
1983 		g_propagate_error (error, local_error);
1984 
1985 	return success;
1986 }
1987 
1988 static gboolean
imapx_conn_manager_expunge_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,gboolean skip_sync_changes,GCancellable * cancellable,GError ** error)1989 imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
1990 				 CamelIMAPXMailbox *mailbox,
1991 				 gboolean skip_sync_changes,
1992 				 GCancellable *cancellable,
1993 				 GError **error)
1994 {
1995 	CamelIMAPXJob *job;
1996 	gboolean success;
1997 
1998 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
1999 
2000 	if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
2001 		return FALSE;
2002 
2003 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_EXPUNGE, mailbox,
2004 		imapx_conn_manager_expunge_run_sync, NULL, NULL);
2005 
2006 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2007 
2008 	camel_imapx_job_unref (job);
2009 
2010 	return success;
2011 }
2012 
2013 gboolean
camel_imapx_conn_manager_expunge_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)2014 camel_imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
2015 				       CamelIMAPXMailbox *mailbox,
2016 				       GCancellable *cancellable,
2017 				       GError **error)
2018 {
2019 	return imapx_conn_manager_expunge_sync (conn_man, mailbox, FALSE, cancellable, error);
2020 }
2021 
2022 struct GetMessageJobData {
2023 	CamelFolderSummary *summary;
2024 	CamelDataCache *message_cache;
2025 	gchar *message_uid;
2026 };
2027 
2028 static void
get_message_job_data_free(gpointer ptr)2029 get_message_job_data_free (gpointer ptr)
2030 {
2031 	struct GetMessageJobData *job_data = ptr;
2032 
2033 	if (job_data) {
2034 		g_clear_object (&job_data->summary);
2035 		g_clear_object (&job_data->message_cache);
2036 		camel_pstring_free (job_data->message_uid);
2037 		g_slice_free (struct GetMessageJobData, job_data);
2038 	}
2039 }
2040 
2041 static gboolean
imapx_conn_manager_get_message_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2042 imapx_conn_manager_get_message_run_sync (CamelIMAPXJob *job,
2043 					 CamelIMAPXServer *server,
2044 					 GCancellable *cancellable,
2045 					 GError **error)
2046 {
2047 	struct GetMessageJobData *job_data;
2048 	CamelIMAPXMailbox *mailbox;
2049 	CamelStream *result;
2050 	GError *local_error = NULL;
2051 
2052 	g_return_val_if_fail (job != NULL, FALSE);
2053 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2054 
2055 	mailbox = camel_imapx_job_get_mailbox (job);
2056 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2057 
2058 	job_data = camel_imapx_job_get_user_data (job);
2059 	g_return_val_if_fail (job_data != NULL, FALSE);
2060 	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
2061 	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
2062 	g_return_val_if_fail (job_data->message_uid != NULL, FALSE);
2063 
2064 	result = camel_imapx_server_get_message_sync (
2065 		server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid,
2066 		cancellable, &local_error);
2067 
2068 	camel_imapx_job_set_result (job, result != NULL, result, local_error, result ? g_object_unref : NULL);
2069 
2070 	if (local_error)
2071 		g_propagate_error (error, local_error);
2072 
2073 	return result != NULL;
2074 }
2075 
2076 static gboolean
imapx_conn_manager_get_message_matches(CamelIMAPXJob * job,CamelIMAPXJob * other_job)2077 imapx_conn_manager_get_message_matches (CamelIMAPXJob *job,
2078 					CamelIMAPXJob *other_job)
2079 {
2080 	struct GetMessageJobData *job_data, *other_job_data;
2081 
2082 	g_return_val_if_fail (job != NULL, FALSE);
2083 	g_return_val_if_fail (other_job != NULL, FALSE);
2084 
2085 	if ((camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_GET_MESSAGE &&
2086 	    camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_SYNC_MESSAGE) ||
2087 	    (camel_imapx_job_get_kind (other_job) != CAMEL_IMAPX_JOB_GET_MESSAGE &&
2088 	    camel_imapx_job_get_kind (other_job) != CAMEL_IMAPX_JOB_SYNC_MESSAGE)) {
2089 		return FALSE;
2090 	}
2091 
2092 	job_data = camel_imapx_job_get_user_data (job);
2093 	other_job_data = camel_imapx_job_get_user_data (other_job);
2094 
2095 	if (!job_data || !other_job_data)
2096 		return FALSE;
2097 
2098 	return job_data->summary == other_job_data->summary && g_strcmp0 (job_data->message_uid, other_job_data->message_uid) == 0;
2099 }
2100 
2101 static void
imapx_conn_manager_get_message_copy_result(CamelIMAPXJob * job,gconstpointer set_result,gpointer * out_result)2102 imapx_conn_manager_get_message_copy_result (CamelIMAPXJob *job,
2103 					    gconstpointer set_result,
2104 					    gpointer *out_result)
2105 {
2106 	if (!set_result || !*out_result)
2107 		return;
2108 
2109 	*out_result = g_object_ref ((gpointer) set_result);
2110 }
2111 
2112 CamelStream *
camel_imapx_conn_manager_get_message_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,CamelFolderSummary * summary,CamelDataCache * message_cache,const gchar * message_uid,GCancellable * cancellable,GError ** error)2113 camel_imapx_conn_manager_get_message_sync (CamelIMAPXConnManager *conn_man,
2114 					   CamelIMAPXMailbox *mailbox,
2115 					   CamelFolderSummary *summary,
2116 					   CamelDataCache *message_cache,
2117 					   const gchar *message_uid,
2118 					   GCancellable *cancellable,
2119 					   GError **error)
2120 {
2121 	CamelIMAPXJob *job;
2122 	struct GetMessageJobData *job_data;
2123 	CamelStream *result;
2124 	gpointer result_data = NULL;
2125 
2126 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
2127 
2128 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_GET_MESSAGE, mailbox,
2129 		imapx_conn_manager_get_message_run_sync,
2130 		imapx_conn_manager_get_message_matches,
2131 		imapx_conn_manager_get_message_copy_result);
2132 
2133 	job_data = g_slice_new0 (struct GetMessageJobData);
2134 	job_data->summary = g_object_ref (summary);
2135 	job_data->message_cache = g_object_ref (message_cache);
2136 	job_data->message_uid = (gchar *) camel_pstring_strdup (message_uid);
2137 
2138 	camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free);
2139 
2140 	if (camel_imapx_conn_manager_run_job_sync (conn_man, job, imapx_conn_manager_get_message_matches, cancellable, error) &&
2141 	    camel_imapx_job_take_result_data (job, &result_data)) {
2142 		result = result_data;
2143 	} else {
2144 		result = NULL;
2145 	}
2146 
2147 	camel_imapx_job_unref (job);
2148 
2149 	return result;
2150 }
2151 
2152 struct CopyMessageJobData {
2153 	CamelIMAPXMailbox *destination;
2154 	GPtrArray *uids;
2155 	gboolean delete_originals;
2156 	gboolean remove_deleted_flags;
2157 };
2158 
2159 static void
copy_message_job_data_free(gpointer ptr)2160 copy_message_job_data_free (gpointer ptr)
2161 {
2162 	struct CopyMessageJobData *job_data = ptr;
2163 
2164 	if (job_data) {
2165 		g_clear_object (&job_data->destination);
2166 		g_ptr_array_free (job_data->uids, TRUE);
2167 		g_slice_free (struct CopyMessageJobData, job_data);
2168 	}
2169 }
2170 
2171 static gboolean
imapx_conn_manager_copy_message_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2172 imapx_conn_manager_copy_message_run_sync (CamelIMAPXJob *job,
2173 					  CamelIMAPXServer *server,
2174 					  GCancellable *cancellable,
2175 					  GError **error)
2176 {
2177 	struct CopyMessageJobData *job_data;
2178 	CamelIMAPXMailbox *mailbox;
2179 	GError *local_error = NULL;
2180 	gboolean success;
2181 
2182 	g_return_val_if_fail (job != NULL, FALSE);
2183 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2184 
2185 	mailbox = camel_imapx_job_get_mailbox (job);
2186 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2187 
2188 	job_data = camel_imapx_job_get_user_data (job);
2189 	g_return_val_if_fail (job_data != NULL, FALSE);
2190 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (job_data->destination), FALSE);
2191 	g_return_val_if_fail (job_data->uids != NULL, FALSE);
2192 
2193 	success = camel_imapx_server_copy_message_sync (
2194 		server, mailbox, job_data->destination, job_data->uids, job_data->delete_originals,
2195 		job_data->remove_deleted_flags, cancellable, &local_error);
2196 
2197 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2198 
2199 	if (local_error)
2200 		g_propagate_error (error, local_error);
2201 
2202 	return success;
2203 }
2204 
2205 static gboolean
imapx_conn_manager_copy_message_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,CamelIMAPXMailbox * destination,GPtrArray * uids,gboolean delete_originals,gboolean remove_deleted_flags,gboolean skip_sync_changes,GCancellable * cancellable,GError ** error)2206 imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
2207 				      CamelIMAPXMailbox *mailbox,
2208 				      CamelIMAPXMailbox *destination,
2209 				      GPtrArray *uids,
2210 				      gboolean delete_originals,
2211 				      gboolean remove_deleted_flags,
2212 				      gboolean skip_sync_changes,
2213 				      GCancellable *cancellable,
2214 				      GError **error)
2215 {
2216 	CamelIMAPXJob *job;
2217 	struct CopyMessageJobData *job_data;
2218 	gboolean success;
2219 	gint ii;
2220 
2221 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2222 
2223 	if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
2224 		return FALSE;
2225 
2226 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_COPY_MESSAGE, mailbox,
2227 		imapx_conn_manager_copy_message_run_sync,
2228 		imapx_conn_manager_nothing_matches,
2229 		NULL);
2230 
2231 	job_data = g_slice_new0 (struct CopyMessageJobData);
2232 	job_data->destination = g_object_ref (destination);
2233 	job_data->uids = g_ptr_array_new_full (uids->len, (GDestroyNotify) camel_pstring_free);
2234 	job_data->delete_originals = delete_originals;
2235 	job_data->remove_deleted_flags = remove_deleted_flags;
2236 
2237 	for (ii = 0; ii < uids->len; ii++) {
2238 		g_ptr_array_add (job_data->uids, (gpointer) camel_pstring_strdup (uids->pdata[ii]));
2239 	}
2240 
2241 	camel_imapx_job_set_user_data (job, job_data, copy_message_job_data_free);
2242 
2243 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2244 
2245 	camel_imapx_job_unref (job);
2246 
2247 	if (success) {
2248 		CamelFolder *dest;
2249 
2250 		dest = imapx_conn_manager_ref_folder_sync (conn_man, destination, cancellable, NULL);
2251 
2252 		/* Update destination folder only if it's not frozen,
2253 		 * to avoid updating for each "move" action on a single
2254 		 * message while filtering. */
2255 		if (dest && !camel_folder_is_frozen (dest)) {
2256 			/* Ignore errors here */
2257 			camel_imapx_conn_manager_refresh_info_sync (conn_man, destination, cancellable, NULL);
2258 		}
2259 
2260 		g_clear_object (&dest);
2261 	}
2262 
2263 	return success;
2264 }
2265 
2266 gboolean
camel_imapx_conn_manager_copy_message_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,CamelIMAPXMailbox * destination,GPtrArray * uids,gboolean delete_originals,gboolean remove_deleted_flags,GCancellable * cancellable,GError ** error)2267 camel_imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
2268 					    CamelIMAPXMailbox *mailbox,
2269 					    CamelIMAPXMailbox *destination,
2270 					    GPtrArray *uids,
2271 					    gboolean delete_originals,
2272 					    gboolean remove_deleted_flags,
2273 					    GCancellable *cancellable,
2274 					    GError **error)
2275 {
2276 	return imapx_conn_manager_copy_message_sync (conn_man, mailbox, destination, uids,
2277 		delete_originals, remove_deleted_flags, FALSE, cancellable, error);
2278 }
2279 
2280 struct AppendMessageJobData {
2281 	CamelFolderSummary *summary;
2282 	CamelDataCache *message_cache;
2283 	CamelMimeMessage *message;
2284 	const CamelMessageInfo *mi;
2285 };
2286 
2287 static void
append_message_job_data_free(gpointer ptr)2288 append_message_job_data_free (gpointer ptr)
2289 {
2290 	struct AppendMessageJobData *job_data = ptr;
2291 
2292 	if (job_data) {
2293 		g_clear_object (&job_data->summary);
2294 		g_clear_object (&job_data->message_cache);
2295 		g_clear_object (&job_data->message);
2296 		g_slice_free (struct AppendMessageJobData, job_data);
2297 	}
2298 }
2299 
2300 static gboolean
imapx_conn_manager_append_message_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2301 imapx_conn_manager_append_message_run_sync (CamelIMAPXJob *job,
2302 					    CamelIMAPXServer *server,
2303 					    GCancellable *cancellable,
2304 					    GError **error)
2305 {
2306 	struct AppendMessageJobData *job_data;
2307 	CamelIMAPXMailbox *mailbox;
2308 	gchar *appended_uid = NULL;
2309 	GError *local_error = NULL;
2310 	gboolean success;
2311 
2312 	g_return_val_if_fail (job != NULL, FALSE);
2313 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2314 
2315 	mailbox = camel_imapx_job_get_mailbox (job);
2316 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2317 
2318 	job_data = camel_imapx_job_get_user_data (job);
2319 	g_return_val_if_fail (job_data != NULL, FALSE);
2320 	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
2321 	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
2322 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (job_data->message), FALSE);
2323 
2324 	success = camel_imapx_server_append_message_sync (server, mailbox, job_data->summary, job_data->message_cache,
2325 		job_data->message, job_data->mi, &appended_uid, cancellable, &local_error);
2326 
2327 	camel_imapx_job_set_result (job, success, appended_uid, local_error, appended_uid ? g_free : NULL);
2328 
2329 	if (local_error)
2330 		g_propagate_error (error, local_error);
2331 
2332 	return success;
2333 }
2334 
2335 gboolean
camel_imapx_conn_manager_append_message_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,CamelFolderSummary * summary,CamelDataCache * message_cache,CamelMimeMessage * message,const CamelMessageInfo * mi,gchar ** append_uid,GCancellable * cancellable,GError ** error)2336 camel_imapx_conn_manager_append_message_sync (CamelIMAPXConnManager *conn_man,
2337 					      CamelIMAPXMailbox *mailbox,
2338 					      CamelFolderSummary *summary,
2339 					      CamelDataCache *message_cache,
2340 					      CamelMimeMessage *message,
2341 					      const CamelMessageInfo *mi,
2342 					      gchar **append_uid,
2343 					      GCancellable *cancellable,
2344 					      GError **error)
2345 {
2346 	CamelIMAPXJob *job;
2347 	struct AppendMessageJobData *job_data;
2348 	gboolean success;
2349 
2350 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2351 
2352 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_APPEND_MESSAGE, mailbox,
2353 		imapx_conn_manager_append_message_run_sync,
2354 		imapx_conn_manager_nothing_matches,
2355 		NULL);
2356 
2357 	job_data = g_slice_new0 (struct AppendMessageJobData);
2358 	job_data->summary = g_object_ref (summary);
2359 	job_data->message_cache = g_object_ref (message_cache);
2360 	job_data->message = g_object_ref (message);
2361 	job_data->mi = mi;
2362 
2363 	camel_imapx_job_set_user_data (job, job_data, append_message_job_data_free);
2364 
2365 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2366 	if (success) {
2367 		gpointer result_data = NULL;
2368 
2369 		success = camel_imapx_job_take_result_data (job, &result_data);
2370 		if (success && append_uid)
2371 			*append_uid = result_data;
2372 		else
2373 			g_free (result_data);
2374 	}
2375 
2376 	camel_imapx_job_unref (job);
2377 
2378 	return success;
2379 }
2380 
2381 static gboolean
imapx_conn_manager_sync_message_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2382 imapx_conn_manager_sync_message_run_sync (CamelIMAPXJob *job,
2383 					  CamelIMAPXServer *server,
2384 					  GCancellable *cancellable,
2385 					  GError **error)
2386 {
2387 	struct GetMessageJobData *job_data;
2388 	CamelIMAPXMailbox *mailbox;
2389 	GError *local_error = NULL;
2390 	gboolean success;
2391 
2392 	g_return_val_if_fail (job != NULL, FALSE);
2393 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2394 
2395 	mailbox = camel_imapx_job_get_mailbox (job);
2396 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2397 
2398 	job_data = camel_imapx_job_get_user_data (job);
2399 	g_return_val_if_fail (job_data != NULL, FALSE);
2400 	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
2401 	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
2402 	g_return_val_if_fail (job_data->message_uid != NULL, FALSE);
2403 
2404 	success = camel_imapx_server_sync_message_sync (
2405 		server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid,
2406 		cancellable, &local_error);
2407 
2408 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2409 
2410 	if (local_error)
2411 		g_propagate_error (error, local_error);
2412 
2413 	return success;
2414 }
2415 
2416 gboolean
camel_imapx_conn_manager_sync_message_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,CamelFolderSummary * summary,CamelDataCache * message_cache,const gchar * message_uid,GCancellable * cancellable,GError ** error)2417 camel_imapx_conn_manager_sync_message_sync (CamelIMAPXConnManager *conn_man,
2418 					    CamelIMAPXMailbox *mailbox,
2419 					    CamelFolderSummary *summary,
2420 					    CamelDataCache *message_cache,
2421 					    const gchar *message_uid,
2422 					    GCancellable *cancellable,
2423 					    GError **error)
2424 {
2425 	CamelIMAPXJob *job;
2426 	struct GetMessageJobData *job_data;
2427 	gboolean success;
2428 
2429 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2430 
2431 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_MESSAGE, mailbox,
2432 		imapx_conn_manager_sync_message_run_sync,
2433 		imapx_conn_manager_get_message_matches,
2434 		NULL);
2435 
2436 	job_data = g_slice_new0 (struct GetMessageJobData);
2437 	job_data->summary = g_object_ref (summary);
2438 	job_data->message_cache = g_object_ref (message_cache);
2439 	job_data->message_uid = (gchar *) camel_pstring_strdup (message_uid);
2440 
2441 	camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free);
2442 
2443 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, imapx_conn_manager_get_message_matches, cancellable, error);
2444 
2445 	camel_imapx_job_unref (job);
2446 
2447 	return success;
2448 }
2449 
2450 static gboolean
imapx_conn_manager_create_mailbox_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2451 imapx_conn_manager_create_mailbox_run_sync (CamelIMAPXJob *job,
2452 					    CamelIMAPXServer *server,
2453 					    GCancellable *cancellable,
2454 					    GError **error)
2455 {
2456 	const gchar *mailbox_name;
2457 	GError *local_error = NULL;
2458 	gboolean success;
2459 
2460 	g_return_val_if_fail (job != NULL, FALSE);
2461 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2462 
2463 	mailbox_name = camel_imapx_job_get_user_data (job);
2464 	g_return_val_if_fail (mailbox_name != NULL, FALSE);
2465 
2466 	success = camel_imapx_server_create_mailbox_sync (server, mailbox_name, cancellable, &local_error);
2467 
2468 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2469 
2470 	if (local_error)
2471 		g_propagate_error (error, local_error);
2472 
2473 	return success;
2474 }
2475 
2476 gboolean
camel_imapx_conn_manager_create_mailbox_sync(CamelIMAPXConnManager * conn_man,const gchar * mailbox_name,GCancellable * cancellable,GError ** error)2477 camel_imapx_conn_manager_create_mailbox_sync (CamelIMAPXConnManager *conn_man,
2478 					      const gchar *mailbox_name,
2479 					      GCancellable *cancellable,
2480 					      GError **error)
2481 {
2482 	CamelIMAPXJob *job;
2483 	gboolean success;
2484 
2485 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2486 
2487 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_CREATE_MAILBOX, NULL,
2488 		imapx_conn_manager_create_mailbox_run_sync,
2489 		imapx_conn_manager_nothing_matches,
2490 		NULL);
2491 
2492 	camel_imapx_job_set_user_data (job, g_strdup (mailbox_name), g_free);
2493 
2494 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2495 
2496 	camel_imapx_job_unref (job);
2497 
2498 	return success;
2499 }
2500 
2501 static gboolean
imapx_conn_manager_delete_mailbox_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2502 imapx_conn_manager_delete_mailbox_run_sync (CamelIMAPXJob *job,
2503 					    CamelIMAPXServer *server,
2504 					    GCancellable *cancellable,
2505 					    GError **error)
2506 {
2507 	CamelIMAPXMailbox *mailbox;
2508 	GError *local_error = NULL;
2509 	gboolean success;
2510 
2511 	g_return_val_if_fail (job != NULL, FALSE);
2512 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2513 
2514 	mailbox = camel_imapx_job_get_mailbox (job);
2515 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2516 
2517 	success = camel_imapx_server_delete_mailbox_sync (server, mailbox, cancellable, &local_error);
2518 
2519 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2520 
2521 	if (local_error)
2522 		g_propagate_error (error, local_error);
2523 
2524 	return success;
2525 }
2526 
2527 gboolean
camel_imapx_conn_manager_delete_mailbox_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)2528 camel_imapx_conn_manager_delete_mailbox_sync (CamelIMAPXConnManager *conn_man,
2529 					      CamelIMAPXMailbox *mailbox,
2530 					      GCancellable *cancellable,
2531 					      GError **error)
2532 {
2533 	CamelIMAPXJob *job;
2534 	gboolean success;
2535 
2536 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2537 
2538 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_DELETE_MAILBOX, mailbox,
2539 		imapx_conn_manager_delete_mailbox_run_sync,
2540 		imapx_conn_manager_nothing_matches,
2541 		NULL);
2542 
2543 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2544 
2545 	camel_imapx_job_unref (job);
2546 
2547 	return success;
2548 }
2549 
2550 static gboolean
imapx_conn_manager_rename_mailbox_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2551 imapx_conn_manager_rename_mailbox_run_sync (CamelIMAPXJob *job,
2552 					    CamelIMAPXServer *server,
2553 					    GCancellable *cancellable,
2554 					    GError **error)
2555 {
2556 	CamelIMAPXMailbox *mailbox;
2557 	const gchar *new_mailbox_name;
2558 	GError *local_error = NULL;
2559 	gboolean success;
2560 
2561 	g_return_val_if_fail (job != NULL, FALSE);
2562 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2563 
2564 	mailbox = camel_imapx_job_get_mailbox (job);
2565 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2566 
2567 	new_mailbox_name = camel_imapx_job_get_user_data (job);
2568 	g_return_val_if_fail (new_mailbox_name != NULL, FALSE);
2569 
2570 	success = camel_imapx_server_rename_mailbox_sync (server, mailbox, new_mailbox_name, cancellable, &local_error);
2571 
2572 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2573 
2574 	if (local_error)
2575 		g_propagate_error (error, local_error);
2576 
2577 	return success;
2578 }
2579 
2580 gboolean
camel_imapx_conn_manager_rename_mailbox_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,const gchar * new_mailbox_name,GCancellable * cancellable,GError ** error)2581 camel_imapx_conn_manager_rename_mailbox_sync (CamelIMAPXConnManager *conn_man,
2582 					      CamelIMAPXMailbox *mailbox,
2583 					      const gchar *new_mailbox_name,
2584 					      GCancellable *cancellable,
2585 					      GError **error)
2586 {
2587 	CamelIMAPXJob *job;
2588 	gboolean success;
2589 
2590 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2591 
2592 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_RENAME_MAILBOX, mailbox,
2593 		imapx_conn_manager_rename_mailbox_run_sync,
2594 		imapx_conn_manager_nothing_matches,
2595 		NULL);
2596 
2597 	camel_imapx_job_set_user_data (job, g_strdup (new_mailbox_name), g_free);
2598 
2599 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2600 
2601 	camel_imapx_job_unref (job);
2602 
2603 	return success;
2604 }
2605 
2606 static gboolean
imapx_conn_manager_subscribe_mailbox_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2607 imapx_conn_manager_subscribe_mailbox_run_sync (CamelIMAPXJob *job,
2608 					       CamelIMAPXServer *server,
2609 					       GCancellable *cancellable,
2610 					       GError **error)
2611 {
2612 	CamelIMAPXMailbox *mailbox;
2613 	GError *local_error = NULL;
2614 	gboolean success;
2615 
2616 	g_return_val_if_fail (job != NULL, FALSE);
2617 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2618 
2619 	mailbox = camel_imapx_job_get_mailbox (job);
2620 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2621 
2622 	success = camel_imapx_server_subscribe_mailbox_sync (server, mailbox, cancellable, &local_error);
2623 
2624 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2625 
2626 	if (local_error)
2627 		g_propagate_error (error, local_error);
2628 
2629 	return success;
2630 }
2631 
2632 gboolean
camel_imapx_conn_manager_subscribe_mailbox_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)2633 camel_imapx_conn_manager_subscribe_mailbox_sync (CamelIMAPXConnManager *conn_man,
2634 						 CamelIMAPXMailbox *mailbox,
2635 						 GCancellable *cancellable,
2636 						 GError **error)
2637 {
2638 	CamelIMAPXJob *job;
2639 	gboolean success;
2640 
2641 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2642 
2643 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, mailbox,
2644 		imapx_conn_manager_subscribe_mailbox_run_sync, NULL, NULL);
2645 
2646 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2647 
2648 	camel_imapx_job_unref (job);
2649 
2650 	return success;
2651 }
2652 
2653 static gboolean
imapx_conn_manager_unsubscribe_mailbox_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2654 imapx_conn_manager_unsubscribe_mailbox_run_sync (CamelIMAPXJob *job,
2655 						 CamelIMAPXServer *server,
2656 						 GCancellable *cancellable,
2657 						 GError **error)
2658 {
2659 	CamelIMAPXMailbox *mailbox;
2660 	GError *local_error = NULL;
2661 	gboolean success;
2662 
2663 	g_return_val_if_fail (job != NULL, FALSE);
2664 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2665 
2666 	mailbox = camel_imapx_job_get_mailbox (job);
2667 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2668 
2669 	success = camel_imapx_server_unsubscribe_mailbox_sync (server, mailbox, cancellable, &local_error);
2670 
2671 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2672 
2673 	if (local_error)
2674 		g_propagate_error (error, local_error);
2675 
2676 	return success;
2677 }
2678 
2679 gboolean
camel_imapx_conn_manager_unsubscribe_mailbox_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)2680 camel_imapx_conn_manager_unsubscribe_mailbox_sync (CamelIMAPXConnManager *conn_man,
2681 						   CamelIMAPXMailbox *mailbox,
2682 						   GCancellable *cancellable,
2683 						   GError **error)
2684 {
2685 	CamelIMAPXJob *job;
2686 	gboolean success;
2687 
2688 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2689 
2690 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, mailbox,
2691 		imapx_conn_manager_unsubscribe_mailbox_run_sync, NULL, NULL);
2692 
2693 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2694 
2695 	camel_imapx_job_unref (job);
2696 
2697 	return success;
2698 }
2699 
2700 static gboolean
imapx_conn_manager_update_quota_info_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2701 imapx_conn_manager_update_quota_info_run_sync (CamelIMAPXJob *job,
2702 					       CamelIMAPXServer *server,
2703 					       GCancellable *cancellable,
2704 					       GError **error)
2705 {
2706 	CamelIMAPXMailbox *mailbox;
2707 	GError *local_error = NULL;
2708 	gboolean success;
2709 
2710 	g_return_val_if_fail (job != NULL, FALSE);
2711 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2712 
2713 	mailbox = camel_imapx_job_get_mailbox (job);
2714 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2715 
2716 	success = camel_imapx_server_update_quota_info_sync (server, mailbox, cancellable, &local_error);
2717 
2718 	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
2719 
2720 	if (local_error)
2721 		g_propagate_error (error, local_error);
2722 
2723 	return success;
2724 }
2725 
2726 gboolean
camel_imapx_conn_manager_update_quota_info_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,GCancellable * cancellable,GError ** error)2727 camel_imapx_conn_manager_update_quota_info_sync (CamelIMAPXConnManager *conn_man,
2728 						 CamelIMAPXMailbox *mailbox,
2729 						 GCancellable *cancellable,
2730 						 GError **error)
2731 {
2732 	CamelIMAPXJob *job;
2733 	gboolean success;
2734 
2735 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
2736 
2737 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, mailbox,
2738 		imapx_conn_manager_update_quota_info_run_sync, NULL, NULL);
2739 
2740 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2741 
2742 	camel_imapx_job_unref (job);
2743 
2744 	return success;
2745 }
2746 
2747 static gchar **
imapx_copy_strv(const gchar * const * words)2748 imapx_copy_strv (const gchar * const *words)
2749 {
2750 	gchar **copy;
2751 	gint ii;
2752 
2753 	if (!words || !*words)
2754 		return NULL;
2755 
2756 	copy = g_new0 (gchar *, g_strv_length ((gchar **) words) + 1);
2757 
2758 	for (ii = 0; words[ii]; ii++) {
2759 		copy[ii] = g_strdup (words[ii]);
2760 	}
2761 
2762 	copy[ii] = NULL;
2763 
2764 	return copy;
2765 }
2766 
2767 static gboolean
imapx_equal_strv(const gchar * const * words1,const gchar * const * words2)2768 imapx_equal_strv (const gchar * const *words1,
2769 		  const gchar * const *words2)
2770 {
2771 	gint ii;
2772 
2773 	if (words1 == words2)
2774 		return TRUE;
2775 
2776 	if (!words1 || !words2)
2777 		return FALSE;
2778 
2779 	for (ii = 0; words1[ii] && words2[ii]; ii++) {
2780 		if (g_strcmp0 (words1[ii], words2[ii]) != 0)
2781 			return FALSE;
2782 	}
2783 
2784 	return !words1[ii] && !words2[ii];
2785 }
2786 
2787 struct UidSearchJobData {
2788 	gchar *criteria_prefix;
2789 	gchar *search_key;
2790 	gchar **words;
2791 };
2792 
2793 static void
uid_search_job_data_free(gpointer ptr)2794 uid_search_job_data_free (gpointer ptr)
2795 {
2796 	struct UidSearchJobData *job_data = ptr;
2797 
2798 	if (ptr) {
2799 		g_free (job_data->criteria_prefix);
2800 		g_free (job_data->search_key);
2801 		g_strfreev (job_data->words);
2802 		g_slice_free (struct UidSearchJobData, job_data);
2803 	}
2804 }
2805 
2806 static gboolean
imapx_conn_manager_uid_search_run_sync(CamelIMAPXJob * job,CamelIMAPXServer * server,GCancellable * cancellable,GError ** error)2807 imapx_conn_manager_uid_search_run_sync (CamelIMAPXJob *job,
2808 					CamelIMAPXServer *server,
2809 					GCancellable *cancellable,
2810 					GError **error)
2811 {
2812 	struct UidSearchJobData *job_data;
2813 	CamelIMAPXMailbox *mailbox;
2814 	GPtrArray *uids = NULL;
2815 	GError *local_error = NULL;
2816 
2817 	g_return_val_if_fail (job != NULL, FALSE);
2818 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
2819 
2820 	mailbox = camel_imapx_job_get_mailbox (job);
2821 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
2822 
2823 	job_data = camel_imapx_job_get_user_data (job);
2824 	g_return_val_if_fail (job_data != NULL, FALSE);
2825 
2826 	uids = camel_imapx_server_uid_search_sync (server, mailbox, job_data->criteria_prefix,
2827 		job_data->search_key, (const gchar * const *) job_data->words, cancellable, &local_error);
2828 
2829 	camel_imapx_job_set_result (job, uids != NULL, uids, local_error, uids ? (GDestroyNotify) g_ptr_array_free : NULL);
2830 
2831 	if (local_error)
2832 		g_propagate_error (error, local_error);
2833 
2834 	return uids != NULL;
2835 }
2836 
2837 static gboolean
imapx_conn_manager_uid_search_matches(CamelIMAPXJob * job,CamelIMAPXJob * other_job)2838 imapx_conn_manager_uid_search_matches (CamelIMAPXJob *job,
2839 				       CamelIMAPXJob *other_job)
2840 {
2841 	struct UidSearchJobData *job_data, *other_job_data;
2842 
2843 	g_return_val_if_fail (job != NULL, FALSE);
2844 	g_return_val_if_fail (other_job != NULL, FALSE);
2845 
2846 	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_UID_SEARCH ||
2847 	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
2848 		return FALSE;
2849 
2850 	job_data = camel_imapx_job_get_user_data (job);
2851 	other_job_data = camel_imapx_job_get_user_data (other_job);
2852 
2853 	if (!job_data || !other_job_data)
2854 		return job_data == other_job_data;
2855 
2856 	return g_strcmp0 (job_data->criteria_prefix, other_job_data->criteria_prefix) == 0 &&
2857 	       g_strcmp0 (job_data->search_key, other_job_data->search_key) == 0 &&
2858 	       imapx_equal_strv ((const gchar * const  *) job_data->words, (const gchar * const  *) other_job_data->words);
2859 }
2860 
2861 GPtrArray *
camel_imapx_conn_manager_uid_search_sync(CamelIMAPXConnManager * conn_man,CamelIMAPXMailbox * mailbox,const gchar * criteria_prefix,const gchar * search_key,const gchar * const * words,GCancellable * cancellable,GError ** error)2862 camel_imapx_conn_manager_uid_search_sync (CamelIMAPXConnManager *conn_man,
2863 					  CamelIMAPXMailbox *mailbox,
2864 					  const gchar *criteria_prefix,
2865 					  const gchar *search_key,
2866 					  const gchar * const *words,
2867 					  GCancellable *cancellable,
2868 					  GError **error)
2869 {
2870 	struct UidSearchJobData *job_data;
2871 	GPtrArray *uids = NULL;
2872 	CamelIMAPXJob *job;
2873 	gboolean success;
2874 
2875 	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
2876 
2877 	job_data = g_slice_new0 (struct UidSearchJobData);
2878 	job_data->criteria_prefix = g_strdup (criteria_prefix);
2879 	job_data->search_key = g_strdup (search_key);
2880 	job_data->words = imapx_copy_strv (words);
2881 
2882 	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UID_SEARCH, mailbox,
2883 		imapx_conn_manager_uid_search_run_sync,
2884 		imapx_conn_manager_uid_search_matches,
2885 		NULL);
2886 
2887 	camel_imapx_job_set_user_data (job, job_data, uid_search_job_data_free);
2888 
2889 	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
2890 	if (success) {
2891 		gpointer result_data = NULL;
2892 
2893 		success = camel_imapx_job_take_result_data (job, &result_data);
2894 		if (success)
2895 			uids = result_data;
2896 	}
2897 
2898 	camel_imapx_job_unref (job);
2899 
2900 	return uids;
2901 }
2902 
2903 /* for debugging purposes only */
2904 void
camel_imapx_conn_manager_dump_queue_status(CamelIMAPXConnManager * conn_man)2905 camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *conn_man)
2906 {
2907 	GList *llink;
2908 	GSList *slink;
2909 
2910 	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
2911 
2912 	CON_READ_LOCK (conn_man);
2913 
2914 	printf ("%s: opened connections:%d\n", G_STRFUNC, g_list_length (conn_man->priv->connections));
2915 
2916 	for (llink = conn_man->priv->connections; llink != NULL; llink = g_list_next (llink)) {
2917 		ConnectionInfo *cinfo = llink->data;
2918 		CamelIMAPXCommand *cmd = NULL;
2919 
2920 		if (cinfo)
2921 			cmd = cinfo->is ? camel_imapx_server_ref_current_command (cinfo->is) : NULL;
2922 
2923 		printf ("   connection:%p server:[%c] %p busy:%d command:%s\n", cinfo,
2924 			cinfo && cinfo->is ? camel_imapx_server_get_tagprefix (cinfo->is) : '?',
2925 			cinfo ? cinfo->is : NULL, cinfo ? cinfo->busy : FALSE,
2926 			cmd ? camel_imapx_job_get_kind_name (cmd->job_kind) : "[null]");
2927 
2928 		if (cmd)
2929 			camel_imapx_command_unref (cmd);
2930 	}
2931 
2932 	CON_READ_UNLOCK (conn_man);
2933 
2934 	JOB_QUEUE_LOCK (conn_man);
2935 
2936 	printf ("Queued jobs:%d\n", g_slist_length (conn_man->priv->job_queue));
2937 	for (slink = conn_man->priv->job_queue; slink; slink = g_slist_next (slink)) {
2938 		CamelIMAPXJob *job = slink->data;
2939 
2940 		printf ("   job:%p kind:%s mailbox:%s\n", job,
2941 			job ? camel_imapx_job_get_kind_name (camel_imapx_job_get_kind (job)) : "[null]",
2942 			job && camel_imapx_job_get_mailbox (job) ? camel_imapx_mailbox_get_name (camel_imapx_job_get_mailbox (job)) : "[null]");
2943 	}
2944 
2945 	JOB_QUEUE_UNLOCK (conn_man);
2946 }
2947