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