[evolution-mapi] Bug #607429 - Crash on "Move Folder to" within MAPI hierarchy



commit f921193991c3b37c171fc9017c51eafd3b7a93dd
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jan 22 16:48:00 2010 +0100

    Bug #607429 - Crash on "Move Folder to" within MAPI hierarchy

 src/camel/camel-mapi-folder.c                  |   12 +-
 src/camel/camel-mapi-notifications.c           |    4 +-
 src/camel/camel-mapi-store.c                   |  488 ++++++++++++++----------
 src/libexchangemapi/exchange-mapi-connection.c |   68 ++++
 src/libexchangemapi/exchange-mapi-connection.h |    3 +
 5 files changed, 371 insertions(+), 204 deletions(-)
---
diff --git a/src/camel/camel-mapi-folder.c b/src/camel/camel-mapi-folder.c
index 8d15113..6f0b175 100644
--- a/src/camel/camel-mapi-folder.c
+++ b/src/camel/camel-mapi-folder.c
@@ -2039,13 +2039,17 @@ mapi_append_message (CamelFolder *folder, CamelMimeMessage *message,
 	MapiItem *item = NULL;
 	mapi_id_t fid = 0, mid = 0;
 	const gchar *folder_id;
+	guint32 folder_flags = 0;
 
 	/*Reject outbox / sent & trash*/
-	si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary,
-				       folder->full_name);
+	si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, folder->full_name);
+	if (si) {
+		folder_flags = si->flags;
+		camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+	}
 
