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 /* ExchangeAccount: Handles a single configured Connector account. This
21 * is strictly a model object. ExchangeStorage handles the view.
28 #include "exchange-account.h"
29 #include "exchange-hierarchy-webdav.h"
30 #include "exchange-hierarchy-favorites.h"
31 #include "exchange-hierarchy-gal.h"
32 #include "exchange-folder-size.h"
33 #include "e-folder-exchange.h"
34 #include "e2k-autoconfig.h"
35 #include "e2k-encoding-utils.h"
36 #include "e2k-kerberos.h"
37 #include "e2k-propnames.h"
39 #include "e2k-utils.h"
40 #include "exchange-hierarchy-foreign.h"
42 /* This is an ugly hack to avoid API break */
43 /* Added for get_authtype */
44 #include "exchange-esource.h"
45 #include <libedataserverui/e-passwords.h>
47 #include <libgnome/gnome-util.h>
49 #include <glade/glade-xml.h>
50 #include <gtk/gtkdialog.h>
51 #include <gtk/gtklabel.h>
57 #define ADS_UF_DONT_EXPIRE_PASSWORD 0x10000
58 #define ONE_HUNDRED_NANOSECOND 0.000000100
59 #define SECONDS_IN_DAY 86400
61 struct _ExchangeAccountPrivate {
64 GHashTable *standard_uris;
65 ExchangeFolderSize *fsize;
68 gboolean connecting, connected;
71 GPtrArray *hierarchies;
72 GHashTable *hierarchies_by_folder, *foreign_hierarchies;
73 ExchangeHierarchy *favorites_hierarchy;
74 GHashTable *folders, *fresh_folders;
75 char *uri_authority, *http_uri_schema;
76 gboolean uris_use_email, offline_sync;
78 char *identity_name, *identity_email, *source_uri, *password_key;
79 char *username, *password, *windows_domain, *nt_domain, *ad_server;
81 E2kAutoconfigAuthPref auth_pref;
82 int ad_limit, passwd_exp_warn_period, quota_limit;
84 EAccountList *account_list;
87 GMutex *discover_data_lock;
88 GList *discover_datas;
98 static guint signals [LAST_SIGNAL] = { 0 };
100 #define PARENT_TYPE G_TYPE_OBJECT
101 static GObjectClass *parent_class = NULL;
103 static void dispose (GObject *);
104 static void finalize (GObject *);
105 static void remove_hierarchy (ExchangeAccount *account, ExchangeHierarchy *hier);
108 class_init (GObjectClass *object_class)
110 parent_class = g_type_class_ref (PARENT_TYPE);
112 /* virtual method override */
113 object_class->dispose = dispose;
114 object_class->finalize = finalize;
118 g_signal_new ("connected",
119 G_OBJECT_CLASS_TYPE (object_class),
121 G_STRUCT_OFFSET (ExchangeAccountClass, connected),
123 g_cclosure_marshal_VOID__OBJECT,
126 signals[NEW_FOLDER] =
127 g_signal_new ("new_folder",
128 G_OBJECT_CLASS_TYPE (object_class),
130 G_STRUCT_OFFSET (ExchangeAccountClass, new_folder),
132 g_cclosure_marshal_VOID__POINTER,
135 signals[REMOVED_FOLDER] =
136 g_signal_new ("removed_folder",
137 G_OBJECT_CLASS_TYPE (object_class),
139 G_STRUCT_OFFSET (ExchangeAccountClass, removed_folder),
141 g_cclosure_marshal_VOID__POINTER,
147 init (GObject *object)
149 ExchangeAccount *account = EXCHANGE_ACCOUNT (object);
151 account->priv = g_new0 (ExchangeAccountPrivate, 1);
152 account->priv->connect_lock = g_mutex_new ();
153 account->priv->hierarchies = g_ptr_array_new ();
154 account->priv->hierarchies_by_folder = g_hash_table_new (NULL, NULL);
155 account->priv->foreign_hierarchies = g_hash_table_new (g_str_hash, g_str_equal);
156 account->priv->folders = g_hash_table_new (g_str_hash, g_str_equal);
157 account->priv->fresh_folders = NULL;
158 account->priv->discover_data_lock = g_mutex_new ();
159 account->priv->account_online = UNSUPPORTED_MODE;
160 account->priv->nt_domain = NULL;
161 account->priv->fsize = exchange_folder_size_new ();
165 free_name (gpointer name, gpointer value, gpointer data)
171 free_folder (gpointer key, gpointer folder, gpointer data)
173 g_object_unref (folder);
177 dispose (GObject *object)
179 ExchangeAccount *account = EXCHANGE_ACCOUNT (object);
182 if (account->priv->account) {
183 g_object_unref (account->priv->account);
184 account->priv->account = NULL;
187 if (account->priv->account_list) {
188 g_object_unref (account->priv->account_list);
189 account->priv->account_list = NULL;
192 if (account->priv->ctx) {
193 g_object_unref (account->priv->ctx);
194 account->priv->ctx = NULL;
197 if (account->priv->gc) {
198 g_object_unref (account->priv->gc);
199 account->priv->gc = NULL;
202 if (account->priv->hierarchies) {
203 for (i = 0; i < account->priv->hierarchies->len; i++)
204 g_object_unref (account->priv->hierarchies->pdata[i]);
205 g_ptr_array_free (account->priv->hierarchies, TRUE);
206 account->priv->hierarchies = NULL;
209 if (account->priv->hierarchies_by_folder) {
210 g_hash_table_destroy (account->priv->hierarchies_by_folder);
211 account->priv->hierarchies_by_folder = NULL;
214 if (account->priv->foreign_hierarchies) {
215 g_hash_table_foreach (account->priv->foreign_hierarchies, free_name, NULL);
216 g_hash_table_destroy (account->priv->foreign_hierarchies);
217 account->priv->foreign_hierarchies = NULL;
220 if (account->priv->folders) {
221 g_hash_table_foreach (account->priv->folders, free_folder, NULL);
222 g_hash_table_destroy (account->priv->folders);
223 account->priv->folders = NULL;
226 if (account->priv->fresh_folders) {
227 g_hash_table_foreach (account->priv->fresh_folders, free_folder, NULL);
228 g_hash_table_destroy (account->priv->fresh_folders);
229 account->priv->fresh_folders = NULL;
232 G_OBJECT_CLASS (parent_class)->dispose (object);
236 free_uri (gpointer name, gpointer uri, gpointer data)
243 finalize (GObject *object)
245 ExchangeAccount *account = EXCHANGE_ACCOUNT (object);
247 if (account->account_name)
248 g_free (account->account_name);
249 if (account->storage_dir)
250 g_free (account->storage_dir);
251 if (account->exchange_server)
252 g_free (account->exchange_server);
253 if (account->home_uri)
254 g_free (account->home_uri);
255 if (account->public_uri)
256 g_free (account->public_uri);
257 if (account->legacy_exchange_dn)
258 g_free (account->legacy_exchange_dn);
259 if (account->default_timezone)
260 g_free (account->default_timezone);
262 if (account->priv->standard_uris) {
263 g_hash_table_foreach (account->priv->standard_uris,
265 g_hash_table_destroy (account->priv->standard_uris);
268 if (account->priv->uri_authority)
269 g_free (account->priv->uri_authority);
270 if (account->priv->http_uri_schema)
271 g_free (account->priv->http_uri_schema);
273 if (account->priv->identity_name)
274 g_free (account->priv->identity_name);
275 if (account->priv->identity_email)
276 g_free (account->priv->identity_email);
277 if (account->priv->source_uri)
278 g_free (account->priv->source_uri);
279 if (account->priv->password_key)
280 g_free (account->priv->password_key);
282 if (account->priv->username)
283 g_free (account->priv->username);
284 if (account->priv->password) {
285 memset (account->priv->password, 0,
286 strlen (account->priv->password));
287 g_free (account->priv->password);
289 if (account->priv->windows_domain)
290 g_free (account->priv->windows_domain);
292 if (account->priv->nt_domain)
293 g_free (account->priv->nt_domain);
295 if (account->priv->ad_server)
296 g_free (account->priv->ad_server);
298 if (account->priv->owa_url)
299 g_free (account->priv->owa_url);
301 if (account->priv->connect_lock)
302 g_mutex_free (account->priv->connect_lock);
304 if (account->priv->discover_data_lock)
305 g_mutex_free (account->priv->discover_data_lock);
307 g_free (account->priv);
309 G_OBJECT_CLASS (parent_class)->finalize (object);
313 E2K_MAKE_TYPE (exchange_account, ExchangeAccount, class_init, init, PARENT_TYPE)
317 exchange_account_rescan_tree (ExchangeAccount *account)
322 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
324 if (account->priv->fresh_folders) {
325 g_hash_table_foreach (account->priv->fresh_folders, free_folder, NULL);
326 g_hash_table_destroy (account->priv->fresh_folders);
327 account->priv->fresh_folders = NULL;
329 account->priv->fresh_folders = g_hash_table_new (g_str_hash, g_str_equal);
331 for (i = 0; i < account->priv->hierarchies->len; i++) {
332 /* First include the toplevel folder of the hierarchy as well */
333 toplevel = EXCHANGE_HIERARCHY (account->priv->hierarchies->pdata[i])->toplevel;
334 g_object_ref (toplevel);
335 g_hash_table_insert (account->priv->fresh_folders,
336 (char *)e_folder_exchange_get_path (toplevel),
339 exchange_hierarchy_scan_subtree (account->priv->hierarchies->pdata[i],
340 toplevel, account->priv->account_online);
341 exchange_hierarchy_rescan (account->priv->hierarchies->pdata[i]);
346 * ExchangeHierarchy folder creation/deletion/xfer notifications
350 hierarchy_new_folder (ExchangeHierarchy *hier, EFolder *folder,
351 ExchangeAccount *account)
353 int table_updated = 0;
354 const char *permanent_uri =
355 e_folder_exchange_get_permanent_uri (folder);
358 /* This makes the cleanup easier. We just unref it each time
359 * we find it in account->priv->folders.
361 key = (char *) e_folder_exchange_get_path (folder);
362 if (!g_hash_table_lookup (account->priv->folders, key)) {
363 /* Avoid dupilcations since the user could add a folder as
364 favorite even though it is already marked as favorite */
365 g_object_ref (folder);
366 g_hash_table_insert (account->priv->folders,
372 if (account->priv->fresh_folders) {
373 g_object_ref (folder);
374 g_hash_table_insert (account->priv->fresh_folders,
379 key = (char *) e_folder_get_physical_uri (folder);
380 if (!g_hash_table_lookup (account->priv->folders, key)) {
381 /* Avoid dupilcations since the user could add a folder as
382 favorite even though it is already marked as favorite */
383 g_object_ref (folder);
384 g_hash_table_insert (account->priv->folders,
390 key = (char *) e_folder_exchange_get_internal_uri (folder);
391 if (!g_hash_table_lookup (account->priv->folders, key)) {
392 /* The internal_uri for public folders and favorites folder
393 is same !!! Without this check the folder value could
394 overwrite the previously added folder. */
395 g_object_ref (folder);
396 g_hash_table_insert (account->priv->folders,
402 if (permanent_uri && (!g_hash_table_lookup (account->priv->folders,
404 g_object_ref (folder);
405 g_hash_table_insert (account->priv->folders,
406 (char *)permanent_uri,
413 g_hash_table_insert (account->priv->hierarchies_by_folder,
416 g_signal_emit (account, signals[NEW_FOLDER], 0, folder);
421 hierarchy_removed_folder (ExchangeHierarchy *hier, EFolder *folder,
422 ExchangeAccount *account)
424 if (!g_hash_table_lookup (account->priv->folders,
425 e_folder_exchange_get_path (folder)))
428 g_hash_table_remove (account->priv->folders,
429 e_folder_exchange_get_path (folder));
430 g_hash_table_remove (account->priv->folders,
431 e_folder_get_physical_uri (folder));
432 /* Dont remove this for favorites, as the internal_uri is shared
433 by the public folder as well */
434 if (hier->type != EXCHANGE_HIERARCHY_FAVORITES) {
435 g_hash_table_remove (account->priv->folders,
436 e_folder_exchange_get_internal_uri (folder));
438 g_hash_table_remove (account->priv->hierarchies_by_folder, folder);
439 g_signal_emit (account, signals[REMOVED_FOLDER], 0, folder);
441 if (folder == hier->toplevel)
442 remove_hierarchy (account, hier);
444 g_object_unref (folder);
445 g_object_unref (folder);
446 if (hier->type != EXCHANGE_HIERARCHY_FAVORITES) {
447 g_object_unref (folder);
452 get_folder (ExchangeAccount *account, const char *path,
453 EFolder **folder, ExchangeHierarchy **hier)
455 *folder = g_hash_table_lookup (account->priv->folders, path);
458 *hier = g_hash_table_lookup (account->priv->hierarchies_by_folder,
466 get_parent_and_name (ExchangeAccount *account, const char **path,
467 EFolder **parent, ExchangeHierarchy **hier)
469 char *name, *parent_path;
471 name = strrchr (*path + 1, '/');
475 parent_path = g_strndup (*path, name - *path);
476 *parent = g_hash_table_lookup (account->priv->folders, parent_path);
477 g_free (parent_path);
482 *hier = g_hash_table_lookup (account->priv->hierarchies_by_folder,
491 ExchangeAccountFolderResult
492 exchange_account_create_folder (ExchangeAccount *account,
493 const char *path, const char *type)
495 ExchangeHierarchy *hier;
498 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
499 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
501 if (!get_parent_and_name (account, &path, &parent, &hier))
502 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
504 return exchange_hierarchy_create_folder (hier, parent, path, type);
508 check_if_sf (gpointer key, gpointer value, gpointer user_data)
510 char *sf_href = (char *)value;
511 char *int_uri = (char *)user_data;
513 if (!strcmp (sf_href, int_uri))
514 return TRUE; /* Quit calling the callback */
516 return FALSE; /* Continue calling the callback till end of table */
519 ExchangeAccountFolderResult
520 exchange_account_remove_folder (ExchangeAccount *account, const char *path)
522 ExchangeHierarchy *hier;
526 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
527 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
529 d(g_print ("exchange_account_remove_folder: path=[%s]\n", path));
531 if (!get_folder (account, path, &folder, &hier))
532 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
534 int_uri = e_folder_exchange_get_internal_uri (folder);
536 if (g_hash_table_find (account->priv->standard_uris,
537 check_if_sf, (char *)int_uri)) {
538 return EXCHANGE_ACCOUNT_FOLDER_UNSUPPORTED_OPERATION;
541 return exchange_hierarchy_remove_folder (hier, folder);
544 ExchangeAccountFolderResult
545 exchange_account_xfer_folder (ExchangeAccount *account,
546 const char *source_path,
547 const char *dest_path,
548 gboolean remove_source)
550 EFolder *source, *dest_parent;
551 ExchangeHierarchy *source_hier, *dest_hier;
554 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
555 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
557 if (!get_folder (account, source_path, &source, &source_hier) ||
558 !get_parent_and_name (account, &dest_path, &dest_parent, &dest_hier)) {
559 /* Source or dest seems to not exist */
560 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
562 if (source_hier != dest_hier) {
563 /* Can't move something between hierarchies */
564 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
567 name = e_folder_get_name (source);
568 if (exchange_account_get_standard_uri (account, name))
569 return EXCHANGE_ACCOUNT_FOLDER_UNSUPPORTED_OPERATION;
573 return exchange_hierarchy_xfer_folder (source_hier, source,
574 dest_parent, dest_path,
579 remove_hierarchy (ExchangeAccount *account, ExchangeHierarchy *hier)
583 for (i = 0; i < account->priv->hierarchies->len; i++) {
584 if (account->priv->hierarchies->pdata[i] == hier) {
585 g_ptr_array_remove_index_fast (account->priv->hierarchies, i);
589 g_hash_table_remove (account->priv->foreign_hierarchies,
591 g_signal_handlers_disconnect_by_func (hier, hierarchy_new_folder, account);
592 g_signal_handlers_disconnect_by_func (hier, hierarchy_removed_folder, account);
593 g_object_unref (hier);
597 setup_hierarchy (ExchangeAccount *account, ExchangeHierarchy *hier)
599 g_ptr_array_add (account->priv->hierarchies, hier);
601 g_signal_connect (hier, "new_folder",
602 G_CALLBACK (hierarchy_new_folder), account);
603 g_signal_connect (hier, "removed_folder",
604 G_CALLBACK (hierarchy_removed_folder), account);
606 exchange_hierarchy_add_to_storage (hier);
610 setup_hierarchy_foreign (ExchangeAccount *account, ExchangeHierarchy *hier)
612 g_hash_table_insert (account->priv->foreign_hierarchies,
613 (char *)hier->owner_email, hier);
614 setup_hierarchy (account, hier);
617 struct discover_data {
618 const char *user, *folder_name;
622 static ExchangeHierarchy *
623 get_hierarchy_for (ExchangeAccount *account, E2kGlobalCatalogEntry *entry)
625 ExchangeHierarchy *hier;
626 char *hierarchy_name, *source;
627 char *physical_uri_prefix, *internal_uri_prefix;
629 hier = g_hash_table_lookup (account->priv->foreign_hierarchies,
634 /* i18n: This is the title of an "other user's folders"
635 hierarchy. Eg, "John Doe's Folders". */
636 hierarchy_name = g_strdup_printf (_("%s's Folders"),
637 entry->display_name);
638 source = g_strdup_printf ("exchange://%s@%s/", entry->mailbox,
639 account->exchange_server);
640 physical_uri_prefix = g_strdup_printf ("exchange://%s/;%s",
641 account->priv->uri_authority,
643 internal_uri_prefix = exchange_account_get_foreign_uri (account, entry,
646 hier = exchange_hierarchy_foreign_new (account, hierarchy_name,
650 entry->email, source);
651 g_free (hierarchy_name);
652 g_free (physical_uri_prefix);
653 g_free (internal_uri_prefix);
656 setup_hierarchy_foreign (account, hier);
660 ExchangeAccountFolderResult
661 exchange_account_discover_shared_folder (ExchangeAccount *account,
663 const char *folder_name,
666 struct discover_data dd;
667 ExchangeHierarchy *hier;
669 E2kGlobalCatalogStatus status;
670 E2kGlobalCatalogEntry *entry;
672 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
673 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
675 if (!account->priv->gc)
676 return EXCHANGE_ACCOUNT_FOLDER_GC_NOTREACHABLE;
678 email = strchr (user, '<');
680 email = g_strndup (email + 1, strcspn (email + 1, ">"));
682 email = g_strdup (user);
683 hier = g_hash_table_lookup (account->priv->foreign_hierarchies, email);
686 return exchange_hierarchy_foreign_add_folder (hier, folder_name, folder);
690 dd.folder_name = folder_name;
691 e2k_operation_init (&dd.op);
693 g_mutex_lock (account->priv->discover_data_lock);
694 account->priv->discover_datas =
695 g_list_prepend (account->priv->discover_datas, &dd);
696 g_mutex_unlock (account->priv->discover_data_lock);
698 status = e2k_global_catalog_lookup (account->priv->gc, &dd.op,
699 E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL,
701 E2K_GLOBAL_CATALOG_LOOKUP_EMAIL |
702 E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX,
705 e2k_operation_free (&dd.op);
707 g_mutex_lock (account->priv->discover_data_lock);
708 account->priv->discover_datas =
709 g_list_remove (account->priv->discover_datas, &dd);
710 g_mutex_unlock (account->priv->discover_data_lock);
712 if (status != E2K_GLOBAL_CATALOG_OK) {
713 if (status == E2K_GLOBAL_CATALOG_ERROR)
714 return EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR;
715 if (status == E2K_GLOBAL_CATALOG_NO_SUCH_USER)
716 return EXCHANGE_ACCOUNT_FOLDER_NO_SUCH_USER;
718 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
721 hier = get_hierarchy_for (account, entry);
722 return exchange_hierarchy_foreign_add_folder (hier, folder_name, folder);
726 exchange_account_cancel_discover_shared_folder (ExchangeAccount *account,
728 const char *folder_name)
730 struct discover_data *dd;
733 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
735 g_mutex_lock (account->priv->discover_data_lock);
736 for (dds = account->priv->discover_datas; dds; dds = dds->next) {
738 if (!strcmp (dd->user, user) &&
739 !strcmp (dd->folder_name, folder_name))
743 g_mutex_unlock (account->priv->discover_data_lock);
747 e2k_operation_cancel (&dd->op);
748 g_mutex_unlock (account->priv->discover_data_lock);
751 /* We can't actually cancel the hierarchy's attempt to get
752 * the folder, but we can remove the hierarchy if appropriate.
754 if (dd->hier && exchange_hierarchy_is_empty (dd->hier))
755 hierarchy_removed_folder (dd->hier, dd->hier->toplevel, account);
759 ExchangeAccountFolderResult
760 exchange_account_remove_shared_folder (ExchangeAccount *account,
763 ExchangeHierarchy *hier;
766 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
767 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
769 if (!get_folder (account, path, &folder, &hier))
770 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
771 if (!EXCHANGE_IS_HIERARCHY_FOREIGN (hier))
772 return EXCHANGE_ACCOUNT_FOLDER_UNSUPPORTED_OPERATION;
774 return exchange_hierarchy_remove_folder (hier, folder);
777 ExchangeAccountFolderResult
778 exchange_account_open_folder (ExchangeAccount *account, const char *path)
780 ExchangeHierarchy *hier;
784 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account),
785 EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
787 d(g_print ("exchange_account_remove_folder: path=[%s]\n", path));
789 if (!get_folder (account, path, &folder, &hier))
790 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
792 exchange_account_is_offline (account, &mode);
793 if (mode == ONLINE_MODE && !account->priv->connected &&
794 hier == (ExchangeHierarchy *)account->priv->hierarchies->pdata[0] &&
795 folder == hier->toplevel) {
796 /* The shell is asking us to open the personal folders
797 * hierarchy, but we're already planning to do that
798 * anyway. So just ignore the request for now.
800 return EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST;
803 return exchange_hierarchy_scan_subtree (hier, folder, mode);
806 ExchangeAccountFolderResult
807 exchange_account_add_favorite (ExchangeAccount *account,
810 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
811 g_return_val_if_fail (E_IS_FOLDER (folder), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
813 return exchange_hierarchy_favorites_add_folder (
814 account->priv->favorites_hierarchy,
818 ExchangeAccountFolderResult
819 exchange_account_remove_favorite (ExchangeAccount *account,
822 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
823 g_return_val_if_fail (E_IS_FOLDER (folder), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
825 return exchange_hierarchy_remove_folder (
826 EXCHANGE_HIERARCHY (account->priv->favorites_hierarchy),
831 exchange_account_is_favorite_folder (ExchangeAccount *account,
834 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
835 g_return_val_if_fail (E_IS_FOLDER (folder), EXCHANGE_ACCOUNT_FOLDER_GENERIC_ERROR);
837 return exchange_hierarchy_favorites_is_added (
838 EXCHANGE_HIERARCHY (account->priv->favorites_hierarchy),
843 context_redirect (E2kContext *ctx, E2kHTTPStatus status,
844 const char *old_uri, const char *new_uri,
845 ExchangeAccount *account)
849 folder = g_hash_table_lookup (account->priv->folders, old_uri);
853 g_hash_table_remove (account->priv->folders, old_uri);
854 e_folder_exchange_set_internal_uri (folder, new_uri);
855 g_hash_table_insert (account->priv->folders,
856 (char *)e_folder_exchange_get_internal_uri (folder),
861 set_sf_prop (const char *propname, E2kPropType type,
862 gpointer href, gpointer user_data)
864 ExchangeAccount *account = user_data;
866 propname = strrchr (propname, ':');
870 g_hash_table_insert (account->priv->standard_uris,
872 e2k_strdup_with_trailing_slash (href));
875 static const char *mailbox_info_props[] = {
876 E2K_PR_STD_FOLDER_CALENDAR,
877 E2K_PR_STD_FOLDER_CONTACTS,
878 E2K_PR_STD_FOLDER_DELETED_ITEMS,
879 E2K_PR_STD_FOLDER_DRAFTS,
880 E2K_PR_STD_FOLDER_INBOX,
881 E2K_PR_STD_FOLDER_JOURNAL,
882 E2K_PR_STD_FOLDER_NOTES,
883 E2K_PR_STD_FOLDER_OUTBOX,
884 E2K_PR_STD_FOLDER_SENT_ITEMS,
885 E2K_PR_STD_FOLDER_TASKS,
886 E2K_PR_STD_FOLDER_ROOT,
887 E2K_PR_STD_FOLDER_SENDMSG,
890 E2K_PR_EXCHANGE_TIMEZONE
892 static const int n_mailbox_info_props = G_N_ELEMENTS (mailbox_info_props);
897 account_moved (ExchangeAccount *account, E2kAutoconfig *ac)
899 E2kAutoconfigResult result;
902 result = e2k_autoconfig_check_exchange (ac, NULL);
903 if (result != E2K_AUTOCONFIG_OK)
905 result = e2k_autoconfig_check_global_catalog (ac, NULL);
906 if (result != E2K_AUTOCONFIG_OK)
909 eaccount = account->priv->account;
911 if (eaccount->source->url && eaccount->transport->url &&
912 !strcmp (eaccount->source->url, eaccount->transport->url)) {
913 g_free (eaccount->transport->url);
914 eaccount->transport->url = g_strdup (ac->account_uri);
916 g_free (eaccount->source->url);
917 eaccount->source->url = g_strdup (ac->account_uri);
919 e_account_list_change (account->priv->account_list, eaccount);
920 e_account_list_save (account->priv->account_list);
926 get_password (ExchangeAccount *account, E2kAutoconfig *ac, ExchangeAccountResult error)
930 if (error != EXCHANGE_ACCOUNT_CONNECT_SUCCESS)
931 e_passwords_forget_password ("Exchange", account->priv->password_key);
933 password = e_passwords_get_password ("Exchange", account->priv->password_key);
935 // SURF : if (exchange_component_is_interactive (global_exchange_component)) {
936 gboolean remember, oldremember;
940 prompt = g_strdup_printf (_("Enter password for %s"),
941 account->account_name);
942 oldremember = remember =
943 account->priv->account->source->save_passwd;
944 password = e_passwords_ask_password (
947 account->priv->password_key,
949 E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET,
952 if (remember != oldremember) {
953 account->priv->account->source->save_passwd = remember;
957 else if (!account->priv->account->source->save_passwd) {
958 /* get_password returns the password cached but user has not
959 * selected remember password option, forget this password
960 * whis is stored temporarily by e2k_validate_user()
962 e_passwords_forget_password ("Exchange", account->priv->password_key);
968 else if (!account->priv->account->source->save_passwd) {
969 /* get_password returns the password cached but user has not
970 * selected remember password option, forget this password
971 * whis is stored temporarily by e2k_validate_user()
973 e_passwords_forget_password ("Exchange", account->priv->password_key);
977 e2k_autoconfig_set_password (ac, password);
978 memset (password, 0, strlen (password));
986 /* This uses the kerberos calls to check if the authentication failure
987 * was due to the password getting expired. If the password has expired
988 * this returns TRUE, else it returns FALSE.
992 is_password_expired (ExchangeAccount *account, E2kAutoconfig *ac)
995 E2kKerberosResult result;
1000 domain = ac->w2k_domain;
1002 domain = strchr (account->priv->identity_email, '@');
1009 result = e2k_kerberos_check_password (ac->username, domain,
1011 if (result != E2K_KERBEROS_OK &&
1012 result != E2K_KERBEROS_PASSWORD_EXPIRED) {
1013 /* try again with nt domain */
1014 domain = ac->nt_domain;
1016 result = e2k_kerberos_check_password (ac->username,
1021 return (result == E2K_KERBEROS_PASSWORD_EXPIRED);
1026 find_passwd_exp_period (ExchangeAccount *account, E2kGlobalCatalogEntry *entry)
1028 double max_pwd_age = 0;
1029 int max_pwd_age_days;
1031 E2kGlobalCatalogStatus gcstatus;
1033 /* If user has not selected password expiry warning option, return */
1034 if (account->priv->passwd_exp_warn_period == -1)
1037 /* Check for password expiry period */
1038 /* This needs to be invoked after is_password_expired(), i.e.,
1039 only if password is not expired */
1041 /* Check for account control value for a user */
1043 e2k_operation_init (&gcop);
1044 gcstatus = e2k_global_catalog_lookup (account->priv->gc,
1046 E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL,
1047 account->priv->identity_email,
1048 E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL,
1050 e2k_operation_free (&gcop);
1051 if (gcstatus != E2K_GLOBAL_CATALOG_OK)
1054 if (entry->user_account_control & ADS_UF_DONT_EXPIRE_PASSWORD) {
1055 return -1; /* Password is not set to expire */
1058 /* Here we don't check not setting the password and expired password */
1059 /* Check for the maximum password age set */
1061 e2k_operation_init (&gcop);
1062 max_pwd_age = lookup_passwd_max_age (account->priv->gc, &gcop);
1063 e2k_operation_free (&gcop);
1065 if (max_pwd_age > 0) {
1066 /* Calculate password expiry period */
1068 ( max_pwd_age * ONE_HUNDRED_NANOSECOND ) / SECONDS_IN_DAY;
1070 if (max_pwd_age_days <= account->priv->passwd_exp_warn_period) {
1071 return max_pwd_age_days;
1078 exchange_account_get_password (ExchangeAccount *account)
1080 return e_passwords_get_password ("Exchange", account->priv->password_key);
1084 exchange_account_forget_password (ExchangeAccount *account)
1086 e_passwords_forget_password ("Exchange", account->priv->password_key);
1089 ExchangeAccountResult
1090 exchange_account_set_password (ExchangeAccount *account, char *old_pass, char *new_pass)
1093 E2kKerberosResult result;
1096 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), EXCHANGE_ACCOUNT_PASSWORD_CHANGE_FAILED);
1097 g_return_val_if_fail (old_pass != NULL, EXCHANGE_ACCOUNT_PASSWORD_CHANGE_FAILED);
1098 g_return_val_if_fail (new_pass != NULL, EXCHANGE_ACCOUNT_PASSWORD_CHANGE_FAILED);
1100 domain = account->priv->gc ? account->priv->gc->domain : NULL;
1102 domain = strchr (account->priv->identity_email, '@');
1107 /* email id is not proper, we return instead of trying nt_domain */
1108 return EXCHANGE_ACCOUNT_CONFIG_ERROR;
1111 result = e2k_kerberos_change_password (account->priv->username, domain,
1112 old_pass, new_pass);
1113 if (result != E2K_KERBEROS_OK && result != E2K_KERBEROS_PASSWORD_TOO_WEAK) {
1114 /* try with nt_domain */
1115 domain = account->priv->nt_domain;
1117 result = e2k_kerberos_change_password (account->priv->username,
1122 case E2K_KERBEROS_OK:
1123 e_passwords_forget_password ("Exchange", account->priv->password_key);
1124 e_passwords_add_password (account->priv->password_key, new_pass);
1125 if (account->priv->account->source->save_passwd)
1126 e_passwords_remember_password ("Exchange", account->priv->password_key);
1129 case E2K_KERBEROS_PASSWORD_TOO_WEAK:
1130 return EXCHANGE_ACCOUNT_PASSWORD_WEAK_ERROR;
1132 case E2K_KERBEROS_FAILED:
1134 return EXCHANGE_ACCOUNT_PASSWORD_CHANGE_FAILED;
1137 return EXCHANGE_ACCOUNT_PASSWORD_CHANGE_SUCCESS;
1139 g_warning ("exchange_account_set_password: Not implemented (no KRB5)");
1140 return EXCHANGE_ACCOUNT_PASSWORD_CHANGE_FAILED;
1145 exchange_account_set_save_password (ExchangeAccount *account, gboolean save_password)
1147 account->priv->account->source->save_passwd = save_password;
1151 exchange_account_is_save_password (ExchangeAccount *account)
1153 return account->priv->account->source->save_passwd;
1158 * exchange_account_set_offline:
1159 * @account: an #ExchangeAccount
1161 * This nullifies the connection and sets the account as offline.
1162 * The caller should take care that the required data is fetched
1163 * before calling this method.
1165 * Return value: Returns TRUE is successfully sets the account to
1166 * offline or FALSE if failed
1169 exchange_account_set_offline (ExchangeAccount *account)
1172 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), FALSE);
1174 g_mutex_lock (account->priv->connect_lock);
1175 if (account->priv->ctx) {
1176 g_object_unref (account->priv->ctx);
1177 account->priv->ctx = NULL;
1180 account->priv->account_online = OFFLINE_MODE;
1181 g_mutex_unlock (account->priv->connect_lock);
1186 * exchange_account_set_online:
1187 * @account: an #ExchangeAccount
1189 * This nullifies the connection and sets the account as offline.
1190 * The caller should take care that the required data is fetched
1191 * before calling this method.
1193 * Return value: Returns TRUE is successfully sets the account to
1194 * offline or FALSE if failed
1197 exchange_account_set_online (ExchangeAccount *account)
1199 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), FALSE);
1201 g_mutex_lock (account->priv->connect_lock);
1202 account->priv->account_online = ONLINE_MODE;
1203 g_mutex_unlock (account->priv->connect_lock);
1209 * exchange_account_is_offline:
1210 * @account: an #ExchangeAccount
1212 * Return value: Returns TRUE if account is offline
1215 exchange_account_is_offline (ExchangeAccount *account, int *state)
1217 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
1219 *state = account->priv->account_online;
1223 setup_account_hierarchies (ExchangeAccount *account)
1225 ExchangeHierarchy *hier, *personal_hier;
1226 ExchangeAccountFolderResult fresult;
1227 char *phys_uri_prefix, *dir;
1232 exchange_account_is_offline (account, &mode);
1234 if (mode == UNSUPPORTED_MODE)
1237 /* Check if folder hierarchies are already setup. */
1238 if (account->priv->hierarchies->len > 0)
1239 goto hierarchies_created;
1241 /* Set up Personal Folders hierarchy */
1242 phys_uri_prefix = g_strdup_printf ("exchange://%s/;personal",
1243 account->priv->uri_authority);
1244 hier = exchange_hierarchy_webdav_new (account,
1245 EXCHANGE_HIERARCHY_PERSONAL,
1246 _("Personal Folders"),
1249 account->priv->identity_name,
1250 account->priv->identity_email,
1251 account->priv->source_uri,
1254 setup_hierarchy (account, hier);
1255 g_free (phys_uri_prefix);
1257 /* Favorite Public Folders */
1258 phys_uri_prefix = g_strdup_printf ("exchange://%s/;favorites",
1259 account->priv->uri_authority);
1260 hier = exchange_hierarchy_favorites_new (account,
1261 _("Favorite Public Folders"),
1264 account->public_uri,
1265 account->priv->identity_name,
1266 account->priv->identity_email,
1267 account->priv->source_uri);
1268 setup_hierarchy (account, hier);
1269 g_free (phys_uri_prefix);
1270 account->priv->favorites_hierarchy = hier;
1272 /* Public Folders */
1273 phys_uri_prefix = g_strdup_printf ("exchange://%s/;public",
1274 account->priv->uri_authority);
1275 hier = exchange_hierarchy_webdav_new (account,
1276 EXCHANGE_HIERARCHY_PUBLIC,
1277 /* i18n: Outlookism */
1278 _("All Public Folders"),
1280 account->public_uri,
1281 account->priv->identity_name,
1282 account->priv->identity_email,
1283 account->priv->source_uri,
1285 setup_hierarchy (account, hier);
1286 g_free (phys_uri_prefix);
1288 /* Global Address List */
1289 phys_uri_prefix = g_strdup_printf ("gal://%s/gal",
1290 account->priv->uri_authority);
1291 /* i18n: Outlookism */
1292 hier = exchange_hierarchy_gal_new (account, _("Global Address List"),
1294 setup_hierarchy (account, hier);
1295 g_free (phys_uri_prefix);
1297 /* Other users' folders */
1298 d = g_dir_open (account->storage_dir, 0, NULL);
1300 while ((dent = g_dir_read_name (d))) {
1301 if (!strchr (dent, '@'))
1303 dir = g_strdup_printf ("%s/%s", account->storage_dir, dent);
1304 hier = exchange_hierarchy_foreign_new_from_dir (account, dir);
1309 setup_hierarchy_foreign (account, hier);
1314 hierarchies_created:
1316 /* Scan the personal and favorite folders so we can resolve references
1317 * to the Calendar, Contacts, etc even if the tree isn't
1321 /* Assuming the first element being personal hierarchy. */
1322 personal_hier = account->priv->hierarchies->pdata[0];
1324 fresult = exchange_hierarchy_scan_subtree (personal_hier,
1325 personal_hier->toplevel,
1327 if (fresult != EXCHANGE_ACCOUNT_FOLDER_OK) {
1328 account->priv->connecting = FALSE;
1332 account->mbox_size = exchange_hierarchy_webdav_get_total_folder_size (
1333 EXCHANGE_HIERARCHY_WEBDAV (personal_hier));
1335 fresult = exchange_hierarchy_scan_subtree (
1336 account->priv->favorites_hierarchy,
1337 account->priv->favorites_hierarchy->toplevel,
1339 if (fresult != EXCHANGE_ACCOUNT_FOLDER_OK &&
1340 fresult != EXCHANGE_ACCOUNT_FOLDER_DOES_NOT_EXIST) {
1341 account->priv->connecting = FALSE;
1348 * exchange_account_connect:
1349 * @account: an #ExchangeAccount
1351 * This attempts to connect to @account. If the shell has enabled user
1352 * interaction, then it will prompt for a password if needed, and put
1353 * up an error message if the connection attempt failed.
1355 * Return value: an #E2kContext, or %NULL if the connection attempt
1359 exchange_account_connect (ExchangeAccount *account, const char *pword,
1360 ExchangeAccountResult *info_result)
1363 E2kAutoconfigResult result;
1364 E2kHTTPStatus status;
1365 gboolean redirected = FALSE;
1367 int nresults = 0, mode;
1368 GByteArray *entryid;
1370 E2kGlobalCatalogStatus gcstatus;
1371 E2kGlobalCatalogEntry *entry;
1373 char *user_name = NULL;
1375 *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR;
1376 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1378 *info_result = EXCHANGE_ACCOUNT_CONNECT_SUCCESS;
1379 exchange_account_is_offline (account, &mode);
1381 g_mutex_lock (account->priv->connect_lock);
1383 if (mode == UNSUPPORTED_MODE) {
1384 *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR;
1385 account->priv->connecting = FALSE;
1386 g_mutex_unlock (account->priv->connect_lock);
1390 if (account->priv->connecting || mode == OFFLINE_MODE) {
1391 g_mutex_unlock (account->priv->connect_lock);
1392 if (mode == OFFLINE_MODE) {
1393 setup_account_hierarchies (account);
1394 *info_result = EXCHANGE_ACCOUNT_OFFLINE;
1397 *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR;
1400 } else if (account->priv->ctx) {
1401 g_mutex_unlock (account->priv->connect_lock);
1402 return account->priv->ctx;
1405 account->priv->connecting = TRUE;
1407 if (account->priv->windows_domain)
1408 user_name = g_strdup_printf ("%s\\%s", account->priv->windows_domain, account->priv->username);
1410 user_name = g_strdup (account->priv->username);
1412 ac = e2k_autoconfig_new (account->home_uri,
1415 account->priv->auth_pref);
1418 e2k_autoconfig_set_gc_server (ac, account->priv->ad_server,
1419 account->priv->ad_limit);
1422 account->priv->connecting = FALSE;
1423 g_mutex_unlock (account->priv->connect_lock);
1424 *info_result = EXCHANGE_ACCOUNT_PASSWORD_INCORRECT;
1425 e2k_autoconfig_free (ac);
1429 e2k_autoconfig_set_password (ac, pword);
1432 account->priv->ctx = e2k_autoconfig_get_context (ac, NULL, &result);
1434 if (!account->priv->nt_domain && ac->nt_domain)
1435 account->priv->nt_domain = g_strdup (ac->nt_domain);
1437 account->priv->nt_domain = NULL;
1439 if (result != E2K_AUTOCONFIG_OK) {
1441 if ( is_password_expired (account, ac)) {
1442 *info_result = EXCHANGE_ACCOUNT_PASSWORD_EXPIRED;
1443 account->priv->connecting = FALSE;
1444 g_mutex_unlock (account->priv->connect_lock);
1445 e2k_autoconfig_free (ac);
1451 case E2K_AUTOCONFIG_AUTH_ERROR:
1452 *info_result = EXCHANGE_ACCOUNT_PASSWORD_INCORRECT;
1453 e2k_autoconfig_free (ac);
1454 account->priv->connecting = FALSE;
1455 g_mutex_unlock (account->priv->connect_lock);
1458 case E2K_AUTOCONFIG_AUTH_ERROR_TRY_DOMAIN:
1459 *info_result = EXCHANGE_ACCOUNT_DOMAIN_ERROR;
1460 e2k_autoconfig_free (ac);
1461 account->priv->connecting = FALSE;
1462 g_mutex_unlock (account->priv->connect_lock);
1465 case E2K_AUTOCONFIG_AUTH_ERROR_TRY_NTLM:
1467 goto try_connect_again;
1469 case E2K_AUTOCONFIG_AUTH_ERROR_TRY_BASIC:
1471 goto try_connect_again;
1473 case E2K_AUTOCONFIG_REDIRECT:
1474 if (!redirected && account_moved (account, ac))
1475 goto try_connect_again;
1478 case E2K_AUTOCONFIG_TRY_SSL:
1479 if (account_moved (account, ac))
1480 goto try_connect_again;
1487 e2k_autoconfig_free (ac);
1488 account->priv->connecting = FALSE;
1489 account->priv->account_online = OFFLINE_MODE; /* correct? */
1492 case E2K_AUTOCONFIG_REDIRECT:
1493 case E2K_AUTOCONFIG_TRY_SSL:
1494 *info_result = EXCHANGE_ACCOUNT_MAILBOX_NA;
1496 case E2K_AUTOCONFIG_EXCHANGE_5_5:
1497 *info_result = EXCHANGE_ACCOUNT_VERSION_ERROR;
1499 case E2K_AUTOCONFIG_NOT_EXCHANGE:
1500 case E2K_AUTOCONFIG_NO_OWA:
1501 *info_result = EXCHANGE_ACCOUNT_WSS_ERROR;
1503 case E2K_AUTOCONFIG_NO_MAILBOX:
1504 *info_result = EXCHANGE_ACCOUNT_NO_MAILBOX;
1506 case E2K_AUTOCONFIG_CANT_RESOLVE:
1507 *info_result = EXCHANGE_ACCOUNT_RESOLVE_ERROR;
1509 case E2K_AUTOCONFIG_CANT_CONNECT:
1510 *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR;
1512 case E2K_AUTOCONFIG_CANCELLED:
1515 *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR;
1519 g_mutex_unlock (account->priv->connect_lock);
1523 account->priv->gc = e2k_autoconfig_get_global_catalog (ac, NULL);
1524 e2k_autoconfig_free (ac);
1526 status = e2k_context_propfind (account->priv->ctx, NULL,
1529 n_mailbox_info_props,
1530 &results, &nresults);
1532 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
1533 account->priv->connecting = FALSE;
1534 *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR;
1535 g_mutex_unlock (account->priv->connect_lock);
1536 return NULL; /* FIXME: what error has happened? */
1540 account->priv->standard_uris =
1541 g_hash_table_new (e2k_ascii_strcase_hash,
1542 e2k_ascii_strcase_equal);
1543 e2k_properties_foreach (results[0].props, set_sf_prop, account);
1545 /* FIXME: we should get these from the autoconfig */
1546 entryid = e2k_properties_get_prop (results[0].props, PR_STORE_ENTRYID);
1548 account->legacy_exchange_dn = g_strdup (e2k_entryid_to_dn (entryid));
1550 tz = e2k_properties_get_prop (results[0].props, E2K_PR_EXCHANGE_TIMEZONE);
1552 account->default_timezone = g_strdup (tz);
1555 if (!setup_account_hierarchies (account)) {
1556 *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR;
1557 g_mutex_unlock (account->priv->connect_lock);
1559 e2k_results_free (results, nresults);
1560 return NULL; /* FIXME: what error has happened? */
1563 account->priv->account_online = ONLINE_MODE;
1564 account->priv->connecting = FALSE;
1565 account->priv->connected = TRUE;
1567 if (!account->priv->gc)
1569 /* Check for quota usage */
1570 e2k_operation_init (&gcop);
1571 gcstatus = e2k_global_catalog_lookup (account->priv->gc, &gcop,
1572 E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL,
1573 account->priv->identity_email,
1574 E2K_GLOBAL_CATALOG_LOOKUP_QUOTA,
1576 e2k_operation_free (&gcop);
1578 /* FIXME: warning message should have quota limit value
1580 if (gcstatus == E2K_GLOBAL_CATALOG_OK) {
1582 if (entry->quota_norecv &&
1583 account->mbox_size >= entry->quota_norecv) {
1584 *info_result = EXCHANGE_ACCOUNT_QUOTA_RECIEVE_ERROR;
1585 account->priv->quota_limit = entry->quota_norecv;
1586 } else if (entry->quota_nosend &&
1587 account->mbox_size >= entry->quota_nosend) {
1588 *info_result = EXCHANGE_ACCOUNT_QUOTA_SEND_ERROR;
1589 account->priv->quota_limit = entry->quota_nosend;
1590 } else if (entry->quota_warn &&
1591 account->mbox_size >= entry->quota_warn) {
1592 *info_result = EXCHANGE_ACCOUNT_QUOTA_WARN;
1593 account->priv->quota_limit = entry->quota_warn;
1598 g_signal_connect (account->priv->ctx, "redirect",
1599 G_CALLBACK (context_redirect), account);
1601 g_signal_emit (account, signals[CONNECTED], 0, account->priv->ctx);
1602 g_mutex_unlock (account->priv->connect_lock);
1604 e2k_results_free (results, nresults);
1605 return account->priv->ctx;
1609 * exchange_account_is_offline_sync_set:
1610 * @account: an #ExchangeAccount
1612 * Return value: TRUE if offline_sync is set for @account and FALSE if not.
1615 exchange_account_is_offline_sync_set (ExchangeAccount *account, int *mode)
1617 *mode = UNSUPPORTED_MODE;
1619 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
1621 if (account->priv->offline_sync)
1622 *mode = OFFLINE_MODE;
1624 *mode = ONLINE_MODE;
1628 * exchange_account_get_context:
1629 * @account: an #ExchangeAccount
1631 * Return value: @account's #E2kContext, if it is connected and
1632 * online, or %NULL if not.
1635 exchange_account_get_context (ExchangeAccount *account)
1637 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1639 return account->priv->ctx;
1643 * exchange_account_get_global_catalog:
1644 * @account: an #ExchangeAccount
1646 * Return value: @account's #E2kGlobalCatalog, if it is connected and
1647 * online, or %NULL if not.
1650 exchange_account_get_global_catalog (ExchangeAccount *account)
1652 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1654 return account->priv->gc;
1658 * exchange_account_fetch:
1659 * @acct: an #ExchangeAccount
1661 * Return value: @account's #EAccount, if it is connected and
1662 * online, or %NULL if not.
1665 exchange_account_fetch (ExchangeAccount *acct)
1667 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (acct), NULL);
1669 return acct->priv->account;
1673 * exchange_account_get_standard_uri:
1674 * @account: an #ExchangeAccount
1675 * @item: the short name of the standard URI
1677 * Looks up the value of one of the standard URIs on @account.
1678 * Supported values for @item are:
1679 * "calendar", "contacts", "deleteditems", "drafts", "inbox",
1680 * "journal", "notes", "outbox", "sentitems", "tasks", and
1681 * "sendmsg" (the special mail submission URI)
1683 * Return value: the value of the standard URI, or %NULL if the
1684 * account is not connected or the property is invalid or not
1685 * defined on @account.
1688 exchange_account_get_standard_uri (ExchangeAccount *account, const char *item)
1690 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1692 if (!account->priv->standard_uris)
1695 return g_hash_table_lookup (account->priv->standard_uris, item);
1699 * exchange_account_get_standard_uri_for:
1700 * @account: an #ExchangeAccount
1701 * @home_uri: the home URI of a user
1702 * @std_uri_prop: the %E2K_PR_STD_FOLDER property to look up
1704 * Looks up the URI of a folder in another user's mailbox.
1706 * Return value: the URI of the folder, or %NULL if either the folder
1707 * doesn't exist or the user doesn't have permission to access it.
1710 exchange_account_get_standard_uri_for (ExchangeAccount *account,
1711 const char *home_uri,
1712 const char *std_uri_prop)
1714 char *foreign_uri, *prop;
1715 E2kHTTPStatus status;
1719 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1721 foreign_uri = e2k_uri_concat (home_uri, "NON_IPM_SUBTREE");
1722 status = e2k_context_propfind (account->priv->ctx, NULL, foreign_uri,
1724 &results, &nresults);
1725 g_free (foreign_uri);
1727 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status) || nresults == 0)
1730 prop = e2k_properties_get_prop (results[0].props, std_uri_prop);
1732 foreign_uri = e2k_strdup_with_trailing_slash (prop);
1735 e2k_results_free (results, nresults);
1741 * exchange_account_get_foreign_uri:
1742 * @account: an #ExchangeAccount
1743 * @entry: an #E2kGlobalCatalogEntry with mailbox data
1744 * @std_uri_prop: the %E2K_PR_STD_FOLDER property to look up, or %NULL
1746 * Looks up the URI of a folder in another user's mailbox. If
1747 * @std_uri_prop is %NULL, the URI for the top level of the user's
1748 * mailbox is returned.
1750 * Return value: the URI of the folder, or %NULL if either the folder
1751 * doesn't exist or the user doesn't have permission to access it.
1754 exchange_account_get_foreign_uri (ExchangeAccount *account,
1755 E2kGlobalCatalogEntry *entry,
1756 const char *std_uri_prop)
1758 char *home_uri, *foreign_uri;
1760 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1762 if (account->priv->uris_use_email) {
1765 mailbox = g_strndup (entry->email, strcspn (entry->email, "@"));
1766 home_uri = g_strdup_printf (account->priv->http_uri_schema,
1767 entry->exchange_server, mailbox);
1770 home_uri = g_strdup_printf (account->priv->http_uri_schema,
1771 entry->exchange_server,
1777 foreign_uri = exchange_account_get_standard_uri_for (account,
1786 * exchange_account_get_hierarchy_by_email:
1787 * @account: an #ExchangeAccount
1788 * @email: email id of the foreign user
1790 * If the hierarchy is present just return it back. Else try to get it
1791 * from the filesystem and return it.
1793 * Return value: Returns the ExchangeHierarchy of the foreign user's folder.
1797 exchange_account_get_hierarchy_by_email (ExchangeAccount *account, const char *email)
1800 ExchangeHierarchy *hier = NULL;
1803 g_return_val_if_fail (email != NULL, NULL);
1805 hier = g_hash_table_lookup (account->priv->foreign_hierarchies, email);
1807 dir = g_strdup_printf ("%s/%s", account->storage_dir, email);
1808 if (g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
1809 hier = exchange_hierarchy_foreign_new_from_dir (account, dir);
1812 exchange_account_is_offline (account, &mode);
1813 setup_hierarchy_foreign (account, hier);
1822 * exchange_account_get_folder:
1823 * @account: an #ExchangeAccount
1824 * @path_or_uri: the shell path or URI referring to the folder
1826 * Return value: an #EFolder corresponding to the indicated
1830 exchange_account_get_folder (ExchangeAccount *account,
1831 const char *path_or_uri)
1833 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1837 return g_hash_table_lookup (account->priv->folders, path_or_uri);
1841 folder_comparator (const void *a, const void *b)
1843 EFolder **fa = (EFolder **)a;
1844 EFolder **fb = (EFolder **)b;
1846 return strcmp (e_folder_exchange_get_path (*fa),
1847 e_folder_exchange_get_path (*fb));
1850 struct _folders_tree {
1857 add_folder (gpointer key, gpointer value, gpointer folders)
1859 EFolder *folder = value;
1861 d(g_print ("%s(%d):%s: key=[%s]\t folder-path=[%s]\n", __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION,
1862 key, e_folder_exchange_get_path (folder)));
1864 /* Each folder appears under three different keys, but
1865 * we only want to add it to the results array once. So
1866 * we only add when we see the "path" key.
1868 if (!strcmp (key, e_folder_exchange_get_path (folder)))
1869 g_ptr_array_add (folders, folder);
1873 add_folder_tree (gpointer key, gpointer value, gpointer folders)
1875 EFolder *folder = value;
1876 struct _folders_tree *fld_tree = (struct _folders_tree *) folders;
1878 if (!fld_tree || !fld_tree->path)
1881 if (g_str_has_prefix (key, fld_tree->path))
1882 add_folder (key, folder, fld_tree->folders);
1886 * exchange_account_get_folders:
1887 * @account: an #ExchangeAccount
1889 * Return an array of folders (sorted such that parents will occur
1890 * before children). If the caller wants to keep up to date with the
1891 * list of folders, he should also listen to %new_folder and
1892 * %removed_folder signals.
1894 * Return value: an array of folders. The array should be freed with
1895 * g_ptr_array_free().
1898 exchange_account_get_folders (ExchangeAccount *account)
1902 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1904 folders = g_ptr_array_new ();
1905 /* if (account->priv->fresh_folders)
1906 g_hash_table_foreach (account->priv->fresh_folders, add_folder, folders);
1909 g_hash_table_foreach (account->priv->folders, add_folder, folders);
1911 qsort (folders->pdata, folders->len,
1912 sizeof (EFolder *), folder_comparator);
1918 * exchange_account_get_folder_tree:
1919 * @account: an #ExchangeAccount
1921 * Return an array of folders (sorted such that parents will occur
1922 * before children). If the caller wants to keep up to date with the
1923 * list of folders, he should also listen to %new_folder and
1924 * %removed_folder signals.
1926 * Return value: an array of folders. The array should be freed with
1927 * g_ptr_array_free().
1930 exchange_account_get_folder_tree (ExchangeAccount *account, char* path)
1932 GPtrArray *folders = NULL;
1933 EFolder *folder = NULL;
1934 ExchangeHierarchy *hier = NULL;
1936 struct _folders_tree *fld_tree = NULL;
1938 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1940 if (!get_folder (account, path, &folder, &hier))
1943 exchange_hierarchy_scan_subtree (hier, folder, account->priv->account_online);
1945 folders = g_ptr_array_new ();
1946 fld_tree = g_new0 (struct _folders_tree, 1);
1947 fld_tree->path = path;
1948 fld_tree->folders = folders;
1950 /* if (account->priv->fresh_folders)
1951 g_hash_table_foreach (account->priv->fresh_folders, add_folder, folders);
1954 g_hash_table_foreach (account->priv->folders, add_folder_tree, fld_tree);
1956 qsort (folders->pdata, folders->len,
1957 sizeof (EFolder *), folder_comparator);
1965 * exchange_account_get_quota_limit:
1966 * @account: an #ExchangeAccount
1968 * Return the value of the quota limit reached.
1970 * Return value: an int
1973 exchange_account_get_quota_limit (ExchangeAccount *account)
1975 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), 0);
1977 return account->priv->quota_limit;
1981 exchange_account_check_password_expiry (ExchangeAccount *account)
1983 E2kGlobalCatalogEntry *entry; /* This is never set before it's used! */
1984 int max_pwd_age_days = -1;
1986 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), 0);
1988 max_pwd_age_days = find_passwd_exp_period (account, entry);
1989 return max_pwd_age_days;
1993 exchange_account_get_username (ExchangeAccount *account)
1995 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
1997 return account->priv->username;
2001 * exchange_account_get_email_id :
2002 * @account : #ExchangeAccount
2004 * Retunrs user's e-mail id.
2006 * Return value : e-mail id string.
2009 exchange_account_get_email_id (ExchangeAccount *account)
2011 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
2013 return account->priv->identity_email;
2017 * exchange_account_folder_size_add :
2018 * @account : #ExchangeAccount
2020 * @size : Size of @folder_name
2022 * Updates the #ExchangeFolderSize object with the @size of @folder_name
2024 * Return value : void
2027 exchange_account_folder_size_add (ExchangeAccount *account,
2028 const char *folder_name,
2031 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
2033 exchange_folder_size_update (account->priv->fsize, folder_name, size);
2037 * exchange_account_folder_size_remove :
2038 * @account : #ExchangeAccount
2041 * Removes the entry for @folder_name in #ExchangeFolderSize object
2043 * Return value : void
2046 exchange_account_folder_size_remove (ExchangeAccount *account,
2047 const char *folder_name)
2049 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
2051 exchange_folder_size_remove (account->priv->fsize, folder_name);
2055 * exchange_account_folder_size_rename :
2056 * @account : #ExchangeAccount
2057 * @old_name : Old name of the folder
2058 * @new_name : New name of the folder
2060 * Removes the entry for @old_name in #ExchangeFolderSize object and adds
2061 * a new entry for @new_name with the same folder size
2063 * Return value : void
2066 exchange_account_folder_size_rename (ExchangeAccount *account,
2067 const char *old_name,
2068 const char *new_name)
2070 gdouble cached_size;
2072 g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
2074 cached_size = exchange_folder_size_get (account->priv->fsize,
2076 if (cached_size >= 0) {
2077 exchange_folder_size_remove (account->priv->fsize, old_name);
2078 exchange_folder_size_update (account->priv->fsize,
2079 new_name, cached_size);
2085 * exchange_account_folder_size_get_model :
2086 * @account : #ExchangeAccount
2088 * Returns the model store of #ExchangeFolderSize object
2090 * Return value : The model store. A GtkListStore
2093 exchange_account_folder_size_get_model (ExchangeAccount *account)
2095 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
2097 return exchange_folder_size_get_model (account->priv->fsize);
2101 exchange_account_get_authtype (ExchangeAccount *account)
2103 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL);
2105 if (account->priv->auth_pref == E2K_AUTOCONFIG_USE_BASIC)
2106 return g_strdup ("Basic");
2107 else if (account->priv->auth_pref == E2K_AUTOCONFIG_USE_NTLM)
2108 return g_strdup ("NTLM");
2115 * exchange_account_new:
2116 * @adata: an #EAccount
2118 * An #ExchangeAccount is essentially an #E2kContext with
2119 * associated configuration.
2121 * Return value: a new #ExchangeAccount corresponding to @adata
2124 exchange_account_new (EAccountList *account_list, EAccount *adata)
2126 ExchangeAccount *account;
2127 char *enc_user, *mailbox;
2128 const char *param, *proto="http", *owa_path, *pf_server, *owa_url;
2129 const char *passwd_exp_warn_period, *offline_sync;
2132 uri = e2k_uri_new (adata->source->url);
2134 g_warning ("Could not parse exchange uri '%s'",
2135 adata->source->url);
2139 account = g_object_new (EXCHANGE_TYPE_ACCOUNT, NULL);
2142 account->priv->account_list = account_list;
2143 g_object_ref (account_list);
2144 account->priv->account = adata;
2145 g_object_ref (adata);
2147 account->account_name = g_strdup (adata->name);
2149 account->storage_dir = g_strdup_printf ("%s/.evolution/exchange/%s@%s",
2151 uri->user, uri->host);
2152 /*account->account_filename = strrchr (account->storage_dir, '/') + 1;
2153 e_filename_make_safe (account->account_filename); */
2156 account->priv->identity_name = g_strdup (adata->id->name);
2157 account->priv->identity_email = g_strdup (adata->id->address);
2159 /* URI, etc, info */
2160 enc_user = e2k_uri_encode (uri->user, FALSE, "@/;:");
2163 account->priv->uri_authority = g_strdup_printf ("%s;auth=%s@%s", enc_user,
2164 uri->authmech, uri->host);
2166 account->priv->uri_authority = g_strdup_printf ("%s@%s", enc_user,
2170 account->account_filename = account->priv->uri_authority;
2172 account->priv->source_uri = g_strdup_printf ("exchange://%s/", account->priv->uri_authority);
2174 /* Backword compatibility; FIXME, we should just migrate the
2175 * password from this to source_uri.
2176 * old_uri_authority = g_strdup_printf ("%s@%s", enc_user,
2178 * old_uri_authority needs to be used in the key for migrating
2179 * passwords remembered.
2181 account->priv->password_key = g_strdup_printf ("exchange://%s/",
2182 account->priv->uri_authority);
2184 account->priv->username = g_strdup (uri->user);
2186 account->priv->windows_domain = g_strdup (uri->domain);
2188 account->priv->windows_domain = NULL;
2189 account->exchange_server = g_strdup (uri->host);
2190 if (uri->authmech && !strcmp (uri->authmech, "Basic"))
2191 account->priv->auth_pref = E2K_AUTOCONFIG_USE_BASIC;
2193 account->priv->auth_pref = E2K_AUTOCONFIG_USE_NTLM;
2194 param = e2k_uri_get_param (uri, "ad_server");
2195 if (param && *param) {
2196 account->priv->ad_server = g_strdup (param);
2197 param = e2k_uri_get_param (uri, "ad_limit");
2199 account->priv->ad_limit = atoi (param);
2202 passwd_exp_warn_period = e2k_uri_get_param (uri, "passwd_exp_warn_period");
2203 if (!passwd_exp_warn_period || !*passwd_exp_warn_period)
2204 account->priv->passwd_exp_warn_period = -1;
2206 account->priv->passwd_exp_warn_period = atoi (passwd_exp_warn_period);
2208 offline_sync = e2k_uri_get_param (uri, "offline_sync");
2210 account->priv->offline_sync = FALSE;
2212 account->priv->offline_sync = TRUE;
2214 owa_path = e2k_uri_get_param (uri, "owa_path");
2215 if (!owa_path || !*owa_path)
2216 owa_path = "exchange";
2217 else if (*owa_path == '/')
2220 pf_server = e2k_uri_get_param (uri, "pf_server");
2221 if (!pf_server || !*pf_server)
2222 pf_server = uri->host;
2224 /* We set protocol reading owa_url, instead of having use_ssl parameter
2225 * because we don't have SSL section anymore in the account creation
2226 * druid and account editor
2228 /* proto = e2k_uri_get_param (uri, "use_ssl") ? "https" : "http"; */
2230 owa_url = e2k_uri_get_param (uri, "owa_url");
2232 account->priv->owa_url = g_strdup (owa_url);
2233 if (!strncmp (owa_url, "https:", 6))
2237 if (uri->port != 0) {
2238 account->priv->http_uri_schema =
2239 g_strdup_printf ("%s://%%s:%d/%s/%%s/",
2240 proto, uri->port, owa_path);
2241 account->public_uri =
2242 g_strdup_printf ("%s://%s:%d/public",
2243 proto, pf_server, uri->port);
2245 account->priv->http_uri_schema =
2246 g_strdup_printf ("%s://%%s/%s/%%s/", proto, owa_path);
2247 account->public_uri =
2248 g_strdup_printf ("%s://%s/public", proto, pf_server);
2251 param = e2k_uri_get_param (uri, "mailbox");
2252 if (!param || !*param)
2254 else if (!g_ascii_strncasecmp (param, account->priv->identity_email, strlen (param)))
2255 account->priv->uris_use_email = TRUE;
2256 mailbox = e2k_uri_encode (param, TRUE, "/");
2257 account->home_uri = g_strdup_printf (account->priv->http_uri_schema,
2258 uri->host, mailbox);
2261 param = e2k_uri_get_param (uri, "filter");
2263 account->filter_inbox = TRUE;
2264 param = e2k_uri_get_param (uri, "filter_junk");
2266 account->filter_junk = TRUE;
2267 param = e2k_uri_get_param (uri, "filter_junk_inbox");
2269 account->filter_junk_inbox_only = TRUE;
2277 * exchange_account_get_hierarchy_by_type:
2278 * @account: an #ExchangeAccount
2279 * @type: Hierarchy type
2281 * Returns the non-foreign hierarchy pointer for the requested type
2283 * Return value: Returns the hierarchy pointer for the requested type
2287 exchange_account_get_hierarchy_by_type (ExchangeAccount* acct,
2288 ExchangeHierarchyType type)
2292 g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (acct), NULL);
2293 g_return_val_if_fail (type != EXCHANGE_HIERARCHY_FOREIGN, NULL);
2295 for (i = 0; i < acct->priv->hierarchies->len; i++) {
2296 if (EXCHANGE_HIERARCHY (acct->priv->hierarchies->pdata[i])->type == type)
2297 return EXCHANGE_HIERARCHY (acct->priv->hierarchies->pdata[i]);