Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
mail-folder-cache.c:1163:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') | ||
mail-folder-cache.c:1163:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') | ||
mail-folder-cache.c:1211:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') | ||
mail-folder-cache.c:1211:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') | ||
mail-folder-cache.c:1261:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') | ||
mail-folder-cache.c:1261:6 | clang-analyzer | Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store') |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Peter Williams <peterw@ximian.com>
18 * Michael Zucchi <notzed@ximian.com>
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 * Copyright (C) 2009 Intel Corporation
23 *
24 */
25
26 /**
27 * SECTION: mail-folder-cache
28 * @short_description: Stores information about open folders
29 **/
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <string.h>
36 #include <time.h>
37
38 #include <glib/gi18n.h>
39 #include <glib/gstdio.h>
40
41 #include <libedataserver/libedataserver.h>
42
43 #include <libemail-utils/mail-mt.h>
44
45 #include "mail-folder-cache.h"
46 #include "e-mail-utils.h"
47 #include "e-mail-folder-utils.h"
48 #include "e-mail-session.h"
49 #include "e-mail-store-utils.h"
50
51 #define w(x)
52 #define d(x)
53
54 #define MAIL_FOLDER_CACHE_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE \
56 ((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCachePrivate))
57
58 /* This code is a mess, there is no reason it should be so complicated. */
59
60 typedef struct _StoreInfo StoreInfo;
61
62 struct _MailFolderCachePrivate {
63 gpointer session; /* weak pointer */
64
65 /* source id for the ping timeout callback */
66 guint ping_id;
67 /* Store to storeinfo table, active stores */
68 GHashTable *stores;
69 /* mutex to protect access to the stores hash */
70 GStaticRecMutex stores_mutex;
71 /* List of folder changes to be executed in gui thread */
72 GQueue updates;
73 /* idle source id for flushing all pending updates */
74 guint update_id;
75 /* hack for people who LIKE to have unsent count */
76 gint count_sent;
77 gint count_trash;
78
79 GQueue local_folder_uris;
80 GQueue remote_folder_uris;
81 };
82
83 enum {
84 PROP_0,
85 PROP_SESSION
86 };
87
88 enum {
89 FOLDER_AVAILABLE,
90 FOLDER_UNAVAILABLE,
91 FOLDER_DELETED,
92 FOLDER_RENAMED,
93 FOLDER_UNREAD_UPDATED,
94 FOLDER_CHANGED,
95 LAST_SIGNAL
96 };
97
98 static guint signals[LAST_SIGNAL];
99
100 struct _folder_info {
101 StoreInfo *store_info; /* 'parent' link */
102
103 gchar *full_name; /* full name of folder/folderinfo */
104
105 guint32 flags;
106 gboolean has_children;
107
108 gpointer folder; /* if known (weak pointer) */
109 };
110
111 /* pending list of updates */
112 struct _folder_update {
113 guint remove:1; /* removing from vfolders */
114 guint delete:1; /* deleting as well? */
115 guint add:1; /* add to vfolder */
116 guint unsub:1; /* unsubcribing? */
117 guint new; /* new mail arrived? */
118
119 gchar *full_name;
120 gchar *oldfull;
121
122 gint unread;
123 CamelStore *store;
124
125 /* for only one new message... */
126 gchar *msg_uid; /* ... its uid ... */
127 gchar *msg_sender; /* ... its sender ... */
128 gchar *msg_subject; /* ... and its subject. */
129 };
130
131 struct _StoreInfo {
132 GHashTable *folders; /* by full_name */
133 CamelStore *store; /* the store for these folders */
134 gboolean first_update; /* TRUE initially, then FALSE forever */
135
136 /* Hold a reference to keep them alive. */
137 CamelFolder *vjunk;
138 CamelFolder *vtrash;
139
140 /* Outstanding folderinfo requests */
141 GQueue folderinfo_updates;
142 };
143
144 struct _update_data {
145 NoteDoneFunc done;
146 gpointer data;
147 MailFolderCache *cache;
148 GCancellable *cancellable;
149 };
150
151 G_DEFINE_TYPE (MailFolderCache, mail_folder_cache, G_TYPE_OBJECT)
152
153 static void
154 free_update (struct _folder_update *up)
155 {
156 g_free (up->full_name);
157 if (up->store)
158 g_object_unref (up->store);
159 g_free (up->oldfull);
160 g_free (up->msg_uid);
161 g_free (up->msg_sender);
162 g_free (up->msg_subject);
163 g_free (up);
164 }
165
166 static void
167 free_folder_info (struct _folder_info *mfi)
168 {
169 g_free (mfi->full_name);
170 g_free (mfi);
171 }
172
173 static StoreInfo *
174 store_info_new (CamelStore *store)
175 {
176 StoreInfo *info;
177 GHashTable *folders;
178
179 folders = g_hash_table_new_full (
180 (GHashFunc) g_str_hash,
181 (GEqualFunc) g_str_equal,
182 (GDestroyNotify) NULL,
183 (GDestroyNotify) free_folder_info);
184
185 info = g_slice_new0 (StoreInfo);
186 info->folders = folders;
187 info->store = g_object_ref (store);
188 info->first_update = TRUE;
189
190 /* If these are vfolders then they need to be opened
191 * now, otherwise they won't keep track of all folders. */
192 if (store->flags & CAMEL_STORE_VJUNK)
193 info->vjunk = camel_store_get_junk_folder_sync (
194 store, NULL, NULL);
195 if (store->flags & CAMEL_STORE_VTRASH)
196 info->vtrash = camel_store_get_trash_folder_sync (
197 store, NULL, NULL);
198
199 g_queue_init (&info->folderinfo_updates);
200
201 return info;
202 }
203
204 static void
205 store_info_free (StoreInfo *info)
206 {
207 struct _update_data *ud;
208
209 while (!g_queue_is_empty (&info->folderinfo_updates)) {
210 ud = g_queue_pop_head (&info->folderinfo_updates);
211 g_cancellable_cancel (ud->cancellable);
212 }
213
214 g_hash_table_destroy (info->folders);
215 g_object_unref (info->store);
216
217 if (info->vjunk != NULL)
218 g_object_unref (info->vjunk);
219
220 if (info->vtrash != NULL)
221 g_object_unref (info->vtrash);
222
223 g_slice_free (StoreInfo, info);
224 }
225
226 static gboolean
227 flush_updates_idle_cb (MailFolderCache *cache)
228 {
229 struct _folder_update *up;
230
231 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
232 while ((up = g_queue_pop_head (&cache->priv->updates)) != NULL) {
233 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
234
235 if (up->remove) {
236 if (up->delete) {
237 g_signal_emit (
238 cache, signals[FOLDER_DELETED], 0,
239 up->store, up->full_name);
240 } else
241 g_signal_emit (
242 cache, signals[FOLDER_UNAVAILABLE], 0,
243 up->store, up->full_name);
244 } else {
245 if (up->oldfull && up->add) {
246 g_signal_emit (
247 cache, signals[FOLDER_RENAMED], 0,
248 up->store, up->oldfull, up->full_name);
249 }
250
251 if (!up->oldfull && up->add)
252 g_signal_emit (
253 cache, signals[FOLDER_AVAILABLE], 0,
254 up->store, up->full_name);
255 }
256
257 /* update unread counts */
258 g_signal_emit (
259 cache, signals[FOLDER_UNREAD_UPDATED], 0,
260 up->store, up->full_name, up->unread);
261
262 /* indicate that the folder has changed (new mail received, etc) */
263 if (up->store != NULL && up->full_name != NULL) {
264 g_signal_emit (
265 cache, signals[FOLDER_CHANGED], 0, up->store,
266 up->full_name, up->new, up->msg_uid,
267 up->msg_sender, up->msg_subject);
268 }
269
270 if (CAMEL_IS_VEE_STORE (up->store) && !up->remove) {
271 /* Normally the vfolder store takes care of the
272 * folder_opened event itself, but we add folder to
273 * the noting system later, thus we do not know about
274 * search folders to update them in a tree, thus
275 * ensure their changes will be tracked correctly. */
276 CamelFolder *folder;
277
278 /* FIXME camel_store_get_folder_sync() may block. */
279 folder = camel_store_get_folder_sync (
280 up->store, up->full_name, 0, NULL, NULL);
281
282 if (folder) {
283 mail_folder_cache_note_folder (cache, folder);
284 g_object_unref (folder);
285 }
286 }
287
288 free_update (up);
289
290 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
291 }
292 cache->priv->update_id = 0;
293 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
294
295 return FALSE;
296 }
297
298 static void
299 flush_updates (MailFolderCache *cache)
300 {
301 if (cache->priv->update_id > 0)
302 return;
303
304 if (g_queue_is_empty (&cache->priv->updates))
305 return;
306
307 /* Prioritize ahead of GTK+ redraws. */
308 cache->priv->update_id = g_idle_add_full (
309 G_PRIORITY_HIGH_IDLE,
310 (GSourceFunc) flush_updates_idle_cb, cache, NULL);
311 }
312
313 /* This is how unread counts work (and don't work):
314 *
315 * camel_folder_unread_message_count() only gives a correct answer if
316 * the store is paying attention to the folder. (Some stores always
317 * pay attention to all folders, but IMAP can only pay attention to
318 * one folder at a time.) But it doesn't have any way to know when
319 * it's lying, so it's only safe to call it when you know for sure
320 * that the store is paying attention to the folder, such as when it's
321 * just been created, or you get a folder_changed signal on it.
322 *
323 * camel_store_get_folder_info() always gives correct answers for the
324 * folders it checks, but it can also return -1 for a folder, meaning
325 * it didn't check, and so you should stick with your previous answer.
326 *
327 * update_1folder is called from three places: with info != NULL when
328 * the folder is created (or get_folder_info), with info == NULL when
329 * a folder changed event is emitted.
330 *
331 * So if info is NULL, camel_folder_unread_message_count is correct,
332 * and if it's not NULL and its unread_message_count isn't -1, then
333 * it's correct. */
334
335 static void
336 update_1folder (MailFolderCache *cache,
337 struct _folder_info *mfi,
338 gint new,
339 const gchar *msg_uid,
340 const gchar *msg_sender,
341 const gchar *msg_subject,
342 CamelFolderInfo *info)
343 {
344 EMailSession *session;
345 ESourceRegistry *registry;
346 struct _folder_update *up;
347 CamelFolder *folder;
348 gint unread = -1;
349 gint deleted;
350
351 session = mail_folder_cache_get_session (cache);
352 registry = e_mail_session_get_registry (session);
353
354 folder = mfi->folder;
355 if (folder) {
356 gboolean folder_is_sent;
357 gboolean folder_is_drafts;
358 gboolean folder_is_outbox;
359 gboolean folder_is_vtrash;
360 gboolean special_case;
361
362 folder_is_sent = em_utils_folder_is_sent (registry, folder);
363 folder_is_drafts = em_utils_folder_is_drafts (registry, folder);
364 folder_is_outbox = em_utils_folder_is_outbox (registry, folder);
365 folder_is_vtrash = CAMEL_IS_VTRASH_FOLDER (folder);
366
367 special_case =
368 (cache->priv->count_trash && folder_is_vtrash) ||
369 (cache->priv->count_sent && folder_is_sent) ||
370 folder_is_drafts || folder_is_outbox;
371
372 if (special_case) {
373 d (printf (" total count\n"));
374 unread = camel_folder_get_message_count (folder);
375 if (folder_is_drafts || folder_is_outbox) {
376 guint32 junked = 0;
377
378 deleted = camel_folder_get_deleted_message_count (folder);
379 if (deleted > 0)
380 unread -= deleted;
381
382 junked = camel_folder_summary_get_junk_count (folder->summary);
383 if (junked > 0)
384 unread -= junked;
385 }
386 } else {
387 d (printf (" unread count\n"));
388 if (info)
389 unread = info->unread;
390 else
391 unread = camel_folder_get_unread_message_count (folder);
392 }
393 }
394
395 d (printf ("folder updated: unread %d: '%s'\n", unread, mfi->full_name));
396
397 if (unread == -1)
398 return;
399
400 up = g_malloc0 (sizeof (*up));
401 up->full_name = g_strdup (mfi->full_name);
402 up->unread = unread;
403 up->new = new;
404 up->store = g_object_ref (mfi->store_info->store);
405 up->msg_uid = g_strdup (msg_uid);
406 up->msg_sender = g_strdup (msg_sender);
407 up->msg_subject = g_strdup (msg_subject);
408 g_queue_push_tail (&cache->priv->updates, up);
409 flush_updates (cache);
410 }
411
412 static void
413 folder_changed_cb (CamelFolder *folder,
414 CamelFolderChangeInfo *changes,
415 MailFolderCache *cache)
416 {
417 static GHashTable *last_newmail_per_folder = NULL;
418 time_t latest_received, new_latest_received;
419 CamelFolder *local_drafts;
420 CamelFolder *local_outbox;
421 CamelFolder *local_sent;
422 CamelSession *session;
423 CamelStore *parent_store;
424 CamelMessageInfo *info;
425 StoreInfo *si;
426 struct _folder_info *mfi;
427 const gchar *full_name;
428 gint new = 0;
429 gint i;
430 guint32 flags;
431 gchar *uid = NULL, *sender = NULL, *subject = NULL;
432
433 full_name = camel_folder_get_full_name (folder);
434 parent_store = camel_folder_get_parent_store (folder);
435 session = camel_service_get_session (CAMEL_SERVICE (parent_store));
436
437 if (!last_newmail_per_folder)
438 last_newmail_per_folder = g_hash_table_new (g_direct_hash, g_direct_equal);
439
440 /* it's fine to hash them by folder pointer here */
441 latest_received = GPOINTER_TO_INT (
442 g_hash_table_lookup (last_newmail_per_folder, folder));
443 new_latest_received = latest_received;
444
445 local_drafts = e_mail_session_get_local_folder (
446 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_DRAFTS);
447 local_outbox = e_mail_session_get_local_folder (
448 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
449 local_sent = e_mail_session_get_local_folder (
450 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_SENT);
451
452 if (!CAMEL_IS_VEE_FOLDER (folder)
453 && folder != local_drafts
454 && folder != local_outbox
455 && folder != local_sent
456 && changes && (changes->uid_added->len > 0)) {
457 /* for each added message, check to see that it is
458 * brand new, not junk and not already deleted */
459 for (i = 0; i < changes->uid_added->len; i++) {
460 info = camel_folder_get_message_info (
461 folder, changes->uid_added->pdata[i]);
462 if (info) {
463 flags = camel_message_info_flags (info);
464 if (((flags & CAMEL_MESSAGE_SEEN) == 0) &&
465 ((flags & CAMEL_MESSAGE_JUNK) == 0) &&
466 ((flags & CAMEL_MESSAGE_DELETED) == 0) &&
467 (camel_message_info_date_received (info) > latest_received)) {
468 if (camel_message_info_date_received (info) > new_latest_received)
469 new_latest_received = camel_message_info_date_received (info);
470 new++;
471 if (new == 1) {
472 uid = g_strdup (camel_message_info_uid (info));
473 sender = g_strdup (camel_message_info_from (info));
474 subject = g_strdup (camel_message_info_subject (info));
475 } else {
476 g_free (uid);
477 g_free (sender);
478 g_free (subject);
479
480 uid = NULL;
481 sender = NULL;
482 subject = NULL;
483 }
484 }
485 camel_folder_free_message_info (folder, info);
486 }
487 }
488 }
489
490 if (new > 0)
491 g_hash_table_insert (
492 last_newmail_per_folder, folder,
493 GINT_TO_POINTER (new_latest_received));
494
495 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
496 if (cache->priv->stores != NULL
497 && (si = g_hash_table_lookup (cache->priv->stores, parent_store)) != NULL
498 && (mfi = g_hash_table_lookup (si->folders, full_name)) != NULL
499 && mfi->folder == folder) {
500 update_1folder (cache, mfi, new, uid, sender, subject, NULL);
501 }
502 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
503
504 g_free (uid);
505 g_free (sender);
506 g_free (subject);
507 }
508
509 static void
510 unset_folder_info (MailFolderCache *cache,
511 struct _folder_info *mfi,
512 gint delete,
513 gint unsub)
514 {
515 struct _folder_update *up;
516
517 d (printf ("unset folderinfo '%s'\n", mfi->uri));
518
519 if (mfi->folder) {
520 CamelFolder *folder = mfi->folder;
521
522 g_signal_handlers_disconnect_by_func (
523 folder, folder_changed_cb, cache);
524
525 g_object_remove_weak_pointer (
526 G_OBJECT (mfi->folder), &mfi->folder);
527 }
528
529 if ((mfi->flags & CAMEL_FOLDER_NOSELECT) == 0) {
530 up = g_malloc0 (sizeof (*up));
531
532 up->remove = TRUE;
533 up->delete = delete;
534 up->unsub = unsub;
535 up->store = g_object_ref (mfi->store_info->store);
536 up->full_name = g_strdup (mfi->full_name);
537
538 g_queue_push_tail (&cache->priv->updates, up);
539 flush_updates (cache);
540 }
541 }
542
543 static void
544 setup_folder (MailFolderCache *cache,
545 CamelFolderInfo *fi,
546 StoreInfo *si)
547 {
548 struct _folder_info *mfi;
549 struct _folder_update *up;
550
551 mfi = g_hash_table_lookup (si->folders, fi->full_name);
552 if (mfi) {
553 update_1folder (cache, mfi, 0, NULL, NULL, NULL, fi);
554 } else {
555 mfi = g_malloc0 (sizeof (*mfi));
556 mfi->full_name = g_strdup (fi->full_name);
557 mfi->store_info = si;
558 mfi->flags = fi->flags;
559 mfi->has_children = fi->child != NULL;
560
561 g_hash_table_insert (si->folders, mfi->full_name, mfi);
562
563 up = g_malloc0 (sizeof (*up));
564 up->full_name = g_strdup (mfi->full_name);
565 up->unread = fi->unread;
566 up->store = g_object_ref (si->store);
567
568 if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
569 up->add = TRUE;
570
571 g_queue_push_tail (&cache->priv->updates, up);
572 flush_updates (cache);
573 }
574 }
575
576 static void
577 create_folders (MailFolderCache *cache,
578 CamelFolderInfo *fi,
579 StoreInfo *si)
580 {
581 while (fi) {
582 setup_folder (cache, fi, si);
583
584 if (fi->child)
585 create_folders (cache, fi->child, si);
586
587 fi = fi->next;
588 }
589 }
590
591 static void
592 store_folder_subscribed_cb (CamelStore *store,
593 CamelFolderInfo *info,
594 MailFolderCache *cache)
595 {
596 StoreInfo *si;
597
598 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
599 si = g_hash_table_lookup (cache->priv->stores, store);
600 if (si)
601 setup_folder (cache, info, si);
602 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
603 }
604
605 static void
606 store_folder_created_cb (CamelStore *store,
607 CamelFolderInfo *info,
608 MailFolderCache *cache)
609 {
610 /* We only want created events to do more work
611 * if we dont support subscriptions. */
612 if (!CAMEL_IS_SUBSCRIBABLE (store))
613 store_folder_subscribed_cb (store, info, cache);
614 }
615
616 static void
617 store_folder_opened_cb (CamelStore *store,
618 CamelFolder *folder,
619 MailFolderCache *cache)
620 {
621 mail_folder_cache_note_folder (cache, folder);
622 }
623
624 static void
625 store_folder_unsubscribed_cb (CamelStore *store,
626 CamelFolderInfo *info,
627 MailFolderCache *cache)
628 {
629 StoreInfo *si;
630 struct _folder_info *mfi;
631
632 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
633 si = g_hash_table_lookup (cache->priv->stores, store);
634 if (si) {
635 mfi = g_hash_table_lookup (si->folders, info->full_name);
636 if (mfi) {
637 unset_folder_info (cache, mfi, TRUE, TRUE);
638 g_hash_table_remove (si->folders, mfi->full_name);
639 }
640 }
641 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
642 }
643
644 static void
645 store_folder_deleted_cb (CamelStore *store,
646 CamelFolderInfo *info,
647 MailFolderCache *cache)
648 {
649 /* We only want deleted events to do more work
650 * if we dont support subscriptions. */
651 if (!CAMEL_IS_SUBSCRIBABLE (store))
652 store_folder_unsubscribed_cb (store, info, cache);
653 }
654
655 static void
656 rename_folders (MailFolderCache *cache,
657 StoreInfo *si,
658 const gchar *oldbase,
659 const gchar *newbase,
660 CamelFolderInfo *fi)
661 {
662 gchar *old, *olduri, *oldfile, *newuri, *newfile;
663 struct _folder_info *mfi;
664 struct _folder_update *up;
665 const gchar *config_dir;
666
667 up = g_malloc0 (sizeof (*up));
668
669 /* Form what was the old name, and try and look it up */
670 old = g_strdup_printf ("%s%s", oldbase, fi->full_name + strlen (newbase));
671 mfi = g_hash_table_lookup (si->folders, old);
672 if (mfi) {
673 up->oldfull = mfi->full_name;
674
675 /* Be careful not to invoke the destroy function. */
676 g_hash_table_steal (si->folders, mfi->full_name);
677
678 /* Its a rename op */
679 mfi->full_name = g_strdup (fi->full_name);
680 mfi->flags = fi->flags;
681 mfi->has_children = fi->child != NULL;
682
683 g_hash_table_insert (si->folders, mfi->full_name, mfi);
684 } else {
685 /* Its a new op */
686 mfi = g_malloc0 (sizeof (*mfi));
687 mfi->full_name = g_strdup (fi->full_name);
688 mfi->store_info = si;
689 mfi->flags = fi->flags;
690 mfi->has_children = fi->child != NULL;
691
692 g_hash_table_insert (si->folders, mfi->full_name, mfi);
693 }
694
695 up->full_name = g_strdup (mfi->full_name);
696 up->unread = fi->unread==-1 ? 0 : fi->unread;
697 up->store = g_object_ref (si->store);
698
699 if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
700 up->add = TRUE;
701
702 g_queue_push_tail (&cache->priv->updates, up);
703 flush_updates (cache);
704
705 /* rename the meta-data we maintain ourselves */
706 config_dir = mail_session_get_config_dir ();
707 olduri = e_mail_folder_uri_build (si->store, old);
708 e_filename_make_safe (olduri);
709 newuri = e_mail_folder_uri_build (si->store, fi->full_name);
710 e_filename_make_safe (newuri);
711 oldfile = g_strdup_printf ("%s/custom_view-%s.xml", config_dir, olduri);
712 newfile = g_strdup_printf ("%s/custom_view-%s.xml", config_dir, newuri);
713 g_rename (oldfile, newfile);
714 g_free (oldfile);
715 g_free (newfile);
716 oldfile = g_strdup_printf ("%s/current_view-%s.xml", config_dir, olduri);
717 newfile = g_strdup_printf ("%s/current_view-%s.xml", config_dir, newuri);
718 g_rename (oldfile, newfile);
719 g_free (oldfile);
720 g_free (newfile);
721 g_free (olduri);
722 g_free (newuri);
723
724 g_free (old);
725 }
726
727 static void
728 get_folders (CamelFolderInfo *fi,
729 GPtrArray *folders)
730 {
731 while (fi) {
732 g_ptr_array_add (folders, fi);
733
734 if (fi->child)
735 get_folders (fi->child, folders);
736
737 fi = fi->next;
738 }
739 }
740
741 static gint
742 folder_cmp (gconstpointer ap,
743 gconstpointer bp)
744 {
745 const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
746 const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];
747
748 return strcmp (a->full_name, b->full_name);
749 }
750
751 static void
752 store_folder_renamed_cb (CamelStore *store,
753 const gchar *old_name,
754 CamelFolderInfo *info,
755 MailFolderCache *cache)
756 {
757 StoreInfo *si;
758
759 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
760 si = g_hash_table_lookup (cache->priv->stores, store);
761 if (si) {
762 GPtrArray *folders = g_ptr_array_new ();
763 CamelFolderInfo *top;
764 gint i;
765
766 /* Ok, so for some reason the folderinfo we have comes in all messed up from
767 * imap, should find out why ... this makes it workable */
768 get_folders (info, folders);
769 qsort (folders->pdata, folders->len, sizeof (folders->pdata[0]), folder_cmp);
770
771 top = folders->pdata[0];
772 for (i = 0; i < folders->len; i++) {
773 rename_folders (cache, si, old_name, top->full_name, folders->pdata[i]);
774 }
775
776 g_ptr_array_free (folders, TRUE);
777
778 }
779 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
780 }
781
782 static void
783 unset_folder_info_hash (gchar *path,
784 struct _folder_info *mfi,
785 gpointer data)
786 {
787 MailFolderCache *cache = (MailFolderCache *) data;
788 unset_folder_info (cache, mfi, FALSE, FALSE);
789 }
790
791 static void
792 mail_folder_cache_first_update (MailFolderCache *cache,
793 StoreInfo *info)
794 {
795 EMailSession *session;
796 const gchar *uid;
797
798 session = mail_folder_cache_get_session (cache);
799 uid = camel_service_get_uid (CAMEL_SERVICE (info->store));
800
801 if (info->vjunk != NULL)
802 mail_folder_cache_note_folder (cache, info->vjunk);
803
804 if (info->vtrash != NULL)
805 mail_folder_cache_note_folder (cache, info->vtrash);
806
807 /* Some extra work for the "On This Computer" store. */
808 if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
809 CamelFolder *folder;
810 gint ii;
811
812 for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
813 folder = e_mail_session_get_local_folder (session, ii);
814 mail_folder_cache_note_folder (cache, folder);
815 }
816 }
817 }
818
819 static void
820 update_folders (CamelStore *store,
821 GAsyncResult *result,
822 struct _update_data *ud)
823 {
824 CamelFolderInfo *fi;
825 StoreInfo *si;
826 GError *error = NULL;
827 gboolean free_fi = TRUE;
828
829 fi = camel_store_get_folder_info_finish (store, result, &error);
830
831 /* Silently ignore cancellation errors. */
832 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
833 g_error_free (error);
834
835 } else if (error != NULL) {
836 g_warning ("%s", error->message);
837 g_error_free (error);
838 }
839
840 g_static_rec_mutex_lock (&ud->cache->priv->stores_mutex);
841 si = g_hash_table_lookup (ud->cache->priv->stores, store);
842 if (si && !g_cancellable_is_cancelled (ud->cancellable)) {
843 /* The 'si' is still there, so we can remove ourselves from
844 * its list. Or else its not, and we're on our own and free
845 * anyway. */
846 g_queue_remove (&si->folderinfo_updates, ud);
847
848 if (fi != NULL)
849 create_folders (ud->cache, fi, si);
850 }
851 g_static_rec_mutex_unlock (&ud->cache->priv->stores_mutex);
852
853 /* Do some extra work for the first update. */
854 if (si != NULL && si->first_update) {
855 mail_folder_cache_first_update (ud->cache, si);
856 si->first_update = FALSE;
857 }
858
859 if (ud->done != NULL)
860 free_fi = ud->done (ud->cache, store, fi, ud->data);
861 if (fi && free_fi)
862 camel_store_free_folder_info (store, fi);
863
864 if (ud->cancellable != NULL)
865 g_object_unref (ud->cancellable);
866
867 g_free (ud);
868 }
869
870 struct _ping_store_msg {
871 MailMsg base;
872 CamelStore *store;
873 };
874
875 static gchar *
876 ping_store_desc (struct _ping_store_msg *m)
877 {
878 gchar *service_name;
879 gchar *msg;
880
881 service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
882 msg = g_strdup_printf (_("Pinging %s"), service_name);
883 g_free (service_name);
884
885 return msg;
886 }
887
888 static void
889 ping_store_exec (struct _ping_store_msg *m,
890 GCancellable *cancellable,
891 GError **error)
892 {
893 CamelServiceConnectionStatus status;
894 CamelService *service;
895 gboolean online = FALSE;
896
897 service = CAMEL_SERVICE (m->store);
898 status = camel_service_get_connection_status (service);
899
900 if (status == CAMEL_SERVICE_CONNECTED) {
901 if (CAMEL_IS_DISCO_STORE (m->store) &&
902 camel_disco_store_status (
903 CAMEL_DISCO_STORE (m->store)) !=CAMEL_DISCO_STORE_OFFLINE)
904 online = TRUE;
905 else if (CAMEL_IS_OFFLINE_STORE (m->store) &&
906 camel_offline_store_get_online (
907 CAMEL_OFFLINE_STORE (m->store)))
908 online = TRUE;
909 }
910 if (online)
911 camel_store_noop_sync (m->store, cancellable, error);
912 }
913
914 static void
915 ping_store_free (struct _ping_store_msg *m)
916 {
917 g_object_unref (m->store);
918 }
919
920 static MailMsgInfo ping_store_info = {
921 sizeof (struct _ping_store_msg),
922 (MailMsgDescFunc) ping_store_desc,
923 (MailMsgExecFunc) ping_store_exec,
924 (MailMsgDoneFunc) NULL,
925 (MailMsgFreeFunc) ping_store_free
926 };
927
928 static void
929 ping_store (CamelStore *store)
930 {
931 CamelServiceConnectionStatus status;
932 CamelService *service;
933 struct _ping_store_msg *m;
934
935 service = CAMEL_SERVICE (store);
936 status = camel_service_get_connection_status (service);
937
938 if (status != CAMEL_SERVICE_CONNECTED)
939 return;
940
941 m = mail_msg_new (&ping_store_info);
942 m->store = g_object_ref (store);
943
944 mail_msg_slow_ordered_push (m);
945 }
946
947 static gboolean
948 ping_cb (MailFolderCache *cache)
949 {
950 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
951
952 g_hash_table_foreach (cache->priv->stores, (GHFunc) ping_store, NULL);
953
954 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
955
956 return TRUE;
957 }
958
959 static gboolean
960 store_has_folder_hierarchy (CamelStore *store)
961 {
962 CamelProvider *provider;
963
964 g_return_val_if_fail (store != NULL, FALSE);
965
966 provider = camel_service_get_provider (CAMEL_SERVICE (store));
967 g_return_val_if_fail (provider != NULL, FALSE);
968
969 if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
970 return TRUE;
971
972 if (provider->flags & CAMEL_PROVIDER_IS_EXTERNAL)
973 return TRUE;
974
975 return FALSE;
976 }
977
978 static void
979 store_go_online_cb (CamelStore *store,
980 GAsyncResult *result,
981 struct _update_data *ud)
982 {
983 /* FIXME Not checking result for error. */
984
985 g_static_rec_mutex_lock (&ud->cache->priv->stores_mutex);
986
987 if (g_hash_table_lookup (ud->cache->priv->stores, store) != NULL &&
988 !g_cancellable_is_cancelled (ud->cancellable)) {
989 /* We're already in the store update list. */
990 if (store_has_folder_hierarchy (store))
991 camel_store_get_folder_info (
992 store, NULL,
993 CAMEL_STORE_FOLDER_INFO_FAST |
994 CAMEL_STORE_FOLDER_INFO_RECURSIVE |
995 CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
996 G_PRIORITY_DEFAULT, ud->cancellable,
997 (GAsyncReadyCallback) update_folders, ud);
998 } else {
999 /* The store vanished, that means we were probably cancelled,
1000 * or at any rate, need to clean ourselves up. */
1001 if (ud->cancellable != NULL)
1002 g_object_unref (ud->cancellable);
1003 g_free (ud);
1004 }
1005
1006 g_static_rec_mutex_unlock (&ud->cache->priv->stores_mutex);
1007 }
1008
1009 static GList *
1010 find_folder_uri (GQueue *queue,
1011 CamelSession *session,
1012 const gchar *folder_uri)
1013 {
1014 GList *head, *link;
1015
1016 head = g_queue_peek_head_link (queue);
1017
1018 for (link = head; link != NULL; link = g_list_next (link))
1019 if (e_mail_folder_uri_equal (session, link->data, folder_uri))
1020 break;
1021
1022 return link;
1023 }
1024
1025 struct _find_info {
1026 const gchar *folder_uri;
1027 struct _folder_info *fi;
1028 };
1029
1030 static void
1031 storeinfo_find_folder_info (CamelStore *store,
1032 StoreInfo *si,
1033 struct _find_info *fi)
1034 {
1035 gchar *folder_name;
1036 gboolean success;
1037
1038 if (fi->fi != NULL)
1039 return;
1040
1041 success = e_mail_folder_uri_parse (
1042 camel_service_get_session (CAMEL_SERVICE (store)),
1043 fi->folder_uri, NULL, &folder_name, NULL);
1044
1045 if (success) {
1046 fi->fi = g_hash_table_lookup (si->folders, folder_name);
1047 g_free (folder_name);
1048 }
1049 }
1050
1051 static void
1052 mail_folder_cache_set_session (MailFolderCache *cache,
1053 EMailSession *session)
1054 {
1055 g_return_if_fail (E_IS_MAIL_SESSION (session));
1056 g_return_if_fail (cache->priv->session == NULL);
1057
1058 cache->priv->session = session;
1059
1060 g_object_add_weak_pointer (
1061 G_OBJECT (cache->priv->session),
1062 &cache->priv->session);
1063 }
1064
1065 static void
1066 mail_folder_cache_set_property (GObject *object,
1067 guint property_id,
1068 const GValue *value,
1069 GParamSpec *pspec)
1070 {
1071 switch (property_id) {
1072 case PROP_SESSION:
1073 mail_folder_cache_set_session (
1074 MAIL_FOLDER_CACHE (object),
1075 g_value_get_object (value));
1076 return;
1077 }
1078
1079 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1080 }
1081
1082 static void
1083 mail_folder_cache_get_property (GObject *object,
1084 guint property_id,
1085 GValue *value,
1086 GParamSpec *pspec)
1087 {
1088 switch (property_id) {
1089 case PROP_SESSION:
1090 g_value_set_object (
1091 value,
1092 mail_folder_cache_get_session (
1093 MAIL_FOLDER_CACHE (object)));
1094 return;
1095 }
1096
1097 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1098 }
1099
1100 static void
1101 mail_folder_cache_dispose (GObject *object)
1102 {
1103 MailFolderCachePrivate *priv;
1104
1105 priv = MAIL_FOLDER_CACHE_GET_PRIVATE (object);
1106
1107 if (priv->session != NULL) {
1108 g_object_remove_weak_pointer (
1109 G_OBJECT (priv->session), &priv->session);
1110 priv->session = NULL;
1111 }
1112
1113 /* Chain up to parent's dispose() method. */
1114 G_OBJECT_CLASS (mail_folder_cache_parent_class)->dispose (object);
1115 }
1116
1117 static void
1118 mail_folder_cache_finalize (GObject *object)
1119 {
1120 MailFolderCachePrivate *priv;
1121
1122 priv = MAIL_FOLDER_CACHE_GET_PRIVATE (object);
1123
1124 g_hash_table_destroy (priv->stores);
1125 g_static_rec_mutex_free (&priv->stores_mutex);
1126
1127 if (priv->ping_id > 0) {
1128 g_source_remove (priv->ping_id);
1129 priv->ping_id = 0;
1130 }
1131
1132 if (priv->update_id > 0) {
1133 g_source_remove (priv->update_id);
1134 priv->update_id = 0;
1135 }
1136
1137 while (!g_queue_is_empty (&priv->local_folder_uris))
1138 g_free (g_queue_pop_head (&priv->local_folder_uris));
1139
1140 while (!g_queue_is_empty (&priv->remote_folder_uris))
1141 g_free (g_queue_pop_head (&priv->remote_folder_uris));
1142
1143 /* Chain up to parent's finalize() method. */
1144 G_OBJECT_CLASS (mail_folder_cache_parent_class)->finalize (object);
1145 }
1146
1147 static void
1148 mail_folder_cache_folder_available (MailFolderCache *cache,
1149 CamelStore *store,
1150 const gchar *folder_name)
1151 {
1152 CamelService *service;
1153 CamelSession *session;
1154 CamelProvider *provider;
1155 GQueue *queue;
1156 gchar *folder_uri;
1157
1158 /* Disregard virtual stores. */
1159 if (CAMEL_IS_VEE_STORE (store))
1160 return;
1161
1162 /* Disregard virtual Junk folders. */
1163 if (store->flags & CAMEL_STORE_VJUNK)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1164 if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
1165 return;
1166
1167 /* Disregard virtual Trash folders. */
1168 if (store->flags & CAMEL_STORE_VTRASH)
1169 if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
1170 return;
1171
1172 service = CAMEL_SERVICE (store);
1173 session = camel_service_get_session (service);
1174 provider = camel_service_get_provider (service);
1175
1176 /* Reuse the stores mutex just because it's handy. */
1177 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1178
1179 folder_uri = e_mail_folder_uri_build (store, folder_name);
1180
1181 if (provider->flags & CAMEL_PROVIDER_IS_REMOTE)
1182 queue = &cache->priv->remote_folder_uris;
1183 else
1184 queue = &cache->priv->local_folder_uris;
1185
1186 if (find_folder_uri (queue, session, folder_uri) == NULL)
1187 g_queue_push_tail (queue, folder_uri);
1188 else
1189 g_free (folder_uri);
1190
1191 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1192 }
1193
1194 static void
1195 mail_folder_cache_folder_unavailable (MailFolderCache *cache,
1196 CamelStore *store,
1197 const gchar *folder_name)
1198 {
1199 CamelService *service;
1200 CamelSession *session;
1201 CamelProvider *provider;
1202 GQueue *queue;
1203 GList *link;
1204 gchar *folder_uri;
1205
1206 /* Disregard virtual stores. */
1207 if (CAMEL_IS_VEE_STORE (store))
1208 return;
1209
1210 /* Disregard virtual Junk folders. */
1211 if (store->flags & CAMEL_STORE_VJUNK)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1212 if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
1213 return;
1214
1215 /* Disregard virtual Trash folders. */
1216 if (store->flags & CAMEL_STORE_VTRASH)
1217 if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
1218 return;
1219
1220 service = CAMEL_SERVICE (store);
1221 session = camel_service_get_session (service);
1222 provider = camel_service_get_provider (service);
1223
1224 /* Reuse the stores mutex just because it's handy. */
1225 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1226
1227 folder_uri = e_mail_folder_uri_build (store, folder_name);
1228
1229 if (provider->flags & CAMEL_PROVIDER_IS_REMOTE)
1230 queue = &cache->priv->remote_folder_uris;
1231 else
1232 queue = &cache->priv->local_folder_uris;
1233
1234 link = find_folder_uri (queue, session, folder_uri);
1235 if (link != NULL) {
1236 g_free (link->data);
1237 g_queue_delete_link (queue, link);
1238 }
1239
1240 g_free (folder_uri);
1241
1242 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1243 }
1244
1245 static void
1246 mail_folder_cache_folder_deleted (MailFolderCache *cache,
1247 CamelStore *store,
1248 const gchar *folder_name)
1249 {
1250 CamelService *service;
1251 CamelSession *session;
1252 GQueue *queue;
1253 GList *link;
1254 gchar *folder_uri;
1255
1256 /* Disregard virtual stores. */
1257 if (CAMEL_IS_VEE_STORE (store))
1258 return;
1259
1260 /* Disregard virtual Junk folders. */
1261 if (store->flags & CAMEL_STORE_VJUNK)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1262 if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
1263 return;
1264
1265 /* Disregard virtual Trash folders. */
1266 if (store->flags & CAMEL_STORE_VTRASH)
1267 if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
1268 return;
1269
1270 service = CAMEL_SERVICE (store);
1271 session = camel_service_get_session (service);
1272
1273 /* Reuse the stores mutex just because it's handy. */
1274 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1275
1276 folder_uri = e_mail_folder_uri_build (store, folder_name);
1277
1278 queue = &cache->priv->local_folder_uris;
1279 link = find_folder_uri (queue, session, folder_uri);
1280 if (link != NULL) {
1281 g_free (link->data);
1282 g_queue_delete_link (queue, link);
1283 }
1284
1285 queue = &cache->priv->remote_folder_uris;
1286 link = find_folder_uri (queue, session, folder_uri);
1287 if (link != NULL) {
1288 g_free (link->data);
1289 g_queue_delete_link (queue, link);
1290 }
1291
1292 g_free (folder_uri);
1293
1294 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1295 }
1296
1297 static void
1298 mail_folder_cache_class_init (MailFolderCacheClass *class)
1299 {
1300 GObjectClass *object_class;
1301
1302 g_type_class_add_private (class, sizeof (MailFolderCachePrivate));
1303
1304 object_class = G_OBJECT_CLASS (class);
1305 object_class->set_property = mail_folder_cache_set_property;
1306 object_class->get_property = mail_folder_cache_get_property;
1307 object_class->dispose = mail_folder_cache_dispose;
1308 object_class->finalize = mail_folder_cache_finalize;
1309
1310 class->folder_available = mail_folder_cache_folder_available;
1311 class->folder_unavailable = mail_folder_cache_folder_unavailable;
1312 class->folder_deleted = mail_folder_cache_folder_deleted;
1313
1314 g_object_class_install_property (
1315 object_class,
1316 PROP_SESSION,
1317 g_param_spec_object (
1318 "session",
1319 "Session",
1320 "Mail session",
1321 E_TYPE_MAIL_SESSION,
1322 G_PARAM_READWRITE |
1323 G_PARAM_CONSTRUCT_ONLY |
1324 G_PARAM_STATIC_STRINGS));
1325
1326 /**
1327 * MailFolderCache::folder-available
1328 * @store: the #CamelStore containing the folder
1329 * @folder_name: the name of the folder
1330 *
1331 * Emitted when a folder becomes available
1332 **/
1333 signals[FOLDER_AVAILABLE] = g_signal_new (
1334 "folder-available",
1335 G_OBJECT_CLASS_TYPE (object_class),
1336 G_SIGNAL_RUN_FIRST,
1337 G_STRUCT_OFFSET (MailFolderCacheClass, folder_available),
1338 NULL, NULL, NULL,
1339 G_TYPE_NONE, 2,
1340 CAMEL_TYPE_STORE,
1341 G_TYPE_STRING);
1342
1343 /**
1344 * MailFolderCache::folder-unavailable
1345 * @store: the #CamelStore containing the folder
1346 * @folder_name: the name of the folder
1347 *
1348 * Emitted when a folder becomes unavailable. This represents a
1349 * transient condition. See MailFolderCache::folder-deleted to be
1350 * notified when a folder is permanently removed.
1351 **/
1352 signals[FOLDER_UNAVAILABLE] = g_signal_new (
1353 "folder-unavailable",
1354 G_OBJECT_CLASS_TYPE (object_class),
1355 G_SIGNAL_RUN_FIRST,
1356 G_STRUCT_OFFSET (MailFolderCacheClass, folder_unavailable),
1357 NULL, NULL, NULL,
1358 G_TYPE_NONE, 2,
1359 CAMEL_TYPE_STORE,
1360 G_TYPE_STRING);
1361
1362 /**
1363 * MailFolderCache::folder-deleted
1364 * @store: the #CamelStore containing the folder
1365 * @folder_name: the name of the folder
1366 *
1367 * Emitted when a folder is deleted
1368 **/
1369 signals[FOLDER_DELETED] = g_signal_new (
1370 "folder-deleted",
1371 G_OBJECT_CLASS_TYPE (object_class),
1372 G_SIGNAL_RUN_FIRST,
1373 G_STRUCT_OFFSET (MailFolderCacheClass, folder_deleted),
1374 NULL, NULL, /* accumulator */
1375 NULL,
1376 G_TYPE_NONE, 2,
1377 CAMEL_TYPE_STORE,
1378 G_TYPE_STRING);
1379
1380 /**
1381 * MailFolderCache::folder-renamed
1382 * @store: the #CamelStore containing the folder
1383 * @old_folder_name: the old name of the folder
1384 * @new_folder_name: the new name of the folder
1385 *
1386 * Emitted when a folder is renamed
1387 **/
1388 signals[FOLDER_RENAMED] = g_signal_new (
1389 "folder-renamed",
1390 G_OBJECT_CLASS_TYPE (object_class),
1391 G_SIGNAL_RUN_FIRST,
1392 G_STRUCT_OFFSET (MailFolderCacheClass, folder_renamed),
1393 NULL, NULL, NULL,
1394 G_TYPE_NONE, 3,
1395 CAMEL_TYPE_STORE,
1396 G_TYPE_STRING,
1397 G_TYPE_STRING);
1398
1399 /**
1400 * MailFolderCache::folder-unread-updated
1401 * @store: the #CamelStore containing the folder
1402 * @folder_name: the name of the folder
1403 * @unread: the number of unread mails in the folder
1404 *
1405 * Emitted when a we receive an update to the unread count for a folder
1406 **/
1407 signals[FOLDER_UNREAD_UPDATED] = g_signal_new (
1408 "folder-unread-updated",
1409 G_OBJECT_CLASS_TYPE (object_class),
1410 G_SIGNAL_RUN_FIRST,
1411 G_STRUCT_OFFSET (MailFolderCacheClass, folder_unread_updated),
1412 NULL, NULL, NULL,
1413 G_TYPE_NONE, 3,
1414 CAMEL_TYPE_STORE,
1415 G_TYPE_STRING,
1416 G_TYPE_INT);
1417
1418 /**
1419 * MailFolderCache::folder-changed
1420 * @store: the #CamelStore containing the folder
1421 * @folder_name: the name of the folder
1422 * @new_messages: the number of new messages for the folder
1423 * @msg_uid: uid of the new message, or NULL
1424 * @msg_sender: sender of the new message, or NULL
1425 * @msg_subject: subject of the new message, or NULL
1426 *
1427 * Emitted when a folder has changed. If @new_messages is not
1428 * exactly 1, @msg_uid, @msg_sender, and @msg_subject will be NULL.
1429 **/
1430 signals[FOLDER_CHANGED] = g_signal_new (
1431 "folder-changed",
1432 G_OBJECT_CLASS_TYPE (object_class),
1433 G_SIGNAL_RUN_FIRST,
1434 G_STRUCT_OFFSET (MailFolderCacheClass, folder_changed),
1435 NULL, NULL, NULL,
1436 G_TYPE_NONE, 6,
1437 CAMEL_TYPE_STORE,
1438 G_TYPE_STRING,
1439 G_TYPE_INT,
1440 G_TYPE_STRING,
1441 G_TYPE_STRING,
1442 G_TYPE_STRING);
1443 }
1444
1445 static void
1446 mail_folder_cache_init (MailFolderCache *cache)
1447 {
1448 const gchar *buf;
1449 guint timeout;
1450
1451 cache->priv = MAIL_FOLDER_CACHE_GET_PRIVATE (cache);
1452
1453 /* initialize values */
1454 cache->priv->stores = g_hash_table_new (NULL, NULL);
1455 g_static_rec_mutex_init (&cache->priv->stores_mutex);
1456
1457 g_queue_init (&cache->priv->updates);
1458 cache->priv->count_sent = getenv ("EVOLUTION_COUNT_SENT") != NULL;
1459 cache->priv->count_trash = getenv ("EVOLUTION_COUNT_TRASH") != NULL;
1460
1461 buf = getenv ("EVOLUTION_PING_TIMEOUT");
1462 timeout = buf ? strtoul (buf, NULL, 10) : 600;
1463 cache->priv->ping_id = g_timeout_add_seconds (
1464 timeout, (GSourceFunc) ping_cb, cache);
1465
1466 g_queue_init (&cache->priv->local_folder_uris);
1467 g_queue_init (&cache->priv->remote_folder_uris);
1468 }
1469
1470 MailFolderCache *
1471 mail_folder_cache_new (EMailSession *session)
1472 {
1473 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1474
1475 return g_object_new (
1476 MAIL_TYPE_FOLDER_CACHE,
1477 "session", session, NULL);
1478 }
1479
1480 EMailSession *
1481 mail_folder_cache_get_session (MailFolderCache *cache)
1482 {
1483 g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), NULL);
1484
1485 return E_MAIL_SESSION (cache->priv->session);
1486 }
1487
1488 /**
1489 * mail_folder_cache_note_store:
1490 *
1491 * Add a store whose folders should appear in the shell The folders are scanned
1492 * from the store, and/or added at runtime via the folder_created event. The
1493 * @done function returns if we can free folder info.
1494 */
1495 void
1496 mail_folder_cache_note_store (MailFolderCache *cache,
1497 CamelStore *store,
1498 GCancellable *cancellable,
1499 NoteDoneFunc done,
1500 gpointer data)
1501 {
1502 CamelSession *session;
1503 StoreInfo *si;
1504 struct _update_data *ud;
1505 gint hook = 0;
1506
1507 g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
1508 g_return_if_fail (CAMEL_IS_STORE (store));
1509
1510 session = camel_service_get_session (CAMEL_SERVICE (store));
1511
1512 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1513
1514 si = g_hash_table_lookup (cache->priv->stores, store);
1515 if (si == NULL) {
1516 si = store_info_new (store);
1517 g_hash_table_insert (cache->priv->stores, store, si);
1518 hook = TRUE;
1519 }
1520
1521 ud = g_malloc0 (sizeof (*ud));
1522 ud->done = done;
1523 ud->data = data;
1524 ud->cache = cache;
1525
1526 if (G_IS_CANCELLABLE (cancellable))
1527 ud->cancellable = g_object_ref (cancellable);
1528
1529 /* We might get a race when setting up a store, such that it is
1530 * still left in offline mode, after we've gone online. This
1531 * catches and fixes it up when the shell opens us. */
1532 if (CAMEL_IS_DISCO_STORE (store)) {
1533 if (camel_session_get_online (session) &&
1534 camel_disco_store_status (CAMEL_DISCO_STORE (store)) ==
1535 CAMEL_DISCO_STORE_OFFLINE) {
1536 e_mail_store_go_online (
1537 store, G_PRIORITY_DEFAULT, cancellable,
1538 (GAsyncReadyCallback) store_go_online_cb, ud);
1539 } else {
1540 goto normal_setup;
1541 }
1542 } else if (CAMEL_IS_OFFLINE_STORE (store)) {
1543 if (camel_session_get_online (session) &&
1544 !camel_offline_store_get_online (
1545 CAMEL_OFFLINE_STORE (store))) {
1546 e_mail_store_go_online (
1547 store, G_PRIORITY_DEFAULT, cancellable,
1548 (GAsyncReadyCallback) store_go_online_cb, ud);
1549 } else {
1550 goto normal_setup;
1551 }
1552 } else {
1553 normal_setup:
1554 if (store_has_folder_hierarchy (store))
1555 camel_store_get_folder_info (
1556 store, NULL,
1557 CAMEL_STORE_FOLDER_INFO_FAST |
1558 CAMEL_STORE_FOLDER_INFO_RECURSIVE |
1559 CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
1560 G_PRIORITY_DEFAULT, cancellable,
1561 (GAsyncReadyCallback) update_folders, ud);
1562 }
1563
1564 g_queue_push_tail (&si->folderinfo_updates, ud);
1565
1566 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1567
1568 /* there is potential for race here, but it is safe as we check
1569 * for the store anyway */
1570 if (hook) {
1571 g_signal_connect (
1572 store, "folder-opened",
1573 G_CALLBACK (store_folder_opened_cb), cache);
1574 g_signal_connect (
1575 store, "folder-created",
1576 G_CALLBACK (store_folder_created_cb), cache);
1577 g_signal_connect (
1578 store, "folder-deleted",
1579 G_CALLBACK (store_folder_deleted_cb), cache);
1580 g_signal_connect (
1581 store, "folder-renamed",
1582 G_CALLBACK (store_folder_renamed_cb), cache);
1583 }
1584
1585 if (hook && CAMEL_IS_SUBSCRIBABLE (store)) {
1586 g_signal_connect (
1587 store, "folder-subscribed",
1588 G_CALLBACK (store_folder_subscribed_cb), cache);
1589 g_signal_connect (
1590 store, "folder-unsubscribed",
1591 G_CALLBACK (store_folder_unsubscribed_cb), cache);
1592 }
1593 }
1594
1595 /**
1596 * mail_folder_cache_note_folder:
1597 *
1598 * When a folder has been opened, notify it for watching. The folder must have
1599 * already been created on the store (which has already been noted) before the
1600 * folder can be opened
1601 */
1602 void
1603 mail_folder_cache_note_folder (MailFolderCache *cache,
1604 CamelFolder *folder)
1605 {
1606 CamelStore *parent_store;
1607 StoreInfo *si;
1608 struct _folder_info *mfi;
1609 const gchar *full_name;
1610
1611 g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
1612 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1613
1614 full_name = camel_folder_get_full_name (folder);
1615 parent_store = camel_folder_get_parent_store (folder);
1616
1617 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1618 if (cache->priv->stores == NULL
1619 || (si = g_hash_table_lookup (cache->priv->stores, parent_store)) == NULL
1620 || (mfi = g_hash_table_lookup (si->folders, full_name)) == NULL) {
1621 w (g_warning ("Noting folder before store initialised"));
1622 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1623 return;
1624 }
1625
1626 /* dont do anything if we already have this */
1627 if (mfi->folder == folder) {
1628 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1629 return;
1630 }
1631
1632 mfi->folder = folder;
1633
1634 g_object_add_weak_pointer (G_OBJECT (folder), &mfi->folder);
1635
1636 update_1folder (cache, mfi, 0, NULL, NULL, NULL, NULL);
1637
1638 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1639
1640 g_signal_connect (
1641 folder, "changed",
1642 G_CALLBACK (folder_changed_cb), cache);
1643 }
1644
1645 /**
1646 * mail_folder_cache_get_folder_from_uri:
1647 *
1648 * Gets the #CamelFolder for the supplied @uri.
1649 *
1650 * Returns: %TRUE if the URI is available, folderp is set to a reffed
1651 * folder if the folder has also already been opened
1652 */
1653 gboolean
1654 mail_folder_cache_get_folder_from_uri (MailFolderCache *cache,
1655 const gchar *uri,
1656 CamelFolder **folderp)
1657 {
1658 struct _find_info fi = { uri, NULL };
1659
1660 if (cache->priv->stores == NULL)
1661 return FALSE;
1662
1663 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1664 g_hash_table_foreach (
1665 cache->priv->stores, (GHFunc)
1666 storeinfo_find_folder_info, &fi);
1667 if (folderp) {
1668 if (fi.fi && fi.fi->folder)
1669 *folderp = g_object_ref (fi.fi->folder);
1670 else
1671 *folderp = NULL;
1672 }
1673 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1674
1675 return fi.fi != NULL;
1676 }
1677
1678 gboolean
1679 mail_folder_cache_get_folder_info_flags (MailFolderCache *cache,
1680 CamelFolder *folder,
1681 CamelFolderInfoFlags *flags)
1682 {
1683 struct _find_info fi = { NULL, NULL };
1684 gchar *folder_uri;
1685
1686 if (cache->priv->stores == NULL)
1687 return FALSE;
1688
1689 folder_uri = e_mail_folder_uri_from_folder (folder);
1690 fi.folder_uri = folder_uri;
1691
1692 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1693 g_hash_table_foreach (
1694 cache->priv->stores, (GHFunc)
1695 storeinfo_find_folder_info, &fi);
1696 if (flags) {
1697 if (fi.fi)
1698 *flags = fi.fi->flags;
1699 else
1700 *flags = 0;
1701 }
1702 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1703
1704 g_free (folder_uri);
1705
1706 return fi.fi != NULL;
1707 }
1708
1709 /* Returns whether folder 'folder' has children based on folder_info->child property.
1710 * If not found returns FALSE and sets 'found' to FALSE, if not NULL. */
1711 gboolean
1712 mail_folder_cache_get_folder_has_children (MailFolderCache *cache,
1713 CamelFolder *folder,
1714 gboolean *found)
1715 {
1716 struct _find_info fi = { NULL, NULL };
1717 gchar *folder_uri;
1718
1719 g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), FALSE);
1720 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1721
1722 if (cache->priv->stores == NULL)
1723 return FALSE;
1724
1725 folder_uri = e_mail_folder_uri_from_folder (folder);
1726 fi.folder_uri = folder_uri;
1727
1728 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1729 g_hash_table_foreach (
1730 cache->priv->stores, (GHFunc)
1731 storeinfo_find_folder_info, &fi);
1732 if (found != NULL)
1733 *found = fi.fi != NULL;
1734 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1735
1736 g_free (folder_uri);
1737
1738 return fi.fi != NULL && fi.fi->has_children;
1739 }
1740
1741 void
1742 mail_folder_cache_get_local_folder_uris (MailFolderCache *self,
1743 GQueue *out_queue)
1744 {
1745 GList *head, *link;
1746
1747 g_return_if_fail (MAIL_IS_FOLDER_CACHE (self));
1748 g_return_if_fail (out_queue != NULL);
1749
1750 /* Reuse the stores mutex just because it's handy. */
1751 g_static_rec_mutex_lock (&self->priv->stores_mutex);
1752
1753 head = g_queue_peek_head_link (&self->priv->local_folder_uris);
1754
1755 for (link = head; link != NULL; link = g_list_next (link))
1756 g_queue_push_tail (out_queue, g_strdup (link->data));
1757
1758 g_static_rec_mutex_unlock (&self->priv->stores_mutex);
1759 }
1760
1761 void
1762 mail_folder_cache_get_remote_folder_uris (MailFolderCache *self,
1763 GQueue *out_queue)
1764 {
1765 GList *head, *link;
1766
1767 g_return_if_fail (MAIL_IS_FOLDER_CACHE (self));
1768 g_return_if_fail (out_queue != NULL);
1769
1770 /* Reuse the stores mutex just because it's handy. */
1771 g_static_rec_mutex_lock (&self->priv->stores_mutex);
1772
1773 head = g_queue_peek_head_link (&self->priv->remote_folder_uris);
1774
1775 for (link = head; link != NULL; link = g_list_next (link))
1776 g_queue_push_tail (out_queue, g_strdup (link->data));
1777
1778 g_static_rec_mutex_unlock (&self->priv->stores_mutex);
1779 }
1780
1781 void
1782 mail_folder_cache_service_removed (MailFolderCache *cache,
1783 CamelService *service)
1784 {
1785 StoreInfo *si;
1786
1787 g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
1788 g_return_if_fail (CAMEL_IS_SERVICE (service));
1789
1790 if (cache->priv->stores == NULL)
1791 return;
1792
1793 g_static_rec_mutex_lock (&cache->priv->stores_mutex);
1794
1795 si = g_hash_table_lookup (cache->priv->stores, service);
1796 if (si != NULL) {
1797 g_hash_table_remove (cache->priv->stores, service);
1798
1799 g_signal_handlers_disconnect_matched (
1800 service, G_SIGNAL_MATCH_DATA,
1801 0, 0, NULL, NULL, cache);
1802
1803 g_hash_table_foreach (
1804 si->folders, (GHFunc)
1805 unset_folder_info_hash, cache);
1806
1807 store_info_free (si);
1808 }
1809
1810 g_static_rec_mutex_unlock (&cache->priv->stores_mutex);
1811 }
1812
1813 void
1814 mail_folder_cache_service_enabled (MailFolderCache *cache,
1815 CamelService *service)
1816 {
1817 g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
1818 g_return_if_fail (CAMEL_IS_SERVICE (service));
1819
1820 mail_folder_cache_note_store (
1821 cache, CAMEL_STORE (service), NULL, NULL, NULL);
1822 }
1823
1824 void
1825 mail_folder_cache_service_disabled (MailFolderCache *cache,
1826 CamelService *service)
1827 {
1828 g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
1829 g_return_if_fail (CAMEL_IS_SERVICE (service));
1830
1831 /* To the folder cache, disabling a service is the same as
1832 * removing it. We keep a separate callback function only
1833 * to use as a breakpoint target in a debugger. */
1834 mail_folder_cache_service_removed (cache, service);
1835 }