-	if (((si->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH) ||
-	    ((si->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_OUTBOX)) {
+	if (((folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_TRASH) ||
+	    ((folder_flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_OUTBOX)) {
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, 
 				      _("Cannot append message to folder '%s'"),
 				      folder->full_name);
diff --git a/src/camel/camel-mapi-notifications.c b/src/camel/camel-mapi-notifications.c
index 58e1065..6180357 100644
--- a/src/camel/camel-mapi-notifications.c
+++ b/src/camel/camel-mapi-notifications.c
@@ -116,9 +116,11 @@ mapi_new_mail_fetch (CamelSession *session, CamelSessionThreadMsg *msg)
 	while (info_count >= 0) {
 		info = camel_store_summary_index ((CamelStoreSummary *)store->summary, info_count);
 		mapi_info = (CamelMapiStoreInfo *)info;
-		if (!g_strcmp0 (mapi_info->folder_id, folder_id)){
+		if (info && !g_strcmp0 (mapi_info->folder_id, folder_id)){
 			folder_name = mapi_info->full_name;
 		}
+		if (info)
+			camel_store_summary_info_free ((CamelStoreSummary *)store->summary, info);
 		info_count--;
 	}
 
diff --git a/src/camel/camel-mapi-store.c b/src/camel/camel-mapi-store.c
index d98a300..1c40348 100644
--- a/src/camel/camel-mapi-store.c
+++ b/src/camel/camel-mapi-store.c
@@ -110,16 +110,48 @@ static gboolean mapi_folder_subscribed (CamelStore *store, const char *folder_na
 static void		mapi_unsubscribe_folder(CamelStore *, const char *, CamelException *);
 static void		mapi_noop(CamelStore *, CamelException *);
 static CamelFolderInfo * mapi_build_folder_info(CamelMapiStore *mapi_store, const char *parent_name, const char *folder_name);
-static void mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelException *ex);
 static gboolean mapi_fid_is_system_folder (CamelMapiStore *mapi_store, const char *fid);
 
 static void mapi_update_folder_hash_tables (CamelMapiStore *store, const gchar *name, const gchar *fid, const gchar *parent_id);
 /* static const gchar* mapi_folders_hash_table_name_lookup (CamelMapiStore *store, const gchar *fid, gboolean use_cache); */
-static const gchar* mapi_folders_hash_table_pfid_lookup (CamelMapiStore *store, const gchar *fid, gboolean use_cache);
 #if 0
 static const gchar* mapi_folders_hash_table_fid_lookup (CamelMapiStore *store, const gchar *name, gboolean use_cache);
 #endif
 
+#if 0
+static void
+dump_summary (CamelMapiStore *mstore)
+{
+	CamelStoreSummary *summary = (CamelStoreSummary *) mstore->summary;
+	gint summary_count = camel_store_summary_count (summary);
+	guint i = 0;
+
+	printf ("%s: dumping %d items:\n", G_STRFUNC, summary_count);
+	for (i = 0; i < summary_count; i++) {
+		CamelStoreInfo *csi = camel_store_summary_index(summary, i);
+		CamelMapiStoreInfo *si = (CamelMapiStoreInfo *) csi;
+
+		printf ("[%2d] ", i);
+		if (si == NULL) {
+			printf ("NULL\n");
+			continue;
+		} /*else if (strstr (si->full_name, "/s3/") == NULL) {
+			printf ("   '%s'\n", si->full_name);
+			camel_store_summary_info_free ((CamelStoreSummary *)mstore->summary, csi);
+			continue;
+		}*/
+
+		printf ("   full name: '%s'\n", si->full_name);
+		printf ("   folder id: '%s'\n", si->folder_id);
+		printf ("   parent id: '%s'\n", si->parent_id);
+		printf ("   path: '%s'\n", csi->path);
+		printf ("   uri: '%s'\n", csi->uri?csi->uri:"[null]");
+		printf ("   flags:%x unread:%d total:%d\n", csi->flags, csi->unread, csi->total);
+		camel_store_summary_info_free ((CamelStoreSummary *)mstore->summary, csi);
+	}
+	printf ("\n");
+}
+#endif
 
 static guint
 mapi_hash_folder_name(gconstpointer key)
@@ -257,10 +289,10 @@ static void mapi_construct(CamelService *service, CamelSession *session,
 		store->flags |= CAMEL_STORE_FILTER_INBOX;
 
 	/*Hash Table*/	
-	priv->id_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	priv->name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	priv->parent_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	priv->default_folders = g_hash_table_new_full (g_int_hash, g_str_equal, g_free, g_free);
+	priv->id_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* folder ID to folder Full name */
+	priv->name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* folder Full name to folder ID */
+	/*priv->parent_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); / * folder ID to its parent folder ID */
+	priv->default_folders = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free); /* default folder type to folder ID */
 
 	store->flags &= ~CAMEL_STORE_VJUNK;
 	store->flags &= ~CAMEL_STORE_VTRASH;
@@ -604,111 +636,37 @@ mapi_delete_folder(CamelStore *store, const char *folder_name, CamelException *e
 /* 			camel_object_unref (mapi_store->current_folder); */
 		mapi_forget_folder(mapi_store,folder_name,ex);
 
+		/* remove from name_cache at the end, because the folder_id is from there */
+		/*g_hash_table_remove (priv->parent_hash, folder_id);*/
 		g_hash_table_remove (priv->id_hash, folder_id);
 		g_hash_table_remove (priv->name_hash, folder_name);
-		
-		g_hash_table_remove (priv->parent_hash, folder_id);
 	} 
 
 	CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
 
 }
 
-//FIXME : Moves this function to toplevel camel. same is used in GW.
-
-static char *
-mapi_path_to_physical (const char *prefix, const char *vpath)
-{
-	const char *p, *newp;
-	char *dp;
-	char *ppath;
-	int ppath_len;
-	int prefix_len;
-
-	while (*vpath == '/')
-		vpath++;
-	if (!prefix)
-		prefix = "";
-
-	/* Calculate the length of the real path. */
-	ppath_len = strlen (vpath);
-	ppath_len++;	/* For the ending zero.  */
-
-	prefix_len = strlen (prefix);
-	ppath_len += prefix_len;
-	ppath_len++;	/* For the separating slash.  */
-
-	p = vpath;
-	while (1) {
-		newp = strchr (p, '/');
-		if (newp == NULL)
-			break;
-
-		/* Skip consecutive slashes.  */
-		while (*newp == '/')
-			newp++;
-
-		p = newp;
-	};
-
-	ppath = g_malloc (ppath_len);
-	dp = ppath;
-
-	memcpy (dp, prefix, prefix_len);
-	dp += prefix_len;
-	*(dp++) = '/';
-
-	/* Copy the mangled path.  */
-	p = vpath;
- 	while (1) {
-		newp = strchr (p, '/');
-		if (newp == NULL) {
-			strcpy (dp, p);
-			break;
-		}
-
-		memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too.  */
-		dp += newp - p + 1;
-
-		*(dp++) = '/';
-
-		/* Skip consecutive slashes.  */
-		while (*newp == '/')
-			newp++;
-
-		p = newp;
-	}
-
-	return ppath;
-}
-
 static void 
 mapi_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex)
 {
 	CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
 	CamelMapiStorePrivate  *priv = mapi_store->priv;
 	CamelStoreInfo *si = NULL;
-	char *oldpath, *newpath, *storepath;
-	const char *folder_id;
-	char *temp = NULL;
-	mapi_id_t fid;
-
+	gchar *old_parent, *new_parent, *tmp;
+	gboolean move_cache = TRUE;
+	const gchar *old_fid_str, *new_parent_fid_str = NULL;
+	mapi_id_t old_fid;
 
 	CAMEL_SERVICE_REC_LOCK (mapi_store, connect_lock);
-	
+
 	if (!camel_mapi_store_connected ((CamelMapiStore *)store, ex)) {
 		CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
 		return;
 	}
-	
-	temp = strrchr (old_name, '/');
-	if (temp) 
-		temp++;
-	else
-		temp = (char *)old_name;
 
-	folder_id = camel_mapi_store_folder_id_lookup (mapi_store, temp);
-	if (!folder_id) {
+	/* Need a full name of a folder */
+	old_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, old_name);
+	if (!old_fid_str) {
 		/*To translators : '%s' is current name of the folder */
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, 
 				      _("Cannot rename MAPI folder `%s'. Folder does not exist."),
@@ -718,7 +676,7 @@ mapi_rename_folder(CamelStore *store, const char *old_name, const char *new_name
 	}
 
 	/*Do not allow rename for system folders.*/
-	if (mapi_fid_is_system_folder (mapi_store, folder_id)) {
+	if (mapi_fid_is_system_folder (mapi_store, old_fid_str)) {
 		/*To translators : '%s to %s' is current name of the folder  and
 		 new name of the folder.*/
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, 
@@ -727,57 +685,177 @@ mapi_rename_folder(CamelStore *store, const char *old_name, const char *new_name
 		return;
 	}
 
-	exchange_mapi_util_mapi_id_from_string (folder_id, &fid);
-		
-	temp = strrchr (new_name, '/');
-	if (temp) 
-		temp++;
-	else
-		temp = (char *)new_name;
-	
-	if (!exchange_mapi_rename_folder (fid , temp))
-	{
-		/*To translators : '%s to %s' is current name of the folder  and
-		 new name of the folder.*/
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, 
-				      _("Cannot rename MAPI folder `%s' to `%s'"), old_name, new_name);
+	old_parent = g_strdup (old_name);
+	tmp = strrchr (old_parent, '/');
+	if (tmp) {
+		*tmp = '\0';
+	} else {
+		strcpy (old_parent, "");
+	}
+
+	new_parent = g_strdup (new_name);
+	tmp = strrchr (new_parent, '/');
+	if (tmp) {
+		*tmp = '\0';
+		tmp++; /* here's a new folder name now */
+	} else {
+		strcpy (new_parent, "");
+		tmp = NULL;
+	}
 
+	if (!exchange_mapi_util_mapi_id_from_string (old_fid_str, &old_fid)) {
+		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot rename MAPI folder `%s' to `%s'"), old_name, new_name);
 		CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+		g_free (old_parent);
+		g_free (new_parent);
 		return;
 	}
 
-	CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+	if (tmp == NULL || g_str_equal (old_parent, new_parent)) {
+		gchar *folder_id;
 
-	g_hash_table_replace (priv->id_hash, g_strdup(folder_id), g_strdup(temp));
+		/* renaming in the same folder, thus no MoveFolder necessary */
+		if (!exchange_mapi_rename_folder (old_fid, tmp ? tmp : new_name)) {
+			/*To translators : '%s to %s' is current name of the folder  and
+			new name of the folder.*/
+			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, 
+						  _("Cannot rename MAPI folder `%s' to `%s'"), old_name, new_name);
 
-	g_hash_table_insert (priv->name_hash, g_strdup(new_name), g_strdup(folder_id));
-	g_hash_table_remove (priv->name_hash, old_name);
+			CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+			g_free (old_parent);
+			g_free (new_parent);
+			return;
+		}
 
-	storepath = g_strdup_printf ("%s/folders", priv->storage_path);
-	oldpath = mapi_path_to_physical (storepath, old_name);
-	newpath = mapi_path_to_physical (storepath, new_name);
-	g_free (storepath);
+		folder_id = g_strdup (old_fid_str);
 
-	/*XXX: make sure the summary is also renamed*/
-	si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, old_name);
+		/* this frees old_fid_str */
+		g_hash_table_remove (priv->name_hash, old_name);
+		g_hash_table_remove (priv->id_hash, folder_id);
 
-	if (si) {
-		camel_store_info_set_string((CamelStoreSummary *)mapi_store->summary, si,
-					    CAMEL_STORE_INFO_PATH, new_name);
-		camel_store_info_set_string((CamelStoreSummary *)mapi_store->summary, si,
-					    CAMEL_MAPI_STORE_INFO_FULL_NAME, new_name);
+		mapi_update_folder_hash_tables (mapi_store, new_name, folder_id, NULL);
+
+		g_free (folder_id);
+	} else {
+		const gchar *old_parent_fid_str;
+		mapi_id_t old_parent_fid, new_parent_fid;
+		gchar *folder_id;
+
+		old_parent_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, old_parent);
+		new_parent_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, new_parent);
+		if (!old_parent_fid_str && new_parent_fid_str) {
+			CamelStoreInfo *new_si;
+
+			/* Folder was in a store-summary, and is known, but the parent gone.
+			   The reason might be that this is a subfolder whose parent got moved
+			   a second ago. Thus just update local summary with proper paths. */
+			move_cache = FALSE;
+
+			new_si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, new_name);
+			if (new_si) {
+				si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, old_name);
+				if (si) {
+					/* for cases where folder sync realized new folders before this got updated;
+					   this shouldn't duplicate the info in summary, but remove the old one */
+					camel_store_summary_remove ((CamelStoreSummary *)mapi_store->summary, si);
+					camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+					si = NULL;
+				}
+				camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, new_si);
+			}
+		} else if (!old_parent_fid_str || !new_parent_fid_str ||
+			   !exchange_mapi_util_mapi_id_from_string (old_parent_fid_str, &old_parent_fid) ||
+			   !exchange_mapi_util_mapi_id_from_string (new_parent_fid_str, &new_parent_fid) ||
+			   !exchange_mapi_move_folder (old_fid, old_parent_fid, new_parent_fid, tmp)) {
+			CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot rename MAPI folder `%s' to `%s'"), old_name, new_name);
+			g_free (old_parent);
+			g_free (new_parent);
+			return;
+		} else {
+			/* folder was moved, update all subfolders immediately, thus
+			   the next get_folder_info call will know about them */
+			gint sz, i;
+
+			sz = camel_store_summary_count ((CamelStoreSummary*) mapi_store->summary);
+			for (i = 0; i < sz; i++) {
+				const gchar *full_name;
+
+				si = camel_store_summary_index ((CamelStoreSummary *) mapi_store->summary, i);
+				if (!si)
+					continue;
+
+				full_name = camel_mapi_store_info_full_name (mapi_store->summary, si);
+				if (full_name && g_str_has_prefix (full_name, old_name) && !g_str_equal (full_name, old_name) &&
+				    full_name [strlen (old_name)] == '/' && full_name [strlen (old_name) + 1] != '\0') {
+					/* it's a subfolder of old_name */
+					const gchar *fid = camel_mapi_store_info_folder_id (mapi_store->summary, si);
+
+					if (fid) {
+						gchar *new_full_name;
+
+						/* do not remove it from name_hash yet, because this function
+						   will be called for it again */
+						/* g_hash_table_remove (priv->name_hash, full_name); */
+						g_hash_table_remove (priv->id_hash, fid);
+
+						/* parent is still the same, only the path changed */
+						new_full_name = g_strconcat (new_name, full_name + strlen (old_name) + (g_str_has_suffix (new_name, "/") ? 1 : 0), NULL);
+
+						mapi_update_folder_hash_tables (mapi_store, new_full_name, fid, NULL);
+
+						camel_store_info_set_string ((CamelStoreSummary *)mapi_store->summary, si, CAMEL_STORE_INFO_PATH, new_full_name);
+						camel_store_info_set_string ((CamelStoreSummary *)mapi_store->summary, si, CAMEL_MAPI_STORE_INFO_FULL_NAME, new_full_name);
+						camel_store_summary_touch ((CamelStoreSummary *)mapi_store->summary);
+
+						g_free (new_full_name);
+					}
+				}
+				camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+			}
+		}
 
-		camel_store_summary_touch((CamelStoreSummary *)mapi_store->summary);
+		folder_id = g_strdup (old_fid_str);
+
+		/* this frees old_fid_str */
+		g_hash_table_remove (priv->name_hash, old_name);
+		g_hash_table_remove (priv->id_hash, folder_id);
+		/*g_hash_table_remove (priv->parent_hash, folder_id);*/
+
+		mapi_update_folder_hash_tables (mapi_store, new_name, folder_id, new_parent_fid_str);
+
+		g_free (folder_id);
 	}
 
-	if (g_rename (oldpath, newpath) == -1) {
-		g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
-				oldpath, newpath, strerror (errno));
+	CAMEL_SERVICE_REC_UNLOCK (mapi_store, connect_lock);
+
+	si = camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, old_name);
+	if (si) {
+		camel_store_info_set_string ((CamelStoreSummary *)mapi_store->summary, si, CAMEL_STORE_INFO_PATH, new_name);
+		camel_store_info_set_string ((CamelStoreSummary *)mapi_store->summary, si, CAMEL_MAPI_STORE_INFO_FULL_NAME, new_name);
+		if (new_parent_fid_str) {
+			camel_store_info_set_string ((CamelStoreSummary *)mapi_store->summary, si, CAMEL_MAPI_STORE_INFO_PARENT_ID, new_parent_fid_str);
+		}
+		camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+		camel_store_summary_touch ((CamelStoreSummary *)mapi_store->summary);
 	}
 
-	g_free (oldpath);
-	g_free (newpath);
+	if (move_cache) {
+		gchar *oldpath, *newpath;
+
+		oldpath = g_build_filename (priv->storage_path, "folders", old_name, NULL);
+		newpath = g_build_filename (priv->storage_path, "folders", new_name, NULL);
+
+		if (g_file_test (oldpath, G_FILE_TEST_IS_DIR) && g_rename (oldpath, newpath) == -1) {
+			g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset", oldpath, newpath, g_strerror (errno));
+		}
+
+		g_free (oldpath);
+		g_free (newpath);
+	}
 
+	g_free (old_parent);
+	g_free (new_parent);
 }
 
 static guint32 hexnib(guint32 c)
