evolution-3.6.4/libemail-engine/mail-vfolder.c

No issues found

   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  *		Michael Zucchi <notzed@ximian.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 
  29 #include <glib/gi18n.h>
  30 
  31 #include "libevolution-utils/e-alert-dialog.h"
  32 
  33 #include "libemail-utils/mail-mt.h"
  34 #include "libemail-engine/mail-folder-cache.h"
  35 #include "libemail-engine/e-mail-folder-utils.h"
  36 #include "libemail-engine/e-mail-session.h"
  37 #include "libemail-engine/e-mail-utils.h"
  38 #include "libemail-engine/mail-ops.h"
  39 #include "libemail-engine/mail-tools.h"
  40 
  41 #include <libemail-utils/em-vfolder-context.h>
  42 #include <libemail-utils/em-vfolder-rule.h>
  43 #include "mail-vfolder.h"
  44 
  45 #define d(x)  /* (printf("%s:%s: ",  G_STRLOC, G_STRFUNC), (x))*/
  46 
  47 /* Note: Once we completely move mail to EDS, this context wont be available for UI. 
  48  * and vfoldertypes.xml should be moved here really. */
  49 EMVFolderContext *context;	/* context remains open all time */
  50 
  51 /* lock for accessing shared resources (below) */
  52 G_LOCK_DEFINE_STATIC (vfolder);
  53 
  54 static GHashTable *vfolder_hash;
  55 /* This is a slightly hacky solution to shutting down, we poll this variable in various
  56  * loops, and just quit processing if it is set. */
  57 static volatile gint vfolder_shutdown;	/* are we shutting down? */
  58 
  59 static void rule_changed (EFilterRule *rule, CamelFolder *folder);
  60 
  61 /* ********************************************************************** */
  62 
  63 static GList *
  64 vfolder_get_include_subfolders_uris (EMailSession *session,
  65                                      const gchar *base_uri,
  66                                      GCancellable *cancellable)
  67 {
  68 	GList *uris = NULL;
  69 	CamelStore *store = NULL;
  70 	gchar *folder_name = NULL;
  71 	CamelFolderInfo *fi;
  72 	const CamelFolderInfo *cur;
  73 
  74 	g_return_val_if_fail (session != NULL, NULL);
  75 	g_return_val_if_fail (base_uri != NULL, NULL);
  76 	g_return_val_if_fail (*base_uri == '*', NULL);
  77 
  78 	if (!e_mail_folder_uri_parse (CAMEL_SESSION (session), base_uri + 1, &store, &folder_name, NULL))
  79 		return NULL;
  80 
  81 	fi = camel_store_get_folder_info_sync (
  82 		store, folder_name,
  83 		CAMEL_STORE_FOLDER_INFO_RECURSIVE, cancellable, NULL);
  84 	cur = fi;
  85 	while (cur) {
  86 		if ((cur->flags & CAMEL_FOLDER_NOSELECT) == 0) {
  87 			gchar *fi_uri = e_mail_folder_uri_build (store, cur->full_name);
  88 
  89 			if (fi_uri)
  90 				uris = g_list_prepend (uris, fi_uri);
  91 		}
  92 
  93 		/* move to the next fi */
  94 		if (cur->child) {
  95 			cur = cur->child;
  96 		} else if (cur->next) {
  97 			cur = cur->next;
  98 		} else {
  99 			while (cur && !cur->next) {
 100 				cur = cur->parent;
 101 			}
 102 
 103 			if (cur)
 104 				cur = cur->next;
 105 		}
 106 	}
 107 
 108 	if (fi)
 109 		camel_store_free_folder_info (store, fi);
 110 
 111 	g_object_unref (store);
 112 	g_free (folder_name);
 113 
 114 	return g_list_reverse (uris);
 115 }
 116 
 117 struct _setup_msg {
 118 	MailMsg base;
 119 
 120 	EMailSession *session;
 121 	CamelFolder *folder;
 122 	gchar *query;
 123 	GList *sources_uri;
 124 };
 125 
 126 static gchar *
 127 vfolder_setup_desc (struct _setup_msg *m)
 128 {
 129 	return g_strdup_printf (
 130 		_("Setting up Search Folder: %s"),
 131 		camel_folder_get_full_name (m->folder));
 132 }
 133 
 134 static void
 135 vfolder_setup_exec (struct _setup_msg *m,
 136                     GCancellable *cancellable,
 137                     GError **error)
 138 {
 139 	GList *l, *list = NULL;
 140 	CamelFolder *folder;
 141 
 142 	camel_vee_folder_set_expression ((CamelVeeFolder *) m->folder, m->query);
 143 
 144 	for (l = m->sources_uri;
 145 	     l && !vfolder_shutdown && !g_cancellable_is_cancelled (cancellable);
 146 	     l = l->next) {
 147 		const gchar *uri = l->data;
 148 
 149 		d (printf (" Adding uri: %s\n", uri));
 150 
 151 		if (!uri || !*uri || !uri[1])
 152 			continue;
 153 
 154 		if (*uri == '*') {
 155 			/* include folder and its subfolders */
 156 			GList *uris, *iter;
 157 
 158 			uris = vfolder_get_include_subfolders_uris (m->session, uri, cancellable);
 159 			for (iter = uris; iter; iter = iter->next) {
 160 				const gchar *fi_uri = iter->data;
 161 
 162 				folder = e_mail_session_uri_to_folder_sync (
 163 					m->session, fi_uri, 0, cancellable, NULL);
 164 				if (folder != NULL)
 165 					list = g_list_append (list, folder);
 166 			}
 167 
 168 			g_list_free_full (uris, g_free);
 169 		} else {
 170 			folder = e_mail_session_uri_to_folder_sync (m->session, l->data, 0, cancellable, NULL);
 171 			if (folder != NULL)
 172 				list = g_list_append (list, folder);
 173 		}
 174 	}
 175 
 176 	if (!vfolder_shutdown && !g_cancellable_is_cancelled (cancellable))
 177 		camel_vee_folder_set_folders ((CamelVeeFolder *) m->folder, list, cancellable);
 178 
 179 	g_list_free_full (list, g_object_unref);
 180 }
 181 
 182 static void
 183 vfolder_setup_done (struct _setup_msg *m)
 184 {
 185 }
 186 
 187 static void
 188 vfolder_setup_free (struct _setup_msg *m)
 189 {
 190 	camel_folder_thaw (m->folder);
 191 
 192 	g_object_unref (m->session);
 193 	g_object_unref (m->folder);
 194 	g_free (m->query);
 195 	g_list_free_full (m->sources_uri, g_free);
 196 }
 197 
 198 static MailMsgInfo vfolder_setup_info = {
 199 	sizeof (struct _setup_msg),
 200 	(MailMsgDescFunc) vfolder_setup_desc,
 201 	(MailMsgExecFunc) vfolder_setup_exec,
 202 	(MailMsgDoneFunc) vfolder_setup_done,
 203 	(MailMsgFreeFunc) vfolder_setup_free
 204 };
 205 
 206 /* sources_uri should be camel uri's */
 207 static gint
 208 vfolder_setup (EMailSession *session,
 209                CamelFolder *folder,
 210                const gchar *query,
 211                GList *sources_uri)
 212 {
 213 	struct _setup_msg *m;
 214 	gint id;
 215 
 216 	m = mail_msg_new (&vfolder_setup_info);
 217 	m->session = g_object_ref (session);
 218 	m->folder = g_object_ref (folder);
 219 	m->query = g_strdup (query);
 220 	m->sources_uri = sources_uri;
 221 
 222 	camel_folder_freeze (m->folder);
 223 
 224 	id = m->base.seq;
 225 	mail_msg_slow_ordered_push (m);
 226 
 227 	return id;
 228 }
 229 
 230 /* ********************************************************************** */
 231 
 232 static void
 233 vfolder_add_remove_one (GList *vfolders,
 234                         gboolean remove,
 235                         CamelFolder *folder,
 236                         GCancellable *cancellable)
 237 {
 238 	GList *iter;
 239 
 240 	for (iter = vfolders; iter && !vfolder_shutdown; iter = iter->next) {
 241 		CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (iter->data);
 242 
 243 		if (!vfolder)
 244 			continue;
 245 
 246 		if (remove)
 247 			camel_vee_folder_remove_folder (vfolder, folder, cancellable);
 248 		else
 249 			camel_vee_folder_add_folder (vfolder, folder, cancellable);
 250 	}
 251 }
 252 
 253 struct _adduri_msg {
 254 	MailMsg base;
 255 
 256 	EMailSession *session;
 257 	gchar *uri;
 258 	GList *folders;
 259 	gint remove;
 260 };
 261 
 262 static gchar *
 263 vfolder_adduri_desc (struct _adduri_msg *m)
 264 {
 265 	CamelStore *store;
 266 	CamelService *service;
 267 	const gchar *display_name;
 268 	gchar *folder_name;
 269 	gchar *description;
 270 	gboolean success;
 271 
 272 	success = e_mail_folder_uri_parse (
 273 		CAMEL_SESSION (m->session), m->uri,
 274 		&store, &folder_name, NULL);
 275 
 276 	if (!success)
 277 		return NULL;
 278 
 279 	service = CAMEL_SERVICE (store);
 280 	display_name = camel_service_get_display_name (service);
 281 
 282 	description = g_strdup_printf (
 283 		_("Updating Search Folders for '%s' - %s"),
 284 		display_name, folder_name);
 285 
 286 	g_object_unref (store);
 287 	g_free (folder_name);
 288 
 289 	return description;
 290 }
 291 
 292 static void
 293 vfolder_adduri_exec (struct _adduri_msg *m,
 294                      GCancellable *cancellable,
 295                      GError **error)
 296 {
 297 	CamelFolder *folder = NULL;
 298 	MailFolderCache *folder_cache;
 299 
 300 	if (vfolder_shutdown)
 301 		return;
 302 
 303 	folder_cache = e_mail_session_get_folder_cache (m->session);
 304 
 305 	/* we dont try lookup the cache if we are removing it, its no longer there */
 306 
 307 	if (!m->remove &&
 308 	    !mail_folder_cache_get_folder_from_uri (folder_cache, m->uri[0] == '*' ? m->uri + 1 : m->uri, NULL)) {
 309 		g_warning (
 310 			"Folder '%s' disappeared while I was "
 311 			"adding/removing it to/from my vfolder", m->uri);
 312 		return;
 313 	}
 314 
 315 	if (m->uri[0] == '*') {
 316 		GList *uris, *iter;
 317 
 318 		uris = vfolder_get_include_subfolders_uris (m->session, m->uri, cancellable);
 319 		for (iter = uris; iter; iter = iter->next) {
 320 			const gchar *fi_uri = iter->data;
 321 
 322 			folder = e_mail_session_uri_to_folder_sync (
 323 				m->session, fi_uri, 0, cancellable, NULL);
 324 			if (folder != NULL) {
 325 				vfolder_add_remove_one (m->folders, m->remove, folder, cancellable);
 326 				g_object_unref (folder);
 327 			}
 328 		}
 329 
 330 		g_list_free_full (uris, g_free);
 331 	} else {
 332 		/* always pick fresh folders - they are
 333 		 * from CamelStore's folders bag anyway */
 334 		folder = e_mail_session_uri_to_folder_sync (
 335 			m->session, m->uri, 0, cancellable, error);
 336 
 337 		if (folder != NULL) {
 338 			vfolder_add_remove_one (m->folders, m->remove, folder, cancellable);
 339 			g_object_unref (folder);
 340 		}
 341 	}
 342 }
 343 
 344 static void
 345 vfolder_adduri_done (struct _adduri_msg *m)
 346 {
 347 }
 348 
 349 static void
 350 vfolder_adduri_free (struct _adduri_msg *m)
 351 {
 352 	g_object_unref (m->session);
 353 	g_list_foreach (m->folders, (GFunc) camel_folder_thaw, NULL);
 354 	g_list_free_full (m->folders, g_object_unref);
 355 	g_free (m->uri);
 356 }
 357 
 358 static MailMsgInfo vfolder_adduri_info = {
 359 	sizeof (struct _adduri_msg),
 360 	(MailMsgDescFunc) vfolder_adduri_desc,
 361 	(MailMsgExecFunc) vfolder_adduri_exec,
 362 	(MailMsgDoneFunc) vfolder_adduri_done,
 363 	(MailMsgFreeFunc) vfolder_adduri_free
 364 };
 365 
 366 /* uri should be a camel uri */
 367 static gint
 368 vfolder_adduri (EMailSession *session,
 369                 const gchar *uri,
 370                 GList *folders,
 371                 gint remove)
 372 {
 373 	struct _adduri_msg *m;
 374 	gint id;
 375 
 376 	m = mail_msg_new (&vfolder_adduri_info);
 377 	m->session = g_object_ref (session);
 378 	m->folders = folders;
 379 	m->uri = g_strdup (uri);
 380 	m->remove = remove;
 381 
 382 	g_list_foreach (m->folders, (GFunc) camel_folder_freeze, NULL);
 383 
 384 	id = m->base.seq;
 385 	mail_msg_slow_ordered_push (m);
 386 
 387 	return id;
 388 }
 389 
 390 /* ********************************************************************** */
 391 
 392 /* so special we never use it */
 393 static gint
 394 folder_is_spethal (CamelStore *store,
 395                    const gchar *folder_name)
 396 {
 397 	/* This is a bit of a hack, but really the only way it can be done
 398 	 * at the moment. */
 399 
 400 	if (store->flags & CAMEL_STORE_VTRASH)
 401 		if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
 402 			return TRUE;
 403 
 404 	if (store->flags & CAMEL_STORE_VJUNK)
 405 		if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
 406 			return TRUE;
 407 
 408 	return FALSE;
 409 }
 410 
 411 /**
 412  * mail_vfolder_add_folder:
 413  * @store: a #CamelStore
 414  * @folder: a folder name
 415  * @remove: whether the folder should be removed or added
 416  *
 417  * Called when a new folder becomes (un)available.  If @store is not a
 418  * CamelVeeStore, the folder is added/removed from the list of cached source
 419  * folders.  Then each vfolder rule is checked to see if the specified folder
 420  * matches a source of the rule.  It builds a list of vfolders that use (or
 421  * would use) the specified folder as a source.  It then adds (or removes)
 422  * this folder to (from) those vfolders via camel_vee_folder_add/
 423  * remove_folder() but does not modify the actual filters or write changes
 424  * to disk.
 425  *
 426  * NOTE: This function must be called from the main thread.
 427  */
 428 static void
 429 mail_vfolder_add_folder (CamelStore *store,
 430                          const gchar *folder_name,
 431                          gint remove)
 432 {
 433 	CamelService *service;
 434 	CamelSession *session;
 435 	EFilterRule *rule;
 436 	EMVFolderRule *vrule;
 437 	const gchar *source;
 438 	CamelVeeFolder *vf;
 439 	CamelProvider *provider;
 440 	GList *folders = NULL, *folders_include_subfolders = NULL;
 441 	gint remote;
 442 	gchar *uri;
 443 
 444 	g_return_if_fail (CAMEL_IS_STORE (store));
 445 	g_return_if_fail (folder_name != NULL);
 446 
 447 	service = CAMEL_SERVICE (store);
 448 	session = camel_service_get_session (service);
 449 	provider = camel_service_get_provider (service);
 450 
 451 	remote = (provider->flags & CAMEL_PROVIDER_IS_REMOTE) != 0;
 452 
 453 	if (folder_is_spethal (store, folder_name))
 454 		return;
 455 
 456 	g_return_if_fail (mail_in_main_thread ());
 457 
 458 	uri = e_mail_folder_uri_build (store, folder_name);
 459 
 460 	G_LOCK (vfolder);
 461 
 462 	if (context == NULL)
 463 		goto done;
 464 
 465 	rule = NULL;
 466 	while ((rule = e_rule_context_next_rule ((ERuleContext *) context, rule, NULL))) {
 467 		gint found = FALSE;
 468 
 469 		if (!rule->name) {
 470 			d (printf ("invalid rule (%p): rule->name is set to NULL\n", rule));
 471 			continue;
 472 		}
 473 
 474 		vrule = (EMVFolderRule *) rule;
 475 
 476 		/* Don't auto-add any sent/drafts folders etc,
 477 		 * they must be explictly listed as a source. */
 478 		if (rule->source
 479 		    && !CAMEL_IS_VEE_STORE (store)
 480 		    && ((em_vfolder_rule_get_with (vrule) == EM_VFOLDER_RULE_WITH_LOCAL && !remote)
 481 			|| (em_vfolder_rule_get_with (vrule) == EM_VFOLDER_RULE_WITH_REMOTE_ACTIVE && remote)
 482 			|| (em_vfolder_rule_get_with (vrule) == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)))
 483 			found = TRUE;
 484 
 485 		source = NULL;
 486 		while (!found && (source = em_vfolder_rule_next_source (vrule, source))) {
 487 			found = e_mail_folder_uri_equal (session, uri, source);
 488 		}
 489 
 490 		if (found) {
 491 			vf = g_hash_table_lookup (vfolder_hash, rule->name);
 492 			if (!vf) {
 493 				g_warning ("vf is NULL for %s\n", rule->name);
 494 				continue;
 495 			}
 496 			g_object_ref (vf);
 497 
 498 			if (em_vfolder_rule_source_get_include_subfolders (vrule, uri))
 499 				folders_include_subfolders = g_list_prepend (folders_include_subfolders, vf);
 500 			else
 501 				folders = g_list_prepend (folders, vf);
 502 		}
 503 	}
 504 
 505 done:
 506 	G_UNLOCK (vfolder);
 507 
 508 	if (folders != NULL)
 509 		vfolder_adduri (
 510 			E_MAIL_SESSION (session),
 511 			uri, folders, remove);
 512 
 513 	if (folders_include_subfolders) {
 514 		gchar *exuri = g_strconcat ("*", uri, NULL);
 515 
 516 		vfolder_adduri (
 517 			E_MAIL_SESSION (session),
 518 			exuri, folders_include_subfolders, remove);
 519 
 520 		g_free (exuri);
 521 	}
 522 
 523 	g_free (uri);
 524 }
 525 
 526 /**
 527  * mail_vfolder_delete_folder:
 528  * @store: a #CamelStore
 529  * @folder_name: a folder name
 530  *
 531  * Looks through all vfolder rules to see if @folder_name is listed as a
 532  * source for any vfolder rules.  If the folder is found in the source for
 533  * any rule, it is removed and the user is alerted to the fact that the
 534  * vfolder rules have been updated.  The new vfolder rules are written
 535  * to disk.
 536  *
 537  * XXX: It doesn't appear that the changes to the vfolder rules are sent
 538  * down to the camel level, however. So the actual vfolders will not change
 539  * behavior until evolution is restarted (?)
 540  *
 541  * NOTE: This function must be called from the main thread.
 542  */
 543 static void
 544 mail_vfolder_delete_folder (CamelStore *store,
 545                             const gchar *folder_name)
 546 {
 547 	ERuleContext *rule_context;
 548 	EFilterRule *rule;
 549 	CamelService *service;
 550 	CamelSession *session;
 551 	const gchar *source;
 552 	CamelVeeFolder *vf;
 553 	GString *changed;
 554 	guint changed_count;
 555 	gchar *uri;
 556 
 557 	g_return_if_fail (CAMEL_IS_STORE (store));
 558 	g_return_if_fail (folder_name != NULL);
 559 
 560 	if (folder_is_spethal (store, folder_name))
 561 		return;
 562 
 563 	d (printf ("Deleting uri to check: %s\n", uri));
 564 
 565 	g_return_if_fail (mail_in_main_thread ());
 566 
 567 	service = CAMEL_SERVICE (store);
 568 	session = camel_service_get_session (service);
 569 
 570 	uri = e_mail_folder_uri_build (store, folder_name);
 571 
 572 	changed_count = 0;
 573 	changed = g_string_new ("");
 574 
 575 	G_LOCK (vfolder);
 576 
 577 	if (context == NULL)
 578 		goto done;
 579 
 580 	rule_context = E_RULE_CONTEXT (context);
 581 
 582 	/* see if any rules directly reference this removed uri */
 583 	rule = NULL;
 584 	while ((rule = e_rule_context_next_rule (rule_context, rule, NULL))) {
 585 		EMVFolderRule *vf_rule = EM_VFOLDER_RULE (rule);
 586 
 587 		if (!rule->name)
 588 			continue;
 589 
 590 		source = NULL;
 591 		while ((source = em_vfolder_rule_next_source (vf_rule, source))) {
 592 			/* Remove all sources that match, ignore changed events though
 593 			 * because the adduri call above does the work async */
 594 			if (e_mail_folder_uri_equal (session, uri, source)) {
 595 				vf = g_hash_table_lookup (
 596 					vfolder_hash, rule->name);
 597 
 598 				if (!vf) {
 599 					g_warning ("vf is NULL for %s\n", rule->name);
 600 					continue;
 601 				}
 602 
 603 				g_signal_handlers_disconnect_matched (
 604 					rule, G_SIGNAL_MATCH_FUNC |
 605 					G_SIGNAL_MATCH_DATA, 0, 0, NULL,
 606 					rule_changed, vf);
 607 
 608 				em_vfolder_rule_remove_source (vf_rule, source);
 609 
 610 				g_signal_connect (
 611 					rule, "changed",
 612 					G_CALLBACK (rule_changed), vf);
 613 
 614 				if (changed_count == 0) {
 615 					g_string_append (changed, rule->name);
 616 				} else {
 617 					if (changed_count == 1) {
 618 						g_string_prepend (changed, "    ");
 619 						g_string_append (changed, "\n");
 620 					}
 621 					g_string_append_printf (
 622 						changed, "    %s\n",
 623 						rule->name);
 624 				}
 625 
 626 				changed_count++;
 627 				source = NULL;
 628 			}
 629 		}
 630 	}
 631 
 632 done:
 633 	G_UNLOCK (vfolder);
 634 
 635 	if (changed_count > 0) {
 636 		EAlertSink *alert_sink;
 637 		const gchar *config_dir;
 638 		gchar *user, *info;
 639 
 640 		alert_sink = mail_msg_get_alert_sink ();
 641 
 642 		info = g_strdup_printf (ngettext (
 643 			/* Translators: The first %s is name of the affected
 644 			 * search folder(s), the second %s is the URI of the
 645 			 * removed folder. For more than one search folder is
 646 			 * each of them on a separate line, with four spaces
 647 			 * in front of its name, without quotes. */
 648 			"The Search Folder \"%s\" has been modified to "
 649 			"account for the deleted folder\n\"%s\".",
 650 			"The following Search Folders\n%s have been modified "
 651 			"to account for the deleted folder\n\"%s\".",
 652 			changed_count), changed->str, uri);
 653 		e_alert_submit (
 654 			alert_sink, "mail:vfolder-updated", info, NULL);
 655 		g_free (info);
 656 
 657 		config_dir = mail_session_get_config_dir ();
 658 		user = g_build_filename (config_dir, "vfolders.xml", NULL);
 659 		e_rule_context_save ((ERuleContext *) context, user);
 660 		g_free (user);
 661 	}
 662 
 663 	g_string_free (changed, TRUE);
 664 
 665 	g_free (uri);
 666 }
 667 
 668 /* called when a uri is renamed in a store */
 669 static void
 670 mail_vfolder_rename_folder (CamelStore *store,
 671                             const gchar *old_folder_name,
 672                             const gchar *new_folder_name)
 673 {
 674 	ERuleContext *rule_context;
 675 	EFilterRule *rule;
 676 	const gchar *source;
 677 	CamelVeeFolder *vf;
 678 	CamelService *service;
 679 	CamelSession *session;
 680 	gint changed = 0;
 681 	gchar *old_uri;
 682 	gchar *new_uri;
 683 
 684 	d (printf ("vfolder rename uri: %s to %s\n", cfrom, cto));
 685 
 686 	if (context == NULL)
 687 		return;
 688 
 689 	if (folder_is_spethal (store, old_folder_name))
 690 		return;
 691 
 692 	if (folder_is_spethal (store, new_folder_name))
 693 		return;
 694 
 695 	g_return_if_fail (mail_in_main_thread ());
 696 
 697 	service = CAMEL_SERVICE (store);
 698 	session = camel_service_get_session (service);
 699 
 700 	old_uri = e_mail_folder_uri_build (store, old_folder_name);
 701 	new_uri = e_mail_folder_uri_build (store, new_folder_name);
 702 
 703 	G_LOCK (vfolder);
 704 
 705 	rule_context = E_RULE_CONTEXT (context);
 706 
 707 	/* see if any rules directly reference this removed uri */
 708 	rule = NULL;
 709 	while ((rule = e_rule_context_next_rule (rule_context, rule, NULL))) {
 710 		EMVFolderRule *vf_rule = EM_VFOLDER_RULE (rule);
 711 
 712 		source = NULL;
 713 		while ((source = em_vfolder_rule_next_source (vf_rule, source))) {
 714 			/* Remove all sources that match, ignore changed events though
 715 			 * because the adduri call above does the work async */
 716 			if (e_mail_folder_uri_equal (session, old_uri, source)) {
 717 				vf = g_hash_table_lookup (vfolder_hash, rule->name);
 718 				if (!vf) {
 719 					g_warning ("vf is NULL for %s\n", rule->name);
 720 					continue;
 721 				}
 722 
 723 				g_signal_handlers_disconnect_matched (
 724 					rule, G_SIGNAL_MATCH_FUNC |
 725 					G_SIGNAL_MATCH_DATA, 0, 0, NULL,
 726 					rule_changed, vf);
 727 
 728 				em_vfolder_rule_remove_source (vf_rule, source);
 729 				em_vfolder_rule_add_source (vf_rule, new_uri);
 730 
 731 				g_signal_connect (
 732 					vf_rule, "changed",
 733 					G_CALLBACK (rule_changed), vf);
 734 
 735 				changed++;
 736 				source = NULL;
 737 			}
 738 		}
 739 	}
 740 
 741 	G_UNLOCK (vfolder);
 742 
 743 	if (changed) {
 744 		const gchar *config_dir;
 745 		gchar *user;
 746 
 747 		d (printf ("Vfolders updated from renamed folder\n"));
 748 		config_dir = mail_session_get_config_dir ();
 749 		user = g_build_filename (config_dir, "vfolders.xml", NULL);
 750 		e_rule_context_save ((ERuleContext *) context, user);
 751 		g_free (user);
 752 	}
 753 
 754 	g_free (old_uri);
 755 	g_free (new_uri);
 756 }
 757 
 758 /* ********************************************************************** */
 759 
 760 static void context_rule_added (ERuleContext *ctx, EFilterRule *rule, EMailSession *session);
 761 
 762 static void
 763 rule_add_sources (EMailSession *session,
 764                   GQueue *queue,
 765                   GList **sources_urip,
 766                   EMVFolderRule *rule)
 767 {
 768 	GList *sources_uri = *sources_urip;
 769 	MailFolderCache *folder_cache;
 770 	GList *head, *link;
 771 
 772 	folder_cache = e_mail_session_get_folder_cache (session);
 773 
 774 	head = g_queue_peek_head_link (queue);
 775 	for (link = head; link != NULL; link = g_list_next (link)) {
 776 		const gchar *uri = link->data;
 777 
 778 		/* always pick fresh folders - they are
 779 		 * from CamelStore's folders bag anyway */
 780 		if (mail_folder_cache_get_folder_from_uri (folder_cache, uri, NULL)) {
 781 			/* "tag" uris with subfolders with a star prefix */
 782 			if (!rule || !em_vfolder_rule_source_get_include_subfolders (rule, uri))
 783 				sources_uri = g_list_prepend (sources_uri, g_strdup (uri));
 784 			else
 785 				sources_uri = g_list_prepend (sources_uri, g_strconcat ("*", uri, NULL));
 786 		}
 787 	}
 788 
 789 	*sources_urip = sources_uri;
 790 }
 791 
 792 static EMailSession *
 793 get_session (CamelFolder *folder)
 794 {
 795 	CamelStore *store;
 796 
 797 	store = camel_folder_get_parent_store (folder);
 798 
 799 	return (EMailSession *) camel_service_get_session (CAMEL_SERVICE (store));
 800 }
 801 
 802 static void
 803 rule_changed (EFilterRule *rule,
 804               CamelFolder *folder)
 805 {
 806 	EMailSession *session;
 807 	CamelService *service;
 808 	GList *sources_uri = NULL;
 809 	GString *query;
 810 	const gchar *full_name;
 811 
 812 	full_name = camel_folder_get_full_name (folder);
 813 	session = get_session (folder);
 814 
 815 	service = camel_session_ref_service (
 816 		CAMEL_SESSION (session), E_MAIL_SESSION_VFOLDER_UID);
 817 	g_return_if_fail (service != NULL);
 818 
 819 	/* If the folder has changed name, then
 820 	 * add it, then remove the old manually. */
 821 	if (strcmp (full_name, rule->name) != 0) {
 822 		gchar *oldname;
 823 
 824 		gpointer key;
 825 		gpointer oldfolder;
 826 
 827 		G_LOCK (vfolder);
 828 		if (g_hash_table_lookup_extended (
 829 				vfolder_hash, full_name, &key, &oldfolder)) {
 830 			g_hash_table_remove (vfolder_hash, key);
 831 			g_free (key);
 832 			g_hash_table_insert (
 833 				vfolder_hash, g_strdup (rule->name), folder);
 834 			G_UNLOCK (vfolder);
 835 		} else {
 836 			G_UNLOCK (vfolder);
 837 			g_warning (
 838 				"couldn't find a vfolder rule "
 839 				"in our table? %s", full_name);
 840 		}
 841 
 842 		oldname = g_strdup (full_name);
 843 		/* FIXME Not passing a GCancellable or GError. */
 844 		camel_store_rename_folder_sync (
 845 			CAMEL_STORE (service),
 846 			oldname, rule->name, NULL, NULL);
 847 		g_free (oldname);
 848 	}
 849 
 850 	g_object_unref (service);
 851 	service = NULL;
 852 
 853 	d (printf ("Filter rule changed? for folder '%s'!!\n", folder->name));
 854 
 855 	camel_vee_folder_set_auto_update (
 856 		CAMEL_VEE_FOLDER (folder),
 857 		em_vfolder_rule_get_autoupdate ((EMVFolderRule *) rule));
 858 
 859 	if (em_vfolder_rule_get_with ((EMVFolderRule *) rule) == EM_VFOLDER_RULE_WITH_SPECIFIC) {
 860 		/* find any (currently available) folders, and add them to the ones to open */
 861 		rule_add_sources (
 862 			session, em_vfolder_rule_get_sources ((EMVFolderRule *) rule),
 863 			&sources_uri, (EMVFolderRule *) rule);
 864 	}
 865 
 866 	G_LOCK (vfolder);
 867 
 868 	if (em_vfolder_rule_get_with ((EMVFolderRule *) rule) == EM_VFOLDER_RULE_WITH_LOCAL ||
 869 	    em_vfolder_rule_get_with ((EMVFolderRule *) rule) == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE) {
 870 
 871 		MailFolderCache *cache;
 872 		GQueue queue = G_QUEUE_INIT;
 873 
 874 		cache = e_mail_session_get_folder_cache (session);
 875 		mail_folder_cache_get_local_folder_uris (cache, &queue);
 876 
 877 		rule_add_sources (session, &queue, &sources_uri, NULL);
 878 
 879 		while (!g_queue_is_empty (&queue))
 880 			g_free (g_queue_pop_head (&queue));
 881 	}
 882 
 883 	if (em_vfolder_rule_get_with ((EMVFolderRule *) rule) == EM_VFOLDER_RULE_WITH_REMOTE_ACTIVE ||
 884 	    em_vfolder_rule_get_with ((EMVFolderRule *) rule) == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE) {
 885 
 886 		MailFolderCache *cache;
 887 		GQueue queue = G_QUEUE_INIT;
 888 
 889 		cache = e_mail_session_get_folder_cache (session);
 890 		mail_folder_cache_get_remote_folder_uris (cache, &queue);
 891 
 892 		rule_add_sources (session, &queue, &sources_uri, NULL);
 893 
 894 		while (!g_queue_is_empty (&queue))
 895 			g_free (g_queue_pop_head (&queue));
 896 	}
 897 
 898 	G_UNLOCK (vfolder);
 899 
 900 	query = g_string_new ("");
 901 	e_filter_rule_build_code (rule, query);
 902 
 903 	vfolder_setup (session, folder, query->str, sources_uri);
 904 
 905 	g_string_free (query, TRUE);
 906 }
 907 
 908 static void
 909 context_rule_added (ERuleContext *ctx,
 910                     EFilterRule *rule,
 911                     EMailSession *session)
 912 {
 913 	CamelFolder *folder;
 914 	CamelService *service;
 915 
 916 	d (printf ("rule added: %s\n", rule->name));
 917 
 918 	service = camel_session_ref_service (
 919 		CAMEL_SESSION (session), E_MAIL_SESSION_VFOLDER_UID);
 920 	g_return_if_fail (service != NULL);
 921 
 922 	/* this always runs quickly */
 923 	/* FIXME Not passing a GCancellable or GError. */
 924 	folder = camel_store_get_folder_sync (
 925 		CAMEL_STORE (service), rule->name, 0, NULL, NULL);
 926 	if (folder) {
 927 		g_signal_connect (
 928 			rule, "changed",
 929 			G_CALLBACK (rule_changed), folder);
 930 
 931 		G_LOCK (vfolder);
 932 		g_hash_table_insert (vfolder_hash, g_strdup (rule->name), folder);
 933 		G_UNLOCK (vfolder);
 934 
 935 		rule_changed (rule, folder);
 936 	}
 937 
 938 	g_object_unref (service);
 939 }
 940 
 941 static void
 942 context_rule_removed (ERuleContext *ctx,
 943                       EFilterRule *rule,
 944                       EMailSession *session)
 945 {
 946 	CamelService *service;
 947 	gpointer key, folder = NULL;
 948 
 949 	d (printf ("rule removed; %s\n", rule->name));
 950 
 951 	service = camel_session_ref_service (
 952 		CAMEL_SESSION (session), E_MAIL_SESSION_VFOLDER_UID);
 953 	g_return_if_fail (service != NULL);
 954 
 955 	/* TODO: remove from folder info cache? */
 956 
 957 	G_LOCK (vfolder);
 958 	if (g_hash_table_lookup_extended (vfolder_hash, rule->name, &key, &folder)) {
 959 		g_hash_table_remove (vfolder_hash, key);
 960 		g_free (key);
 961 	}
 962 	G_UNLOCK (vfolder);
 963 
 964 	/* FIXME Not passing a GCancellable  or GError. */
 965 	camel_store_delete_folder_sync (
 966 		CAMEL_STORE (service), rule->name, NULL, NULL);
 967 	/* this must be unref'd after its deleted */
 968 	if (folder)
 969 		g_object_unref ((CamelFolder *) folder);
 970 
 971 	g_object_unref (service);
 972 }
 973 
 974 static void
 975 store_folder_deleted_cb (CamelStore *store,
 976                          CamelFolderInfo *info)
 977 {
 978 	EFilterRule *rule;
 979 	gchar *user;
 980 
 981 	d (printf ("Folder deleted: %s\n", info->name));
 982 
 983 	/* Unmatched folder doesn't have any rule */
 984 	if (g_strcmp0 (CAMEL_UNMATCHED_NAME, info->full_name) == 0)
 985 		return;
 986 
 987 	/* Warning not thread safe, but might be enough */
 988 	G_LOCK (vfolder);
 989 
 990 	/* delete it from our list */
 991 	rule = e_rule_context_find_rule ((ERuleContext *) context, info->full_name, NULL);
 992 	if (rule) {
 993 		const gchar *config_dir;
 994 		EMailSession *session = E_MAIL_SESSION (camel_service_get_session (CAMEL_SERVICE (store)));
 995 
 996 		/* We need to stop listening to removed events,
 997 		 * otherwise we'll try and remove it again. */
 998 		g_signal_handlers_disconnect_matched (
 999 			context, G_SIGNAL_MATCH_FUNC,
1000 			0, 0, NULL, context_rule_removed, NULL);
1001 		e_rule_context_remove_rule ((ERuleContext *) context, rule);
1002 		g_object_unref (rule);
1003 		g_signal_connect (
1004 			context, "rule_removed",
1005 			G_CALLBACK (context_rule_removed), session);
1006 
1007 		config_dir = mail_session_get_config_dir ();
1008 		user = g_build_filename (config_dir, "vfolders.xml", NULL);
1009 		e_rule_context_save ((ERuleContext *) context, user);
1010 		g_free (user);
1011 	} else {
1012 		g_warning (
1013 			"Cannot find rule for deleted vfolder '%s'",
1014 			info->display_name);
1015 	}
1016 
1017 	G_UNLOCK (vfolder);
1018 }
1019 
1020 static void
1021 store_folder_renamed_cb (CamelStore *store,
1022                          const gchar *old_name,
1023                          CamelFolderInfo *info)
1024 {
1025 	EFilterRule *rule;
1026 	gchar *user;
1027 
1028 	gpointer key, folder;
1029 
1030 	/* This should be more-or-less thread-safe */
1031 
1032 	d (printf ("Folder renamed to '%s' from '%s'\n", info->full_name, old_name));
1033 
1034 	/* Folder is already renamed? */
1035 	G_LOCK (vfolder);
1036 	d (printf ("Changing folder name in hash table to '%s'\n", info->full_name));
1037 	if (g_hash_table_lookup_extended (vfolder_hash, old_name, &key, &folder)) {
1038 		const gchar *config_dir;
1039 
1040 		g_hash_table_remove (vfolder_hash, key);
1041 		g_free (key);
1042 		g_hash_table_insert (vfolder_hash, g_strdup (info->full_name), folder);
1043 
1044 		rule = e_rule_context_find_rule ((ERuleContext *) context, old_name, NULL);
1045 		if (!rule) {
1046 			G_UNLOCK (vfolder);
1047 			g_warning ("Rule shouldn't be NULL\n");
1048 			return;
1049 		}
1050 
1051 		g_signal_handlers_disconnect_matched (
1052 			rule, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
1053 			0, 0, NULL, rule_changed, folder);
1054 		e_filter_rule_set_name (rule, info->full_name);
1055 		g_signal_connect (
1056 			rule, "changed",
1057 			G_CALLBACK (rule_changed), folder);
1058 
1059 		config_dir = mail_session_get_config_dir ();
1060 		user = g_build_filename (config_dir, "vfolders.xml", NULL);
1061 		e_rule_context_save ((ERuleContext *) context, user);
1062 		g_free (user);
1063 
1064 		G_UNLOCK (vfolder);
1065 	} else {
1066 		G_UNLOCK (vfolder);
1067 		g_warning ("couldn't find a vfolder rule in our table? %s", info->full_name);
1068 	}
1069 }
1070 
1071 static void
1072 folder_available_cb (MailFolderCache *cache,
1073                      CamelStore *store,
1074                      const gchar *folder_name)
1075 {
1076 	mail_vfolder_add_folder (store, folder_name, FALSE);
1077 }
1078 
1079 static void
1080 folder_unavailable_cb (MailFolderCache *cache,
1081                        CamelStore *store,
1082                        const gchar *folder_name)
1083 {
1084 	mail_vfolder_add_folder (store, folder_name, TRUE);
1085 }
1086 
1087 static void
1088 folder_deleted_cb (MailFolderCache *cache,
1089                    CamelStore *store,
1090                    const gchar *folder_name)
1091 {
1092 	mail_vfolder_delete_folder (store, folder_name);
1093 }
1094 
1095 static void
1096 folder_renamed_cb (MailFolderCache *cache,
1097                    CamelStore *store,
1098                    const gchar *old_folder_name,
1099                    const gchar *new_folder_name,
1100                    gpointer user_data)
1101 {
1102 	mail_vfolder_rename_folder (store, old_folder_name, new_folder_name);
1103 }
1104 
1105 void
1106 vfolder_load_storage (EMailSession *session)
1107 {
1108 	/* lock for loading storage, it is safe to call it more than once */
1109 	G_LOCK_DEFINE_STATIC (vfolder_hash);
1110 
1111 	CamelStore *vfolder_store;
1112 	const gchar *config_dir;
1113 	gchar *user;
1114 	EFilterRule *rule;
1115 	MailFolderCache *folder_cache;
1116 	gchar *xmlfile;
1117 
1118 	G_LOCK (vfolder_hash);
1119 
1120 	if (vfolder_hash) {
1121 		/* we have already initialized */
1122 		G_UNLOCK (vfolder_hash);
1123 		return;
1124 	}
1125 
1126 	vfolder_hash = g_hash_table_new (g_str_hash, g_str_equal);
1127 
1128 	G_UNLOCK (vfolder_hash);
1129 
1130 	config_dir = mail_session_get_config_dir ();
1131 	vfolder_store = e_mail_session_get_vfolder_store (session);
1132 
1133 	g_signal_connect (
1134 		vfolder_store, "folder-deleted",
1135 		G_CALLBACK (store_folder_deleted_cb), NULL);
1136 
1137 	g_signal_connect (
1138 		vfolder_store, "folder-renamed",
1139 		G_CALLBACK (store_folder_renamed_cb), NULL);
1140 
1141 	/* load our rules */
1142 	user = g_build_filename (config_dir, "vfolders.xml", NULL);
1143 	/* This needs editor context which is only in the mail/. But really to run here we dont need editor context.
1144 	 * So till we split this to EDS, we would let mail/ create this and later one it is any ways two separate
1145 	 * contexts. */
1146 	context = e_mail_session_create_vfolder_context (session);
1147 
1148 	xmlfile = g_build_filename (EVOLUTION_PRIVDATADIR, "vfoldertypes.xml", NULL);
1149 	if (e_rule_context_load ((ERuleContext *) context,
1150 			       xmlfile, user) != 0) {
1151 		g_warning ("cannot load vfolders: %s\n", ((ERuleContext *) context)->error);
1152 	}
1153 	g_free (xmlfile);
1154 	g_free (user);
1155 
1156 	g_signal_connect (
1157 		context, "rule_added",
1158 		G_CALLBACK (context_rule_added), session);
1159 	g_signal_connect (
1160 		context, "rule_removed",
1161 		G_CALLBACK (context_rule_removed), session);
1162 
1163 	/* and setup the rules we have */
1164 	rule = NULL;
1165 	while ((rule = e_rule_context_next_rule ((ERuleContext *) context, rule, NULL))) {
1166 		if (rule->name) {
1167 			d (printf ("rule added: %s\n", rule->name));
1168 			context_rule_added ((ERuleContext *) context, rule, session);
1169 		} else {
1170 			d (printf ("invalid rule (%p) encountered: rule->name is NULL\n", rule));
1171 		}
1172 	}
1173 
1174 	folder_cache = e_mail_session_get_folder_cache (session);
1175 
1176 	g_signal_connect (
1177 		folder_cache, "folder-available",
1178 		G_CALLBACK (folder_available_cb), NULL);
1179 	g_signal_connect (
1180 		folder_cache, "folder-unavailable",
1181 		G_CALLBACK (folder_unavailable_cb), NULL);
1182 	g_signal_connect (
1183 		folder_cache, "folder-deleted",
1184 		G_CALLBACK (folder_deleted_cb), NULL);
1185 	g_signal_connect (
1186 		folder_cache, "folder-renamed",
1187 		G_CALLBACK (folder_renamed_cb), NULL);
1188 }
1189 
1190 static void
1191 vfolder_foreach_cb (gpointer key,
1192                     gpointer data,
1193                     gpointer user_data)
1194 {
1195 	CamelFolder *folder = CAMEL_FOLDER (data);
1196 
1197 	if (folder)
1198 		g_object_unref (folder);
1199 
1200 	g_free (key);
1201 }
1202 
1203 void
1204 mail_vfolder_shutdown (void)
1205 {
1206 	vfolder_shutdown = 1;
1207 
1208 	if (vfolder_hash) {
1209 		g_hash_table_foreach (vfolder_hash, vfolder_foreach_cb, NULL);
1210 		g_hash_table_destroy (vfolder_hash);
1211 		vfolder_hash = NULL;
1212 	}
1213 
1214 	if (context) {
1215 		g_object_unref (context);
1216 		context = NULL;
1217 	}
1218 }