1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Copyright (C) 2002-2004 Novell, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU Lesser General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /* ExchangeHierarchyWebDAV: class for a normal WebDAV folder hierarchy
21 * in the Exchange storage. Eg, the "Personal Folders" and "Public
22 * Folders" hierarchies.
34 #include <glib/gstdio.h>
36 #include "libedataserverui/e-passwords.h"
37 #include "libedataserver/e-source-list.h"
39 #include "exchange-hierarchy-webdav.h"
40 #include "exchange-account.h"
41 #include "e-folder-exchange.h"
42 #include "e2k-context.h"
44 #include "e2k-propnames.h"
45 #include "e2k-restriction.h"
47 #include "e2k-utils.h"
48 #include "exchange-folder-size.h"
49 #include "exchange-esource.h"
53 #define URI_ENCODE_CHARS "@;:/?=."
55 struct _ExchangeHierarchyWebDAVPrivate {
56 GHashTable *folders_by_internal_path;
57 gboolean deep_searchable;
59 gdouble total_folder_size;
62 #define PARENT_TYPE EXCHANGE_TYPE_HIERARCHY
63 static ExchangeHierarchyClass *parent_class = NULL;
65 static void folder_type_map_init (void);
67 static void dispose (GObject *object);
68 static void finalize (GObject *object);
69 static gboolean is_empty (ExchangeHierarchy *hier);
70 static void rescan (ExchangeHierarchy *hier);
71 static ExchangeAccountFolderResult scan_subtree (ExchangeHierarchy *hier,
74 static ExchangeAccountFolderResult create_folder (ExchangeHierarchy *hier,
78 static ExchangeAccountFolderResult remove_folder (ExchangeHierarchy *hier,
80 static ExchangeAccountFolderResult xfer_folder (ExchangeHierarchy *hier,
83 const char *dest_name,
84 gboolean remove_source);
86 static void hierarchy_new_folder (ExchangeHierarchy *hier, EFolder *folder,
88 static void hierarchy_removed_folder (ExchangeHierarchy *hier, EFolder *folder,
92 class_init (GObjectClass *object_class)
94 ExchangeHierarchyClass *exchange_hierarchy_class =
95 EXCHANGE_HIERARCHY_CLASS (object_class);
97 folder_type_map_init ();
99 parent_class = g_type_class_ref (PARENT_TYPE);
101 /* virtual method override */
102 object_class->dispose = dispose;
103 object_class->finalize = finalize;
105 exchange_hierarchy_class->is_empty = is_empty;
106 exchange_hierarchy_class->rescan = rescan;
107 exchange_hierarchy_class->scan_subtree = scan_subtree;
108 exchange_hierarchy_class->create_folder = create_folder;
109 exchange_hierarchy_class->remove_folder = remove_folder;
110 exchange_hierarchy_class->xfer_folder = xfer_folder;
114 init (GObject *object)
116 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (object);
118 hwd->priv = g_new0 (ExchangeHierarchyWebDAVPrivate, 1);
119 hwd->priv->folders_by_internal_path = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_object_unref);
120 hwd->priv->total_folder_size = 0;
122 g_signal_connect (object, "new_folder",
123 G_CALLBACK (hierarchy_new_folder), NULL);
124 g_signal_connect (object, "removed_folder",
125 G_CALLBACK (hierarchy_removed_folder), NULL);
129 dispose (GObject *object)
131 G_OBJECT_CLASS (parent_class)->dispose (object);
135 finalize (GObject *object)
137 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (object);
139 g_hash_table_destroy (hwd->priv->folders_by_internal_path);
140 g_free (hwd->priv->trash_path);
143 G_OBJECT_CLASS (parent_class)->finalize (object);
146 E2K_MAKE_TYPE (exchange_hierarchy_webdav, ExchangeHierarchyWebDAV, class_init, init, PARENT_TYPE)
150 char *contentclass, *component;
151 gboolean offline_supported;
152 } ExchangeFolderType;
154 static ExchangeFolderType folder_types[] = {
155 { "IPF.Note", "mail", FALSE },
156 { "IPF.Contact", "contacts", FALSE },
157 { "IPF.Appointment", "calendar", FALSE },
158 { "IPF.Task", "tasks", FALSE },
161 static GHashTable *folder_type_map;
164 folder_type_map_init (void)
168 folder_type_map = g_hash_table_new (g_str_hash, g_str_equal);
169 for (i = 0; folder_types[i].contentclass; i++) {
170 g_hash_table_insert (folder_type_map,
171 folder_types[i].contentclass,
176 /* We maintain the folders_by_internal_path hash table by listening
177 * to our own signal emissions. (This lets ExchangeHierarchyForeign
178 * remove its folders by just calling exchange_hierarchy_removed_folder.)
181 hierarchy_new_folder (ExchangeHierarchy *hier, EFolder *folder,
184 const char *internal_uri ;
187 g_return_if_fail (E_IS_FOLDER (folder));
188 internal_uri = e_folder_exchange_get_internal_uri (folder);
190 /* This should ideally not be needed. But, this causes a problem when the
191 server has identical folder names [ internal_uri ] for folders. Very much
192 possible in the case of favorite folders */
193 if (g_hash_table_lookup (EXCHANGE_HIERARCHY_WEBDAV (hier)->priv->folders_by_internal_path,
194 (char *)e2k_uri_path (internal_uri)))
197 g_hash_table_insert (EXCHANGE_HIERARCHY_WEBDAV (hier)->priv->folders_by_internal_path,
198 (char *)e2k_uri_path (internal_uri), g_object_ref (folder));
200 mf_path = e_folder_exchange_get_storage_file (folder, "connector-metadata.xml");
201 e_folder_exchange_save_to_file (folder, mf_path);
206 hierarchy_removed_folder (ExchangeHierarchy *hier, EFolder *folder,
209 const char *internal_uri = e_folder_exchange_get_internal_uri (folder);
212 g_hash_table_remove (EXCHANGE_HIERARCHY_WEBDAV (hier)->priv->folders_by_internal_path,
213 (char *)e2k_uri_path (internal_uri));
215 mf_path = e_folder_exchange_get_storage_file (folder, "connector-metadata.xml");
219 e_path_rmdir (hier->account->storage_dir,
220 e_folder_exchange_get_path (folder));
224 is_empty (ExchangeHierarchy *hier)
226 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (hier);
228 /* 1, not 0, because there will always be an entry for toplevel */
229 return g_hash_table_size (hwd->priv->folders_by_internal_path) == 1;
233 e_folder_webdav_new (ExchangeHierarchy *hier, const char *internal_uri,
234 EFolder *parent, const char *name, const char *type,
235 const char *outlook_class, int unread,
236 gboolean offline_supported)
239 char *real_type, *http_uri, *physical_uri, *fixed_name = NULL;
241 d( g_print ("exchange-hierarchy-webdave.c:e_folder_webdave_new: internal_uri=[%s], name=[%s], type=[%s], class=[%s]\n",
242 internal_uri, name, type, outlook_class));
244 if (hier->type == EXCHANGE_HIERARCHY_PUBLIC &&
245 !strstr (type, "/public"))
246 real_type = g_strdup_printf ("%s/public", type);
247 else if (hier->type == EXCHANGE_HIERARCHY_FOREIGN &&
248 !strcmp (type, "calendar"))
249 real_type = g_strdup ("calendar/public"); /* Hack */
251 real_type = g_strdup (type);
253 fixed_name = e2k_uri_encode (name, FALSE, URI_ENCODE_CHARS);
254 physical_uri = e2k_uri_concat (e_folder_get_physical_uri (parent), fixed_name);
258 folder = e_folder_exchange_new (hier, name,
259 real_type, outlook_class,
260 physical_uri, internal_uri);
263 char *encoded_name = NULL;
264 const char *new_internal_uri;
269 /* appending "/" here, so that hash table lookup in rescan() succeeds */
270 if (name[len-1] != '/') {
271 encoded_name = e2k_uri_encode (name, FALSE, URI_ENCODE_CHARS);
273 temp_name = g_strndup (name, len-1);
274 encoded_name = e2k_uri_encode (temp_name, FALSE, URI_ENCODE_CHARS);
277 temp_name = g_strdup_printf ("%s/", encoded_name);
278 g_free (encoded_name);
280 new_internal_uri = e_folder_exchange_get_internal_uri (parent);
281 http_uri = e2k_uri_concat (new_internal_uri, temp_name);
282 d(g_print ("exchange-hierarchy-webdave.c:e_folder_webdave_new: http_uri=[%s], new_internal_uri=[%s], temp_name=[%s], name[%s]\n",
283 http_uri, new_internal_uri, temp_name, name));
286 folder = e_folder_exchange_new (hier, name,
287 real_type, outlook_class,
288 physical_uri, http_uri);
291 g_free (physical_uri);
294 if (unread && hier->type != EXCHANGE_HIERARCHY_PUBLIC)
295 e_folder_set_unread_count (folder, unread);
296 if (offline_supported)
297 e_folder_set_can_sync_offline (folder, offline_supported);
299 /* FIXME: set is_stock */
304 static ExchangeAccountFolderResult
305 create_folder (ExchangeHierarchy *hier, EFolder *parent,
306 const char *name, const char *type)
309 E2kProperties *props;
310 E2kHTTPStatus status;
311 char *permanent_url = NULL;
314 exchange_account_is_offline (hier->account, &mode);
315 if (mode != ONLINE_MODE)
316 return EXCHANGE_ACCOUNT_FOLDER_OFFLINE;
318 for (i = 0; folder_types[i].component; i++) {
319 if (!strcmp (folder_types[i].component, type))
322 if (!folder_types[i].component)
323 return EXCHANGE_ACCOUNT_FOLDER_UNKNOWN_TYPE;
325 dest = e_folder_webdav_new (hier, NULL, parent, name, type,
326 folder_types[i].contentclass, 0,
327 folder_types[i].offline_supported);
329 props = e2k_properties_new ();
330 e2k_properties_set_string (props, E2K_PR_EXCHANGE_FOLDER_CLASS,
331 g_strdup (folder_types[i].contentclass));
333 status = e_folder_exchange_mkcol (dest, NULL, props,
335 e2k_properties_free (props);
337 if (E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
338 e_folder_exchange_set_permanent_uri (dest, permanent_url);
339 g_free (permanent_url);
340 exchange_hierarchy_new_folder (hier, dest);
341 g_object_unref (dest);
343 /* update the folder size table, new folder, initialize the size to 0 */
344 exchange_account_folder_size_add (hier->account, name, 0);
345 return EXCHANGE_ACCOUNT_FOLDER_OK;
348 g_object_unref (dest);
349 if (status == E2K_HTTP_METHOD_NOT_ALLOWED)
350 return EXCHANGE_ACCOUNT_FOLDER_ALREADY_EXISTS;
351 else if (status == E2K_HTTP_CONFLICT)
352 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
353 else if (status == E2K_HTTP_FORBIDDEN)
354 return EXCHANGE_ACCOUNT_FOLDER_PERMISSION_DENIED;
356 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
359 static ExchangeAccountFolderResult
360 remove_folder (ExchangeHierarchy *hier, EFolder *folder)
362 E2kHTTPStatus status;
365 exchange_account_is_offline (hier->account, &mode);
367 if (mode != ONLINE_MODE)
368 return EXCHANGE_ACCOUNT_FOLDER_OFFLINE;
370 if (folder == hier->toplevel)
371 return EXCHANGE_ACCOUNT_FOLDER_PERMISSION_DENIED;
373 status = e_folder_exchange_delete (folder, NULL);
374 if (E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
375 exchange_hierarchy_removed_folder (hier, folder);
377 /* update the folder size info */
378 exchange_account_folder_size_remove (hier->account,
379 e_folder_get_name(folder));
380 return EXCHANGE_ACCOUNT_FOLDER_OK;
382 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
385 static ExchangeAccountFolderResult
386 xfer_folder (ExchangeHierarchy *hier, EFolder *source,
387 EFolder *dest_parent, const char *dest_name,
388 gboolean remove_source)
390 E2kHTTPStatus status;
392 char *permanent_url = NULL, *physical_uri, *source_parent;
393 const char *folder_type = NULL, *source_folder_name;
394 ExchangeAccountFolderResult ret_code;
397 exchange_account_is_offline (hier->account, &mode);
398 if (mode != ONLINE_MODE)
399 return EXCHANGE_ACCOUNT_FOLDER_OFFLINE;
401 if (source == hier->toplevel)
402 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
404 dest = e_folder_webdav_new (hier, NULL, dest_parent, dest_name,
405 e_folder_get_type_string (source),
406 e_folder_exchange_get_outlook_class (source),
407 e_folder_get_unread_count (source),
408 e_folder_get_can_sync_offline (source));
410 status = e_folder_exchange_transfer_dir (source, NULL, dest,
414 if (E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
415 folder_type = e_folder_get_type_string (source);
417 e_folder_exchange_set_permanent_uri (dest, permanent_url);
419 exchange_hierarchy_removed_folder (hier, source);
420 exchange_hierarchy_new_folder (hier, dest);
421 scan_subtree (hier, dest, mode);
422 physical_uri = g_strdup (e_folder_get_physical_uri (source));
423 g_object_unref (dest);
424 ret_code = EXCHANGE_ACCOUNT_FOLDER_OK;
426 /* Find if folder movement or rename.
427 * update folder size in case of rename.
430 source_folder_name = strrchr (physical_uri, '/') + 1;
431 source_parent = g_strndup (physical_uri,
432 source_folder_name - physical_uri);
433 if (!strcmp (e_folder_get_physical_uri (dest_parent), source_parent)) {
434 /* rename - remove folder entry from hash, and
435 * update the hash table with new name
437 exchange_account_folder_size_rename (hier->account,
438 source_folder_name+1, dest_name);
440 g_free (source_parent);
442 physical_uri = e2k_uri_concat (
443 e_folder_get_physical_uri (dest_parent),
445 g_object_unref (dest);
446 if (status == E2K_HTTP_FORBIDDEN ||
447 status == E2K_HTTP_UNAUTHORIZED)
448 ret_code = EXCHANGE_ACCOUNT_FOLDER_PERMISSION_DENIED;
450 ret_code = EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
453 /* Remove the ESource of the source folder, in case of rename/move */
454 if ((hier->type == EXCHANGE_HIERARCHY_PERSONAL ||
455 hier->type == EXCHANGE_HIERARCHY_FAVORITES) && remove_source &&
456 ret_code == EXCHANGE_ACCOUNT_FOLDER_OK) {
458 if ((strcmp (folder_type, "calendar") == 0) ||
459 (strcmp (folder_type, "calendar/public") == 0)) {
460 remove_folder_esource (hier->account,
461 EXCHANGE_CALENDAR_FOLDER,
464 else if ((strcmp (folder_type, "tasks") == 0) ||
465 (strcmp (folder_type, "tasks/public") == 0)){
466 remove_folder_esource (hier->account,
467 EXCHANGE_TASKS_FOLDER,
470 else if ((strcmp (folder_type, "contacts") == 0) ||
471 (strcmp (folder_type, "contacts/public") == 0)) {
472 remove_folder_esource (hier->account,
473 EXCHANGE_CONTACTS_FOLDER,
478 g_free (physical_uri);
483 add_href (gpointer path, gpointer folder, gpointer hrefs)
485 const char *folder_type;
487 folder_type = e_folder_get_type_string (folder);
492 if (!strcmp (folder_type, "noselect"))
495 g_ptr_array_add (hrefs, path);
498 /* E2K_PR_EXCHANGE_FOLDER_SIZE also can be used for reading folder size */
499 static const char *rescan_props[] = {
500 E2K_PR_EXCHANGE_FOLDER_SIZE,
501 E2K_PR_HTTPMAIL_UNREAD_COUNT
503 static const int n_rescan_props = sizeof (rescan_props) / sizeof (rescan_props[0]);
506 rescan (ExchangeHierarchy *hier)
508 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (hier);
509 const char *prop = E2K_PR_HTTPMAIL_UNREAD_COUNT;
510 const char *folder_size, *folder_name;
516 gboolean personal = ( hier->type == EXCHANGE_HIERARCHY_PERSONAL );
519 exchange_account_is_offline (hier->account, &mode);
520 if ( (mode != ONLINE_MODE) ||
521 hier->type == EXCHANGE_HIERARCHY_PUBLIC)
524 hrefs = g_ptr_array_new ();
525 g_hash_table_foreach (hwd->priv->folders_by_internal_path,
528 g_ptr_array_free (hrefs, TRUE);
533 iter = e_folder_exchange_bpropfind_start (hier->toplevel, NULL,
534 (const char **)hrefs->pdata,
536 rescan_props, n_rescan_props);
537 g_ptr_array_free (hrefs, TRUE);
539 while ((result = e2k_result_iter_next (iter))) {
540 folder = g_hash_table_lookup (hwd->priv->folders_by_internal_path,
541 e2k_uri_path (result->href));
545 prop = e2k_properties_get_prop (result->props,
546 E2K_PR_HTTPMAIL_UNREAD_COUNT);
549 unread = atoi (prop);
551 if (unread != e_folder_get_unread_count (folder))
552 e_folder_set_unread_count (folder, unread);
554 folder_size = e2k_properties_get_prop (result->props,
555 E2K_PR_EXCHANGE_FOLDER_SIZE);
558 folder_name = e_folder_get_name (folder);
559 fsize_d = g_ascii_strtod (folder_size, NULL)/1024;
560 exchange_account_folder_size_add (hier->account,
561 folder_name, fsize_d);
563 hwd->priv->total_folder_size =
564 hwd->priv->total_folder_size + fsize_d;
567 e2k_result_iter_free (iter);
568 g_object_unref (hier);
571 ExchangeAccountFolderResult
572 exchange_hierarchy_webdav_status_to_folder_result (E2kHTTPStatus status)
574 if (E2K_HTTP_STATUS_IS_SUCCESSFUL (status))
575 return EXCHANGE_ACCOUNT_FOLDER_OK;
576 else if (status == E2K_HTTP_NOT_FOUND)
577 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
578 else if (status == E2K_HTTP_UNAUTHORIZED)
579 return EXCHANGE_ACCOUNT_FOLDER_PERMISSION_DENIED;
581 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
585 exchange_hierarchy_webdav_get_total_folder_size (ExchangeHierarchyWebDAV *hwd)
587 g_return_val_if_fail (EXCHANGE_IS_HIERARCHY_WEBDAV (hwd), -1);
589 return hwd->priv->total_folder_size;
593 exchange_hierarchy_webdav_parse_folder (ExchangeHierarchyWebDAV *hwd,
598 ExchangeFolderType *folder_type;
599 const char *name, *prop, *outlook_class, *permanenturl;
603 g_return_val_if_fail (EXCHANGE_IS_HIERARCHY_WEBDAV (hwd), NULL);
604 g_return_val_if_fail (E_IS_FOLDER (parent), NULL);
606 /* It's possible to have a localized inbox folder named, eg,
607 * "Innboks", with children whose URIs go through "Inbox"
608 * instead. (See bugzilla 27065.) This is probably related to
609 * the IMAP "INBOX" convention. Anyway, the important bit is
610 * that you can't know a folder's parent URI just by looking
611 * at its own URI. Since we only ever scan one folder at a
612 * time here, we just keep track of what the parent was. If we
613 * were going to read multiple folders at once, we could deal
614 * with this by fetching DAV:parentname.
617 name = e2k_properties_get_prop (result->props,
618 E2K_PR_DAV_DISPLAY_NAME);
622 prop = e2k_properties_get_prop (result->props,
623 E2K_PR_HTTPMAIL_UNREAD_COUNT);
624 unread = prop ? atoi (prop) : 0;
625 prop = e2k_properties_get_prop (result->props,
626 E2K_PR_DAV_HAS_SUBS);
627 hassubs = prop && atoi (prop);
629 outlook_class = e2k_properties_get_prop (result->props,
630 E2K_PR_EXCHANGE_FOLDER_CLASS);
633 folder_type = g_hash_table_lookup (folder_type_map, outlook_class);
635 folder_type = &folder_types[0]; /* mail */
637 outlook_class = folder_type->contentclass;
640 * The permanenturl Field provides a unique identifier for an item
641 * across the *store* and will not change as long as the item remains
642 * in the same folder. The permanenturl Field contains the ID of the
643 * parent folder of the item, which changes when the item is moved to a
644 * different folder or deleted. Changing a field on an item will not
645 * change the permanenturl Field and neither will adding more items to
646 * the folder with the same display name or message subject.
648 permanenturl = e2k_properties_get_prop (result->props,
649 E2K_PR_EXCHANGE_PERMANENTURL);
650 /* Check for errors */
652 folder = e_folder_webdav_new (EXCHANGE_HIERARCHY (hwd),
653 result->href, parent,
654 name, folder_type->component,
655 outlook_class, unread,
656 folder_type->offline_supported);
657 if (hwd->priv->trash_path && !strcmp (e2k_uri_path (result->href), hwd->priv->trash_path))
658 e_folder_set_custom_icon (folder, "stock_delete");
660 e_folder_exchange_set_has_subfolders (folder, TRUE);
662 /* Favorite folders and subscribed folders will not have
665 e_folder_exchange_set_permanent_uri (folder, permanenturl);
672 add_folders (ExchangeHierarchy *hier, EFolder *folder, gpointer folders)
674 g_object_ref (folder);
675 g_ptr_array_add (folders, folder);
678 static const char *folder_props[] = {
679 E2K_PR_EXCHANGE_FOLDER_CLASS,
680 E2K_PR_HTTPMAIL_UNREAD_COUNT,
681 E2K_PR_DAV_DISPLAY_NAME,
682 E2K_PR_EXCHANGE_PERMANENTURL,
683 E2K_PR_EXCHANGE_FOLDER_SIZE,
686 static const int n_folder_props = sizeof (folder_props) / sizeof (folder_props[0]);
688 static const char *pub_folder_props[] = {
689 E2K_PR_DAV_DISPLAY_NAME,
692 static const int n_pub_folder_props = sizeof (pub_folder_props) / sizeof (pub_folder_props[0]);
694 static ExchangeAccountFolderResult
695 scan_subtree (ExchangeHierarchy *hier, EFolder *parent, int mode)
697 static E2kRestriction *folders_rn;
698 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (hier);
699 GSList *subtrees = NULL;
702 E2kHTTPStatus status;
703 EFolder *folder, *tmp;
707 const char *name, *folder_size, *deleted_items_uri, *int_uri;
708 gboolean personal = ( EXCHANGE_HIERARCHY (hwd)->type == EXCHANGE_HIERARCHY_PERSONAL );
711 if (!e_folder_exchange_get_rescan_tree (parent)) {
712 d(g_print ("%s(%d):%s: Donot RESCAN [%s] \n", __FILE__, __LINE__, __PRETTY_FUNCTION__,
713 e_folder_get_name (parent)));
714 return EXCHANGE_ACCOUNT_FOLDER_OK;
718 if (mode == OFFLINE_MODE) {
719 folders = g_ptr_array_new ();
720 exchange_hierarchy_webdav_offline_scan_subtree (EXCHANGE_HIERARCHY (hier), add_folders, folders);
721 for (i = 0; i <folders->len; i++) {
722 tmp = (EFolder *)folders->pdata[i];
723 exchange_hierarchy_new_folder (hier, (EFolder *)folders->pdata[i]);
725 return EXCHANGE_ACCOUNT_FOLDER_OK;
731 e2k_restriction_andv (
732 e2k_restriction_prop_bool (E2K_PR_DAV_IS_COLLECTION,
734 e2k_restriction_prop_bool (E2K_PR_DAV_IS_HIDDEN,
735 E2K_RELOP_EQ, FALSE),
739 if (hier->type == EXCHANGE_HIERARCHY_PUBLIC)
740 iter = e_folder_exchange_search_start (parent, NULL,
743 folders_rn, NULL, TRUE);
745 iter = e_folder_exchange_search_start (parent, NULL,
746 folder_props, n_folder_props,
747 folders_rn, NULL, TRUE);
749 while ((result = e2k_result_iter_next (iter))) {
750 folder = exchange_hierarchy_webdav_parse_folder (hwd, parent, result);
754 if (hwd->priv->deep_searchable &&
755 e_folder_exchange_get_has_subfolders (folder)) {
756 e_folder_exchange_set_has_subfolders (folder, FALSE);
757 subtrees = g_slist_prepend (subtrees, folder);
759 exchange_hierarchy_new_folder (hier, folder);
760 //g_object_unref (folder);
762 /* Check the folder size here */
763 if (hier->type != EXCHANGE_HIERARCHY_PUBLIC) {
764 name = e2k_properties_get_prop (result->props,
765 E2K_PR_DAV_DISPLAY_NAME);
766 folder_size = e2k_properties_get_prop (result->props,
767 E2K_PR_EXCHANGE_FOLDER_SIZE);
769 /* FIXME : Find a better way of doing this */
770 fsize_d = g_ascii_strtod (folder_size, NULL)/1024 ;
771 exchange_account_folder_size_add (hier->account, name, fsize_d);
775 /* calculate mail box size only for personal folders */
776 hwd->priv->total_folder_size =
777 hwd->priv->total_folder_size + fsize_d;
782 status = e2k_result_iter_free (iter);
784 deleted_items_uri = exchange_account_get_standard_uri (hier->account, "deleteditems");
787 folder = subtrees->data;
788 subtrees = g_slist_remove (subtrees, folder);
789 /* Dont scan the subtree for deleteditems folder */
790 int_uri = e_folder_exchange_get_internal_uri (folder);
791 if (int_uri && !strcmp (int_uri, deleted_items_uri))
793 scan_subtree (hier, folder, mode);
796 e_folder_exchange_set_rescan_tree (parent, FALSE);
798 return exchange_hierarchy_webdav_status_to_folder_result (status);
801 struct scan_offline_data {
802 ExchangeHierarchy *hier;
803 ExchangeHierarchyWebDAVScanCallback callback;
809 scan_offline_cb (const char *physical_path, const char *path, gpointer data)
811 struct scan_offline_data *sod = data;
816 mf_name = g_build_filename (physical_path, "connector-metadata.xml", NULL);
817 folder = e_folder_exchange_new_from_file (sod->hier, mf_name);
822 sod->badpaths = g_ptr_array_new ();
823 g_ptr_array_add (sod->badpaths, g_strdup (path));
828 sod->callback (sod->hier, folder, sod->user_data);
829 g_object_unref (folder);
835 * exchange_hierarchy_webdav_offline_scan_subtree:
836 * @hier: a (webdav) hierarchy
837 * @callbackb: a callback
838 * @user_data: data for @cb
840 * Scans the offline folder tree cache for @hier and calls @cb
841 * with each folder successfully constructed from offline data
844 exchange_hierarchy_webdav_offline_scan_subtree (ExchangeHierarchy *hier,
845 ExchangeHierarchyWebDAVScanCallback callback,
848 struct scan_offline_data sod;
853 g_return_if_fail (EXCHANGE_IS_HIERARCHY (hier));
856 sod.callback = callback;
857 sod.user_data = user_data;
860 path = e_folder_exchange_get_path (hier->toplevel);
861 prefix = e2k_strdup_with_trailing_slash (path);
862 dir = e_path_to_physical (hier->account->storage_dir, prefix);
864 e_path_find_folders (dir, scan_offline_cb, &sod);
867 for (i = 0; i < sod.badpaths->len; i++) {
868 e_path_rmdir (dir, sod.badpaths->pdata[i]);
869 g_free (sod.badpaths->pdata[i]);
871 g_ptr_array_free (sod.badpaths, TRUE);
878 exchange_hierarchy_webdav_construct (ExchangeHierarchyWebDAV *hwd,
879 ExchangeAccount *account,
880 ExchangeHierarchyType type,
881 const char *hierarchy_name,
882 const char *physical_uri_prefix,
883 const char *internal_uri_prefix,
884 const char *owner_name,
885 const char *owner_email,
886 const char *source_uri,
887 gboolean deep_searchable)
891 g_return_if_fail (EXCHANGE_IS_HIERARCHY_WEBDAV (hwd));
892 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
894 hwd->priv->deep_searchable = deep_searchable;
896 toplevel = e_folder_exchange_new (EXCHANGE_HIERARCHY (hwd),
900 internal_uri_prefix);
901 e_folder_set_custom_icon (toplevel, "stock_folder");
902 e_folder_exchange_set_has_subfolders (toplevel, TRUE);
903 exchange_hierarchy_construct (EXCHANGE_HIERARCHY (hwd),
904 account, type, toplevel,
905 owner_name, owner_email, source_uri);
906 g_object_unref (toplevel);
908 if (type == EXCHANGE_HIERARCHY_PERSONAL) {
909 const char *trash_uri;
911 trash_uri = exchange_account_get_standard_uri (account, "deleteditems");
913 hwd->priv->trash_path = e2k_strdup_with_trailing_slash (e2k_uri_path (trash_uri));
918 exchange_hierarchy_webdav_new (ExchangeAccount *account,
919 ExchangeHierarchyType type,
920 const char *hierarchy_name,
921 const char *physical_uri_prefix,
922 const char *internal_uri_prefix,
923 const char *owner_name,
924 const char *owner_email,
925 const char *source_uri,
926 gboolean deep_searchable)
928 ExchangeHierarchy *hier;
930 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
932 hier = g_object_new (EXCHANGE_TYPE_HIERARCHY_WEBDAV, NULL);
934 exchange_hierarchy_webdav_construct (EXCHANGE_HIERARCHY_WEBDAV (hier),
935 account, type, hierarchy_name,
938 owner_name, owner_email,
939 source_uri, deep_searchable);