@@ -978,12 +1056,16 @@ mapi_get_folder_info_offline (CamelStore *store, const char *top,
 			continue;
 
 		/* Allow only All Public Folders heirarchy */
-		if (subscription_list)
-			if (!(si->flags & CAMEL_MAPI_FOLDER_PUBLIC)) continue;
+		if (subscription_list && (!(si->flags & CAMEL_MAPI_FOLDER_PUBLIC))) {
+			camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+			continue;
+		}
 
 		/*Allow Mailbox and Favourites (Subscribed public folders)*/
-		if (subscribed)
-			if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) continue;
+		if (subscribed && (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED))) {
+			camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, si);
+			continue;
+		}
 
 		if ( !strcmp(name, camel_mapi_store_info_full_name (mapi_store->summary, si))
 		     || match_path (path, camel_mapi_store_info_full_name (mapi_store->summary, si))) {
@@ -1046,7 +1128,6 @@ mapi_convert_to_folder_info (CamelMapiStore *store, ExchangeMAPIFolder *folder,
 
 	const char *par_name = NULL;
 	CamelFolderInfo *fi;
-	CamelMapiStorePrivate *priv = store->priv;
 
 	name = exchange_mapi_folder_get_name (folder);
 
@@ -1097,45 +1178,22 @@ mapi_convert_to_folder_info (CamelMapiStore *store, ExchangeMAPIFolder *folder,
 	parent = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", mapi_id_folder);
 
 	par_name = mapi_folders_hash_table_name_lookup (store, parent, TRUE);
-
 	if (par_name != NULL) {
-		const gchar *temp_parent = NULL;
-		const gchar *temp = NULL;
 		gchar *str = g_strconcat (par_name, "/", name, NULL);
 
-		fi->name = g_strdup (name);
-
-		temp_parent = mapi_folders_hash_table_pfid_lookup (store, parent, TRUE);
-
-		while (temp_parent) {
-			gchar *old_str;
-
-			temp = mapi_folders_hash_table_name_lookup (store, temp_parent, TRUE);
-			if (temp == NULL) {
-				break;
-			}
-
-			old_str = str;
-			str = g_strconcat ( temp, "/", old_str, NULL);
-			g_free (old_str);
-
-			temp_parent = mapi_folders_hash_table_pfid_lookup (store, temp_parent, TRUE);
-		} 
-
-		fi->full_name = g_strdup (str);
+		fi->full_name = str; /* takes ownership of the string */
 		fi->uri = g_strconcat (url, str, NULL);
-		g_free (str);
-	}
-	else {
+	} else {
 		fi->name =  g_strdup (name);
 		fi->full_name = g_strdup (name);
 		fi->uri = g_strconcat (url, "", name, NULL);
 	}
 
-	g_free (parent);
-
 	/*name_hash returns the container id given the name */
-	g_hash_table_insert (priv->name_hash, g_strdup(fi->full_name), id);
+	mapi_update_folder_hash_tables (store, fi->full_name, id, parent);
+
+	g_free (parent);
+	g_free (id);
 
 	fi->total = folder->total;
 	fi->unread = folder->unread_count;
@@ -1156,24 +1214,23 @@ camel_mapi_store_connected (CamelMapiStore *store, CamelException *ex)
 
 
 static void
-mapi_update_folder_hash_tables (CamelMapiStore *store, const gchar *name, const gchar *fid,
-				const gchar *parent_id)
+mapi_update_folder_hash_tables (CamelMapiStore *store, const gchar *full_name, const gchar *fid, const gchar *parent_id)
 {
 	CamelMapiStorePrivate  *priv = store->priv;
 
-	if (fid && name) {
+	if (fid && full_name) {
 		/*id_hash returns the name for a given container id*/
 		if (!g_hash_table_lookup (priv->id_hash, fid))
-			g_hash_table_insert (priv->id_hash, g_strdup (fid), g_strdup(name)); 
+			g_hash_table_insert (priv->id_hash, g_strdup (fid), g_strdup (full_name)); 
 
 		/* name_hash : name <-> fid mapping */
-		if (!g_hash_table_lookup (priv->name_hash, name))
-			g_hash_table_insert (priv->name_hash, g_strdup(name), g_strdup (fid));
+		if (!g_hash_table_lookup (priv->name_hash, full_name))
+			g_hash_table_insert (priv->name_hash, g_strdup (full_name), g_strdup (fid));
 	}
 
 	/*parent_hash returns the parent container id, given an id*/
-	if (fid && parent_id && !g_hash_table_lookup (priv->parent_hash, fid))
-		g_hash_table_insert (priv->parent_hash, g_strdup(fid), g_strdup(parent_id));
+	/*if (fid && parent_id && !g_hash_table_lookup (priv->parent_hash, fid))
+		g_hash_table_insert (priv->parent_hash, g_strdup (fid), g_strdup (parent_id));*/
 
 }
 
@@ -1190,6 +1247,7 @@ mapi_folders_update_hash_tables_from_cache (CamelMapiStore *store)
 		if (si == NULL) continue;
 
 		mapi_update_folder_hash_tables (store, si->full_name, si->folder_id, si->parent_id);
+		camel_store_summary_info_free (summary, (CamelStoreInfo *)si);
 	}
 }
 
@@ -1227,21 +1285,22 @@ mapi_folders_hash_table_fid_lookup (CamelMapiStore *store, const gchar *name,
 }
 #endif
 
-/*Parent FID.*/
-static const gchar*
-mapi_folders_hash_table_pfid_lookup (CamelMapiStore *store, const gchar *fid,
-				    gboolean use_cache)
+static void
+remove_path_from_store_summary (const gchar *path, gpointer value, CamelMapiStore *mstore)
 {
-	CamelMapiStorePrivate  *priv = store->priv;
-
-	const gchar *pfid = g_hash_table_lookup (priv->parent_hash, fid);
+	const gchar *folder_id;
 
-	if (!pfid && use_cache)
-		mapi_folders_update_hash_tables_from_cache (store);
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (mstore != NULL);
 
-	pfid = g_hash_table_lookup (priv->parent_hash, fid);
+	folder_id = g_hash_table_lookup (mstore->priv->name_hash, path);
+	if (folder_id) {
+		/* name_hash as the second, because folder_id is from there */
+		g_hash_table_remove (mstore->priv->id_hash, folder_id);
+		g_hash_table_remove (mstore->priv->name_hash, path);
+	}
 
-	return pfid;
+	camel_store_summary_remove_path ((CamelStoreSummary *)mstore->summary, path);
 }
 
 static void
@@ -1256,6 +1315,7 @@ mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelE
 	CamelMapiStoreInfo *mapi_si = NULL;
 	guint32 count, i;
 	CamelStoreInfo *si = NULL;
+	GHashTable *old_cache_folders;
 
 	if (((CamelOfflineStore *) store)->state == CAMEL_OFFLINE_STORE_NETWORK_AVAIL) {
 		if (((CamelService *)store)->status == CAMEL_SERVICE_DISCONNECTED){
@@ -1281,6 +1341,18 @@ mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelE
 		return;
 	}
 
+	/* remember all folders in cache before update */
+	old_cache_folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	count = camel_store_summary_count ((CamelStoreSummary *)store->summary);
+	for (i = 0; i < count; i++) {
+		si = camel_store_summary_index ((CamelStoreSummary *)store->summary, i);
+		if (si == NULL)
+			continue;
+
+		g_hash_table_insert (old_cache_folders, g_strdup (camel_store_info_path (store->summary, si)), GINT_TO_POINTER (1));
+		camel_store_summary_info_free ((CamelStoreSummary *)store->summary, si);
+	}
+
 	subscription_list = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST);
 
 	if (subscription_list) {
@@ -1316,11 +1388,25 @@ mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelE
 	/*populate the hash table for finding the mapping from container id <-> folder name*/
 	for (;temp_list != NULL ; temp_list = g_slist_next (temp_list) ) {
 		const char *full_name;
-		gchar *fid = NULL, *parent_id = NULL;
+		gchar *fid = NULL, *parent_id = NULL, *tmp = NULL;
 
-		full_name = exchange_mapi_folder_get_name ((ExchangeMAPIFolder *)(temp_list->data));
 		fid = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", exchange_mapi_folder_get_fid((ExchangeMAPIFolder *)(temp_list->data)));
 		parent_id = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", exchange_mapi_folder_get_parent_id ((ExchangeMAPIFolder *)(temp_list->data)));
+		full_name = g_hash_table_lookup (priv->id_hash, fid);
+		if (!full_name) {
+			const gchar *par_full_name;
+
+			par_full_name = g_hash_table_lookup (priv->id_hash, parent_id);
+			if (par_full_name) {
+				tmp = g_strconcat (par_full_name, "/", exchange_mapi_folder_get_name (temp_list->data), NULL);
+				full_name = tmp;
+			} else {
+				full_name = exchange_mapi_folder_get_name (temp_list->data);
+			}
+		}
+
+		/* remove from here; what lefts is not on the server any more */
+		g_hash_table_remove (old_cache_folders, full_name);
 
 		mapi_update_folder_hash_tables (store, full_name, fid, parent_id);
 
@@ -1333,6 +1419,7 @@ mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelE
 
 		g_free (fid);
 		g_free (parent_id);
+		g_free (tmp);
 	}
 
 	for (;folder_list != NULL; folder_list = g_slist_next (folder_list)) {
@@ -1360,28 +1447,21 @@ mapi_folders_sync (CamelMapiStore *store, const char *top, guint32 flags, CamelE
 			if (mapi_si == NULL) {
 				continue;
 			}
+
+			camel_store_summary_info_ref ((CamelStoreSummary *)store->summary, (CamelStoreInfo *)mapi_si);
 		}
 
 		mapi_si->info.flags |= info->flags;
 		mapi_si->info.total = info->total;
 		mapi_si->info.unread = info->unread;
-	}
 
-	/* Weed out deleted folders*/
-	count = camel_store_summary_count ((CamelStoreSummary *)store->summary);
-	for (i=0;i<count;i++) {
-		si = camel_store_summary_index ((CamelStoreSummary *)store->summary, i);
-		if (si == NULL)
-			continue;
-
-		info = g_hash_table_lookup (priv->name_hash, camel_store_info_path (store->summary, si));
-		if (!info) {
-			camel_store_summary_remove ((CamelStoreSummary *)store->summary, si);
-		}
-
-		camel_store_summary_info_free ((CamelStoreSummary *)store->summary, si);
+		camel_store_summary_info_free ((CamelStoreSummary *)store->summary, (CamelStoreInfo *)mapi_si);
+		camel_store_free_folder_info ((CamelStore *)store, info);
 	}
 
+	/* Weed out deleted folders */
+	g_hash_table_foreach (old_cache_folders, (GHFunc) remove_path_from_store_summary, store);
+	g_hash_table_destroy (old_cache_folders);
 
 	camel_store_summary_touch ((CamelStoreSummary *)store->summary);
 	camel_store_summary_save ((CamelStoreSummary *)store->summary);
@@ -1422,8 +1502,11 @@ mapi_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelExc
 		}
 	}
 
-	if (check_for_connection((CamelService *)store, ex) || 
-	    ((CamelService *)store)->status == CAMEL_SERVICE_CONNECTING) {
+	/* update folders from the server only when asking for the top most or the 'top' is not known;
+	   otherwise believe the local cache, because folders sync is pretty slow operation to be done
+	   one every single question on the folder info */
+	if ((!top || !*top || !camel_mapi_store_folder_id_lookup (mapi_store, top)) &&
+	    (check_for_connection((CamelService *)store, ex) || ((CamelService *)store)->status == CAMEL_SERVICE_CONNECTING)) {
 		mapi_folders_sync (mapi_store, top, flags, ex);
 
 		if (camel_exception_is_set (ex)) {
@@ -1444,10 +1527,17 @@ mapi_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelExc
 const gchar *
 camel_mapi_store_folder_id_lookup_offline (CamelMapiStore *mapi_store, const char *folder_name)
 {
-	CamelMapiStoreInfo *mapi_si = NULL;
+	CamelMapiStoreInfo *mapi_si;
+	const gchar *folder_id;
 
 	mapi_si = (CamelMapiStoreInfo *)camel_store_summary_path ((CamelStoreSummary *)mapi_store->summary, folder_name);
-	return mapi_si->folder_id;
+	g_return_val_if_fail (mapi_si != NULL, NULL);
+	folder_id = mapi_si->folder_id;
+
+	/* shouldn't be the last reference to it, right? */
+	camel_store_summary_info_free ((CamelStoreSummary *)mapi_store->summary, (CamelStoreInfo *) mapi_si);
+
+	return folder_id;
 }
 
 const gchar *
diff --git a/src/libexchangemapi/exchange-mapi-connection.c b/src/libexchangemapi/exchange-mapi-connection.c
index e86e646..e1eece5 100644
--- a/src/libexchangemapi/exchange-mapi-connection.c
+++ b/src/libexchangemapi/exchange-mapi-connection.c
@@ -1803,6 +1803,74 @@ cleanup:
 	return result;
 }
 
+/* moves folder 'src_fid' to folder 'des_fid' under name 'new_name' (no path in a new_name),
+   'src_parent_fid' is folder ID of a parent of the src_fid */
+gboolean
+exchange_mapi_move_folder (mapi_id_t src_fid, mapi_id_t src_parent_fid, mapi_id_t des_fid, const char *new_name)
+{
+	enum MAPISTATUS retval;
+	mapi_object_t obj_store;
+	mapi_object_t obj_src, obj_src_parent, obj_des;
+	gboolean result = FALSE;
+
+	g_return_val_if_fail (src_fid != 0, FALSE);
+	g_return_val_if_fail (src_parent_fid != 0, FALSE);
+	g_return_val_if_fail (des_fid != 0, FALSE);
+	g_return_val_if_fail (new_name != NULL, FALSE);
+	g_return_val_if_fail (strchr (new_name, '/') == NULL, FALSE);
+
+	LOCK ();
+	LOGALL ();
+
+	mapi_object_init (&obj_store);
+	mapi_object_init (&obj_src);
+	mapi_object_init (&obj_src_parent);
+	mapi_object_init (&obj_des);
+
+	retval = OpenMsgStore (global_mapi_session, &obj_store);
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("OpenMsgStore", GetLastError());
+		goto cleanup;
+	}
+
+	retval = OpenFolder (&obj_store, src_fid, &obj_src);
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("OpenFolder src_fid", GetLastError());
+		goto cleanup;
+	}
+
+	retval = OpenFolder (&obj_store, src_parent_fid, &obj_src_parent);
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("OpenFolder src_parent_fid", GetLastError());
+		goto cleanup;
+	}
+
+	retval = OpenFolder (&obj_store, des_fid, &obj_des);
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("OpenFolder des_fid", GetLastError());
+		goto cleanup;
+	}
+
+	retval = MoveFolder (&obj_src, &obj_src_parent, &obj_des, (char *)new_name, TRUE);
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("MoveFolder", GetLastError());
+		goto cleanup;
+	}
+
+	result = TRUE;
+
+cleanup:
+	mapi_object_release (&obj_des);
+	mapi_object_release (&obj_src_parent);
+	mapi_object_release (&obj_src);
+	mapi_object_release (&obj_store);
+
+	LOGNONE ();
+	UNLOCK ();
+
+	return result;
+}
+
 struct SPropTagArray *
 exchange_mapi_util_resolve_named_props (uint32_t olFolder, mapi_id_t fid, 
 					BuildNameID build_name_id, gpointer ni_data)
diff --git a/src/libexchangemapi/exchange-mapi-connection.h b/src/libexchangemapi/exchange-mapi-connection.h
index 258e516..7ef029c 100644
--- a/src/libexchangemapi/exchange-mapi-connection.h
+++ b/src/libexchangemapi/exchange-mapi-connection.h
@@ -162,6 +162,9 @@ exchange_mapi_empty_folder (mapi_id_t fid);
 gboolean 
 exchange_mapi_rename_folder (mapi_id_t fid, const char *new_name);
 
+gboolean
+exchange_mapi_move_folder (mapi_id_t src_fid, mapi_id_t src_parent_fid, mapi_id_t des_fid, const char *new_name);
+
 GSList *
 exchange_mapi_util_check_restriction (mapi_id_t fid, struct mapi_SRestriction *res);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]