evolution-3.6.4/libemail-engine/mail-folder-cache.c

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)
Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(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)
Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(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)
Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'store')
(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 }