ews_get_folder_info_sync (CamelStore *store,
                          const gchar *top,
                          guint32 flags,
                          GCancellable *cancellable,
                          GError **error)
{
	CamelEwsStore *ews_store;
	CamelEwsStorePrivate *priv;
	CamelFolderInfo *fi = NULL;
	EEwsConnection *connection;
	gchar *old_sync_state, *new_sync_state = NULL;
	gboolean initial_setup = FALSE;
	GSList *folders_created = NULL, *folders_updated = NULL;
	GSList *folders_deleted = NULL;
	gboolean includes_last_folder;
	gboolean success;
	GError *local_error = NULL;

	ews_store = (CamelEwsStore *) store;
	priv = ews_store->priv;

	if ((flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0 &&
	    camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) {
		CamelSession *session;

		session = camel_service_ref_session (CAMEL_SERVICE (ews_store));
		if (session) {
			ews_exchange_server_categories_cb (session, cancellable, ews_store, NULL);
			g_object_unref (session);
		}
	}

	if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) {
		gboolean includes_last_folder = TRUE;
		GSList *folders = NULL, *to_check = NULL;
		EwsFolderId *folder_id;

		if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
				_("Cannot list EWS public folders in offline mode"));
			return NULL;
		}

		g_mutex_lock (&priv->get_finfo_lock);

		g_slist_free_full (priv->public_folders, g_object_unref);
		priv->public_folders = NULL;

		connection = camel_ews_store_ref_connection (ews_store);
		folder_id = e_ews_folder_id_new ("publicfoldersroot", NULL, TRUE);
		to_check = g_slist_append (to_check, folder_id);

		while (!local_error && !g_cancellable_is_cancelled (cancellable) && to_check) {
			folder_id = to_check->data;
			to_check = g_slist_remove (to_check, folder_id);

			while (e_ews_connection_find_folder_sync (connection, EWS_PRIORITY_MEDIUM, folder_id, &includes_last_folder, &folders,
				cancellable, &local_error) && !local_error &&
				!g_cancellable_is_cancelled (cancellable)) {
				GSList *fiter;

				if (!folders)
					break;

				for (fiter = folders; fiter != NULL; fiter = fiter->next) {
					EEwsFolder *folder = fiter->data;

					if (e_ews_folder_get_child_count (folder) > 0) {
						const EwsFolderId *fid = e_ews_folder_get_id (folder);

						if (fid)
							to_check = g_slist_prepend (to_check,
								e_ews_folder_id_new (fid->id, fid->change_key, fid->is_distinguished_id));
					}

					if (!e_ews_folder_get_parent_id (folder)) {
						if (!folder_id->is_distinguished_id) {
							e_ews_folder_set_parent_id (folder,
								e_ews_folder_id_new (folder_id->id, folder_id->change_key, folder_id->is_distinguished_id));
						} else {
							e_ews_folder_set_parent_id (folder, e_ews_folder_id_new (EWS_PUBLIC_FOLDER_ROOT_ID, NULL, FALSE));
						}
					}
				}

				priv->public_folders = g_slist_concat (priv->public_folders, folders);
				folders = NULL;

				if (includes_last_folder)
					break;
			}

			e_ews_folder_id_free (folder_id);
		}

		g_mutex_unlock (&priv->get_finfo_lock);

		g_object_unref (connection);
		g_slist_free_full (to_check, (GDestroyNotify) e_ews_folder_id_free);

		camel_ews_store_ensure_virtual_folders (ews_store);

		if (local_error) {
			camel_ews_store_maybe_disconnect (ews_store, local_error);
			g_propagate_error (error, local_error);

			return NULL;
		}

		if (!priv->public_folders) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
				_("Cannot find any EWS public folders"));
			return NULL;
		}

		goto offline;
	}

	g_mutex_lock (&priv->get_finfo_lock);
	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
		camel_ews_store_ensure_virtual_folders (ews_store);
		g_mutex_unlock (&priv->get_finfo_lock);
		goto offline;
	}

	if (!camel_service_connect_sync ((CamelService *) store, cancellable, error)) {
		camel_offline_store_set_online_sync (CAMEL_OFFLINE_STORE (store), FALSE, NULL, NULL);
		camel_ews_store_ensure_virtual_folders (ews_store);
		g_mutex_unlock (&priv->get_finfo_lock);
		return NULL;
	}

	old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL);
	if (!old_sync_state)
		initial_setup = TRUE;

	if (!initial_setup && (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) != 0) {
		time_t now = time (NULL);

		g_free (old_sync_state);
		if (now - priv->last_refresh_time > FINFO_REFRESH_INTERVAL && ews_refresh_finfo (ews_store))
			ews_store->priv->last_refresh_time = time (NULL);

		g_mutex_unlock (&priv->get_finfo_lock);
		goto offline;
	}

	connection = camel_ews_store_ref_connection (ews_store);

	success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state,
		&new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted,
		cancellable, &local_error);

	g_free (old_sync_state);
	old_sync_state = NULL;

	if (!initial_setup && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
		g_clear_error (&local_error);
		ews_store_forget_all_folders (ews_store);
		camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", "");
		camel_ews_store_summary_clear (ews_store->summary);

		success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL,
			&new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted,
			cancellable, &local_error);
	}

	g_object_unref (connection);

	if (!success) {
		if (local_error)
			g_warning (
				"Unable to fetch the folder hierarchy: %s :%d \n",
				local_error->message, local_error->code);
		else
			g_warning ("Unable to fetch the folder hierarchy.\n");

		camel_ews_store_maybe_disconnect (ews_store, local_error);
		g_propagate_error (error, local_error);

		g_mutex_unlock (&priv->get_finfo_lock);
		return NULL;
	}
	ews_update_folder_hierarchy (
		ews_store, new_sync_state, includes_last_folder,
		folders_created, folders_deleted, folders_updated, NULL);
	g_mutex_unlock (&priv->get_finfo_lock);

offline:
	fi = folder_info_from_store_summary (ews_store, top, flags, cancellable, error);
	return fi;
}