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 /* ExchangeHierarchyForeign: class for a hierarchy consisting of a
21 * selected subset of folders from another user's mailbox.
33 #include <glib/gstdio.h>
35 #include "libedataserver/e-source-list.h"
36 #include "libedataserver/e-xml-hash-utils.h"
37 #include "libedataserver/e-xml-utils.h"
39 #include "e-folder-exchange.h"
40 #include "e2k-propnames.h"
41 #include "e2k-types.h"
43 #include "e2k-utils.h"
44 #include "exchange-account.h"
45 #include "exchange-esource.h"
46 #include "exchange-hierarchy-foreign.h"
47 #include "exchange-types.h"
49 struct _ExchangeHierarchyForeignPrivate {
50 GMutex *hide_private_lock;
51 gboolean checked_hide_private;
54 extern const char *exchange_localfreebusy_path;
56 #define PARENT_TYPE EXCHANGE_TYPE_HIERARCHY_SOMEDAV
57 static ExchangeHierarchySomeDAVClass *parent_class = NULL;
59 static GPtrArray *get_hrefs (ExchangeHierarchySomeDAV *hsd);
60 static ExchangeAccountFolderResult create_folder (ExchangeHierarchy *hier,
64 static ExchangeAccountFolderResult remove_folder (ExchangeHierarchy *hier,
66 static ExchangeAccountFolderResult scan_subtree (ExchangeHierarchy *hier,
69 static void finalize (GObject *object);
72 class_init (GObjectClass *object_class)
74 ExchangeHierarchyClass *hierarchy_class =
75 EXCHANGE_HIERARCHY_CLASS (object_class);
76 ExchangeHierarchySomeDAVClass *somedav_class =
77 EXCHANGE_HIERARCHY_SOMEDAV_CLASS (object_class);
79 parent_class = g_type_class_ref (PARENT_TYPE);
81 /* virtual method override */
82 object_class->finalize = finalize;
84 hierarchy_class->create_folder = create_folder;
85 hierarchy_class->remove_folder = remove_folder;
86 hierarchy_class->scan_subtree = scan_subtree;
88 somedav_class->get_hrefs = get_hrefs;
92 init (GObject *object)
94 ExchangeHierarchyForeign *hfor = EXCHANGE_HIERARCHY_FOREIGN (object);
95 ExchangeHierarchy *hier = EXCHANGE_HIERARCHY (object);
97 hfor->priv = g_new0 (ExchangeHierarchyForeignPrivate, 1);
98 hfor->priv->hide_private_lock = g_mutex_new ();
99 hier->hide_private_items = TRUE;
103 finalize (GObject *object)
105 ExchangeHierarchyForeign *hfor = EXCHANGE_HIERARCHY_FOREIGN (object);
107 g_mutex_free (hfor->priv->hide_private_lock);
110 G_OBJECT_CLASS (parent_class)->finalize (object);
113 E2K_MAKE_TYPE (exchange_hierarchy_foreign, ExchangeHierarchyForeign, class_init, init, PARENT_TYPE)
115 static const char *privacy_props[] = {
116 PR_DELEGATES_ENTRYIDS,
117 PR_DELEGATES_SEE_PRIVATE,
119 static const int n_privacy_props = sizeof (privacy_props) / sizeof (privacy_props[0]);
122 check_hide_private (ExchangeHierarchy *hier)
124 ExchangeHierarchyForeign *hfor = EXCHANGE_HIERARCHY_FOREIGN (hier);
126 E2kHTTPStatus status;
129 GPtrArray *entryids, *privflags;
131 const char *my_dn, *delegate_dn;
134 g_mutex_lock (hfor->priv->hide_private_lock);
136 if (hfor->priv->checked_hide_private) {
137 g_mutex_unlock (hfor->priv->hide_private_lock);
141 uri = e2k_uri_concat (hier->account->home_uri,
142 "NON_IPM_SUBTREE/Freebusy%20Data/LocalFreebusy.EML");
143 ctx = exchange_account_get_context (hier->account);
145 status = e2k_context_propfind (ctx, NULL, uri,
146 privacy_props, n_privacy_props,
147 &results, &nresults);
150 hfor->priv->checked_hide_private = TRUE;
152 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status) || nresults == 0) {
153 g_mutex_unlock (hfor->priv->hide_private_lock);
156 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (results[0].status) ||
157 !results[0].props || nresults > 1) {
158 e2k_results_free (results, nresults);
159 g_mutex_unlock (hfor->priv->hide_private_lock);
163 entryids = e2k_properties_get_prop (results[0].props,
164 PR_DELEGATES_ENTRYIDS);
165 privflags = e2k_properties_get_prop (results[0].props,
166 PR_DELEGATES_SEE_PRIVATE);
167 if (entryids && privflags) {
168 my_dn = hier->account->legacy_exchange_dn;
169 for (i = 0; i < entryids->len && i < privflags->len; i++) {
170 entryid = entryids->pdata[i];
171 delegate_dn = e2k_entryid_to_dn (entryid);
174 !g_ascii_strcasecmp (delegate_dn, my_dn) &&
175 privflags->pdata[i] &&
176 atoi (privflags->pdata[i]))
177 hier->hide_private_items = FALSE;
182 e2k_results_free (results, nresults);
183 g_mutex_unlock (hfor->priv->hide_private_lock);
187 remove_all_cb (ExchangeHierarchy *hier, EFolder *folder, gpointer user_data)
189 exchange_hierarchy_removed_folder (hier, folder);
193 hierarchy_foreign_cleanup (ExchangeHierarchy *hier)
197 exchange_hierarchy_webdav_offline_scan_subtree (hier, remove_all_cb,
200 mf_path = e_folder_exchange_get_storage_file (hier->toplevel, "hierarchy.xml");
204 exchange_hierarchy_removed_folder (hier, hier->toplevel);
207 static const char *folder_props[] = {
208 E2K_PR_EXCHANGE_FOLDER_CLASS,
209 E2K_PR_HTTPMAIL_UNREAD_COUNT,
210 E2K_PR_DAV_DISPLAY_NAME,
213 static const int n_folder_props = sizeof (folder_props) / sizeof (folder_props[0]);
215 static ExchangeAccountFolderResult
216 find_folder (ExchangeHierarchy *hier, const char *uri, EFolder **folder_out)
218 ExchangeHierarchyWebDAV *hwd = EXCHANGE_HIERARCHY_WEBDAV (hier);
219 E2kContext *ctx = exchange_account_get_context (hier->account);
220 E2kHTTPStatus status;
226 status = e2k_context_propfind (ctx, NULL, uri,
227 folder_props, n_folder_props,
228 &results, &nresults);
229 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status))
230 return exchange_hierarchy_webdav_status_to_folder_result (status);
233 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
235 access = e2k_properties_get_prop (results[0].props, PR_ACCESS);
236 if (!access || !atoi (access)) {
237 e2k_results_free (results, nresults);
238 return EXCHANGE_ACCOUNT_FOLDER_PERMISSION_DENIED;
241 folder = exchange_hierarchy_webdav_parse_folder (hwd, hier->toplevel,
243 e2k_results_free (results, nresults);
246 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
248 exchange_hierarchy_new_folder (hier, folder);
251 *folder_out = folder;
253 g_object_unref (folder);
254 return EXCHANGE_ACCOUNT_FOLDER_OK;
258 const char *name, *prop;
260 { N_("Calendar"), E2K_PR_STD_FOLDER_CALENDAR },
261 { N_("Contacts"), E2K_PR_STD_FOLDER_CONTACTS },
262 { N_("Deleted Items"), E2K_PR_STD_FOLDER_DELETED_ITEMS },
263 { N_("Drafts"), E2K_PR_STD_FOLDER_DRAFTS },
264 { N_("Inbox"), E2K_PR_STD_FOLDER_INBOX },
265 { N_("Journal"), E2K_PR_STD_FOLDER_JOURNAL },
266 { N_("Notes"), E2K_PR_STD_FOLDER_NOTES },
267 { N_("Outbox"), E2K_PR_STD_FOLDER_OUTBOX },
268 { N_("Sent Items"), E2K_PR_STD_FOLDER_SENT_ITEMS },
269 { N_("Tasks"), E2K_PR_STD_FOLDER_TASKS }
271 const static int n_std_folders = sizeof (std_folders) / sizeof (std_folders[0]);
272 static ExchangeAccountFolderResult
273 create_internal (ExchangeHierarchy *hier, EFolder *parent,
274 const char *name, const char *type, EFolder **folder_out)
276 ExchangeAccountFolderResult result;
277 char *literal_uri = NULL, *standard_uri = NULL;
278 const char *home_uri;
281 /* For now, no nesting */
282 if (parent != hier->toplevel || strchr (name + 1, '/'))
283 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
285 check_hide_private (hier);
287 home_uri = e_folder_exchange_get_internal_uri (hier->toplevel);
288 literal_uri = e2k_uri_concat (home_uri, name);
289 if (exchange_account_get_folder (hier->account, literal_uri)) {
290 g_free (literal_uri);
291 if (exchange_hierarchy_is_empty (hier))
292 hierarchy_foreign_cleanup (hier);
293 return EXCHANGE_ACCOUNT_FOLDER_ALREADY_EXISTS;
296 for (i = 0; i < n_std_folders; i++) {
297 if (g_ascii_strcasecmp (std_folders[i].name, name) != 0 &&
298 g_utf8_collate (_(std_folders[i].name), name) != 0)
301 standard_uri = exchange_account_get_standard_uri_for (
302 hier->account, home_uri, std_folders[i].prop);
305 if (!strcmp (literal_uri, standard_uri)) {
306 g_free (standard_uri);
311 if (exchange_account_get_folder (hier->account, standard_uri)) {
312 g_free (standard_uri);
313 g_free (literal_uri);
314 if (exchange_hierarchy_is_empty (hier))
315 hierarchy_foreign_cleanup (hier);
316 return EXCHANGE_ACCOUNT_FOLDER_ALREADY_EXISTS;
322 result = find_folder (hier, literal_uri, folder_out);
323 if (result == EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST && standard_uri)
324 result = find_folder (hier, standard_uri, folder_out);
326 g_free (literal_uri);
327 g_free (standard_uri);
329 /* If the hierarchy is now empty, then we must have just been
330 * created but then the add failed. So remove it again.
332 if (exchange_hierarchy_is_empty (hier))
333 hierarchy_foreign_cleanup (hier);
339 static ExchangeAccountFolderResult
340 create_folder (ExchangeHierarchy *hier, EFolder *parent,
341 const char *name, const char *type)
343 return create_internal (hier, parent, name, type, NULL);
346 static ExchangeAccountFolderResult
347 remove_folder (ExchangeHierarchy *hier, EFolder *folder)
349 const char *folder_type, *physical_uri;
351 /* Temp Fix for remove fav folders. see #59168 */
352 /* remove ESources */
353 folder_type = e_folder_get_type_string (folder);
354 physical_uri = e_folder_get_physical_uri (folder);
356 if (strcmp (folder_type, "calendar") == 0) {
357 remove_folder_esource (hier->account,
358 EXCHANGE_CALENDAR_FOLDER,
361 else if (strcmp (folder_type, "tasks") == 0) {
362 remove_folder_esource (hier->account,
363 EXCHANGE_TASKS_FOLDER,
366 else if (strcmp (folder_type, "contacts") == 0) {
367 remove_folder_esource (hier->account,
368 EXCHANGE_CONTACTS_FOLDER,
371 if (folder != hier->toplevel)
372 exchange_hierarchy_removed_folder (hier, folder);
374 if (folder == hier->toplevel || exchange_hierarchy_is_empty (hier))
375 hierarchy_foreign_cleanup (hier);
377 return EXCHANGE_ACCOUNT_FOLDER_OK;
380 static ExchangeAccountFolderResult
381 scan_subtree (ExchangeHierarchy *hier, EFolder *folder, int mode)
383 ExchangeAccountFolderResult folder_result;
385 check_hide_private (hier);
387 folder_result = EXCHANGE_HIERARCHY_CLASS (parent_class)->scan_subtree (hier, folder, mode);
389 if (exchange_hierarchy_is_empty (hier))
390 hierarchy_foreign_cleanup (hier);
392 return folder_result;
396 add_href (ExchangeHierarchy *hier, EFolder *folder, gpointer hrefs)
398 char *uri = g_strdup (e_folder_exchange_get_internal_uri (folder));
400 g_ptr_array_add (hrefs, (gpointer) uri);
404 get_hrefs (ExchangeHierarchySomeDAV *hsd)
408 hrefs = g_ptr_array_new ();
409 exchange_hierarchy_webdav_offline_scan_subtree (EXCHANGE_HIERARCHY (hsd), add_href, hrefs);
414 * exchange_hierarchy_foreign_add_folder:
415 * @hier: the hierarchy
416 * @folder_name: the name of the folder to add
417 * @folder: on successful return, the created folder
419 * Adds a new folder to @hier.
421 * Return value: the folder result.
423 ExchangeAccountFolderResult
424 exchange_hierarchy_foreign_add_folder (ExchangeHierarchy *hier,
425 const char *folder_name,
428 ExchangeAccountFolderResult result;
429 const char *folder_type = NULL;
430 const char *physical_uri = NULL;
431 char *new_folder_name;
432 guint folder_mask = 0;
434 result = create_internal (hier, hier->toplevel, folder_name, NULL, folder);
435 if (result == EXCHANGE_ACCOUNT_FOLDER_OK) {
436 /* Add the esources */
437 folder_type = e_folder_get_type_string (*folder);
438 physical_uri = e_folder_get_physical_uri (*folder);
439 new_folder_name = g_strdup_printf("%s's %s",
440 hier->owner_name, folder_name);
442 if (!(strcmp (folder_type, "calendar")) ||
443 !(strcmp (folder_type, "calendar/public"))) {
444 folder_mask = EXCHANGE_CALENDAR_FOLDER | FORIEGN_FOLDER_FLAG;
445 add_folder_esource (hier->account,
450 else if (!(strcmp (folder_type, "tasks")) ||
451 !(strcmp (folder_type, "tasks/public"))) {
452 folder_mask = EXCHANGE_TASKS_FOLDER | FORIEGN_FOLDER_FLAG;
453 add_folder_esource (hier->account,
458 else if (!(strcmp (folder_type, "contacts")) ||
459 !(strcmp (folder_type, "contacts/public")) ||
460 !(strcmp (folder_type, "contacts/ldap"))) {
461 folder_mask = EXCHANGE_CONTACTS_FOLDER | FORIEGN_FOLDER_FLAG;
462 add_folder_esource (hier->account,
467 g_free (new_folder_name);
472 static ExchangeHierarchy *
473 hierarchy_foreign_new (ExchangeAccount *account,
474 const char *hierarchy_name,
475 const char *physical_uri_prefix,
476 const char *internal_uri_prefix,
477 const char *owner_name,
478 const char *owner_email,
479 const char *source_uri)
481 ExchangeHierarchyForeign *hfor;
483 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
485 hfor = g_object_new (EXCHANGE_TYPE_HIERARCHY_FOREIGN, NULL);
487 exchange_hierarchy_webdav_construct (EXCHANGE_HIERARCHY_WEBDAV (hfor),
489 EXCHANGE_HIERARCHY_FOREIGN,
493 owner_name, owner_email,
497 return EXCHANGE_HIERARCHY (hfor);
501 * exchange_hierarchy_foreign_new:
502 * @account: an #ExchangeAccount
503 * @hierarchy_name: the name of the hierarchy
504 * @physical_uri_prefix: the prefix of physical URIs in this hierarchy
505 * @internal_uri_prefix: the prefix of internal (http) URIs in this hierarchy
506 * @owner_name: display name of the owner of the hierarchy
507 * @owner_email: email address of the owner of the hierarchy
508 * @source_uri: evolution-mail source uri for the hierarchy
510 * Creates a new (initially empty) hierarchy for another user's
513 * Return value: the new hierarchy.
516 exchange_hierarchy_foreign_new (ExchangeAccount *account,
517 const char *hierarchy_name,
518 const char *physical_uri_prefix,
519 const char *internal_uri_prefix,
520 const char *owner_name,
521 const char *owner_email,
522 const char *source_uri)
524 ExchangeHierarchy *hier;
529 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
531 hier = hierarchy_foreign_new (account, hierarchy_name,
534 owner_name, owner_email,
537 props = g_hash_table_new (g_str_hash, g_str_equal);
538 g_hash_table_insert (props, "name", (char *)hierarchy_name);
539 g_hash_table_insert (props, "physical_uri_prefix",
540 (char *)physical_uri_prefix);
541 g_hash_table_insert (props, "internal_uri_prefix",
542 (char *)internal_uri_prefix);
543 g_hash_table_insert (props, "owner_name", (char *)owner_name);
544 g_hash_table_insert (props, "owner_email", (char *)owner_email);
545 g_hash_table_insert (props, "source_uri", (char *)source_uri);
547 mf_path = e_folder_exchange_get_storage_file (hier->toplevel, "hierarchy.xml");
548 doc = e_xml_from_hash (props, E_XML_HASH_TYPE_PROPERTY,
549 "foreign-hierarchy");
550 e_xml_save_file (mf_path, doc);
551 g_hash_table_destroy (props);
559 * exchange_hierarchy_foreign_new_from_dir:
560 * @account: an #ExchangeAccount
561 * @folder_path: pathname to a directory containing a hierarchy.xml file
563 * Recreates a new hierarchy from saved values.
565 * Return value: the new hierarchy.
568 exchange_hierarchy_foreign_new_from_dir (ExchangeAccount *account,
569 const char *folder_path)
571 ExchangeHierarchy *hier;
576 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
577 g_return_val_if_fail (folder_path != NULL, NULL);
579 mf_path = g_build_filename (folder_path, "hierarchy.xml", NULL);
581 doc = e_xml_parse_file (mf_path);
587 props = e_xml_to_hash (doc, E_XML_HASH_TYPE_PROPERTY);
590 hier = hierarchy_foreign_new (account,
591 g_hash_table_lookup (props, "name"),
592 g_hash_table_lookup (props, "physical_uri_prefix"),
593 g_hash_table_lookup (props, "internal_uri_prefix"),
594 g_hash_table_lookup (props, "owner_name"),
595 g_hash_table_lookup (props, "owner_email"),
596 g_hash_table_lookup (props, "source_uri"));
598 e_xml_destroy_hash (props);