Rename camel_service_get_settings().
[platform/upstream/evolution-data-server.git] / camel / camel-imapx-conn-manager.c
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  * Authors: Chenthill Palanisamy <pchenthill@novell.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21
22 #include "camel-imapx-conn-manager.h"
23 #include "camel-imapx-settings.h"
24 #include "camel-imapx-store.h"
25 #include "camel-imapx-utils.h"
26
27 #define c(...) camel_imapx_debug(conman, __VA_ARGS__)
28
29 #define CON_READ_LOCK(x) \
30         (g_static_rw_lock_reader_lock (&(x)->priv->rw_lock))
31 #define CON_READ_UNLOCK(x) \
32         (g_static_rw_lock_reader_unlock (&(x)->priv->rw_lock))
33 #define CON_WRITE_LOCK(x) \
34         (g_static_rw_lock_writer_lock (&(x)->priv->rw_lock))
35 #define CON_WRITE_UNLOCK(x) \
36         (g_static_rw_lock_writer_unlock (&(x)->priv->rw_lock))
37
38 #define CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE(obj) \
39         (G_TYPE_INSTANCE_GET_PRIVATE \
40         ((obj), CAMEL_TYPE_IMAPX_CONN_MANAGER, CamelIMAPXConnManagerPrivate))
41
42 typedef struct _ConnectionInfo ConnectionInfo;
43
44 struct _CamelIMAPXConnManagerPrivate {
45         /* XXX Might be easier for this to be a hash table,
46          *     with CamelIMAPXServer pointers as the keys. */
47         GList *connections;
48         gpointer store;  /* weak pointer */
49         GStaticRWLock rw_lock;
50 };
51
52 struct _ConnectionInfo {
53         GMutex *lock;
54         CamelIMAPXServer *is;
55         GHashTable *folder_names;
56         gchar *selected_folder;
57         volatile gint ref_count;
58 };
59
60 enum {
61         PROP_0,
62         PROP_STORE
63 };
64
65 G_DEFINE_TYPE (
66         CamelIMAPXConnManager,
67         camel_imapx_conn_manager,
68         CAMEL_TYPE_OBJECT)
69
70 static void
71 imapx_conn_shutdown (CamelIMAPXServer *is, CamelIMAPXConnManager *con_man);
72
73 static void
74 imapx_conn_update_select (CamelIMAPXServer *is,
75                           const gchar *selected_folder,
76                           CamelIMAPXConnManager *con_man);
77
78 static ConnectionInfo *
79 connection_info_new (CamelIMAPXServer *is)
80 {
81         ConnectionInfo *cinfo;
82         GHashTable *folder_names;
83
84         folder_names = g_hash_table_new_full (
85                 (GHashFunc) g_str_hash,
86                 (GEqualFunc) g_str_equal,
87                 (GDestroyNotify) g_free,
88                 (GDestroyNotify) NULL);
89
90         cinfo = g_slice_new0 (ConnectionInfo);
91         cinfo->lock = g_mutex_new ();
92         cinfo->is = g_object_ref (is);
93         cinfo->folder_names = folder_names;
94         cinfo->ref_count = 1;
95
96         return cinfo;
97 }
98
99 static ConnectionInfo *
100 connection_info_ref (ConnectionInfo *cinfo)
101 {
102         g_return_val_if_fail (cinfo != NULL, NULL);
103         g_return_val_if_fail (cinfo->ref_count > 0, NULL);
104
105         g_atomic_int_inc (&cinfo->ref_count);
106
107         return cinfo;
108 }
109
110 static void
111 connection_info_unref (ConnectionInfo *cinfo)
112 {
113         g_return_if_fail (cinfo != NULL);
114         g_return_if_fail (cinfo->ref_count > 0);
115
116         if (g_atomic_int_dec_and_test (&cinfo->ref_count)) {
117                 camel_imapx_server_connect (cinfo->is, NULL, NULL);
118                 g_mutex_free (cinfo->lock);
119                 g_object_unref (cinfo->is);
120                 g_hash_table_destroy (cinfo->folder_names);
121                 g_free (cinfo->selected_folder);
122
123                 g_slice_free (ConnectionInfo, cinfo);
124         }
125 }
126
127 static void
128 connection_info_cancel_and_unref (ConnectionInfo *cinfo)
129 {
130         g_return_if_fail (cinfo != NULL);
131         g_return_if_fail (cinfo->ref_count > 0);
132
133         g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_shutdown, NULL);
134         g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_update_select, NULL);
135         g_cancellable_cancel (cinfo->is->cancellable);
136         connection_info_unref (cinfo);
137 }
138
139 static gboolean
140 connection_info_is_available (ConnectionInfo *cinfo)
141 {
142         gboolean available;
143
144         g_return_val_if_fail (cinfo != NULL, FALSE);
145
146         g_mutex_lock (cinfo->lock);
147
148         /* Available means it's not tracking any folder names. */
149         available = (g_hash_table_size (cinfo->folder_names) == 0);
150
151         g_mutex_unlock (cinfo->lock);
152
153         return available;
154 }
155
156 static gboolean
157 connection_info_has_folder_name (ConnectionInfo *cinfo,
158                                  const gchar *folder_name)
159 {
160         gpointer value;
161
162         g_return_val_if_fail (cinfo != NULL, FALSE);
163
164         if (folder_name == NULL)
165                 return FALSE;
166
167         g_mutex_lock (cinfo->lock);
168
169         value = g_hash_table_lookup (cinfo->folder_names, folder_name);
170
171         g_mutex_unlock (cinfo->lock);
172
173         return (value != NULL);
174 }
175
176 static void
177 connection_info_insert_folder_name (ConnectionInfo *cinfo,
178                                     const gchar *folder_name)
179 {
180         g_return_if_fail (cinfo != NULL);
181         g_return_if_fail (folder_name != NULL);
182
183         g_mutex_lock (cinfo->lock);
184
185         g_hash_table_insert (
186                 cinfo->folder_names,
187                 g_strdup (folder_name),
188                 GINT_TO_POINTER (1));
189
190         g_mutex_unlock (cinfo->lock);
191 }
192
193 static void
194 connection_info_remove_folder_name (ConnectionInfo *cinfo,
195                                     const gchar *folder_name)
196 {
197         g_return_if_fail (cinfo != NULL);
198         g_return_if_fail (folder_name != NULL);
199
200         g_mutex_lock (cinfo->lock);
201
202         g_hash_table_remove (cinfo->folder_names, folder_name);
203
204         g_mutex_unlock (cinfo->lock);
205 }
206
207 static gchar *
208 connection_info_dup_selected_folder (ConnectionInfo *cinfo)
209 {
210         gchar *selected_folder;
211
212         g_return_val_if_fail (cinfo != NULL, NULL);
213
214         g_mutex_lock (cinfo->lock);
215
216         selected_folder = g_strdup (cinfo->selected_folder);
217
218         g_mutex_unlock (cinfo->lock);
219
220         return selected_folder;
221 }
222
223 static void
224 connection_info_set_selected_folder (ConnectionInfo *cinfo,
225                                      const gchar *selected_folder)
226 {
227         g_return_if_fail (cinfo != NULL);
228
229         g_mutex_lock (cinfo->lock);
230
231         g_free (cinfo->selected_folder);
232         cinfo->selected_folder = g_strdup (selected_folder);
233
234         g_mutex_unlock (cinfo->lock);
235 }
236
237 static GList *
238 imapx_conn_manager_list_info (CamelIMAPXConnManager *con_man)
239 {
240         GList *list;
241
242         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
243
244         CON_READ_LOCK (con_man);
245
246         list = g_list_copy (con_man->priv->connections);
247         g_list_foreach (list, (GFunc) connection_info_ref, NULL);
248
249         CON_READ_UNLOCK (con_man);
250
251         return list;
252 }
253
254 static ConnectionInfo *
255 imapx_conn_manager_lookup_info (CamelIMAPXConnManager *con_man,
256                                 CamelIMAPXServer *is)
257 {
258         ConnectionInfo *cinfo = NULL;
259         GList *list, *link;
260
261         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
262         g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
263
264         CON_READ_LOCK (con_man);
265
266         list = con_man->priv->connections;
267
268         for (link = list; link != NULL; link = g_list_next (link)) {
269                 ConnectionInfo *candidate = link->data;
270
271                 if (candidate->is == is) {
272                         cinfo = connection_info_ref (candidate);
273                         break;
274                 }
275         }
276
277         CON_READ_UNLOCK (con_man);
278
279         return cinfo;
280 }
281
282 static gboolean
283 imapx_conn_manager_remove_info (CamelIMAPXConnManager *con_man,
284                                 ConnectionInfo *cinfo)
285 {
286         GList *list, *link;
287         gboolean removed = FALSE;
288
289         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), FALSE);
290         g_return_val_if_fail (cinfo != NULL, FALSE);
291
292         CON_WRITE_LOCK (con_man);
293
294         list = con_man->priv->connections;
295         link = g_list_find (list, cinfo);
296
297         if (link != NULL) {
298                 list = g_list_delete_link (list, link);
299                 connection_info_unref (cinfo);
300                 removed = TRUE;
301         }
302
303         con_man->priv->connections = list;
304
305         CON_WRITE_UNLOCK (con_man);
306
307         return removed;
308 }
309
310 static void
311 imapx_conn_manager_set_store (CamelIMAPXConnManager *con_man,
312                               CamelStore *store)
313 {
314         g_return_if_fail (CAMEL_IS_STORE (store));
315         g_return_if_fail (con_man->priv->store == NULL);
316
317         con_man->priv->store = store;
318
319         g_object_add_weak_pointer (
320                 G_OBJECT (store), &con_man->priv->store);
321 }
322
323 static void
324 imapx_conn_manager_set_property (GObject *object,
325                                  guint property_id,
326                                  const GValue *value,
327                                  GParamSpec *pspec)
328 {
329         switch (property_id) {
330                 case PROP_STORE:
331                         imapx_conn_manager_set_store (
332                                 CAMEL_IMAPX_CONN_MANAGER (object),
333                                 g_value_get_object (value));
334                         return;
335         }
336
337         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
338 }
339
340 static void
341 imapx_conn_manager_get_property (GObject *object,
342                                  guint property_id,
343                                  GValue *value,
344                                  GParamSpec *pspec)
345 {
346         switch (property_id) {
347                 case PROP_STORE:
348                         g_value_set_object (
349                                 value,
350                                 camel_imapx_conn_manager_get_store (
351                                 CAMEL_IMAPX_CONN_MANAGER (object)));
352                         return;
353         }
354
355         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
356 }
357
358 static void
359 imapx_conn_manager_dispose (GObject *object)
360 {
361         CamelIMAPXConnManagerPrivate *priv;
362
363         priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object);
364
365         g_list_free_full (
366                 priv->connections,
367                 (GDestroyNotify) connection_info_unref);
368         priv->connections = NULL;
369
370         if (priv->store != NULL) {
371                 g_object_remove_weak_pointer (
372                         G_OBJECT (priv->store), &priv->store);
373                 priv->store = NULL;
374         }
375
376         /* Chain up to parent's dispose() method. */
377         G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->dispose (object);
378 }
379
380 static void
381 imapx_conn_manager_finalize (GObject *object)
382 {
383         CamelIMAPXConnManagerPrivate *priv;
384
385         priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object);
386
387         g_static_rw_lock_free (&priv->rw_lock);
388
389         /* Chain up to parent's finalize() method. */
390         G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->finalize (object);
391 }
392
393 static void
394 camel_imapx_conn_manager_class_init (CamelIMAPXConnManagerClass *class)
395 {
396         GObjectClass *object_class;
397
398         g_type_class_add_private (class, sizeof (CamelIMAPXConnManagerPrivate));
399
400         object_class = G_OBJECT_CLASS (class);
401         object_class->set_property = imapx_conn_manager_set_property;
402         object_class->get_property = imapx_conn_manager_get_property;
403         object_class->dispose = imapx_conn_manager_dispose;
404         object_class->finalize = imapx_conn_manager_finalize;
405
406         g_object_class_install_property (
407                 object_class,
408                 PROP_STORE,
409                 g_param_spec_object (
410                         "store",
411                         "Store",
412                         "The CamelStore to which we belong",
413                         CAMEL_TYPE_STORE,
414                         G_PARAM_READWRITE |
415                         G_PARAM_CONSTRUCT_ONLY |
416                         G_PARAM_STATIC_STRINGS));
417 }
418
419 static void
420 camel_imapx_conn_manager_init (CamelIMAPXConnManager *con_man)
421 {
422         con_man->priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (con_man);
423
424         g_static_rw_lock_init (&con_man->priv->rw_lock);
425 }
426
427 /* Static functions go here */
428
429 /* TODO destroy unused connections in a time-out loop */
430 static void
431 imapx_conn_shutdown (CamelIMAPXServer *is,
432                      CamelIMAPXConnManager *con_man)
433 {
434         ConnectionInfo *cinfo;
435
436         /* Returns a new ConnectionInfo reference. */
437         cinfo = imapx_conn_manager_lookup_info (con_man, is);
438
439         if (cinfo != NULL) {
440                 imapx_conn_manager_remove_info (con_man, cinfo);
441                 connection_info_unref (cinfo);
442         }
443 }
444
445 static void
446 imapx_conn_update_select (CamelIMAPXServer *is,
447                           const gchar *selected_folder,
448                           CamelIMAPXConnManager *con_man)
449 {
450         ConnectionInfo *cinfo;
451         gchar *old_selected_folder;
452
453         /* Returns a new ConnectionInfo reference. */
454         cinfo = imapx_conn_manager_lookup_info (con_man, is);
455
456         if (cinfo == NULL)
457                 return;
458
459         old_selected_folder = connection_info_dup_selected_folder (cinfo);
460
461         if (old_selected_folder != NULL) {
462                 IMAPXJobQueueInfo *jinfo;
463
464                 jinfo = camel_imapx_server_get_job_queue_info (is);
465                 if (!g_hash_table_lookup (jinfo->folders, old_selected_folder)) {
466                         connection_info_remove_folder_name (cinfo, old_selected_folder);
467                         c(is->tagprefix, "Removed folder %s from connection folder list - select changed \n", old_selected_folder);
468                 }
469                 camel_imapx_destroy_job_queue_info (jinfo);
470
471                 g_free (old_selected_folder);
472         }
473
474         connection_info_set_selected_folder (cinfo, selected_folder);
475
476         connection_info_unref (cinfo);
477 }
478
479 /* This should find a connection if the slots are full, returns NULL if there are slots available for a new connection for a folder */
480 static CamelIMAPXServer *
481 imapx_find_connection_unlocked (CamelIMAPXConnManager *con_man,
482                                 const gchar *folder_name)
483 {
484         CamelService *service;
485         CamelSettings *settings;
486         CamelIMAPXServer *is = NULL;
487         ConnectionInfo *cinfo = NULL;
488         GList *list, *link;
489         guint concurrent_connections;
490         guint min_jobs = G_MAXUINT;
491
492         /* Caller must be holding CON_WRITE_LOCK. */
493
494         service = CAMEL_SERVICE (con_man->priv->store);
495
496         settings = camel_service_ref_settings (service);
497
498         concurrent_connections =
499                 camel_imapx_settings_get_concurrent_connections (
500                 CAMEL_IMAPX_SETTINGS (settings));
501
502         g_object_unref (settings);
503
504         /* XXX Have a dedicated connection for INBOX ? */
505
506         list = con_man->priv->connections;
507
508         /* If a folder was not given, find the least-busy connection. */
509         if (folder_name == NULL)
510                 goto least_busy;
511
512         /* First try to find a connection already handling this folder. */
513         for (link = list; link != NULL; link = g_list_next (link)) {
514                 ConnectionInfo *candidate = link->data;
515
516                 if (connection_info_has_folder_name (candidate, folder_name)) {
517                         cinfo = connection_info_ref (candidate);
518                         goto exit;
519                 }
520         }
521
522         /* Next try to find a connection not handling any folders. */
523         for (link = list; link != NULL; link = g_list_next (link)) {
524                 ConnectionInfo *candidate = link->data;
525
526                 if (connection_info_is_available (candidate)) {
527                         cinfo = connection_info_ref (candidate);
528                         goto exit;
529                 }
530         }
531
532 least_busy:
533         /* Pick the connection with the least number of jobs in progress. */
534         for (link = list; link != NULL; link = g_list_next (link)) {
535                 ConnectionInfo *candidate = link->data;
536                 IMAPXJobQueueInfo *jinfo = NULL;
537
538                 jinfo = camel_imapx_server_get_job_queue_info (candidate->is);
539
540                 if (cinfo == NULL) {
541                         cinfo = connection_info_ref (candidate);
542                         min_jobs = jinfo->queue_len;
543
544                 } else if (jinfo->queue_len < min_jobs) {
545                         connection_info_unref (cinfo);
546                         cinfo = connection_info_ref (candidate);
547                         min_jobs = jinfo->queue_len;
548                 }
549
550                 camel_imapx_destroy_job_queue_info (jinfo);
551         }
552
553 exit:
554         if (cinfo != NULL && folder_name != NULL)
555                 connection_info_insert_folder_name (cinfo, folder_name);
556
557         if (cinfo != NULL) {
558                 is = g_object_ref (cinfo->is);
559                 connection_info_unref (cinfo);
560         }
561
562         if (camel_debug_flag (conman))
563                 g_assert (!(concurrent_connections == g_list_length (con_man->priv->connections) && is == NULL));
564
565         return is;
566 }
567
568 static CamelIMAPXServer *
569 imapx_create_new_connection_unlocked (CamelIMAPXConnManager *con_man,
570                                       const gchar *folder_name,
571                                       GCancellable *cancellable,
572                                       GError **error)
573 {
574         CamelIMAPXServer *is = NULL;
575         CamelIMAPXStore *imapx_store;
576         CamelStore *store = con_man->priv->store;
577         ConnectionInfo *cinfo = NULL;
578         gboolean success;
579
580         /* Caller must be holding CON_WRITE_LOCK. */
581
582         imapx_store = CAMEL_IMAPX_STORE (store);
583
584         /* Check if we got cancelled while we were waiting. */
585         if (g_cancellable_set_error_if_cancelled (cancellable, error))
586                 return NULL;
587
588         is = camel_imapx_server_new (store);
589
590         /* XXX As part of the connect operation the CamelIMAPXServer will
591          *     have to call camel_session_authenticate_sync(), but it has
592          *     no way to pass itself through in that call so the service
593          *     knows which CamelIMAPXServer is trying to authenticate.
594          *
595          *     IMAPX is the only provider that does multiple connections
596          *     like this, so I didn't want to pollute the CamelSession and
597          *     CamelService authentication APIs with an extra argument.
598          *     Instead we do this little hack so the service knows which
599          *     CamelIMAPXServer to act on in its authenticate_sync() method.
600          *
601          *     Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK
602          *     we should not have multiple IMAPX connections trying to
603          *     authenticate at once, so this should be thread-safe.
604          */
605         imapx_store->authenticating_server = g_object_ref (is);
606         success = camel_imapx_server_connect (is, cancellable, error);
607         g_object_unref (imapx_store->authenticating_server);
608         imapx_store->authenticating_server = NULL;
609
610         if (!success) {
611                 g_object_unref (is);
612                 return NULL;
613         }
614
615         g_signal_connect (
616                 is, "shutdown",
617                 G_CALLBACK (imapx_conn_shutdown), con_man);
618         g_signal_connect (
619                 is, "select_changed",
620                 G_CALLBACK (imapx_conn_update_select), con_man);
621
622         cinfo = connection_info_new (is);
623
624         if (folder_name != NULL)
625                 connection_info_insert_folder_name (cinfo, folder_name);
626
627         /* Takes ownership of the ConnectionInfo. */
628         con_man->priv->connections = g_list_prepend (
629                 con_man->priv->connections, cinfo);
630
631         c(is->tagprefix, "Created new connection for %s and total connections %d \n", folder_name, g_list_length (con_man->priv->connections));
632
633         return is;
634 }
635
636 /****************************/
637
638 CamelIMAPXConnManager *
639 camel_imapx_conn_manager_new (CamelStore *store)
640 {
641         g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
642
643         return g_object_new (
644                 CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL);
645 }
646
647 CamelStore *
648 camel_imapx_conn_manager_get_store (CamelIMAPXConnManager *con_man)
649 {
650         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
651
652         return CAMEL_STORE (con_man->priv->store);
653 }
654
655 CamelIMAPXServer *
656 camel_imapx_conn_manager_get_connection (CamelIMAPXConnManager *con_man,
657                                          const gchar *folder_name,
658                                          GCancellable *cancellable,
659                                          GError **error)
660 {
661         CamelIMAPXServer *is = NULL;
662
663         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
664
665         /* Hold the writer lock while we requisition a CamelIMAPXServer
666          * to prevent other threads from adding or removing connections. */
667         CON_WRITE_LOCK (con_man);
668
669         /* Check if we got cancelled while waiting for the lock. */
670         if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
671                 is = imapx_find_connection_unlocked (con_man, folder_name);
672                 if (is == NULL)
673                         is = imapx_create_new_connection_unlocked (
674                                 con_man, folder_name, cancellable, error);
675         }
676
677         CON_WRITE_UNLOCK (con_man);
678
679         return is;
680 }
681
682 GList *
683 camel_imapx_conn_manager_get_connections (CamelIMAPXConnManager *con_man)
684 {
685         GList *list, *link;
686
687         g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
688
689         list = imapx_conn_manager_list_info (con_man);
690
691         /* Swap ConnectionInfo for CamelIMAPXServer in each link. */
692         for (link = list; link != NULL; link = g_list_next (link)) {
693                 ConnectionInfo *cinfo = link->data;
694                 link->data = g_object_ref (cinfo->is);
695                 connection_info_unref (cinfo);
696         }
697
698         return list;
699 }
700
701 /* Used for handling operations that fails to execute and that needs to removed from folder list */
702 void
703 camel_imapx_conn_manager_update_con_info (CamelIMAPXConnManager *con_man,
704                                           CamelIMAPXServer *is,
705                                           const gchar *folder_name)
706 {
707         ConnectionInfo *cinfo;
708         IMAPXJobQueueInfo *jinfo;
709
710         g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
711
712         /* Returns a new ConnectionInfo reference. */
713         cinfo = imapx_conn_manager_lookup_info (con_man, is);
714
715         if (cinfo == NULL)
716                 return;
717
718         jinfo = camel_imapx_server_get_job_queue_info (cinfo->is);
719         if (!g_hash_table_lookup (jinfo->folders, folder_name)) {
720                 connection_info_remove_folder_name (cinfo, folder_name);
721                 c(is->tagprefix, "Removed folder %s from connection folder list - op done \n", folder_name);
722         }
723         camel_imapx_destroy_job_queue_info (jinfo);
724
725         connection_info_unref (cinfo);
726 }
727
728 void
729 camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man)
730 {
731         g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
732
733         CON_WRITE_LOCK (con_man);
734
735         g_list_free_full (
736                 con_man->priv->connections,
737                 (GDestroyNotify) connection_info_cancel_and_unref);
738         con_man->priv->connections = NULL;
739
740         CON_WRITE_UNLOCK (con_man);
741 }
742