aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--plugins/exchange-operations/ChangeLog241
-rw-r--r--plugins/exchange-operations/Makefile.am37
-rw-r--r--plugins/exchange-operations/exchange-account-setup.c844
-rw-r--r--plugins/exchange-operations/exchange-ask-password.c357
-rw-r--r--plugins/exchange-operations/exchange-change-password.glade243
-rw-r--r--plugins/exchange-operations/exchange-change-password.h21
-rw-r--r--plugins/exchange-operations/exchange-config-listener.c1095
-rw-r--r--plugins/exchange-operations/exchange-config-listener.h58
-rw-r--r--plugins/exchange-operations/exchange-delegates-user.c332
-rw-r--r--plugins/exchange-operations/exchange-delegates-user.h70
-rw-r--r--plugins/exchange-operations/exchange-delegates.c973
-rw-r--r--plugins/exchange-operations/exchange-delegates.glade628
-rw-r--r--plugins/exchange-operations/exchange-delegates.h21
-rw-r--r--plugins/exchange-operations/exchange-folder-size.c343
-rw-r--r--plugins/exchange-operations/exchange-folder-size.h56
-rw-r--r--plugins/exchange-operations/exchange-folder-tree.glade92
-rw-r--r--plugins/exchange-operations/exchange-operations.c94
-rw-r--r--plugins/exchange-operations/exchange-operations.h34
-rw-r--r--plugins/exchange-operations/org-gnome-exchange-operations.eplug.in75
20 files changed, 5619 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index ae45baa857..c31c3c2c3a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2005-06-12 Sarfraaz Ahmed <asarfraaz@novell.com>
+
+ * plugins/exchange-operations : Added a new plugin directory for
+ renaming exchange-account-setup as exchange-operations.
+
2005-06-10 Sarfraaz Ahmed <asarfraaz@novell.com>
* configure.in : Added CAMEL_EXCHANGE CFLAGS/LIBS for exchange plugins.
diff --git a/plugins/exchange-operations/ChangeLog b/plugins/exchange-operations/ChangeLog
new file mode 100644
index 0000000000..ac0c8a56dc
--- /dev/null
+++ b/plugins/exchange-operations/ChangeLog
@@ -0,0 +1,241 @@
+2005-06-12 Sarfraaz Ahmed <asarfraaz@novell.com>
+
+ * Initial commit to rename Exchange-account-setup as Exchange
+ Operations.
+
+2005-06-10 Sarfraaz Ahmed <asarfraaz@novell.com>
+
+ * exchange-account-setup.c (e_plugin_lib_enable)
+ (free_exchange_listener)(e_plugin_lib_enable) : Added new to
+ initialise the config listener.
+ (update_state) : Now updates the state directly from the server and not
+ from the file as earlier.
+ (owa_authenticate_user) : Authenticates and validates directly from
+ the server instead of using the camel APIs as earlier.
+ (set_oof_info) : Sets the state directly on the server
+ * Makefile.am : Use CAMEL_EXCHANGE CFLAGS and LIBS
+
+2005-05-21 Sarfraaz Ahmed <asarfraaz@novell.com>
+
+ Added a bunch of files to create exchange plugins for UI changes
+ * exchange-change-password.[ch]/.glade
+ * exchange-delegates.[ch]/.glade
+ * exchange-delegate-user.[ch]
+ * exchange-folder-size.[ch]
+ * exchange-folder-tree.glade
+
+2005-05-16 Not Zed <NotZed@Ximian.com>
+
+ * exchange-account-setup.c: moved e-error to e-util
+
+2005-03-18 Sushma Rai <rsushma@novell.com>
+
+ * exchange-account-setup.c (org_gnome_exchange_owa_url): When the
+ Exchange account doesn't contain OWA url parameter, setting the OWA
+ url value in the receive page and saving it.
+ (construct_owa_url): Forming OWA url, from the existing parameters for
+ hostname, ssl, OWA path and mailbox name.
+ see #73627
+
+2005-03-17 Sushma Rai <rsushma@novell.com>
+
+ * exchange-account-setup.c (exchange_authtype_changed): Setting the
+ authentication mechanism to both transport and source urls.
+
+2005-03-05 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added the commit function
+ to save OOF data. This got missed in the previous commit. Also removed
+ duplicate accountWizard definition.
+
+2005-02-28 JP Rosevear <jpr@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: add account wizard item
+
+2005-02-27 Sushma Rai <rsushma@novell.com>
+
+ * exchange-account-setup.c (org_gnome_exchange_settings): Storing the
+ oof data enterted by user to a file, displaying the existing data on
+ this page.
+ (toggled_state): Signal hander for radiobutton, that sets the oof state.
+ (update_state): Signal handler that reads the oof message entered.
+ (org_gnome_exchange_commit): Stores the oof data and does cleanup.
+ (store_oof_info): Checks for the oof information file, writes the
+ oof state and message to the file.
+ (destroy_oof_data): Frees OOFData.
+
+2005-02-26 Sushma Rai <rsushma@novell.com>
+
+ * exchange-account-setup.c (org_gnome_exchange_check_options):
+ Returning FALSE on NULL host name or set to "" for receive page,
+ so that one can proceed with the account creation only after
+ hostname is retrived and set by validate().
+
+2005-02-24 Björn Torkelsson <torkel@acc.umu.se>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added author and
+ fixed description.
+
+2005-02-21 Not Zed <NotZed@Ximian.com>
+
+ * exchange-account-setup.c (org_gnome_exchange_owa_url): if the
+ host is null in the url, set it to "", so it has a non-null value.
+
+2005-02-07 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added plugin for adding
+ auth type section to editor.
+
+ * exchange-account-setup.c (org_gnome_exchange_auth_section): Adding
+ and handling authentication type for exchange account.
+
+2005-01-28 Not Zed <NotZed@Ximian.com>
+
+ ** related to bug #71520.
+
+ * exchange-account-setup.c: All but re-written.
+ Fixed the license of the file.
+ Fixed a translation string.
+ Modified return condition check.
+ Fixed problem over writing current account with the old data.
+ Removed duplicated code.
+ Removed the hack for handling NULL hostname, now using
+ CAMEL_URL_HIDDEN_HOST url flag in the provider.
+ Using E_ACCOUNT_SOURCE_SAVE_PASSWD for remember password.
+ Removed the way owa url entry was added to table in config section,
+ Now econfig supports tables.
+
+ * exchange-ask-password.c: removed, functionality moved to
+ exchange-account-setup.c.
+
+2005-01-25 Sushma Rai <rsushma@novell.com>
+
+ * exchange-account-setup.c (create_page): Fixed empty
+ string being marked for translation problem. #71644
+
+2005-01-23 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added plugins
+ for handling hiding auth type section in druid.
+
+ * exchange-account-setup.c (add_owa_entry_to_editor): Changed the
+ button label to "Authenticate" from OK
+
+ * exchange-ask-password.c (add_owa_entry): Changed the button label
+ to Authenticate.
+ (org_gnome_exchange_handle_auth): Hiding Auth section in receive page.
+ (org_gnome_exchange_handle_send_auth_option): Hiding the Auth section
+ in send page
+
+2005-01-22 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added
+ org_gnome_exchange_check_options plugin.
+
+ * exchange-ask-password.c (org_gnome_exchange_check_options):
+ Reads OWA URL value and sets use_ssl and owa_url values for source
+ account url.
+
+ * exchange-account-setup.c (org_gnome_exchange_set_url)
+ (add_owa_entry_to_editor): Reading owa url value from gconf and setting
+ owa url value in the account editor. Fixes #71378
+
+2005-01-19 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c (validate_exchange_user): Fix for remembering
+ password if user has selected that option, while creating the account.
+
+2005-01-18 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c (validate_exchange_user): Reading the return
+ value of user validation function. Fixes #71385
+
+2005-01-18 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c (validate_exchange_user): Filling up
+ user name so that page check doesn't fail. Fixes #71384
+
+2005-01-18 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c (org_gnome_exchange_read_url):
+ Setting dummy host name, which will be reset to proper
+ hostname once the user is authenticated.
+
+2005-01-18 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Moved two account
+ editor plugins unser same hook class.
+
+ * exchange-ask-password.c: Reorganized the code.
+ Used accessor functions to read and set EAccount values.
+ Removed editor specific factory function add_owa_entry_to_editor()
+ from here.
+
+ * exchange-account-setup.c: Reorganized the code.
+ Moved add_owa_entry_to_editor() and it's sub functions into this file.
+ (org_gnome_exchange_account_setup): Reading source url and transport
+ url values stored in gconf and filling up the EAccount structure.
+ This fixes the problem of page check failure, as improper source url
+ and transport url values, as we don't read host name in the editor.
+ (org_gnome_exchange_set_url): Similar.
+
+2005-01-17 Sushma Rai <rsushma@novell.com>
+
+ * Makefile.am: Linking to camel libs. Fixes plugin loading problem
+ due to undefined camel symbol, during evolution startup.
+
+2005-01-13 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Combined
+ all the plugins into one.
+
+2005-01-12 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c: (validate_exchange_user):
+ Added one more error condition check.
+
+2005-01-12 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Factory
+ method to add owa url entry to account editor.
+
+ * exchange-ask-password.c: (org_gnome_exchange_set_url)
+ (add_owa_entry_to_editor): Adds owa url entry to the
+ account editor for Exchange account.
+ (validate_exchange_user): Using the CamelProvider private
+ function defined by Exchange camel provider.
+
+2005-01-11 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Removed page check plugin
+
+ * exchange-ask-password.c: Added a button to prompt for password
+ instead of listening on page next signal
+
+2005-01-11 Not Zed <NotZed@Ximian.com>
+
+ * Makefile.am: fix LDFLAGS variable name.
+
+2005-01-10 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c: (validate_exchange_user):
+ Corrected argument order.
+
+2005-01-10 Sushma Rai <rsushma@novell.com>
+
+ * org-gnome-exchange-account-setup.eplug.in: Added plugin to read
+ OWA url entry to the account set up druid.
+
+ * exchange-ask-password.c: Create a entry for OWA URL and reads the
+ URL value.
+
+2005-01-09 Sushma Rai <rsushma@novell.com>
+
+ * exchange-ask-password.c: Pops up password dialog and validates
+ user credentials once owa url and user name are entered.
+
+ * org-gnome-exchange-account-setup.eplug.in: Added page check plugin.
+
+2005-01-09 Sushma Rai <rsushma@novell.com>
+
+ * Intial ckeckin, Plugin for Exchange account specific settings
diff --git a/plugins/exchange-operations/Makefile.am b/plugins/exchange-operations/Makefile.am
new file mode 100644
index 0000000000..d72261e4d2
--- /dev/null
+++ b/plugins/exchange-operations/Makefile.am
@@ -0,0 +1,37 @@
+INCLUDES = -I . \
+ -I$(top_srcdir) \
+ $(EVOLUTION_MAIL_CFLAGS) \
+ $(CAMEL_EXCHANGE_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(LDAP_CFLAGS) \
+ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \
+ -DCONNECTOR_GLADEDIR=\""$(gladedir)"\"
+
+
+@EVO_PLUGIN_RULE@
+
+plugin_DATA = org-gnome-exchange-operations.eplug
+plugin_LTLIBRARIES = liborg-gnome-exchange-operations.la
+
+liborg_gnome_exchange_operations_la_SOURCES = \
+ exchange-operations.c \
+ exchange-config-listener.c \
+ exchange-account-setup.c
+
+liborg_gnome_exchange_operations_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(CAMEL_LIBS) \
+ $(CAMEL_EXCHANGE_LIBS)
+
+liborg_gnome_exchange_operations_la_LDFLAGS = -module -avoid-version
+
+glade_DATA = \
+ exchange-change-password.glade \
+ exchange-delegates.glade \
+ exchange-folder-tree.glade
+
+EXTRA_DIST = \
+ org-gnome-exchange-operations.eplug.in \
+ $(glade_DATA)
+
diff --git a/plugins/exchange-operations/exchange-account-setup.c b/plugins/exchange-operations/exchange-account-setup.c
new file mode 100644
index 0000000000..83186d528e
--- /dev/null
+++ b/plugins/exchange-operations/exchange-account-setup.c
@@ -0,0 +1,844 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Sushma Rai <rsushma@novell.com>
+ * Copyright (C) 2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e-util/e-account.h"
+#include "e-util/e-error.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <glib/gi18n.h>
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkdialog.h>
+#include <gconf/gconf-client.h>
+#include <camel/camel-provider.h>
+#include <camel/camel-url.h>
+#include <camel/camel-service.h>
+#include <libedataserver/e-xml-hash-utils.h>
+#include <exchange/e2k-validate.h>
+#include <exchange/exchange-oof.h>
+#include <exchange/exchange-account.h>
+#include <e-util/e-dialog-utils.h>
+#include "exchange-config-listener.h"
+#include "exchange-operations.h"
+#include "mail/em-account-editor.h"
+#include "mail/em-config.h"
+
+
+static ExchangeConfigListener *exchange_global_config_listener = NULL;
+
+GtkWidget* org_gnome_exchange_settings(EPlugin *epl, EConfigHookItemFactoryData *data);
+GtkWidget *org_gnome_exchange_owa_url(EPlugin *epl, EConfigHookItemFactoryData *data);
+gboolean org_gnome_exchange_check_options(EPlugin *epl, EConfigHookPageCheckData *data);
+GtkWidget *org_gnome_exchange_auth_section (EPlugin *epl, EConfigHookItemFactoryData *data);
+void org_gnome_exchange_commit (EPlugin *epl, EConfigHookItemFactoryData *data);
+
+/* NB: This should be given a better name, it is NOT a camel service, it is only a camel-exchange one */
+typedef gboolean (CamelProviderValidateUserFunc) (CamelURL *camel_url, const char *url, gboolean *remember_password, CamelException *ex);
+
+#define OOF_INFO_FILE_NAME "oof_info.xml"
+
+typedef struct {
+ CamelProviderValidateUserFunc *validate_user;
+}CamelProviderValidate;
+
+CamelServiceAuthType camel_exchange_ntlm_authtype = {
+ /* i18n: "Secure Password Authentication" is an Outlookism */
+ N_("Secure Password"),
+
+ /* i18n: "NTLM" probably doesn't translate */
+ N_("This option will connect to the Exchange server using "
+ "secure password (NTLM) authentication."),
+
+ "",
+ TRUE
+};
+
+CamelServiceAuthType camel_exchange_password_authtype = {
+ N_("Plaintext Password"),
+
+ N_("This option will connect to the Exchange server using "
+ "standard plaintext password authentication."),
+
+ "Basic",
+ TRUE
+};
+
+
+typedef struct {
+ gboolean state;
+ char *message;
+ GtkWidget *text_view;
+}OOFData;
+
+OOFData *oof_data;
+
+static void
+update_state (GtkTextBuffer *buffer, gpointer data)
+{
+ if (gtk_text_buffer_get_modified (buffer)) {
+ GtkTextIter start, end;
+ if (oof_data->message)
+ g_free (oof_data->message);
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ oof_data->message = gtk_text_buffer_get_text (buffer, &start,
+ &end, FALSE);
+ gtk_text_buffer_set_modified (buffer, FALSE);
+ }
+}
+
+static void
+toggled_state (GtkToggleButton *button, gpointer data)
+{
+ gboolean current_oof_state = gtk_toggle_button_get_active (button);
+
+ if (current_oof_state == oof_data->state)
+ return;
+ oof_data->state = current_oof_state;
+ gtk_widget_set_sensitive (oof_data->text_view, current_oof_state);
+}
+
+static void
+btn_chpass_clicked (GtkButton *button, gpointer data)
+{
+ ExchangeAccount *account;
+ GSList *acclist;
+ char *old_password, *new_password;
+#if 0
+ if (!exchange_global_config_listener) {
+ g_print ("FATAL: Exchange global config listener is not initialized.\n");
+ return;
+ }
+ else {
+ g_print ("Exchange global config listener is initialized sucessfully.\n");
+ }
+#endif
+ acclist = exchange_config_listener_get_accounts (exchange_global_config_listener);
+
+ /* FIXME: For now, we have only one account in the list.
+ Find a way to handle multiple accounts.
+ */
+ account = acclist->data;
+
+ old_password = exchange_account_get_password (account);
+ g_print ("Current password is \"%s\"\n", old_password);
+ //new_password = exchange_get_new_password (old_password, TRUE);
+ exchange_account_set_password (account, old_password, new_password);
+
+ g_free (old_password);
+ g_free (new_password);
+}
+
+static void
+btn_dass_clicked (GtkButton *button, gpointer data)
+{
+ ExchangeAccount *account;
+ GSList *acclist;
+
+ acclist = exchange_config_listener_get_accounts (exchange_global_config_listener);
+
+ /* FIXME: For now, we have only one account in the list.
+ Find a way to handle multiple accounts.
+ */
+ account = acclist->data;
+
+ /* TODO: Put delegate assistant display code here */
+}
+
+static void
+btn_fsize_clicked (GtkButton *button, gpointer data)
+{
+ /* TODO: Add folders size display code here */
+}
+
+/* only used in editor */
+GtkWidget *
+org_gnome_exchange_settings(EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ ExchangeAccount *account = NULL;
+ CamelURL *url;
+ const char *source_url;
+ char *message = NULL, *txt = NULL, *oof_message;
+ gboolean oof_state;
+
+ GtkVBox *vbox_settings;
+
+ /* OOF fields */
+ GtkFrame *frm_oof;
+ GtkVBox *vbox_oof;
+ GtkLabel *lbl_oof_desc;
+ GtkTable *tbl_oof_status;
+ GtkLabel *lbl_status;
+ GtkRadioButton *radio_iof, *radio_oof;
+ GtkScrolledWindow *scrwnd_oof;
+ GtkTextView *txtview_oof;
+
+ /* Authentication settings */
+ GtkFrame *frm_auth;
+ GtkVBox *vbox_auth;
+ GtkTable *tbl_auth;
+ GtkLabel *lbl_chpass;
+ GtkButton *btn_chpass;
+ GtkLabel *lbl_dass;
+ GtkButton *btn_dass;
+
+ /* Miscelleneous setting */
+ GtkFrame *frm_misc;
+ GtkVBox *vbox_misc;
+ GtkTable *tbl_misc;
+ GtkLabel *lbl_fsize;
+ GtkButton *btn_fsize;
+
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ account = exchange_operations_get_exchange_account ();
+
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ source_url = e_account_get_string (target_account->account, E_ACCOUNT_SOURCE_URL);
+ url = camel_url_new(source_url, NULL);
+ if (url == NULL
+ || strcmp(url->protocol, "exchange") != 0) {
+ if (url)
+ camel_url_free(url);
+ return NULL;
+ }
+
+ if (data->old) {
+ camel_url_free(url);
+ return data->old;
+ }
+
+ oof_data = g_new0 (OOFData, 1);
+
+ oof_data->state = FALSE;
+ oof_data->message = NULL;
+ oof_data->text_view = NULL;
+
+ /* See if oof info found already */
+
+ if (!exchange_oof_get (account, &oof_state, &message)) {
+ /* SURF : e_notice (NULL, GTK_MESSAGE_ERROR,
+ _("Could not read out-of-office state")); */
+ return NULL;
+ }
+
+ if (message && *message)
+ oof_data->message = g_strdup (message);
+ else
+ oof_data->message = NULL;
+ oof_data->state = oof_state;
+
+ /* construct page */
+
+ vbox_settings = (GtkVBox*) gtk_object_new (GTK_TYPE_VBOX, "homogeneous", FALSE, "spacing", 6, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_settings), 12);
+
+ frm_oof = (GtkFrame*) gtk_object_new (GTK_TYPE_FRAME, "label", _("Out Of Office"), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox_settings), GTK_WIDGET (frm_oof), FALSE, FALSE, 0);
+
+ vbox_oof = (GtkVBox*) gtk_object_new (GTK_TYPE_VBOX, NULL, "homogeneous", FALSE, "spacing", 12, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_oof), 6);
+ gtk_container_add (GTK_CONTAINER (frm_oof), GTK_WIDGET (vbox_oof));
+
+ lbl_oof_desc = (GtkLabel*) gtk_object_new (GTK_TYPE_LABEL, "label", _("The message specified below will be automatically sent to \neach person who sends mail to you while you are out of the office."), "justify", GTK_JUSTIFY_LEFT, NULL);
+ gtk_misc_set_alignment (GTK_MISC (lbl_oof_desc), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox_oof), GTK_WIDGET (lbl_oof_desc), FALSE, FALSE, 0);
+
+ tbl_oof_status = (GtkTable*) gtk_object_new (GTK_TYPE_TABLE, "n-rows", 2, "n-columns", 2, "homogeneous", FALSE, "row-spacing", 6, "column-spacing", 6, NULL);
+ txt = g_strdup_printf ("<b>%s</b>", _("Status:"));
+ lbl_status = (GtkLabel*) gtk_object_new (GTK_TYPE_LABEL, "label", txt, "use-markup", TRUE, NULL);
+ g_free (txt);
+ gtk_misc_set_alignment (GTK_MISC (lbl_status), 0, 0.5);
+ gtk_misc_set_padding (GTK_MISC (lbl_status), 0, 0);
+
+ if (oof_data->state) {
+ radio_oof = (GtkRadioButton*) gtk_object_new (GTK_TYPE_RADIO_BUTTON, "label", _("I am out of the office"), NULL);
+ radio_iof = (GtkRadioButton*) gtk_object_new (GTK_TYPE_RADIO_BUTTON, "label", _("I am in the office"), "group", radio_oof, NULL);
+ }
+ else {
+ radio_iof = (GtkRadioButton*) gtk_object_new (GTK_TYPE_RADIO_BUTTON, "label", _("I am in the office"), NULL);
+ radio_oof = (GtkRadioButton*) gtk_object_new (GTK_TYPE_RADIO_BUTTON, "label", _("I am out of the office"), "group", radio_iof, NULL);
+ }
+ gtk_misc_set_alignment (GTK_MISC (radio_iof), 0, 0.5);
+ gtk_misc_set_alignment (GTK_MISC (radio_oof), 0, 0.5);
+ gtk_signal_connect (GTK_WIDGET (radio_oof), "toggled", G_CALLBACK (toggled_state), NULL);
+
+
+ gtk_table_attach (tbl_oof_status, GTK_WIDGET (lbl_status), 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach (tbl_oof_status, GTK_WIDGET (radio_iof), 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach (tbl_oof_status, GTK_WIDGET (radio_oof), 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox_oof), GTK_WIDGET (tbl_oof_status), FALSE, FALSE, 0);
+
+
+ scrwnd_oof = (GtkScrolledWindow*) gtk_object_new (GTK_TYPE_SCROLLED_WINDOW, "hscrollbar-policy", GTK_POLICY_AUTOMATIC, "vscrollbar-policy", GTK_POLICY_AUTOMATIC, "shadow-type", GTK_SHADOW_IN, NULL);
+ gtk_box_pack_start (GTK_BOX (vbox_oof), GTK_WIDGET (scrwnd_oof), FALSE, FALSE, 0);
+ txtview_oof = (GtkTextView*) gtk_object_new (GTK_TYPE_TEXT_VIEW, "justification", GTK_JUSTIFY_LEFT, "wrap-mode", GTK_WRAP_WORD, "editable", TRUE, NULL);
+ buffer = gtk_text_view_get_buffer (txtview_oof);
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ oof_message = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ if (oof_message && *oof_message) {
+ /* Will this ever happen? */
+ oof_data->message = oof_message;
+ }
+ if (oof_data->message) {
+ /* previuosly set message */
+ gtk_text_buffer_set_text (buffer, oof_data->message, -1);
+ gtk_text_view_set_buffer (txtview_oof, buffer);
+
+ }
+ gtk_text_buffer_set_modified (buffer, FALSE);
+ if (!oof_data->state)
+ gtk_widget_set_sensitive (GTK_WIDGET (txtview_oof), FALSE);
+ oof_data->text_view = txtview_oof;
+ g_signal_connect (buffer, "changed", G_CALLBACK (update_state), NULL);
+ gtk_container_add (GTK_CONTAINER (scrwnd_oof), GTK_WIDGET (txtview_oof));
+
+ /* Security settings */
+ frm_auth = (GtkFrame*) gtk_object_new (GTK_TYPE_FRAME, "label", _("Security"), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox_settings), GTK_WIDGET (frm_auth), FALSE, FALSE, 0);
+
+ vbox_auth = (GtkVBox*) gtk_object_new (GTK_TYPE_VBOX, "homogeneous", FALSE, "spacing", 6, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_auth), 6);
+ gtk_container_add (GTK_CONTAINER (frm_auth), GTK_WIDGET (vbox_auth));
+
+ tbl_auth = (GtkTable*) gtk_object_new (GTK_TYPE_TABLE, "n-rows", 2, "n-columns", 2, "homogeneous", FALSE, "row-spacing", 6, "column-spacing", 6, NULL);
+ lbl_chpass = (GtkLabel*) gtk_object_new (GTK_TYPE_LABEL, "label", _("Change the password for Exchange account"), NULL);
+ gtk_misc_set_alignment (GTK_MISC (lbl_chpass), 0, 0.5);
+ btn_chpass = (GtkButton*) gtk_object_new (GTK_TYPE_BUTTON, "label", _("Change Password"), NULL);
+ lbl_dass = (GtkLabel*) gtk_object_new (GTK_TYPE_LABEL, "label", _("Manage the delegate settings for Exchange account"), NULL);
+ gtk_misc_set_alignment (GTK_MISC (lbl_dass), 0, 0.5);
+ btn_dass = (GtkButton*) gtk_object_new (GTK_TYPE_BUTTON, "label", _("Delegation Assitant"));
+ gtk_signal_connect (GTK_WIDGET (btn_chpass), "clicked", G_CALLBACK (btn_chpass_clicked), NULL);
+ gtk_signal_connect (GTK_WIDGET (btn_dass), "clicked", G_CALLBACK (btn_dass_clicked), NULL);
+ gtk_table_attach_defaults (tbl_auth, GTK_WIDGET (lbl_chpass), 0, 1, 0, 1);
+ gtk_table_attach (tbl_auth, GTK_WIDGET (btn_chpass), 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach_defaults (tbl_auth, GTK_WIDGET (lbl_dass), 0, 1, 1, 2);
+ gtk_table_attach (tbl_auth, GTK_WIDGET (btn_dass), 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_box_pack_start (GTK_BOX (vbox_auth), GTK_WIDGET (tbl_auth), FALSE, FALSE, 0);
+
+ /* Miscelleneous settings */
+ frm_misc = (GtkFrame*) gtk_object_new (GTK_TYPE_FRAME, "label", _("Miscelleneous"), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox_settings), GTK_WIDGET (frm_misc), FALSE, FALSE, 0);
+
+ vbox_misc = (GtkVBox*) gtk_object_new (GTK_TYPE_VBOX, "homogeneous", FALSE, "spacing", 6, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_misc), 6);
+ gtk_container_add (GTK_CONTAINER (frm_misc), GTK_WIDGET (vbox_misc));
+
+ tbl_misc = (GtkTable*) gtk_object_new (GTK_TYPE_TABLE, "n-rows", 1, "n-columns", 1, "homogeneous", FALSE, "row-spacing", 6, "column-spacing", 6, NULL);
+ lbl_fsize = (GtkLabel*) gtk_object_new (GTK_TYPE_LABEL, "label", _("View the size of all Exchange folders"), NULL);
+ gtk_misc_set_alignment (GTK_MISC (lbl_fsize), 0, 0.5);
+ btn_fsize = (GtkButton*) gtk_object_new (GTK_TYPE_BUTTON, "label", _("Folders Size"), NULL);
+ gtk_signal_connect (GTK_WIDGET (btn_fsize), "clicked", G_CALLBACK (btn_fsize_clicked), NULL);
+ gtk_table_attach_defaults (tbl_misc, GTK_WIDGET (lbl_fsize), 0, 1, 0, 1);
+ gtk_table_attach (tbl_misc, GTK_WIDGET (btn_fsize), 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_box_pack_start (GTK_BOX (vbox_misc), GTK_WIDGET (tbl_misc), FALSE, FALSE, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (vbox_settings));
+ gtk_notebook_insert_page (GTK_NOTEBOOK (data->parent), GTK_WIDGET (vbox_settings), gtk_label_new(_("Exchange Settings")), 4);
+ return GTK_WIDGET (vbox_settings);
+
+}
+
+static void
+owa_authenticate_user(GtkWidget *button, EConfig *config)
+{
+ EMConfigTargetAccount *target_account = (EMConfigTargetAccount *)config->target;
+ CamelURL *url=NULL;
+ gboolean remember_password;
+ char *url_string;
+ const char *source_url, *id_name;
+ char *at, *user;
+ gboolean valid = FALSE;
+ ExchangeParams *exchange_params;
+
+ exchange_params = g_new0 (ExchangeParams, 1);
+ exchange_params->host = NULL;
+ exchange_params->ad_server = NULL;
+ exchange_params->mailbox = NULL;
+ exchange_params->owa_path = NULL;
+ exchange_params->is_ntlm = TRUE;
+
+
+ source_url = e_account_get_string (target_account->account, E_ACCOUNT_SOURCE_URL);
+
+ url = camel_url_new(source_url, NULL);
+ if (url->user == NULL) {
+ id_name = e_account_get_string (target_account->account, E_ACCOUNT_ID_ADDRESS);
+ if (id_name) {
+ at = strchr(id_name, '@');
+ user = g_alloca(at-id_name+1);
+ memcpy(user, id_name, at-id_name);
+ user[at-id_name] = 0;
+ camel_url_set_user (url, user);
+ }
+ }
+
+ /* validate_user() CALLS GTK!!!
+
+ THIS IS TOTALLY UNNACCEPTABLE!!!!!!!!
+
+ It must use camel_session_ask_password, and it should return an exception for any problem,
+ which should then be shown using e-error */
+
+ valid = e2k_validate_user ((const char *)camel_url_get_param (url, "owa_url"),
+ url->user, exchange_params, &remember_password);
+ camel_url_set_host (url, valid ? exchange_params->host : "");
+
+ if (valid)
+ camel_url_set_authmech (url, exchange_params->is_ntlm ? "NTLM" : "Basic");
+ camel_url_set_param (url, "ad_server", valid ? exchange_params->ad_server: NULL);
+ camel_url_set_param (url, "mailbox", valid ? exchange_params->mailbox : NULL);
+ camel_url_set_param (url, "owa_path", valid ? exchange_params->owa_path : NULL);
+
+ g_free (exchange_params->owa_path);
+ g_free (exchange_params->mailbox);
+ g_free (exchange_params);
+
+ if (valid) {
+ url_string = camel_url_to_string (url, 0);
+ e_account_set_string (target_account->account, E_ACCOUNT_SOURCE_URL, url_string);
+ e_account_set_string (target_account->account, E_ACCOUNT_TRANSPORT_URL, url_string);
+ e_account_set_bool (target_account->account, E_ACCOUNT_SOURCE_SAVE_PASSWD, remember_password);
+ g_free (url_string);
+ }
+ camel_url_free (url);
+}
+
+static void
+owa_editor_entry_changed(GtkWidget *entry, EConfig *config)
+{
+ const char *uri, *ssl = NULL;
+ CamelURL *url, *owaurl = NULL;
+ char *url_string;
+ EMConfigTargetAccount *target = (EMConfigTargetAccount *)config->target;
+ GtkWidget *button = g_object_get_data((GObject *)entry, "authenticate-button");
+ int active = FALSE;
+
+ /* NB: we set the button active only if we have a parsable uri entered */
+
+ url = camel_url_new(e_account_get_string(target->account, E_ACCOUNT_SOURCE_URL), NULL);
+ uri = gtk_entry_get_text((GtkEntry *)entry);
+ if (uri && uri[0]) {
+ camel_url_set_param(url, "owa_url", uri);
+ owaurl = camel_url_new(uri, NULL);
+ if (owaurl) {
+ active = TRUE;
+
+ /* Reading the owa url and setting use_ssl paramemter */
+ if (!strcmp(owaurl->protocol, "https"))
+ ssl = "always";
+ camel_url_free(owaurl);
+ }
+ } else {
+ camel_url_set_param(url, "owa_url", NULL);
+ }
+
+ camel_url_set_param(url, "use_ssl", ssl);
+ gtk_widget_set_sensitive(button, active);
+
+ url_string = camel_url_to_string(url, 0);
+ e_account_set_string(target->account, E_ACCOUNT_SOURCE_URL, url_string);
+ g_free(url_string);
+}
+
+static void
+destroy_label(GtkWidget *old, GtkWidget *label)
+{
+ gtk_widget_destroy(label);
+}
+
+static char *
+construct_owa_url (CamelURL *url)
+{
+ const char *owa_path, *use_ssl = NULL;
+ const char *protocol = "http", *mailbox_name;
+ char *owa_url;
+
+ use_ssl = camel_url_get_param (url, "use_ssl");
+ if (use_ssl) {
+ if (!strcmp (use_ssl, "always"))
+ protocol = "https";
+ }
+
+ owa_path = camel_url_get_param (url, "owa_path");
+ if (!owa_path)
+ owa_path = "/exchange";
+ mailbox_name = camel_url_get_param (url, "mailbox");
+
+ if (mailbox_name)
+ owa_url = g_strdup_printf("%s://%s%s/%s", protocol, url->host, owa_path, mailbox_name);
+ else
+ owa_url = g_strdup_printf("%s://%s%s", protocol, url->host, owa_path );
+
+ return owa_url;
+}
+
+/* used by editor and druid - same code */
+GtkWidget *
+org_gnome_exchange_owa_url(EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ const char *source_url;
+ char *owa_url = NULL;
+ GtkWidget *owa_entry;
+ CamelURL *url;
+ int row;
+ GtkWidget *hbox, *label, *button;
+
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ source_url = e_account_get_string (target_account->account, E_ACCOUNT_SOURCE_URL);
+ url = camel_url_new(source_url, NULL);
+ if (url == NULL
+ || strcmp(url->protocol, "exchange") != 0) {
+ if (url)
+ camel_url_free(url);
+
+ if (data->old
+ && (label = g_object_get_data((GObject *)data->old, "authenticate-label")))
+ gtk_widget_destroy(label);
+
+ /* TODO: we could remove 'owa-url' from the url,
+ but that will lose it if we come back. Maybe a commit callback could do it */
+
+ return NULL;
+ }
+
+ if (data->old) {
+ camel_url_free(url);
+ return data->old;
+ }
+
+ owa_url = g_strdup (camel_url_get_param(url, "owa_url"));
+
+ /* if the host is null, then user+other info is dropped silently, force it to be kept */
+ if (url->host == NULL) {
+ char *uri;
+
+ camel_url_set_host(url, "");
+ uri = camel_url_to_string(url, 0);
+ e_account_set_string(target_account->account, E_ACCOUNT_SOURCE_URL, uri);
+ g_free(uri);
+ }
+
+ row = ((GtkTable *)data->parent)->nrows;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ label = gtk_label_new_with_mnemonic(_("_OWA Url:"));
+ gtk_widget_show(label);
+
+ owa_entry = gtk_entry_new();
+
+ if (!owa_url) {
+ if (url->host[0] != 0) {
+ char *uri;
+
+ /* url has hostname but not owa_url.
+ * Account has been created using x-c-s or evo is upgraded to 2.2
+ * When invoked from druid, hostname will get set after validation,
+ * so this condition will never be true during account creation.
+ */
+ owa_url = construct_owa_url (url);
+ camel_url_set_param (url, "owa_url", owa_url);
+ uri = camel_url_to_string(url, 0);
+ e_account_set_string(target_account->account, E_ACCOUNT_SOURCE_URL, uri);
+ g_free(uri);
+ }
+ }
+ if (owa_url)
+ gtk_entry_set_text(GTK_ENTRY (owa_entry), owa_url);
+ gtk_label_set_mnemonic_widget((GtkLabel *)label, owa_entry);
+
+ button = gtk_button_new_with_mnemonic (_("A_uthenticate"));
+ gtk_widget_set_sensitive (button, owa_url && owa_url[0]);
+
+ gtk_box_pack_start (GTK_BOX (hbox), owa_entry, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show_all(hbox);
+
+ gtk_table_attach (GTK_TABLE (data->parent), label, 0, 1, row, row+1, 0, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (data->parent), hbox, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
+
+ g_signal_connect (owa_entry, "changed", G_CALLBACK(owa_editor_entry_changed), data->config);
+ g_object_set_data((GObject *)owa_entry, "authenticate-button", button);
+ g_signal_connect (button, "clicked", G_CALLBACK(owa_authenticate_user), data->config);
+
+ /* Track the authenticate label, so we can destroy it if e-config is to destroy the hbox */
+ g_object_set_data((GObject *)hbox, "authenticate-label", label);
+
+ g_free (owa_url);
+ return hbox;
+}
+
+gboolean
+org_gnome_exchange_check_options(EPlugin *epl, EConfigHookPageCheckData *data)
+{
+ EMConfigTargetAccount *target = (EMConfigTargetAccount *)data->config->target;
+ int status = TRUE;
+
+ /* We assume that if the host is set, then the setting is valid.
+ The host gets set when the provider validate() call is made */
+ /* We do this check for receive page also, so that user can
+ * proceed with the account set up only after user is validated,
+ * and host name is reset by validate() call
+ */
+ if (data->pageid == NULL ||
+ strcmp (data->pageid, "10.receive") == 0 ||
+ strcmp (data->pageid, "20.receive_options") == 0) {
+ CamelURL *url;
+
+ url = camel_url_new(e_account_get_string(target->account, E_ACCOUNT_SOURCE_URL), NULL);
+ /* Note: we only care about exchange url's, we WILL get called on all other url's too. */
+ if (url != NULL
+ && strcmp(url->protocol, "exchange") == 0
+ && (url->host == NULL || url->host[0] == 0))
+ status = FALSE;
+
+ if (url)
+ camel_url_free(url);
+ }
+
+ return status;
+}
+
+static void
+set_oof_info ()
+{
+ GSList *accounts, *acc;
+ ExchangeAccount *account = NULL;
+
+ accounts = exchange_config_listener_get_accounts (exchange_global_config_listener);
+ for (acc = accounts; acc; acc = acc->next) {
+ account = acc->data;
+ }
+
+ if (!exchange_oof_set (account, oof_data->state, oof_data->message)) {
+ /* SURF : e_notice (NULL, GTK_MESSAGE_ERROR,
+ _("Could not update out-of-office state")); */
+ }
+
+}
+
+static void
+destroy_oof_data ()
+{
+ if (oof_data->message)
+ g_free (oof_data->message);
+ g_free (oof_data);
+}
+
+void
+org_gnome_exchange_commit (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ const char *source_url;
+ CamelURL *url;
+
+ source_url = e_account_get_string (target_account->account, E_ACCOUNT_SOURCE_URL);
+ url = camel_url_new (source_url, NULL);
+ if (url == NULL
+ || strcmp (url->protocol, "exchange") != 0) {
+ if (url)
+ camel_url_free (url);
+
+ return;
+ }
+ if (data->old) {
+ camel_url_free(url);
+ return;
+ }
+
+ /* Set oof data in exchange account */
+ set_oof_info ();
+ destroy_oof_data ();
+ return;
+}
+
+static void
+exchange_check_authtype (GtkWidget *w, EConfig *config)
+{
+ return;
+}
+
+static void
+exchange_authtype_changed (GtkComboBox *dropdown, EConfig *config)
+{
+ EMConfigTargetAccount *target = (EMConfigTargetAccount *)config->target;
+ int id = gtk_combo_box_get_active(dropdown);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CamelServiceAuthType *authtype;
+ CamelURL *url_source, *url_transport;
+ const char *source_url, *transport_url;
+ char *source_url_string, *transport_url_string;
+
+ source_url = e_account_get_string (target->account,
+ E_ACCOUNT_SOURCE_URL);
+ if (id == -1)
+ return;
+
+ url_source = camel_url_new (source_url, NULL);
+
+ transport_url = e_account_get_string (target->account,
+ E_ACCOUNT_TRANSPORT_URL);
+ url_transport = camel_url_new (transport_url, NULL);
+
+ model = gtk_combo_box_get_model(dropdown);
+ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) {
+ gtk_tree_model_get(model, &iter, 1, &authtype, -1);
+ if (authtype) {
+ camel_url_set_authmech(url_source, authtype->authproto);
+ camel_url_set_authmech(url_transport, authtype->authproto);
+ }
+ else {
+ camel_url_set_authmech(url_source, NULL);
+ camel_url_set_authmech(url_transport, NULL);
+ }
+
+ source_url_string = camel_url_to_string(url_source, 0);
+ transport_url_string = camel_url_to_string(url_transport, 0);
+ e_account_set_string(target->account, E_ACCOUNT_SOURCE_URL, source_url_string);
+ e_account_set_string(target->account, E_ACCOUNT_TRANSPORT_URL, transport_url_string);
+ g_free(source_url_string);
+ g_free(transport_url_string);
+ }
+ camel_url_free(url_source);
+ camel_url_free(url_transport);
+}
+
+
+GtkWidget *
+org_gnome_exchange_auth_section (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ const char *source_url;
+ char *label_text;
+ CamelURL *url;
+ GtkWidget *hbox, *button, *auth_label, *vbox, *label_hide;
+ GtkComboBox *dropdown;
+ GtkTreeIter iter;
+ GtkListStore *store;
+ int i, active=0, auth_changed_id = 0;
+ GList *authtypes, *l, *ll;
+
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ source_url = e_account_get_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL);
+ url = camel_url_new (source_url, NULL);
+ if (url == NULL
+ || strcmp (url->protocol, "exchange") != 0) {
+ if (url)
+ camel_url_free (url);
+
+ return NULL;
+ }
+
+ if (data->old) {
+ camel_url_free(url);
+ return data->old;
+ }
+
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ label_text = g_strdup_printf("<b>%s</b>", _("Authentication Type"));
+ auth_label = gtk_label_new (label_text);
+ g_free (label_text);
+ gtk_label_set_justify (GTK_LABEL (auth_label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment (GTK_MISC (auth_label), 0, 0.5);
+ gtk_misc_set_padding (GTK_MISC (auth_label), 0, 0);
+ gtk_label_set_use_markup (GTK_LABEL (auth_label), TRUE);
+
+ label_hide = gtk_label_new("\n");
+
+ hbox = gtk_hbox_new (FALSE, 6);
+
+ dropdown = (GtkComboBox * )gtk_combo_box_new ();
+
+ button = gtk_button_new_with_mnemonic (_("Ch_eck for Supported Types"));
+
+ authtypes = g_list_prepend (g_list_prepend (NULL, &camel_exchange_password_authtype),
+ &camel_exchange_ntlm_authtype);
+ store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+
+ for (i=0, l=authtypes; l; l=l->next, i++) {
+ CamelServiceAuthType *authtype = l->data;
+ int avail = TRUE;
+
+ if (authtypes) {
+ for (ll = authtypes; ll; ll = g_list_next(ll))
+ if (!strcmp(authtype->authproto,
+ ((CamelServiceAuthType *)ll->data)->authproto))
+ break;
+ avail = ll != NULL;
+ }
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, authtype->name, 1,
+ authtype, 2, !avail, -1);
+
+ if (url && url->authmech && !strcmp(url->authmech, authtype->authproto))
+ active = i;
+ }
+
+ gtk_combo_box_set_model (dropdown, (GtkTreeModel *)store);
+ gtk_combo_box_set_active (dropdown, -1);
+
+ if (auth_changed_id == 0) {
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new();
+
+ gtk_cell_layout_pack_start ((GtkCellLayout *)dropdown, cell, TRUE);
+ gtk_cell_layout_set_attributes ((GtkCellLayout *)dropdown, cell,
+ "text", 0, "strikethrough", 2, NULL);
+
+ auth_changed_id = g_signal_connect (dropdown,
+ "changed",
+ G_CALLBACK (exchange_authtype_changed),
+ data->config);
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK(exchange_check_authtype),
+ data->config);
+ }
+
+ gtk_combo_box_set_active(dropdown, active);
+
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (dropdown), FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), auth_label, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), label_hide, TRUE, TRUE, 0);
+ gtk_widget_show_all (vbox);
+
+ gtk_box_pack_start (GTK_BOX (data->parent), vbox, TRUE, TRUE, 0);
+
+ if (url)
+ camel_url_free(url);
+ g_list_free (authtypes);
+
+ return vbox;
+}
diff --git a/plugins/exchange-operations/exchange-ask-password.c b/plugins/exchange-operations/exchange-ask-password.c
new file mode 100644
index 0000000000..c21bd67c07
--- /dev/null
+++ b/plugins/exchange-operations/exchange-ask-password.c
@@ -0,0 +1,357 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Sushma Rai <rsushma@novell.com>
+ * Copyright (C) 2004 Novell, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <libgnome/gnome-i18n.h>
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkdialog.h>
+#include <camel/camel-provider.h>
+#include <camel/camel-url.h>
+#include "mail/em-account-editor.h"
+#include "mail/em-config.h"
+#include "e-util/e-account.h"
+#include "e-util/e-passwords.h"
+#include "e-util/e-config.h"
+
+int e_plugin_lib_enable (EPluginLib *ep, int enable);
+void exchange_options_commit (EPlugin *epl, EConfigHookItemFactoryData *data);
+GtkWidget *org_gnome_exchange_read_url (EPlugin *epl, EConfigHookItemFactoryData *data);
+gboolean org_gnome_exchange_check_options (EPlugin *epl, EConfigHookPageCheckData *data);
+
+const char *owa_entry_text = NULL;
+
+typedef gboolean (CamelProviderValidateUserFunc) (CamelURL *camel_url, const char *url, gboolean *remember_password, CamelException *ex);
+
+typedef struct {
+ CamelProviderValidateUserFunc *validate_user;
+}CamelProviderValidate;
+
+int
+e_plugin_lib_enable (EPluginLib *ep, int enable)
+{
+ if (enable) {
+ }
+ return 0;
+}
+
+void
+exchange_options_commit (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ return;
+}
+
+static gboolean
+validate_exchange_user (void *data)
+{
+ EMConfigTargetAccount *target_account = data;
+ CamelProviderValidate *validate;
+ CamelURL *url=NULL;
+ CamelProvider *provider = NULL;
+ gboolean valid = FALSE, *remember_password;
+ char *account_url, *url_string;
+ const char *source_url, *id_name;
+ static int count = 0;
+ char *at, *user;
+
+ if (count)
+ return valid;
+
+ source_url = e_account_get_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL);
+ account_url = g_strdup (source_url);
+ provider = camel_provider_get (account_url, NULL);
+ if (!provider) {
+ return FALSE; /* This should never happen */
+ }
+ url = camel_url_new_with_base (NULL, account_url);
+ validate = provider->priv;
+ if (validate) {
+
+ if (url->user == NULL) {
+ id_name = e_account_get_string (target_account->account,
+ E_ACCOUNT_ID_ADDRESS);
+ if (id_name) {
+ at = strchr(id_name, '@');
+ user = g_alloca(at-id_name+1);
+ memcpy(user, id_name, at-id_name);
+ user[at-id_name] = 0;
+
+ camel_url_set_user (url, user);
+ }
+ }
+ valid = validate->validate_user (url, owa_entry_text,
+ remember_password, NULL);
+ }
+
+ /* FIXME: need to check for return value */
+ if (valid) {
+ count ++;
+ url_string = camel_url_to_string (url, 0);
+ e_account_set_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL, url_string);
+ e_account_set_string (target_account->account,
+ E_ACCOUNT_TRANSPORT_URL, url_string);
+ target_account->account->source->save_passwd = *remember_password;
+ }
+
+ camel_url_free (url);
+ g_free (account_url);
+ return valid;
+}
+
+static void
+ok_button_clicked (GtkWidget *button, void *data)
+{
+ gboolean valid = FALSE;
+
+ valid = validate_exchange_user (data); // FIXME: return value
+}
+
+static void
+owa_entry_changed (GtkWidget *entry, void *data)
+{
+ GtkWidget *button = data;
+
+ /* FIXME: return owa_entry_text instead of making it global */
+ owa_entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
+ if (owa_entry_text)
+ gtk_widget_set_sensitive (button, TRUE);
+}
+
+static GtkWidget *
+add_owa_entry (GtkWidget *parent,
+ EConfig *config,
+ EMConfigTargetAccount *target_account)
+{
+ GtkWidget *section, *owa_entry;
+ GtkWidget *hbox, *hbox_inner, *label, *button;
+ GList *container_list, *l;
+ GValue rows = { 0, };
+ GValue cols = { 0, };
+ gint n_rows, n_cols;
+
+ /* Since configure section in the receive page is not plugin enabled
+ * traversing through the container hierarchy to get the reference
+ * to the table, to which owa_url entry has to be added.
+ * This needs to be changed once we can access configure section from
+ * the plugin.
+ */
+
+ container_list = gtk_container_get_children (GTK_CONTAINER (parent));
+ l = g_list_nth (container_list, 1); /* vboxsourceborder */
+ container_list = gtk_container_get_children (GTK_CONTAINER (l->data));
+ l = g_list_nth (container_list, 0); /* sourcevbox */
+ container_list = gtk_container_get_children (GTK_CONTAINER (l->data));
+ l = g_list_nth (container_list, 2); /* source frame */
+ container_list = gtk_container_get_children (GTK_CONTAINER (l->data));
+ l = g_list_nth (container_list, 1); /* hbox173 */
+ container_list = gtk_container_get_children (GTK_CONTAINER (l->data));
+ l = g_list_nth (container_list, 1); /* table 13 */
+ container_list = gtk_container_get_children (GTK_CONTAINER (l->data));
+ l = g_list_nth (container_list, 0); /* table 4*/
+
+ g_value_init (&rows, G_TYPE_INT);
+ g_value_init (&cols, G_TYPE_INT);
+ g_object_get_property (G_OBJECT (l->data), "n-rows", &rows);
+ g_object_get_property (G_OBJECT (l->data), "n-columns", &cols);
+ n_rows = g_value_get_int (&rows);
+ n_cols = g_value_get_int (&cols);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+
+ hbox_inner = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox_inner);
+
+ owa_entry = gtk_entry_new ();
+ gtk_widget_show (owa_entry);
+
+ button = gtk_button_new_with_mnemonic (_("A_uthenticate"));
+ gtk_widget_set_sensitive (button, FALSE);
+ gtk_widget_show (button);
+
+ gtk_box_pack_start (GTK_BOX (hbox_inner), owa_entry, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox_inner), button, TRUE, TRUE, 0);
+
+ label = gtk_label_new_with_mnemonic(_("_OWA Url:"));
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_widget_show (label);
+
+ gtk_box_pack_start (GTK_BOX (hbox), hbox_inner, TRUE, TRUE, 0);
+
+ gtk_table_attach (GTK_TABLE (l->data), label, 0, n_cols-1, n_rows, n_rows+1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach (GTK_TABLE (l->data), hbox, n_cols-1, n_cols, n_rows, n_rows+1, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_widget_show (GTK_WIDGET (l->data));
+
+ g_signal_connect (owa_entry, "changed",
+ G_CALLBACK (owa_entry_changed), button);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (ok_button_clicked), target_account);
+
+ section = gtk_vbox_new (FALSE, 0);
+ gtk_widget_hide (section);
+ return section; /* FIXME: return entry */
+}
+
+GtkWidget *
+org_gnome_exchange_read_url (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ EConfig *config;
+ char *account_url = NULL, *exchange_url = NULL;
+ const char *source_url;
+ GtkWidget *owa_entry = NULL, *parent;
+
+ config = data->config;
+ target_account = (EMConfigTargetAccount *)data->config->target;
+
+ source_url = e_account_get_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL);
+ account_url = g_strdup (source_url);
+ exchange_url = g_strrstr (account_url, "exchange");
+
+ if (exchange_url) {
+ if (data->old)
+ return data->old;
+
+ parent = data->parent;
+ owa_entry = add_owa_entry (parent, config, target_account);
+ }
+ g_free (account_url);
+ return owa_entry;
+}
+
+
+GtkWidget *
+org_gnome_exchange_handle_auth (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ EConfig *config;
+ char *account_url = NULL, *exchange_url = NULL, *url_string;
+ const char *source_url;
+ char *auth_type;
+ GtkWidget *auth_section=NULL, *parent, *section;
+
+ config = data->config;
+ target_account = (EMConfigTargetAccount *)data->config->target;
+
+ source_url = e_account_get_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL);
+ account_url = g_strdup (source_url);
+ exchange_url = g_strrstr (account_url, "exchange");
+
+ if (exchange_url) {
+ parent = data->parent;
+
+ /* We don't need auth section while creating the account. But
+ * we need that in the Editor. And since we get the child vbox
+ * from the plugin, we are finding the parent section and
+ * hiding it. This is a temporary fix and this needs to be handled
+ * in the proper way. */
+ section = gtk_widget_get_parent (gtk_widget_get_parent (parent));
+ gtk_widget_hide (section);
+ }
+ auth_section = gtk_entry_new ();
+ gtk_widget_hide (auth_section);
+ return auth_section;
+}
+
+GtkWidget *
+org_gnome_exchange_handle_send_auth_option (EPlugin *epl, EConfigHookItemFactoryData *data)
+{
+ EMConfigTargetAccount *target_account;
+ EConfig *config;
+ char *account_url = NULL, *exchange_url = NULL, *url_string;
+ const char *source_url;
+ char *auth_type;
+ GtkWidget *auth_section=NULL, *parent, *section;
+
+ config = data->config;
+ target_account = (EMConfigTargetAccount *)data->config->target;
+
+ source_url = e_account_get_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL);
+ account_url = g_strdup (source_url);
+ exchange_url = g_strrstr (account_url, "exchange");
+
+ if (exchange_url) {
+ parent = data->parent;
+ /* We don't need auth section while creating the account. But
+ * we need that in the Editor. And since we get the child vbox
+ * from the plugin, we are finding the parent section and
+ * hiding it. This is a temporary fix and this needs to be handled
+ * in the proper way. */
+ section = gtk_widget_get_parent (
+ gtk_widget_get_parent (gtk_widget_get_parent(parent)));
+ gtk_widget_hide (section);
+ }
+ auth_section = gtk_entry_new ();
+ gtk_widget_hide (auth_section);
+ return auth_section;
+}
+
+gboolean
+org_gnome_exchange_check_options (EPlugin *epl, EConfigHookPageCheckData *data)
+{
+ EMConfigTargetAccount *target_account;
+ EConfig *config;
+ char *account_url = NULL, *exchange_url = NULL, *url_string;
+ char *use_ssl = NULL;
+ static int page_check_count = 0;
+ CamelURL *url;
+
+ if ((strcmp (data->pageid, "20.receive_options")) || page_check_count)
+ return TRUE;
+
+ config = data->config;
+ target_account = (EMConfigTargetAccount *)data->config->target;
+ account_url = g_strdup (target_account->account->source->url);
+ exchange_url = g_strrstr (account_url, "exchange");
+
+ if (exchange_url) {
+ page_check_count ++;
+
+ if (owa_entry_text){
+ if (!strncmp (owa_entry_text, "https:", 6))
+ use_ssl = "always";
+
+ url = camel_url_new_with_base (NULL, account_url);
+
+ if (use_ssl)
+ camel_url_set_param (url, "use_ssl", use_ssl);
+ camel_url_set_param (url, "owa_url", owa_entry_text);
+
+ url_string = camel_url_to_string (url, 0);
+ e_account_set_string (target_account->account,
+ E_ACCOUNT_SOURCE_URL, url_string);
+ camel_url_free (url);
+ }
+ }
+ return TRUE;
+}
diff --git a/plugins/exchange-operations/exchange-change-password.glade b/plugins/exchange-operations/exchange-change-password.glade
new file mode 100644
index 0000000000..fa49da6270
--- /dev/null
+++ b/plugins/exchange-operations/exchange-change-password.glade
@@ -0,0 +1,243 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="pass_dialog">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Change Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Your current password has expired. Please change your password now.</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.52</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">6</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="current_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Current Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="new_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="confirm_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Confirm Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="new_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="confirm_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="current_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_padding">12</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/plugins/exchange-operations/exchange-change-password.h b/plugins/exchange-operations/exchange-change-password.h
new file mode 100644
index 0000000000..155fff897b
--- /dev/null
+++ b/plugins/exchange-operations/exchange-change-password.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_CHANGE_PASSWORD_H__
+#define __EXCHANGE_CHANGE_PASSWORD_H__
+
+#include <exchange/exchange-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+char *exchange_get_new_password (const char *existing_password,
+ gboolean voluntary);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_CHANGE_PASSWORD_H__ */
diff --git a/plugins/exchange-operations/exchange-config-listener.c b/plugins/exchange-operations/exchange-config-listener.c
new file mode 100644
index 0000000000..d41384d79d
--- /dev/null
+++ b/plugins/exchange-operations/exchange-config-listener.c
@@ -0,0 +1,1095 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* Copyright (C) 2001-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeConfigListener: a class that listens to the config database
+ * and handles creating the ExchangeAccount object and making sure that
+ * default folders are updated as needed.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "exchange-config-listener.h"
+
+#include <exchange-account.h>
+#include <exchange-constants.h>
+#include <e-folder-exchange.h>
+#include <e2k-marshal.h>
+#include <e2k-uri.h>
+// SURF : #include "mail-stub-listener.h"
+
+// SURF : #include <libedataserver/e-dialog-utils.h>
+
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-source-group.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+struct _ExchangeConfigListenerPrivate {
+ GConfClient *gconf;
+ guint idle_id;
+
+ char *configured_uri, *configured_name;
+ EAccount *configured_account;
+
+ ExchangeAccount *exchange_account;
+};
+
+typedef struct {
+ const char *name;
+ const char *uri;
+ int type;
+}FolderInfo;
+
+enum {
+ EXCHANGE_ACCOUNT_CREATED,
+ EXCHANGE_ACCOUNT_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+#define PARENT_TYPE E_TYPE_ACCOUNT_LIST
+
+#define CONF_KEY_SELECTED_CAL_SOURCES "/apps/evolution/calendar/display/selected_calendars"
+#define CONF_KEY_SELECTED_TASKS_SOURCES "/apps/evolution/calendar/tasks/selected_tasks"
+
+static EAccountListClass *parent_class = NULL;
+
+static void dispose (GObject *object);
+static void finalize (GObject *object);
+
+static void account_added (EAccountList *account_listener,
+ EAccount *account);
+static void account_changed (EAccountList *account_listener,
+ EAccount *account);
+static void account_removed (EAccountList *account_listener,
+ EAccount *account);
+
+static void
+class_init (GObjectClass *object_class)
+{
+ EAccountListClass *e_account_list_class =
+ E_ACCOUNT_LIST_CLASS (object_class);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* virtual method override */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ e_account_list_class->account_added = account_added;
+ e_account_list_class->account_changed = account_changed;
+ e_account_list_class->account_removed = account_removed;
+
+ /* signals */
+ signals[EXCHANGE_ACCOUNT_CREATED] =
+ g_signal_new ("exchange_account_created",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ExchangeConfigListenerClass, exchange_account_created),
+ NULL, NULL,
+ e2k_marshal_NONE__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+ signals[EXCHANGE_ACCOUNT_REMOVED] =
+ g_signal_new ("exchange_account_removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ExchangeConfigListenerClass, exchange_account_removed),
+ NULL, NULL,
+ e2k_marshal_NONE__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+}
+
+static void
+init (GObject *object)
+{
+ ExchangeConfigListener *config_listener =
+ EXCHANGE_CONFIG_LISTENER (object);
+
+ config_listener->priv = g_new0 (ExchangeConfigListenerPrivate, 1);
+}
+
+static void
+dispose (GObject *object)
+{
+ ExchangeConfigListener *config_listener =
+ EXCHANGE_CONFIG_LISTENER (object);
+
+ if (config_listener->priv->idle_id) {
+ g_source_remove (config_listener->priv->idle_id);
+ config_listener->priv->idle_id = 0;
+ }
+
+ if (config_listener->priv->gconf) {
+ g_object_unref (config_listener->priv->gconf);
+ config_listener->priv->gconf = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ ExchangeConfigListener *config_listener =
+ EXCHANGE_CONFIG_LISTENER (object);
+
+ g_free (config_listener->priv->configured_name);
+ g_free (config_listener->priv->configured_uri);
+ g_free (config_listener->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+E2K_MAKE_TYPE (exchange_config_listener, ExchangeConfigListener, class_init, init, PARENT_TYPE)
+
+
+#define EVOLUTION_URI_PREFIX "evolution:/"
+#define EVOLUTION_URI_PREFIX_LEN (sizeof (EVOLUTION_URI_PREFIX) - 1)
+
+static EFolder *
+standard_folder (ExchangeAccount *account, const char *folder_type)
+{
+ const char *uri;
+
+ uri = exchange_account_get_standard_uri (account, folder_type);
+ if (!uri)
+ return NULL;
+ return exchange_account_get_folder (account, uri);
+}
+
+static void
+set_special_mail_folder (ExchangeAccount *account, const char *folder_type,
+ char **physical_uri)
+{
+ EFolder *folder;
+
+ folder = standard_folder (account, folder_type);
+ if (!folder)
+ return;
+
+ g_free (*physical_uri);
+ *physical_uri = g_strdup (e_folder_get_physical_uri (folder));
+}
+
+static void
+add_defaults_for_account (ExchangeConfigListener *config_listener,
+ E2kContext *ctx,
+ ExchangeAccount *account)
+{
+ EAccount *eaccount;
+
+#if LDEAD
+ add_autocompletion_folders (config_listener->priv->gconf, account);
+#endif
+
+ eaccount = config_listener->priv->configured_account;
+ set_special_mail_folder (account, "drafts",
+ &eaccount->drafts_folder_uri);
+ set_special_mail_folder (account, "sentitems",
+ &eaccount->sent_folder_uri);
+ e_account_list_change (E_ACCOUNT_LIST (config_listener), eaccount);
+ e_account_list_save (E_ACCOUNT_LIST (config_listener));
+}
+
+
+static gboolean
+is_active_exchange_account (EAccount *account)
+{
+ if (!account->enabled)
+ return FALSE;
+ if (!account->source || !account->source->url)
+ return FALSE;
+ return (strncmp (account->source->url, EXCHANGE_URI_PREFIX, 11) == 0);
+}
+
+static void
+add_account_esources (ExchangeAccount *account,
+ GSList *folders)
+{
+ ESource *source = NULL;
+ ESourceGroup *cal_source_group = NULL;
+ ESourceGroup *tasks_source_group = NULL;
+ ESourceGroup *contacts_source_group = NULL;
+ char *relative_uri = NULL, *username = NULL;
+ GSList *ids;
+ GConfClient *client;
+ int mode;
+ ESourceList *cal_source_list, *tasks_source_list, *contacts_source_list;
+ FolderInfo *folder=NULL;
+ gboolean offline_mode = FALSE;
+
+ client = gconf_client_get_default ();
+
+ cal_source_list = e_source_list_new_for_gconf ( client, CONF_KEY_CAL);
+ tasks_source_list = e_source_list_new_for_gconf ( client, CONF_KEY_TASKS);
+ contacts_source_list = e_source_list_new_for_gconf ( client, CONF_KEY_CONTACTS);
+
+ exchange_account_is_offline_sync_set (account, &mode);
+ if (mode == OFFLINE_MODE) {
+ /* If account is marked for offline sync during account
+ * creation, mark all the folders for offline sync
+ */
+ offline_mode = TRUE;
+ }
+
+ username = exchange_account_get_username (account);
+
+ /* For each component create a source group */
+
+ cal_source_group = e_source_group_new (account->account_name,
+ EXCHANGE_URI_PREFIX);
+ tasks_source_group = e_source_group_new (account->account_name,
+ EXCHANGE_URI_PREFIX);
+ contacts_source_group = e_source_group_new (account->account_name,
+ EXCHANGE_URI_PREFIX);
+
+ if (!e_source_list_add_group (contacts_source_list, contacts_source_group, -1) ||
+ !e_source_list_add_group (cal_source_list, cal_source_group, -1) ||
+ !e_source_list_add_group (tasks_source_list, tasks_source_group, -1)) {
+ goto done;
+ }
+ for ( ; folders != NULL ; folders = g_slist_next (folders)) {
+ /* Create source for each folder and add to the group */
+
+ folder = folders->data;
+ if (folder->type == EXCHANGE_CONTACTS_FOLDER) {
+ source = e_source_new_with_absolute_uri (folder->name,
+ folder->uri);
+ if (offline_mode)
+ e_source_set_property (source, "offline_sync", "1");
+ if (username)
+ e_source_set_property (source, "username", username);
+ e_source_group_add_source (contacts_source_group,
+ source, -1);
+ g_object_unref (source);
+ }
+ else if (folder->type == EXCHANGE_CALENDAR_FOLDER){
+ relative_uri = g_strdup (folder->uri +
+ strlen (EXCHANGE_URI_PREFIX));
+ source = e_source_new (folder->name, relative_uri);
+ if (offline_mode)
+ e_source_set_property (source, "offline_sync", "1");
+ if (username)
+ e_source_set_property (source, "username", username);
+ e_source_group_add_source (cal_source_group,
+ source, -1);
+
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+ ids = g_slist_append (ids,
+ g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ g_object_unref (source);
+ g_free (relative_uri);
+
+ }
+ else if (folder->type == EXCHANGE_TASKS_FOLDER){
+ relative_uri = g_strdup (folder->uri +
+ strlen (EXCHANGE_URI_PREFIX));
+ source = e_source_new (folder->name, relative_uri);
+ if (offline_mode)
+ e_source_set_property (source, "offline_sync", "1");
+ if (username)
+ e_source_set_property (source, "username", username);
+ e_source_group_add_source (tasks_source_group,
+ source, -1);
+
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+
+ ids = g_slist_append (ids,
+ g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ g_object_unref (source);
+ g_free (relative_uri);
+ }
+ }
+
+ e_source_list_sync (cal_source_list, NULL);
+ e_source_list_sync (tasks_source_list, NULL);
+ e_source_list_sync (contacts_source_list, NULL);
+
+done:
+ g_object_unref (cal_source_group);
+ g_object_unref (tasks_source_group);
+ g_object_unref (contacts_source_group);
+
+ g_object_unref (cal_source_list);
+ g_object_unref (tasks_source_list);
+ g_object_unref (contacts_source_list);
+
+ g_object_unref (client);
+}
+
+void
+add_folder_esource (ExchangeAccount *account,
+ FolderType folder_type,
+ const char *folder_name,
+ const char *physical_uri)
+{
+ ESource *source = NULL;
+ ESourceGroup *source_group = NULL;
+ char *relative_uri = NULL, *username = NULL;
+ GSList *ids;
+ GConfClient *client;
+ gboolean is_contacts_folder = TRUE, group_new = FALSE, source_new = FALSE;
+ const char *offline = NULL;
+ int mode;
+ ESourceList *source_list;
+
+ client = gconf_client_get_default ();
+
+ username = exchange_account_get_username (account);
+
+ if (folder_type == EXCHANGE_CONTACTS_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CONTACTS);
+ }
+ else if (folder_type == EXCHANGE_CALENDAR_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CAL);
+ relative_uri = g_strdup (physical_uri + strlen (EXCHANGE_URI_PREFIX));
+ is_contacts_folder = FALSE;
+ }
+ else if (folder_type == EXCHANGE_TASKS_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_TASKS);
+ relative_uri = g_strdup (physical_uri + strlen (EXCHANGE_URI_PREFIX));
+ is_contacts_folder = FALSE;
+ }
+
+ exchange_account_is_offline_sync_set (account, &mode);
+
+ if ((source_group = e_source_list_peek_group_by_name (source_list,
+ account->account_name)) == NULL) {
+ source_group = e_source_group_new (account->account_name,
+ EXCHANGE_URI_PREFIX);
+ if (!e_source_list_add_group (source_list, source_group, -1)) {
+ g_object_unref (source_list);
+ g_object_unref (source_group);
+ g_object_unref (client);
+ g_free (relative_uri);
+ return;
+ }
+ if (is_contacts_folder)
+ source = e_source_new_with_absolute_uri (folder_name,
+ physical_uri);
+ else
+ source = e_source_new (folder_name, relative_uri);
+
+ if (mode == OFFLINE_MODE) {
+ /* If account is marked for offline sync during account
+ * creation, mark all the folders for offline sync
+ */
+ e_source_set_property (source, "offline_sync", "1");
+ }
+ if (username)
+ e_source_set_property (source, "username", username);
+ e_source_group_add_source (source_group, source, -1);
+ e_source_list_sync (source_list, NULL);
+ group_new = source_new = TRUE;
+ }
+ else {
+ /* source group already exists*/
+ if((source = e_source_group_peek_source_by_name (source_group,
+ folder_name)) == NULL) {
+ printf("old group, new source\n");
+ if (is_contacts_folder)
+ source = e_source_new_with_absolute_uri (
+ folder_name, physical_uri);
+ else
+ source = e_source_new (folder_name, relative_uri);
+
+ if (mode == OFFLINE_MODE)
+ e_source_set_property (source, "offline_sync", "1");
+ if (username)
+ e_source_set_property (source, "username", username);
+
+ e_source_group_add_source (source_group, source, -1);
+ source_new = TRUE;
+ e_source_list_sync (source_list, NULL);
+ } else {
+ /* source group and source both already exist */
+ offline = e_source_get_property (source, "offline_sync");
+ if (!offline) {
+ /* Folder doesn't have any offline property set */
+ if (mode == OFFLINE_MODE)
+ e_source_set_property (source, "offline_sync", "1");
+ }
+ }
+ }
+
+ if (source && !is_contacts_folder) {
+
+ /* Select the folder created */
+ if (folder_type == EXCHANGE_CALENDAR_FOLDER) {
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+ ids = g_slist_append (ids,
+ g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ }
+ else if (folder_type == EXCHANGE_TASKS_FOLDER) {
+
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+
+ ids = g_slist_append (ids,
+ g_strdup (e_source_peek_uid (source)));
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ }
+ }
+
+ g_free (relative_uri);
+
+ if (source_new)
+ g_object_unref (source);
+ if (group_new)
+ g_object_unref (source_group);
+ g_object_unref (source_list);
+ g_object_unref (client);
+}
+
+static void
+add_sources (ExchangeAccount *account)
+{
+ GPtrArray *exchange_folders;
+
+ exchange_folders = exchange_account_get_folders (account);
+ if (exchange_folders && exchange_folders->len > 0) {
+ int i;
+ const char *folder_type;
+ EFolder *folder;
+ GSList *folders = NULL;
+
+ for (i = 0; i < exchange_folders->len; i++) {
+ FolderInfo *folder_info = g_new0 (FolderInfo, 1);
+
+ folder = exchange_folders->pdata[i];
+ folder_type = e_folder_get_type_string (folder);
+
+ if (!(strcmp (folder_type, "calendar")) ||
+ !(strcmp (folder_type, "calendar/public"))) {
+ folder_info->name = e_folder_get_name (folder);
+ folder_info->uri = e_folder_get_physical_uri (folder);
+ folder_info->type = EXCHANGE_CALENDAR_FOLDER;
+ folders = g_slist_append (folders, folder_info);
+ }
+ else if (!(strcmp (folder_type, "tasks")) ||
+ !(strcmp (folder_type, "tasks/public"))) {
+ folder_info->name = e_folder_get_name (folder);
+ folder_info->uri = e_folder_get_physical_uri (folder);
+ folder_info->type = EXCHANGE_TASKS_FOLDER;
+ folders = g_slist_append (folders, folder_info);
+ }
+ else if (!(strcmp (folder_type, "contacts")) ||
+ !(strcmp (folder_type, "contacts/public")) ||
+ !(strcmp (folder_type, "contacts/ldap"))) {
+ folder_info->name = e_folder_get_name (folder);
+ folder_info->uri = e_folder_get_physical_uri (folder);
+ folder_info->type = EXCHANGE_CONTACTS_FOLDER;
+ folders = g_slist_append (folders, folder_info);
+ }
+ else
+ g_free (folder_info);
+ }
+ /* Add e-sources for all the folders */
+ add_account_esources (account, folders);
+ g_slist_foreach (folders, (GFunc) g_free, NULL);
+ g_slist_free (folders);
+ }
+}
+
+static void
+remove_account_esource (ExchangeAccount *account,
+ FolderType folder_type)
+{
+ ESourceGroup *group;
+ ESource *source = NULL;
+ GSList *groups;
+ GSList *sources;
+ GSList *ids, *node_to_be_deleted;
+ gboolean found_group;
+ const char *source_uid;
+ GConfClient *client;
+ ESourceList *source_list;
+
+ /* Remove the ESource group, to remove all the folders in a component */
+
+ client = gconf_client_get_default ();
+
+ if (folder_type == EXCHANGE_CONTACTS_FOLDER)
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CONTACTS);
+ else if (folder_type == EXCHANGE_CALENDAR_FOLDER)
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CAL);
+ else if (folder_type == EXCHANGE_TASKS_FOLDER)
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_TASKS);
+
+ groups = e_source_list_peek_groups (source_list);
+ found_group = FALSE;
+
+ for ( ; groups != NULL && !found_group; groups = g_slist_next (groups)) {
+ group = E_SOURCE_GROUP (groups->data);
+
+ if (strcmp (e_source_group_peek_name (group), account->account_name) == 0
+ &&
+ strcmp (e_source_group_peek_base_uri (group), EXCHANGE_URI_PREFIX) == 0) {
+ sources = e_source_group_peek_sources (group);
+
+ for( ; sources != NULL; sources = g_slist_next (sources)) {
+ source = E_SOURCE (sources->data);
+ source_uid = e_source_peek_uid (source);
+
+ /* Remove from the selected folders */
+ if (folder_type == EXCHANGE_CALENDAR_FOLDER) {
+ ids = gconf_client_get_list (
+ client,
+ CONF_KEY_SELECTED_CAL_SOURCES ,
+ GCONF_VALUE_STRING, NULL);
+ if (ids) {
+ node_to_be_deleted = g_slist_find_custom (
+ ids,
+ source_uid,
+ (GCompareFunc) strcmp);
+ if (node_to_be_deleted) {
+ g_free (node_to_be_deleted->data);
+ ids = g_slist_delete_link (ids,
+ node_to_be_deleted);
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ }
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ }
+ }
+ else if (folder_type == EXCHANGE_TASKS_FOLDER) {
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES ,
+ GCONF_VALUE_STRING, NULL);
+ if (ids) {
+ node_to_be_deleted = g_slist_find_custom (
+ ids,
+ source_uid,
+ (GCompareFunc) strcmp);
+ if (node_to_be_deleted) {
+ g_free (node_to_be_deleted->data);
+ ids = g_slist_delete_link (ids,
+ node_to_be_deleted);
+ gconf_client_set_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, ids, NULL);
+ }
+ g_slist_foreach (ids, (GFunc) g_free, NULL);
+ g_slist_free (ids);
+ }
+ }
+ e_source_list_remove_group (source_list, group);
+ e_source_list_sync (source_list, NULL);
+ found_group = TRUE;
+ break;
+ }
+ }
+ }
+ g_object_unref (source_list);
+ g_object_unref (client);
+}
+
+void
+remove_folder_esource (ExchangeAccount *account,
+ FolderType folder_type,
+ const char *physical_uri)
+{
+ ESourceGroup *group;
+ ESource *source;
+ GSList *groups;
+ GSList *sources;
+ gboolean found_group, is_contacts_folder = TRUE;
+ char *relative_uri = NULL;
+ const char *source_uid;
+ GSList *ids, *temp_ids, *node_to_be_deleted;
+ GConfClient *client;
+ ESourceList *source_list;
+
+ client = gconf_client_get_default ();
+
+ /* Remove ESource for a given folder */
+ if (folder_type == EXCHANGE_CONTACTS_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CONTACTS);
+ }
+ else if (folder_type == EXCHANGE_CALENDAR_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_CAL);
+ relative_uri = g_strdup (physical_uri + strlen (EXCHANGE_URI_PREFIX));
+ is_contacts_folder = FALSE;
+ }
+ else if (folder_type == EXCHANGE_TASKS_FOLDER) {
+ source_list = e_source_list_new_for_gconf ( client,
+ CONF_KEY_TASKS);
+ relative_uri = g_strdup (physical_uri + strlen (EXCHANGE_URI_PREFIX));
+ is_contacts_folder = FALSE;
+ }
+
+ groups = e_source_list_peek_groups (source_list);
+ found_group = FALSE;
+
+ for ( ; groups != NULL && !found_group; groups = g_slist_next (groups)) {
+ group = E_SOURCE_GROUP (groups->data);
+
+ if (strcmp (e_source_group_peek_name (group), account->account_name) == 0
+ &&
+ strcmp (e_source_group_peek_base_uri (group), EXCHANGE_URI_PREFIX) == 0) {
+
+ sources = e_source_group_peek_sources (group);
+
+ for( ; sources != NULL; sources = g_slist_next (sources)) {
+
+ source = E_SOURCE (sources->data);
+
+ if (((!is_contacts_folder &&
+ strcmp (e_source_peek_relative_uri (source),
+ relative_uri) == 0)) ||
+ (is_contacts_folder &&
+ strcmp (e_source_peek_absolute_uri (source),
+ physical_uri) == 0)) {
+
+ source_uid = e_source_peek_uid (source);
+ /* Folder Deleted - Remove only the source */
+ /*
+ e_source_group_remove_source_by_uid (
+ group,
+ source_uid);
+ */
+ e_source_group_remove_source (
+ group,
+ source);
+ e_source_list_sync (source_list, NULL);
+ if (!is_contacts_folder) {
+ /* Remove from the selected folders */
+ if (folder_type == EXCHANGE_CALENDAR_FOLDER) {
+ ids = gconf_client_get_list (
+ client,
+ CONF_KEY_SELECTED_CAL_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+ if (ids) {
+ node_to_be_deleted = g_slist_find_custom (ids,
+ source_uid,
+ (GCompareFunc) strcmp);
+ if (node_to_be_deleted) {
+ g_free (node_to_be_deleted->data);
+ ids = g_slist_delete_link (ids,
+ node_to_be_deleted);
+ }
+ }
+ temp_ids = ids;
+ for (; temp_ids != NULL; temp_ids = g_slist_next (temp_ids))
+ g_free (temp_ids->data);
+ g_slist_free (ids);
+ }
+ else if (folder_type == EXCHANGE_TASKS_FOLDER) {
+ ids = gconf_client_get_list (client,
+ CONF_KEY_SELECTED_TASKS_SOURCES,
+ GCONF_VALUE_STRING, NULL);
+ if (ids) {
+ node_to_be_deleted = g_slist_find_custom (ids,
+ source_uid,
+ (GCompareFunc) strcmp);
+ if (node_to_be_deleted) {
+ g_free (node_to_be_deleted->data);
+ ids = g_slist_delete_link (ids,
+ node_to_be_deleted);
+ }
+ }
+ temp_ids = ids;
+ for (; temp_ids != NULL; temp_ids = g_slist_next (temp_ids))
+ g_free (temp_ids->data);
+ g_slist_free (ids);
+ }
+ }
+ found_group = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ g_object_unref (source_list);
+ g_free (relative_uri);
+ g_object_unref (client);
+}
+
+static void
+remove_account_esources (ExchangeAccount *account)
+{
+ /* Remove ESources for all the folders in all the components */
+
+ remove_account_esource (account, EXCHANGE_CALENDAR_FOLDER);
+ remove_account_esource (account, EXCHANGE_TASKS_FOLDER);
+ remove_account_esource (account, EXCHANGE_CONTACTS_FOLDER);
+}
+
+static void
+account_added (EAccountList *account_list, EAccount *account)
+{
+ ExchangeConfigListener *config_listener;
+ ExchangeAccount *exchange_account;
+
+ if (!is_active_exchange_account (account))
+ return;
+
+ config_listener = EXCHANGE_CONFIG_LISTENER (account_list);
+ if (config_listener->priv->configured_account) {
+ /* Multiple accounts configured. */
+ /* SURF : e_notice (NULL, GTK_MESSAGE_ERROR,
+ _("You may only configure a single Exchange account"));
+ */
+ return;
+ }
+
+ /* New account! Yippee! */
+ exchange_account = exchange_account_new (account_list, account);
+ if (!exchange_account) {
+ g_warning ("Could not parse exchange uri '%s'",
+ account->source->url);
+ return;
+ }
+
+ config_listener->priv->exchange_account = exchange_account;
+ config_listener->priv->configured_account = account;
+
+ g_free (config_listener->priv->configured_uri);
+ config_listener->priv->configured_uri = g_strdup (account->source->url);
+ g_free (config_listener->priv->configured_name);
+ config_listener->priv->configured_name = g_strdup (account->name);
+
+ if (account == e_account_list_get_default (account_list)) {
+ g_signal_connect_swapped (config_listener->priv->exchange_account,
+ "connected",
+ G_CALLBACK (add_defaults_for_account),
+ config_listener);
+ }
+
+ g_signal_emit (config_listener, signals[EXCHANGE_ACCOUNT_CREATED], 0,
+ exchange_account);
+ add_sources (exchange_account);
+ exchange_account_connect (exchange_account);
+}
+
+struct account_update_data {
+ EAccountList *account_list;
+ EAccount *account;
+};
+
+static void
+configured_account_destroyed (gpointer user_data, GObject *where_account_was)
+{
+ struct account_update_data *aud = user_data;
+
+ if (!EXCHANGE_CONFIG_LISTENER (aud->account_list)->priv->configured_account)
+ account_added (aud->account_list, aud->account);
+
+ g_object_unref (aud->account_list);
+ g_object_unref (aud->account);
+ g_free (aud);
+}
+
+static gboolean
+requires_relogin (char *current_url, char *new_url)
+{
+ E2kUri *current_uri, *new_uri;
+ const char *current_param_val, *new_param_val;
+ const char *params [] = { "owa_url", "ad_server", "use_ssl" };
+ const int n_params = G_N_ELEMENTS (params);
+ int i;
+ gboolean relogin = FALSE;
+
+ current_uri = e2k_uri_new (current_url);
+ new_uri = e2k_uri_new (new_url);
+
+ if (strcmp (current_uri->user, new_uri->user) ||
+ strcmp (current_uri->host, new_uri->host)) {
+ relogin = TRUE;
+ goto end;
+ }
+
+ if (current_uri->authmech || new_uri->authmech) {
+ if (current_uri->authmech && new_uri->authmech) {
+ if (strcmp (current_uri->authmech, new_uri->authmech)) {
+ /* Auth mechanism has changed */
+ relogin = TRUE;
+ goto end;
+ }
+ }
+ else {
+ /* Auth mechanism is set for the first time */
+ relogin = TRUE;
+ goto end;
+ }
+ }
+
+ for (i=0; i<n_params; i++) {
+ current_param_val = e2k_uri_get_param (current_uri, params[i]);
+ new_param_val = e2k_uri_get_param (new_uri, params[i]);
+
+ if (current_param_val && new_param_val) {
+ /* both the urls have params to be compared */
+ if (strcmp (current_param_val, new_param_val)) {
+ relogin = TRUE;
+ break;
+ }
+ }
+ else if (current_param_val || new_param_val){
+ /* check for added or deleted parameter */
+ relogin = TRUE;
+ break;
+ }
+ }
+end:
+ e2k_uri_free (new_uri);
+ e2k_uri_free (current_uri);
+ return relogin;
+}
+
+static void
+account_changed (EAccountList *account_list, EAccount *account)
+{
+ ExchangeConfigListener *config_listener =
+ EXCHANGE_CONFIG_LISTENER (account_list);
+ ExchangeConfigListenerPrivate *priv = config_listener->priv;
+
+ if (account != config_listener->priv->configured_account) {
+ if (!is_active_exchange_account (account))
+ return;
+
+ /* The user has converted an existing non-Exchange
+ * account to an Exchange account, so treat it like an
+ * add.
+ */
+ account_added (account_list, account);
+ return;
+ } else if (!is_active_exchange_account (account)) {
+ /* The user has disabled the Exchange account or
+ * converted it to non-Exchange, so treat it like a
+ * remove.
+ */
+ account_removed (account_list, account);
+ return;
+ }
+
+ if (!strcmp (config_listener->priv->configured_uri, account->source->url) &&
+ !strcmp (config_listener->priv->configured_name, account->name)) {
+ /* The user changed something we don't care about. */
+ return;
+ }
+
+ /* OK, so he modified the active account in a way we care
+ * about. If the user hasn't connected yet, we're still ok.
+ */
+ if (!exchange_account_get_context (config_listener->priv->exchange_account)) {
+ /* Good. Remove the current account, and wait for it
+ * to actually go away (which may not happen immediately
+ * since there may be a function higher up on the stack
+ * still holding a ref on it). Then create the new one.
+ * (We have to wait for it to go away because the new
+ * storage probably still has the same name as the old
+ * one, so trying to create it before the old one is
+ * removed would fail.)
+ */
+ struct account_update_data *aud;
+
+ aud = g_new (struct account_update_data, 1);
+ aud->account = g_object_ref (account);
+ aud->account_list = g_object_ref (account_list);
+ g_object_weak_ref (G_OBJECT (config_listener->priv->exchange_account), configured_account_destroyed, aud);
+
+ account_removed (account_list, account);
+ return;
+ }
+
+ /* If account name has changed, or the url value has changed, which
+ * could be due to change in hostname or some parameter value,
+ * remove old e-sources
+ */
+ if (strcmp (config_listener->priv->configured_uri, account->source->url)) {
+ /* This condition will be true always as order of parameters in
+ * configured_uri and source->url will not match
+ */
+ remove_account_esources (priv->exchange_account);
+
+ /* Ask user to authenticate at next login if username, hostname,
+ * OWA URL or GC server values are changed.
+ */
+ if (requires_relogin (config_listener->priv->configured_uri,
+ account->source->url)) {
+ exchange_account_forget_password (priv->exchange_account);
+ }
+ else {
+ /* FIXME: modify esources and don't ask for re-login */
+ /* modify_esource (priv->exchange_account,
+ * account->source->url);
+ * return;
+ */
+ }
+ }
+ else if (strcmp (config_listener->priv->configured_name, account->name))
+ remove_account_esources (priv->exchange_account);
+
+ /* Nope. Let the user know we're ignoring him. */
+ /* SURF : e_notice (NULL, GTK_MESSAGE_WARNING,
+ _("Changes to Exchange account configuration will "
+ "take\nplace after you quit and restart Evolution."));
+ */
+
+ /* But note the new URI so if he changes something else, we
+ * only warn again if he changes again.
+ */
+ g_free (config_listener->priv->configured_uri);
+ config_listener->priv->configured_uri = g_strdup (account->source->url);
+ g_free (config_listener->priv->configured_name);
+ config_listener->priv->configured_name = g_strdup (account->name);
+}
+
+static void
+account_removed (EAccountList *account_list, EAccount *account)
+{
+ ExchangeConfigListener *config_listener =
+ EXCHANGE_CONFIG_LISTENER (account_list);
+ ExchangeConfigListenerPrivate *priv = config_listener->priv;
+
+ if (account != priv->configured_account)
+ return;
+
+ /* Remove all ESources */
+ remove_account_esources (priv->exchange_account);
+
+ exchange_account_forget_password (priv->exchange_account);
+
+ if (!exchange_account_get_context (priv->exchange_account)) {
+ /* The account isn't connected yet, so we can destroy
+ * it without problems.
+ */
+ g_signal_emit (config_listener,
+ signals[EXCHANGE_ACCOUNT_REMOVED], 0,
+ priv->exchange_account);
+
+ g_object_unref (priv->exchange_account);
+ priv->exchange_account = NULL;
+
+ priv->configured_account = NULL;
+ g_free (priv->configured_uri);
+ priv->configured_uri = NULL;
+ g_free (priv->configured_name);
+ priv->configured_name = NULL;
+ } else {
+ if (account->enabled) {
+ /* SURF : e_notice (NULL, GTK_MESSAGE_INFO,
+ _("The Exchange account will be removed when you quit Evolution"));
+ */
+ }
+ else {
+ /* The account is in use. We can't remove it. */
+ /* SURF : e_notice (NULL, GTK_MESSAGE_INFO,
+ _("The Exchange account will be disabled when you quit Evolution"));
+ */
+ }
+ }
+}
+
+static gboolean
+idle_construct (gpointer data)
+{
+ ExchangeConfigListener *config_listener = data;
+
+ config_listener->priv->idle_id = 0;
+ e_account_list_construct (E_ACCOUNT_LIST (config_listener),
+ config_listener->priv->gconf);
+ return FALSE;
+}
+
+/**
+ * exchange_config_listener_new:
+ *
+ * This creates and returns a new #ExchangeConfigListener, which
+ * monitors GConf and creates and (theoretically) destroys accounts
+ * accordingly. It will emit an %account_created signal when a new
+ * account is created (or shortly after the listener itself is created
+ * if an account already exists).
+ *
+ * Due to various constraints, the user is currently limited to a
+ * single account, and it is not possible to destroy an existing
+ * account. Thus, the %account_created signal will never be emitted
+ * more than once currently.
+ *
+ * Return value: the new config listener.
+ **/
+ExchangeConfigListener *
+exchange_config_listener_new (void)
+{
+ ExchangeConfigListener *config_listener;
+
+ config_listener = g_object_new (EXCHANGE_TYPE_CONFIG_LISTENER, NULL);
+ config_listener->priv->gconf = gconf_client_get_default ();
+
+ config_listener->priv->idle_id =
+ g_idle_add (idle_construct, config_listener);
+
+ return config_listener;
+}
+
+GSList *
+exchange_config_listener_get_accounts (ExchangeConfigListener *config_listener)
+{
+ g_return_val_if_fail (EXCHANGE_IS_CONFIG_LISTENER (config_listener), NULL);
+
+ if (config_listener->priv->exchange_account)
+ return g_slist_append (NULL, config_listener->priv->exchange_account);
+ else
+ return NULL;
+}
+
diff --git a/plugins/exchange-operations/exchange-config-listener.h b/plugins/exchange-operations/exchange-config-listener.h
new file mode 100644
index 0000000000..7cc058dd05
--- /dev/null
+++ b/plugins/exchange-operations/exchange-config-listener.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2001-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_CONFIG_LISTENER_H__
+#define __EXCHANGE_CONFIG_LISTENER_H__
+
+#include <exchange-constants.h>
+
+#include "exchange-types.h"
+#include "e-util/e-account-list.h"
+#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-source-group.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EXCHANGE_TYPE_CONFIG_LISTENER (exchange_config_listener_get_type ())
+#define EXCHANGE_CONFIG_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_TYPE_CONFIG_LISTENER, ExchangeConfigListener))
+#define EXCHANGE_CONFIG_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_TYPE_CONFIG_LISTENER, ExchangeConfigListenerClass))
+#define EXCHANGE_IS_CONFIG_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_TYPE_CONFIG_LISTENER))
+#define EXCHANGE_IS_CONFIG_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_TYPE_CONFIG_LISTENER))
+
+struct _ExchangeConfigListener {
+ EAccountList parent;
+
+ ExchangeConfigListenerPrivate *priv;
+};
+
+struct _ExchangeConfigListenerClass {
+ EAccountListClass parent_class;
+
+ /* signals */
+ void (*exchange_account_created) (ExchangeConfigListener *,
+ ExchangeAccount *);
+ void (*exchange_account_removed) (ExchangeConfigListener *,
+ ExchangeAccount *);
+};
+
+#define CONF_KEY_CAL "/apps/evolution/calendar/sources"
+#define CONF_KEY_TASKS "/apps/evolution/tasks/sources"
+#define CONF_KEY_CONTACTS "/apps/evolution/addressbook/sources"
+#define EXCHANGE_URI_PREFIX "exchange://"
+
+GType exchange_config_listener_get_type (void);
+ExchangeConfigListener *exchange_config_listener_new (void);
+
+GSList *exchange_config_listener_get_accounts (ExchangeConfigListener *config_listener);
+
+void add_folder_esource (ExchangeAccount *account, FolderType folder_type, const char *folder_name, const char *physical_uri);
+void remove_folder_esource (ExchangeAccount *account, FolderType folder_type, const char *physical_uri);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_CONFIG_LISTENER_H__ */
diff --git a/plugins/exchange-operations/exchange-delegates-user.c b/plugins/exchange-operations/exchange-delegates-user.c
new file mode 100644
index 0000000000..38166747f4
--- /dev/null
+++ b/plugins/exchange-operations/exchange-delegates-user.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeDelegatesUser: A single delegate */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "exchange-delegates-user.h"
+
+#include "e2k-global-catalog.h"
+#include "e2k-marshal.h"
+#include "e2k-sid.h"
+#include "e2k-utils.h"
+
+#include <e-util/e-dialog-utils.h>
+#include <e-util/e-dialog-widgets.h>
+#include <e-util/e-gtk-utils.h>
+#include <glade/glade.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkmenushell.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtktogglebutton.h>
+
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtkoptionmenu.h>
+
+#include <string.h>
+
+#define EXCHANGE_DELEGATES_USER_SEPARATOR -2
+#define EXCHANGE_DELEGATES_USER_CUSTOM -3
+/* Can't use E2K_PERMISSIONS_ROLE_CUSTOM, because it's -1, which
+ * means "end of list" to e_dialog_option_menu_get/set
+ */
+
+static const int exchange_perm_map[] = {
+ E2K_PERMISSIONS_ROLE_NONE,
+ E2K_PERMISSIONS_ROLE_REVIEWER,
+ E2K_PERMISSIONS_ROLE_AUTHOR,
+ E2K_PERMISSIONS_ROLE_EDITOR,
+
+ EXCHANGE_DELEGATES_USER_SEPARATOR,
+ EXCHANGE_DELEGATES_USER_CUSTOM,
+
+ -1
+};
+
+const char *exchange_delegates_user_folder_names[] = {
+ "calendar", "tasks", "inbox", "contacts"
+};
+static const char *widget_names[] = {
+ "calendar_perms", "task_perms", "inbox_perms", "contact_perms",
+};
+
+
+enum {
+ EDITED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class = NULL;
+
+static void
+finalize (GObject *object)
+{
+ ExchangeDelegatesUser *user = EXCHANGE_DELEGATES_USER (object);
+
+ if (user->display_name)
+ g_free (user->display_name);
+ if (user->dn)
+ g_free (user->dn);
+ if (user->entryid)
+ g_byte_array_free (user->entryid, TRUE);
+ if (user->sid)
+ g_object_unref (user->sid);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ object_class->finalize = finalize;
+
+ /* signals */
+ signals[EDITED] =
+ g_signal_new ("edited",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ExchangeDelegatesUserClass, edited),
+ NULL, NULL,
+ e2k_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+}
+
+E2K_MAKE_TYPE (exchange_delegates_user, ExchangeDelegatesUser, class_init, NULL, PARENT_TYPE)
+
+static inline gboolean
+is_delegate_role (E2kPermissionsRole role)
+{
+ return (role == E2K_PERMISSIONS_ROLE_NONE ||
+ role == E2K_PERMISSIONS_ROLE_REVIEWER ||
+ role == E2K_PERMISSIONS_ROLE_AUTHOR ||
+ role == E2K_PERMISSIONS_ROLE_EDITOR);
+}
+
+static void
+set_perms (GtkWidget *omenu, E2kPermissionsRole role)
+{
+ if (!is_delegate_role (role)) {
+ GtkWidget *menu, *item;
+
+ menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (omenu));
+
+ item = gtk_menu_item_new ();
+ gtk_widget_set_sensitive (item, FALSE);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label (_("Custom"));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ gtk_widget_show_all (menu);
+
+ role = EXCHANGE_DELEGATES_USER_CUSTOM;
+ }
+
+ e_dialog_option_menu_set (omenu, role, exchange_perm_map);
+}
+
+static void
+parent_window_destroyed (gpointer dialog, GObject *where_parent_window_was)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * exchange_delegates_user_edit:
+ * @user: a delegate
+ * @parent_window: parent window for the editor dialog
+ *
+ * Brings up a dialog to edit @user's permissions as a delegate.
+ * An %edited signal will be emitted if anything changed.
+ *
+ * Return value: %TRUE for "OK", %FALSE for "Cancel".
+ **/
+gboolean
+exchange_delegates_user_edit (ExchangeDelegatesUser *user,
+ GtkWidget *parent_window)
+{
+ GladeXML *xml;
+ GtkWidget *dialog, *table, *label, *menu, *check;
+ char *title;
+ int button, i;
+ E2kPermissionsRole role;
+ gboolean modified;
+
+ g_return_val_if_fail (EXCHANGE_IS_DELEGATES_USER (user), FALSE);
+ g_return_val_if_fail (E2K_IS_SID (user->sid), FALSE);
+
+ /* Grab the Glade widgets */
+ xml = glade_xml_new (
+ CONNECTOR_GLADEDIR "/exchange-delegates.glade",
+ "delegate_permissions", PACKAGE);
+ g_return_val_if_fail (xml, FALSE);
+
+ title = g_strdup_printf (_("Permissions for %s"), user->display_name);
+
+ dialog = glade_xml_get_widget (xml, "delegate_permissions");
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), parent_window);
+
+ table = glade_xml_get_widget (xml, "toplevel_table");
+ gtk_widget_reparent (table, GTK_DIALOG (dialog)->vbox);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 6);
+
+ label = glade_xml_get_widget (xml, "delegate_label");
+ gtk_label_set_text (GTK_LABEL (label), title);
+ g_free (title);
+
+ /* Set up the permissions */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ menu = glade_xml_get_widget (xml, widget_names[i]);
+ set_perms (menu, user->role[i]);
+ }
+ check = glade_xml_get_widget (xml, "see_private_checkbox");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ user->see_private);
+
+ /* Run the dialog, while watching its parent. */
+ g_object_weak_ref (G_OBJECT (parent_window),
+ parent_window_destroyed, dialog);
+ g_object_add_weak_pointer (G_OBJECT (parent_window),
+ (void **)&parent_window);
+ button = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (parent_window) {
+ g_object_remove_weak_pointer (G_OBJECT (parent_window),
+ (void **)&parent_window);
+ g_object_weak_unref (G_OBJECT (parent_window),
+ parent_window_destroyed, dialog);
+ }
+
+ if (button != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return FALSE;
+ }
+
+ /* And update */
+ modified = FALSE;
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ menu = glade_xml_get_widget (xml, widget_names[i]);
+ role = e_dialog_option_menu_get (menu, exchange_perm_map);
+
+ if (is_delegate_role (user->role[i]) &&
+ user->role[i] != role) {
+ user->role[i] = role;
+ modified = TRUE;
+ }
+ }
+ check = glade_xml_get_widget (xml, "see_private_checkbox");
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)) !=
+ user->see_private) {
+ user->see_private = !user->see_private;
+ modified = TRUE;
+ }
+
+ g_object_unref (xml);
+ gtk_widget_destroy (dialog);
+
+ if (modified)
+ g_signal_emit (user, signals[EDITED], 0);
+
+ return TRUE;
+}
+
+/**
+ * exchange_delegates_user_new:
+ * @display_name: the delegate's (UTF8) display name
+ *
+ * Return value: a new delegate user with default permissions (but
+ * with most of the internal data blank).
+ **/
+ExchangeDelegatesUser *
+exchange_delegates_user_new (const char *display_name)
+{
+ ExchangeDelegatesUser *user;
+ int i;
+
+ user = g_object_new (EXCHANGE_TYPE_DELEGATES_USER, NULL);
+ user->display_name = g_strdup (display_name);
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (i == EXCHANGE_DELEGATES_CALENDAR ||
+ i == EXCHANGE_DELEGATES_TASKS)
+ user->role[i] = E2K_PERMISSIONS_ROLE_EDITOR;
+ else
+ user->role[i] = E2K_PERMISSIONS_ROLE_NONE;
+ }
+
+ return user;
+}
+
+/**
+ * exchange_delegates_user_new_from_gc:
+ * @gc: the global catalog object
+ * @email: email address of the new delegate
+ * @creator_entryid: The value of the PR_CREATOR_ENTRYID property
+ * on the LocalFreebusy file.
+ *
+ * Return value: a new delegate user with default permissions and
+ * internal data filled in from the global catalog.
+ **/
+ExchangeDelegatesUser *
+exchange_delegates_user_new_from_gc (E2kGlobalCatalog *gc,
+ const char *email,
+ GByteArray *creator_entryid)
+{
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+ ExchangeDelegatesUser *user;
+ guint8 *p;
+
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL, email,
+ (E2K_GLOBAL_CATALOG_LOOKUP_SID |
+ E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN),
+ &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK)
+ return NULL;
+
+ user = exchange_delegates_user_new (e2k_sid_get_display_name (entry->sid));
+ user->dn = g_strdup (entry->dn);
+ user->sid = entry->sid;
+ g_object_ref (user->sid);
+
+ user->entryid = g_byte_array_new ();
+ p = creator_entryid->data + creator_entryid->len - 2;
+ while (p > creator_entryid->data && *p)
+ p--;
+ g_byte_array_append (user->entryid, creator_entryid->data,
+ p - creator_entryid->data + 1);
+ g_byte_array_append (user->entryid, entry->legacy_exchange_dn,
+ strlen (entry->legacy_exchange_dn));
+ g_byte_array_append (user->entryid, "", 1);
+
+ return user;
+}
diff --git a/plugins/exchange-operations/exchange-delegates-user.h b/plugins/exchange-operations/exchange-delegates-user.h
new file mode 100644
index 0000000000..2b5b291e13
--- /dev/null
+++ b/plugins/exchange-operations/exchange-delegates-user.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_DELEGATES_USER_H__
+#define __EXCHANGE_DELEGATES_USER_H__
+
+#include <exchange-types.h>
+#include <e2k-security-descriptor.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EXCHANGE_TYPE_DELEGATES_USER (exchange_delegates_user_get_type ())
+#define EXCHANGE_DELEGATES_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_TYPE_DELEGATES_USER, ExchangeDelegatesUser))
+#define EXCHANGE_DELEGATES_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_TYPE_DELEGATES_USER, ExchangeDelegatesUserClass))
+#define EXCHANGE_IS_DELEGATES_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_TYPE_DELEGATES_USER))
+#define EXCHANGE_IS_DELEGATES_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_TYPE_DELEGATES_USER))
+
+
+typedef struct _ExchangeDelegatesUser ExchangeDelegatesUser;
+typedef struct _ExchangeDelegatesUserPrivate ExchangeDelegatesUserPrivate;
+typedef struct _ExchangeDelegatesUserClass ExchangeDelegatesUserClass;
+
+enum {
+ EXCHANGE_DELEGATES_CALENDAR,
+ EXCHANGE_DELEGATES_TASKS,
+ EXCHANGE_DELEGATES_INBOX,
+ EXCHANGE_DELEGATES_CONTACTS,
+ EXCHANGE_DELEGATES_LAST
+};
+
+struct _ExchangeDelegatesUser {
+ GObject parent;
+
+ char *display_name, *dn;
+ GByteArray *entryid;
+
+ E2kSid *sid;
+ E2kPermissionsRole role[EXCHANGE_DELEGATES_LAST];
+ gboolean see_private;
+};
+
+struct _ExchangeDelegatesUserClass {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*edited) (ExchangeDelegatesUser *, gpointer);
+};
+
+
+
+GType exchange_delegates_user_get_type (void);
+
+ExchangeDelegatesUser *exchange_delegates_user_new (const char *display_name);
+ExchangeDelegatesUser *exchange_delegates_user_new_from_gc (E2kGlobalCatalog *gc,
+ const char *email,
+ GByteArray *creator_entryid);
+
+gboolean exchange_delegates_user_edit (ExchangeDelegatesUser *user,
+ GtkWidget *parent_window);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_DELEGATES_USER_H__ */
diff --git a/plugins/exchange-operations/exchange-delegates.c b/plugins/exchange-operations/exchange-delegates.c
new file mode 100644
index 0000000000..0721e523ac
--- /dev/null
+++ b/plugins/exchange-operations/exchange-delegates.c
@@ -0,0 +1,973 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeDelegates: Exchange delegate handling.
+ *
+ * FIXME: make this instant-apply
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "exchange-delegates.h"
+#include "exchange-delegates-user.h"
+
+#include <exchange-account.h>
+#include <e2k-propnames.h>
+#include <e2k-security-descriptor.h>
+#include <e2k-sid.h>
+#include <e2k-uri.h>
+#include <e2k-user-dialog.h>
+#include <e2k-utils.h>
+
+#include <e-util/e-dialog-utils.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+
+typedef struct {
+ const char *uri;
+ E2kSecurityDescriptor *sd;
+ gboolean changed;
+} ExchangeDelegatesFolder;
+
+typedef struct {
+ ExchangeAccount *account;
+ char *self_dn;
+
+ GladeXML *xml;
+ GtkWidget *dialog, *parent;
+
+ GtkListStore *model;
+ GtkWidget *table;
+
+ GByteArray *creator_entryid;
+ GPtrArray *users, *added_users, *removed_users;
+ gboolean loaded_folders;
+ ExchangeDelegatesFolder folder[EXCHANGE_DELEGATES_LAST];
+ ExchangeDelegatesFolder freebusy_folder;
+} ExchangeDelegates;
+
+extern const char *exchange_delegates_user_folder_names[];
+
+const char *exchange_localfreebusy_path = "NON_IPM_SUBTREE/Freebusy%20Data/LocalFreebusy.EML";
+
+static void set_perms_for_user (ExchangeDelegatesUser *user, gpointer user_data);
+
+static void
+set_sd_for_href (ExchangeDelegates *delegates,
+ const char *href,
+ E2kSecurityDescriptor *sd)
+{
+ int i;
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (!delegates->folder[i].uri)
+ continue;
+
+ if (!strcmp (href, delegates->folder[i].uri)) {
+ delegates->folder[i].sd = sd;
+ return;
+ }
+ }
+
+ /* else, it's the freebusy folder */
+ delegates->freebusy_folder.uri = g_strdup (href);
+ delegates->freebusy_folder.sd = sd;
+}
+
+/* Given an array of ExchangeDelegatesUser containing display names
+ * and entryids, and an array of E2kSecurityDescriptors containing
+ * SIDs (which contain display names), add SIDs to the delegates. In
+ * the easy case, we can just match the SIDs up with their
+ * corresponding user by display name. However, there are two things
+ * that can go wrong:
+ *
+ * 1. Some users may have been removed from the SDs
+ * 2. Two users may have the same display name
+ *
+ * In both cases, we fall back to using the GC.
+ */
+static gboolean
+fill_in_sids (ExchangeDelegates *delegates)
+{
+ int u, u2, sd, needed_sids;
+ ExchangeDelegatesUser *user, *user2;
+ GList *sids, *s;
+ E2kSid *sid;
+ E2kGlobalCatalog *gc;
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+ gboolean ok = TRUE;
+
+ needed_sids = 0;
+
+ /* Mark users with duplicate names and count the number of
+ * non-duplicate names.
+ */
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid == (E2kSid *)-1)
+ continue;
+ for (u2 = u + 1; u2 < delegates->users->len; u2++) {
+ user2 = delegates->users->pdata[u2];
+ if (!strcmp (user->display_name, user2->display_name))
+ user->sid = user2->sid = (E2kSid *)-1;
+ }
+ if (!user->sid)
+ needed_sids++;
+ }
+
+ /* Scan security descriptors trying to match SIDs until we're
+ * not expecting to find any more.
+ */
+ for (sd = 0; sd < EXCHANGE_DELEGATES_LAST && needed_sids; sd++) {
+ sids = e2k_security_descriptor_get_sids (delegates->folder[sd].sd);
+ for (s = sids; s && needed_sids; s = s->next) {
+ sid = s->data;
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid)
+ continue;
+ if (!strcmp (user->display_name,
+ e2k_sid_get_display_name (sid))) {
+ user->sid = sid;
+ g_object_ref (sid);
+ needed_sids--;
+ }
+ }
+ }
+ g_list_free (sids);
+ }
+
+ /* Now for each user whose SID hasn't yet been found, look it up. */
+ gc = exchange_account_get_global_catalog (delegates->account);
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid && user->sid != (E2kSid *)-1)
+ continue;
+
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ e2k_entryid_to_dn (user->entryid),
+ E2K_GLOBAL_CATALOG_LOOKUP_SID, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK) {
+ user->sid = NULL;
+ ok = FALSE;
+ continue;
+ }
+ user->sid = entry->sid;
+ g_object_ref (user->sid);
+ e2k_global_catalog_entry_free (gc, entry);
+ }
+
+ return ok;
+}
+
+static const char *sd_props[] = {
+ E2K_PR_EXCHANGE_SD_BINARY,
+ E2K_PR_EXCHANGE_SD_XML
+};
+static const int n_sd_props = sizeof (sd_props) / sizeof (sd_props[0]);
+
+/* Read the folder security descriptors and match them up with the
+ * list of delegates.
+ */
+static gboolean
+get_folder_security (ExchangeDelegates *delegates)
+{
+ GPtrArray *hrefs;
+ E2kContext *ctx;
+ E2kHTTPStatus status;
+ E2kResultIter *iter;
+ E2kResult *result;
+ xmlNode *xml_form;
+ GByteArray *binary_form;
+ ExchangeDelegatesUser *user;
+ guint32 perms;
+ int i, u;
+
+ /* If we've been here before, just return the success or
+ * failure result from last time.
+ */
+ if (delegates->freebusy_folder.uri)
+ return delegates->loaded_folders;
+
+ if (!exchange_account_get_global_catalog (delegates->account)) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("No Global Catalog server configured for this account.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ ctx = exchange_account_get_context (delegates->account);
+
+ hrefs = g_ptr_array_new ();
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ delegates->folder[i].uri = exchange_account_get_standard_uri (
+ delegates->account, exchange_delegates_user_folder_names[i]);
+ if (delegates->folder[i].uri) {
+ g_ptr_array_add (hrefs, (char *)e2k_uri_relative (
+ delegates->account->home_uri,
+ delegates->folder[i].uri));
+ }
+ }
+ g_ptr_array_add (hrefs, (char *)exchange_localfreebusy_path);
+
+ iter = e2k_context_bpropfind_start (
+ ctx, NULL, delegates->account->home_uri,
+ (const char **)hrefs->pdata, hrefs->len,
+ sd_props, n_sd_props);
+ g_ptr_array_free (hrefs, TRUE);
+
+ while ((result = e2k_result_iter_next (iter))) {
+ xml_form = e2k_properties_get_prop (result->props,
+ E2K_PR_EXCHANGE_SD_XML);
+ binary_form = e2k_properties_get_prop (result->props,
+ E2K_PR_EXCHANGE_SD_BINARY);
+
+ if (xml_form && binary_form) {
+ set_sd_for_href (delegates, result->href,
+ e2k_security_descriptor_new (xml_form, binary_form));
+ }
+ }
+ status = e2k_result_iter_free (iter);
+
+ if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Could not read folder permissions.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ if (!fill_in_sids (delegates)) {
+ delegates->loaded_folders = FALSE;
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Could not determine folder permissions for delegates.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ /* Fill in delegate structures from the security descriptors */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ perms = e2k_security_descriptor_get_permissions (
+ delegates->folder[i].sd, user->sid);
+ user->role[i] = e2k_permissions_role_find (perms);
+ }
+ }
+
+ delegates->loaded_folders = TRUE;
+ return TRUE;
+}
+
+
+static const char *delegation_props[] = {
+ PR_DELEGATES_DISPLAY_NAMES,
+ PR_DELEGATES_ENTRYIDS,
+ PR_DELEGATES_SEE_PRIVATE,
+ PR_CREATOR_ENTRYID
+};
+static const int n_delegation_props = sizeof (delegation_props) / sizeof (delegation_props[0]);
+
+/* Fetch the list of delegates from the freebusy message. */
+static gboolean
+get_user_list (ExchangeDelegates *delegates)
+{
+ E2kContext *ctx;
+ E2kResultIter *iter;
+ E2kResult *result;
+ GPtrArray *display_names, *entryids, *privflags;
+ GByteArray *entryid;
+ ExchangeDelegatesUser *user;
+ int i;
+
+ ctx = exchange_account_get_context (delegates->account);
+ iter = e2k_context_bpropfind_start (ctx, NULL,
+ delegates->account->home_uri,
+ &exchange_localfreebusy_path, 1,
+ delegation_props, n_delegation_props);
+ result = e2k_result_iter_next (iter);
+ if (!result || !E2K_HTTP_STATUS_IS_SUCCESSFUL (result->status)) {
+ e2k_result_iter_free (iter);
+ return FALSE;
+ }
+
+ delegates->users = g_ptr_array_new ();
+ delegates->added_users = g_ptr_array_new ();
+ delegates->removed_users = g_ptr_array_new ();
+
+ display_names = e2k_properties_get_prop (result->props, PR_DELEGATES_DISPLAY_NAMES);
+ entryids = e2k_properties_get_prop (result->props, PR_DELEGATES_ENTRYIDS);
+ privflags = e2k_properties_get_prop (result->props, PR_DELEGATES_SEE_PRIVATE);
+
+ entryid = e2k_properties_get_prop (result->props, PR_CREATOR_ENTRYID);
+ delegates->creator_entryid = g_byte_array_new ();
+ g_byte_array_append (delegates->creator_entryid, entryid->data, entryid->len);
+
+ if (!display_names || !entryids || !privflags) {
+ e2k_result_iter_free (iter);
+ return TRUE;
+ }
+
+ for (i = 0; i < display_names->len && i < entryids->len && i < privflags->len; i++) {
+ user = exchange_delegates_user_new (display_names->pdata[i]);
+ user->see_private = privflags->pdata[i] && atoi (privflags->pdata[i]);
+ entryid = entryids->pdata[i];
+ user->entryid = g_byte_array_new ();
+ g_byte_array_append (user->entryid, entryid->data, entryid->len);
+
+ g_signal_connect (user, "edited", G_CALLBACK (set_perms_for_user), delegates);
+
+ g_ptr_array_add (delegates->users, user);
+ }
+
+ e2k_result_iter_free (iter);
+ return TRUE;
+}
+
+/* Add or remove a delegate. Everyone must be in one of three states:
+ * 1. only in users (because they started and ended there)
+ * 2. in users and added_users (because they weren't in
+ * users to begin with, but got added)
+ * 3. only in removed_users (because they were in users to
+ * begin with and got removed).
+ * If you're added and then removed, or removed and then added, you have
+ * to end up in state 1. That's what this is for.
+ */
+static void
+add_remove_user (ExchangeDelegatesUser *user,
+ GPtrArray *to_array, GPtrArray *from_array)
+{
+ ExchangeDelegatesUser *match;
+ int i;
+
+ for (i = 0; i < from_array->len; i++) {
+ match = from_array->pdata[i];
+ if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (match->sid),
+ e2k_sid_get_binary_sid (user->sid))) {
+ g_ptr_array_remove_index_fast (from_array, i);
+ g_object_unref (match);
+ return;
+ }
+ }
+
+ g_ptr_array_add (to_array, user);
+ g_object_ref (user);
+}
+
+static void
+set_perms_for_user (ExchangeDelegatesUser *user, gpointer user_data)
+{
+ ExchangeDelegates *delegates = user_data;
+ int i, role;
+ guint32 perms;
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ perms = e2k_permissions_role_get_perms (user->role[i]);
+ e2k_security_descriptor_set_permissions (delegates->folder[i].sd,
+ user->sid, perms);
+ }
+ role = user->role[EXCHANGE_DELEGATES_CALENDAR];
+ if (role == E2K_PERMISSIONS_ROLE_AUTHOR)
+ role = E2K_PERMISSIONS_ROLE_EDITOR;
+ perms = e2k_permissions_role_get_perms (role);
+ e2k_security_descriptor_set_permissions (delegates->freebusy_folder.sd,
+ user->sid, perms);
+}
+
+static void
+add_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ E2kGlobalCatalog *gc;
+ GtkWidget *dialog, *parent_window;
+ const char *delegate_exchange_dn;
+ char *email;
+ ExchangeDelegatesUser *user, *match;
+ int response, u;
+ GtkTreeIter iter;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ dialog = e2k_user_dialog_new (parent_window,
+ _("Delegate To:"), _("Delegate To"));
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return;
+ }
+ email = e2k_user_dialog_get_user (E2K_USER_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ if (email == NULL)
+ return;
+
+ user = exchange_delegates_user_new_from_gc (gc, email,
+ delegates->creator_entryid);
+ if (!user) {
+ e_notice (parent_window, GTK_MESSAGE_ERROR,
+ _("Could not make %s a delegate"), email);
+ g_free (email);
+ return;
+ }
+ g_free (email);
+
+ delegate_exchange_dn = e2k_entryid_to_dn (user->entryid);
+ if (delegate_exchange_dn && !g_ascii_strcasecmp (delegate_exchange_dn, delegates->account->legacy_exchange_dn)) {
+ g_object_unref (user);
+ e_notice (parent_window, GTK_MESSAGE_ERROR,
+ _("You cannot make yourself your own delegate"));
+ return;
+ }
+
+ for (u = 0; u < delegates->users->len; u++) {
+ match = delegates->users->pdata[u];
+ if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (user->sid),
+ e2k_sid_get_binary_sid (match->sid))) {
+ e_notice (parent_window, GTK_MESSAGE_INFO,
+ _("%s is already a delegate"),
+ user->display_name);
+ g_object_unref (user);
+ exchange_delegates_user_edit (match, parent_window);
+ return;
+ }
+ }
+
+ if (!exchange_delegates_user_edit (user, parent_window)) {
+ g_object_unref (user);
+ return;
+ }
+ set_perms_for_user (user, delegates);
+ g_signal_connect (user, "edited",
+ G_CALLBACK (set_perms_for_user), delegates);
+
+ add_remove_user (user, delegates->added_users, delegates->removed_users);
+ g_ptr_array_add (delegates->users, user);
+
+ /* Add the user to the table */
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, user->display_name,
+ -1);
+}
+
+static int
+get_selected_row (GtkWidget *tree_view, GtkTreeIter *iter)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ int *indices, row;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+ if (!gtk_tree_selection_get_selected (selection, &model, iter))
+ return -1;
+
+ path = gtk_tree_model_get_path (model, iter);
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+ gtk_tree_path_free (path);
+
+ return row;
+}
+
+static void
+edit_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ GtkWidget *parent_window;
+ GtkTreeIter iter;
+ int row;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ row = get_selected_row (delegates->table, &iter);
+ g_return_if_fail (row >= 0 && row < delegates->users->len);
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ exchange_delegates_user_edit (delegates->users->pdata[row],
+ parent_window);
+}
+
+static gboolean
+table_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ GtkWidget *parent_window;
+ GtkTreeIter iter;
+ int row;
+
+ if (event->type != GDK_2BUTTON_PRESS)
+ return FALSE;
+
+ row = get_selected_row (delegates->table, &iter);
+ if (row < 0 || row >= delegates->users->len)
+ return FALSE;
+
+ if (!get_folder_security (delegates))
+ return FALSE;
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ exchange_delegates_user_edit (delegates->users->pdata[row],
+ parent_window);
+ return TRUE;
+}
+
+static void
+remove_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ ExchangeDelegatesUser *user;
+ GtkWidget *dialog;
+ int row, btn, i;
+ GtkTreeIter iter;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ row = get_selected_row (delegates->table, &iter);
+ g_return_if_fail (row >= 0 && row < delegates->users->len);
+
+ user = delegates->users->pdata[row];
+
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("Remove the delegate %s?"),
+ user->display_name);
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), widget);
+
+ btn = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ if (btn != GTK_RESPONSE_YES)
+ return;
+
+ add_remove_user (user, delegates->removed_users, delegates->added_users);
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ e2k_security_descriptor_remove_sid (delegates->folder[i].sd,
+ user->sid);
+ }
+ e2k_security_descriptor_remove_sid (delegates->freebusy_folder.sd,
+ user->sid);
+
+ /* Remove the user from the table */
+ gtk_list_store_remove (delegates->model, &iter);
+ g_ptr_array_remove_index (delegates->users, row);
+ g_object_unref (user);
+}
+
+
+static gboolean
+proppatch_sd (E2kContext *ctx, ExchangeDelegatesFolder *folder)
+{
+ GByteArray *binsd;
+ E2kProperties *props;
+ const char *href = "";
+ E2kResultIter *iter;
+ E2kResult *result;
+ E2kHTTPStatus status;
+
+ binsd = e2k_security_descriptor_to_binary (folder->sd);
+ if (!binsd)
+ return FALSE;
+
+ props = e2k_properties_new ();
+ e2k_properties_set_binary (props, E2K_PR_EXCHANGE_SD_BINARY, binsd);
+
+ iter = e2k_context_bproppatch_start (ctx, NULL, folder->uri,
+ &href, 1, props, FALSE);
+ e2k_properties_free (props);
+
+ result = e2k_result_iter_next (iter);
+ if (result) {
+ status = result->status;
+ e2k_result_iter_free (iter);
+ } else
+ status = e2k_result_iter_free (iter);
+
+ return E2K_HTTP_STATUS_IS_SUCCESSFUL (status);
+}
+
+static gboolean
+get_user_dn (E2kGlobalCatalog *gc, ExchangeDelegatesUser *user)
+{
+ E2kGlobalCatalogEntry *entry;
+ E2kGlobalCatalogStatus status;
+ const char *exchange_dn;
+
+ exchange_dn = e2k_entryid_to_dn (user->entryid);
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ exchange_dn, 0, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK)
+ return FALSE;
+
+ user->dn = g_strdup (entry->dn);
+ e2k_global_catalog_entry_free (gc, entry);
+ return TRUE;
+}
+
+static void
+delegates_apply (ExchangeDelegates *delegates)
+{
+ ExchangeDelegatesUser *user;
+ E2kGlobalCatalog *gc;
+ E2kContext *ctx;
+ GPtrArray *display_names, *entryids, *privflags;
+ GByteArray *entryid_dup;
+ char *error = NULL;
+ E2kProperties *props;
+ int i, status;
+
+ if (!delegates->loaded_folders)
+ return;
+
+ /* We can't do this atomically/transactionally, so we need to
+ * make sure that if we fail at any step, things are still in
+ * a semi-consistent state. So we do:
+ *
+ * 1. Remove old delegates from AD
+ * 2. Update LocalFreebusy.EML (the canonical list of delegates)
+ * 3. Add new delegates to AD
+ * 4. Update security descriptors
+ *
+ * If step 1 fails, nothing is changed.
+ *
+ * If step 2 fails, delegates who should have been removed
+ * will have been removed from AD but nothing else, so they
+ * will still show up as being delegates and the user can try
+ * to remove them again later.
+ *
+ * If step 3 fails, delegates who should have been added will
+ * not be in AD, but will be listed as delegates, so the user
+ * can remove them and try adding them again later.
+ *
+ * If step 4 fails, the user can still correct the folder
+ * permissions by hand.
+ */
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+ if (!gc) {
+ error = g_strdup (_("Could not access Active Directory"));
+ goto done;
+ }
+
+ if ((delegates->removed_users || delegates->added_users) && !delegates->self_dn) {
+ E2kGlobalCatalog *gc;
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ delegates->account->legacy_exchange_dn, 0, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK) {
+ error = g_strdup (_("Could not find self in Active Directory"));
+ goto done;
+ }
+
+ delegates->self_dn = g_strdup (entry->dn);
+ e2k_global_catalog_entry_free (gc, entry);
+ }
+
+ /* 1. Remove old delegates from AD */
+ while (delegates->removed_users && delegates->removed_users->len) {
+ user = delegates->removed_users->pdata[0];
+ if (!user->dn && !get_user_dn (gc, user)) {
+ error = g_strdup_printf (
+ _("Could not find delegate %s in Active Directory"),
+ user->display_name);
+ goto done;
+ }
+
+ /* FIXME: cancellable */
+ status = e2k_global_catalog_remove_delegate (gc, NULL,
+ delegates->self_dn,
+ user->dn);
+ if (status != E2K_GLOBAL_CATALOG_OK &&
+ status != E2K_GLOBAL_CATALOG_NO_DATA) {
+ error = g_strdup_printf (
+ _("Could not remove delegate %s"),
+ user->display_name);
+ goto done;
+ }
+
+ g_object_unref (user);
+ g_ptr_array_remove_index_fast (delegates->removed_users, 0);
+ }
+
+ /* 2. Update LocalFreebusy.EML */
+ ctx = exchange_account_get_context (delegates->account);
+
+ if (delegates->users->len) {
+ display_names = g_ptr_array_new ();
+ entryids = g_ptr_array_new ();
+ privflags = g_ptr_array_new ();
+
+ for (i = 0; i < delegates->users->len; i++) {
+ user = delegates->users->pdata[i];
+ g_ptr_array_add (display_names, g_strdup (user->display_name));
+ entryid_dup = g_byte_array_new ();
+ g_byte_array_append (entryid_dup, user->entryid->data,
+ user->entryid->len);
+ g_ptr_array_add (entryids, entryid_dup);
+ g_ptr_array_add (privflags, g_strdup_printf ("%d", user->see_private));
+ }
+
+ props = e2k_properties_new ();
+ e2k_properties_set_string_array (
+ props, PR_DELEGATES_DISPLAY_NAMES, display_names);
+ e2k_properties_set_binary_array (
+ props, PR_DELEGATES_ENTRYIDS, entryids);
+ e2k_properties_set_int_array (
+ props, PR_DELEGATES_SEE_PRIVATE, privflags);
+ } else if (delegates->removed_users) {
+ props = e2k_properties_new ();
+ e2k_properties_remove (props, PR_DELEGATES_DISPLAY_NAMES);
+ e2k_properties_remove (props, PR_DELEGATES_ENTRYIDS);
+ e2k_properties_remove (props, PR_DELEGATES_SEE_PRIVATE);
+ } else
+ props = NULL;
+
+ if (props) {
+ E2kResultIter *iter;
+ E2kResult *result;
+
+ iter = e2k_context_bproppatch_start (
+ ctx, NULL, delegates->account->home_uri,
+ &exchange_localfreebusy_path, 1,
+ props, FALSE);
+ e2k_properties_free (props);
+
+ result = e2k_result_iter_next (iter);
+ if (result) {
+ status = result->status;
+ e2k_result_iter_free (iter);
+ } else
+ status = e2k_result_iter_free (iter);
+
+ if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
+ error = g_strdup (_("Could not update list of delegates."));
+ goto done;
+ }
+ }
+
+ /* 3. Add new delegates to AD */
+ while (delegates->added_users && delegates->added_users->len) {
+ user = delegates->added_users->pdata[0];
+ /* An added user must have come from the GC so
+ * we know user->dn is set.
+ */
+ /* FIXME: cancellable */
+ status = e2k_global_catalog_add_delegate (gc, NULL,
+ delegates->self_dn,
+ user->dn);
+ if (status != E2K_GLOBAL_CATALOG_OK &&
+ status != E2K_GLOBAL_CATALOG_EXISTS) {
+ error = g_strdup_printf (
+ _("Could not add delegate %s"),
+ user->display_name);
+ goto done;
+ }
+ g_ptr_array_remove_index_fast (delegates->added_users, 0);
+ g_object_unref (user);
+ }
+
+ /* 4. Update security descriptors */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++)
+ proppatch_sd (ctx, &delegates->folder[i]);
+ proppatch_sd (ctx, &delegates->freebusy_folder);
+
+ done:
+ if (error) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Failed to update delegates:\n%s"), error);
+ g_free (error);
+ }
+}
+
+static void parent_destroyed (gpointer user_data, GObject *ex_parent);
+
+static void
+delegates_destroy (ExchangeDelegates *delegates)
+{
+ int i;
+
+ g_object_unref (delegates->account);
+
+ if (delegates->parent) {
+ g_object_weak_unref (G_OBJECT (delegates->parent),
+ parent_destroyed, delegates);
+ }
+ if (delegates->dialog)
+ gtk_widget_destroy (delegates->dialog);
+
+ if (delegates->model)
+ g_object_unref (delegates->model);
+
+ if (delegates->self_dn)
+ g_free (delegates->self_dn);
+ if (delegates->creator_entryid)
+ g_byte_array_free (delegates->creator_entryid, TRUE);
+
+ if (delegates->users) {
+ for (i = 0; i < delegates->users->len; i++)
+ g_object_unref (delegates->users->pdata[i]);
+ g_ptr_array_free (delegates->users, TRUE);
+ }
+ if (delegates->added_users) {
+ for (i = 0; i < delegates->added_users->len; i++)
+ g_object_unref (delegates->added_users->pdata[i]);
+ g_ptr_array_free (delegates->added_users, TRUE);
+ }
+ if (delegates->removed_users) {
+ for (i = 0; i < delegates->removed_users->len; i++)
+ g_object_unref (delegates->removed_users->pdata[i]);
+ g_ptr_array_free (delegates->removed_users, TRUE);
+ }
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (delegates->folder[i].sd)
+ g_object_unref (delegates->folder[i].sd);
+ }
+ if (delegates->freebusy_folder.sd)
+ g_object_unref (delegates->freebusy_folder.sd);
+ if (delegates->freebusy_folder.uri)
+ g_free ((char *)delegates->freebusy_folder.uri);
+
+ if (delegates->xml)
+ g_object_unref (delegates->xml);
+
+ g_free (delegates);
+}
+
+
+static void
+dialog_response (GtkDialog *dialog, int response, gpointer user_data)
+{
+ ExchangeDelegates *delegates = user_data;
+
+ if (response == GTK_RESPONSE_OK)
+ delegates_apply (delegates);
+ delegates_destroy (delegates);
+}
+
+static void
+parent_destroyed (gpointer user_data, GObject *ex_parent)
+{
+ ExchangeDelegates *delegates = user_data;
+
+ gtk_widget_destroy (delegates->dialog);
+ delegates_destroy (delegates);
+}
+
+void
+exchange_delegates (ExchangeAccount *account, GtkWidget *parent)
+{
+ ExchangeDelegates *delegates;
+ GtkWidget *button;
+ ExchangeDelegatesUser *user;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ int i;
+
+ g_return_if_fail (GTK_IS_WIDGET (parent));
+ g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
+
+ delegates = g_new0 (ExchangeDelegates, 1);
+ delegates->account = g_object_ref (account);
+
+ delegates->xml = glade_xml_new (CONNECTOR_GLADEDIR "/exchange-delegates.glade", NULL, NULL);
+ g_return_if_fail (delegates->xml != NULL);
+
+ delegates->dialog = glade_xml_get_widget (delegates->xml, "delegates");
+ g_return_if_fail (delegates->dialog != NULL);
+
+ g_signal_connect (delegates->dialog, "response",
+ G_CALLBACK (dialog_response), delegates);
+
+ e_dialog_set_transient_for (GTK_WINDOW (delegates->dialog), parent);
+ delegates->parent = parent;
+ g_object_weak_ref (G_OBJECT (parent), parent_destroyed, delegates);
+
+ /* Set up the buttons */
+ button = glade_xml_get_widget (delegates->xml, "add_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (add_button_clicked_cb), delegates);
+ button = glade_xml_get_widget (delegates->xml, "edit_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (edit_button_clicked_cb), delegates);
+ button = glade_xml_get_widget (delegates->xml, "remove_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (remove_button_clicked_cb), delegates);
+
+ /* Set up the table */
+ delegates->model = gtk_list_store_new (1, G_TYPE_STRING);
+ delegates->table = glade_xml_get_widget (delegates->xml, "delegates_table");
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Name"), gtk_cell_renderer_text_new (), "text", 0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (delegates->table),
+ column);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (delegates->table),
+ GTK_TREE_MODEL (delegates->model));
+
+ /* Get list of delegate users */
+ if (get_user_list (delegates)) {
+ for (i = 0; i < delegates->users->len; i++) {
+ user = delegates->users->pdata[i];
+
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, user->display_name,
+ -1);
+ }
+ g_signal_connect (delegates->table,
+ "button_press_event",
+ G_CALLBACK (table_click_cb), delegates);
+ } else {
+ button = glade_xml_get_widget (delegates->xml, "add_button");
+ gtk_widget_set_sensitive (button, FALSE);
+ button = glade_xml_get_widget (delegates->xml, "edit_button");
+ gtk_widget_set_sensitive (button, FALSE);
+ button = glade_xml_get_widget (delegates->xml, "remove_button");
+ gtk_widget_set_sensitive (button, FALSE);
+
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, _("Error reading delegates list."),
+ -1);
+ }
+
+ gtk_widget_show (delegates->dialog);
+}
diff --git a/plugins/exchange-operations/exchange-delegates.glade b/plugins/exchange-operations/exchange-delegates.glade
new file mode 100644
index 0000000000..4b2e0e7695
--- /dev/null
+++ b/plugins/exchange-operations/exchange-delegates.glade
@@ -0,0 +1,628 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="delegates">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Delegates</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">These users will be able to send mail on your behalf
+and access your folders with the permissions you give them.</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="delegate_hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkTreeView" id="delegates_table">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="add_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="edit_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="remove_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="delegate_permissions">
+ <property name="title" translatable="yes">Delegate Permissions</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="delegate_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Permissions for</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="folders_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="calendar_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">C_alendar:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">calendar_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="task_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Tasks:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">task_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="inbox_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Inbox:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">inbox_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="contact_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_ntacts:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">contact_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="calendar_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget1">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="task_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget6">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="inbox_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget11">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget12">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget14">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="contact_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget16">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget17">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget18">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget19">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget20">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="see_private_checkbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Delegate can see private items</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/plugins/exchange-operations/exchange-delegates.h b/plugins/exchange-operations/exchange-delegates.h
new file mode 100644
index 0000000000..be8f4f89e3
--- /dev/null
+++ b/plugins/exchange-operations/exchange-delegates.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_DELEGATES_H__
+#define __EXCHANGE_DELEGATES_H__
+
+#include <exchange-types.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+void exchange_delegates (ExchangeAccount *account, GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_DELEGATES_CONTROL_H__ */
diff --git a/plugins/exchange-operations/exchange-folder-size.c b/plugins/exchange-operations/exchange-folder-size.c
new file mode 100644
index 0000000000..694155f54d
--- /dev/null
+++ b/plugins/exchange-operations/exchange-folder-size.c
@@ -0,0 +1,343 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeFolderSize: Display the folder tree with the folder sizes */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <e-util/e-dialog-utils.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+
+#include "exchange-hierarchy-webdav.h"
+#include "e-folder-exchange.h"
+#include "exchange-folder-size.h"
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class = NULL;
+
+typedef struct {
+ char *folder_name;
+ gdouble folder_size;
+} folder_info;
+
+
+struct _ExchangeFolderSizePrivate {
+
+ GHashTable *table;
+ GtkListStore *model;
+ GHashTable *row_refs;
+};
+
+enum {
+ COLUMN_NAME,
+ COLUMN_SIZE,
+ NUM_COLUMNS
+};
+
+static gboolean
+free_fsize_table (gpointer key, gpointer value, gpointer data)
+{
+ folder_info *f_info = (folder_info *) value;
+
+ g_free (key);
+ g_free (f_info->folder_name);
+ g_free (f_info);
+ return TRUE;
+}
+
+static gboolean
+free_row_refs (gpointer key, gpointer value, gpointer user_data)
+{
+ g_free (key);
+ gtk_tree_row_reference_free (value);
+ return TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+ ExchangeFolderSize *fsize = EXCHANGE_FOLDER_SIZE (object);
+
+ g_hash_table_foreach_remove (fsize->priv->table, free_fsize_table, NULL);
+ g_hash_table_destroy (fsize->priv->table);
+ g_hash_table_foreach_remove (fsize->priv->row_refs, free_row_refs, NULL);
+ g_hash_table_destroy (fsize->priv->row_refs);
+ if (fsize->priv->model)
+ g_object_unref (fsize->priv->model);
+ g_free (fsize->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* override virtual methods */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+}
+
+static void
+init (GObject *object)
+{
+ ExchangeFolderSize *fsize = EXCHANGE_FOLDER_SIZE (object);
+
+ fsize->priv = g_new0 (ExchangeFolderSizePrivate, 1);
+ fsize->priv->table = g_hash_table_new (g_str_hash, g_str_equal);
+ fsize->priv->model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_DOUBLE);
+ fsize->priv->row_refs = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+E2K_MAKE_TYPE (exchange_folder_size, ExchangeFolderSize, class_init, init, PARENT_TYPE)
+
+/**
+ * exchange_folder_size_new:
+ * @display_name: the delegate's (UTF8) display name
+ *
+ * Return value: a foldersize object with the table initialized
+ **/
+ExchangeFolderSize *
+exchange_folder_size_new (void)
+{
+ ExchangeFolderSize *fsize;
+
+ fsize = g_object_new (EXCHANGE_TYPE_FOLDER_SIZE, NULL);
+
+ return fsize;
+}
+
+void
+exchange_folder_size_update (ExchangeFolderSize *fsize,
+ const char *folder_name,
+ gdouble folder_size)
+{
+ folder_info *f_info, *cached_info;
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ GtkTreeRowReference *row;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize));
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ if (cached_info->folder_size == folder_size) {
+ return;
+ } else {
+ cached_info->folder_size = folder_size;
+ row = g_hash_table_lookup (priv->row_refs, folder_name);
+ path = gtk_tree_row_reference_get_path (row);
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fsize->priv->model), &iter, path)) {
+ gtk_list_store_set (fsize->priv->model, &iter,
+ COLUMN_NAME, cached_info->folder_name,
+ COLUMN_SIZE, cached_info->folder_size,
+ -1);
+ }
+ gtk_tree_path_free (path);
+ return;
+ }
+ } else {
+ f_info = g_new0(folder_info, 1);
+ f_info->folder_name = g_strdup (folder_name);
+ f_info->folder_size = folder_size;
+ g_hash_table_insert (folder_size_table, f_info->folder_name, f_info);
+
+ gtk_list_store_append (fsize->priv->model, &iter);
+ gtk_list_store_set (fsize->priv->model, &iter,
+ COLUMN_NAME, f_info->folder_name,
+ COLUMN_SIZE, f_info->folder_size,
+ -1);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsize->priv->model), &iter);
+ row = gtk_tree_row_reference_new (GTK_TREE_MODEL (fsize->priv->model), path);
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (fsize->priv->row_refs, g_strdup (folder_name), row);
+ }
+}
+
+void
+exchange_folder_size_remove (ExchangeFolderSize *fsize,
+ const char *folder_name)
+{
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ folder_info *cached_info;
+ GtkTreeRowReference *row;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize));
+ g_return_if_fail (folder_name != NULL);
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ row = g_hash_table_lookup (priv->row_refs, folder_name);
+ path = gtk_tree_row_reference_get_path (row);
+ g_hash_table_remove (folder_size_table, folder_name);
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fsize->priv->model), &iter, path)) {
+ gtk_list_store_remove (fsize->priv->model, &iter);
+ }
+ g_hash_table_remove (priv->row_refs, row);
+ gtk_tree_path_free (path);
+ }
+}
+
+gdouble
+exchange_folder_size_get (ExchangeFolderSize *fsize,
+ const char *folder_name)
+{
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ folder_info *cached_info;
+
+ g_return_val_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize), -1);
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ return cached_info->folder_size;
+ }
+ return -1;
+}
+
+static void
+format_size_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkCellRendererText *cell = (GtkCellRendererText *)renderer;
+ gdouble folder_size;
+ char * new_text;
+
+ gtk_tree_model_get(model, iter, COLUMN_SIZE, &folder_size, -1);
+
+ if (folder_size)
+ new_text = g_strdup_printf ("%.2f", folder_size);
+ else
+ new_text = g_strdup ("0");
+
+ g_object_set (cell, "text", new_text, NULL);
+ g_free (new_text);
+}
+
+static void
+parent_destroyed (gpointer dialog, GObject *ex_parent)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+void
+exchange_folder_size_display (EFolder *folder, GtkWidget *parent)
+{
+ ExchangeFolderSizePrivate *priv;
+ ExchangeFolderSize *fsize;
+ ExchangeHierarchy *hier;
+ GtkTreeViewColumn *column;
+ GtkTreeSortable *sortable;
+ GtkCellRenderer *cell;
+ GHashTable *folder_size_table;
+ GladeXML *xml;
+ GtkWidget *dialog, *table;
+ GList *l;
+ char *col_name;
+ int response;
+
+ g_return_if_fail (GTK_IS_WIDGET (parent));
+
+ hier = e_folder_exchange_get_hierarchy (folder);
+ if (!hier)
+ return;
+ /* FIXME: This should be a more generic query and not just
+ specifically for webdav */
+ fsize = exchange_hierarchy_webdav_get_folder_size (EXCHANGE_HIERARCHY_WEBDAV (hier));
+ if (!fsize)
+ return;
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ if (!g_hash_table_size (folder_size_table))
+ return;
+
+ xml = glade_xml_new (CONNECTOR_GLADEDIR "/exchange-folder-tree.glade", NULL, NULL);
+ g_return_if_fail (xml != NULL);
+ dialog = glade_xml_get_widget (xml, "folder_tree");
+ table = glade_xml_get_widget (xml, "folder_treeview");
+
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), parent);
+ /* fsize->parent = parent; */
+ g_object_weak_ref (G_OBJECT (parent), parent_destroyed, dialog);
+
+ /* Set up the table */
+ sortable = GTK_TREE_SORTABLE (priv->model);
+ gtk_tree_sortable_set_sort_column_id (sortable, COLUMN_SIZE, GTK_SORT_DESCENDING);
+
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Folder Name"), gtk_cell_renderer_text_new (), "text", COLUMN_NAME, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (table),
+ column);
+
+ col_name = g_strdup_printf ("%s (KB)", _("Folder Size"));
+ column = gtk_tree_view_column_new_with_attributes (
+ col_name, gtk_cell_renderer_text_new (), "text", COLUMN_SIZE, NULL);
+ g_free (col_name);
+
+ l = gtk_tree_view_column_get_cell_renderers (column);
+ cell = (GtkCellRenderer *)l->data;
+ gtk_tree_view_column_set_cell_data_func (column, cell, format_size_func, NULL, NULL );
+ g_list_free (l);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (table),
+ column);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (table),
+ GTK_TREE_MODEL (priv->model));
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ g_object_unref (xml);
+}
diff --git a/plugins/exchange-operations/exchange-folder-size.h b/plugins/exchange-operations/exchange-folder-size.h
new file mode 100644
index 0000000000..2d673902a1
--- /dev/null
+++ b/plugins/exchange-operations/exchange-folder-size.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_FOLDER_SIZE_H__
+#define __EXCHANGE_FOLDER_SIZE_H__
+
+#include <exchange-types.h>
+#include <e2k-security-descriptor.h>
+#include <e-folder.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EXCHANGE_TYPE_FOLDER_SIZE (exchange_folder_size_get_type ())
+#define EXCHANGE_FOLDER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_TYPE_FOLDER_SIZE, ExchangeFolderSize))
+#define EXCHANGE_FOLDER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_TYPE_FOLDER_SIZE, ExchangeFolderSizeClass))
+#define EXCHANGE_IS_FOLDER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_TYPE_FOLDER_SIZE))
+#define EXCHANGE_IS_FOLDER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_TYPE_FOLDER_SIZE))
+
+
+typedef struct _ExchangeFolderSize ExchangeFolderSize;
+typedef struct _ExchangeFolderSizePrivate ExchangeFolderSizePrivate;
+typedef struct _ExchangeFolderSizeClass ExchangeFolderSizeClass;
+
+struct _ExchangeFolderSize {
+ GObject parent;
+
+ ExchangeFolderSizePrivate *priv;
+};
+
+struct _ExchangeFolderSizeClass {
+ GObjectClass parent_class;
+
+};
+
+GType exchange_folder_size_get_type (void);
+
+ExchangeFolderSize *exchange_folder_size_new (void);
+
+void exchange_folder_size_update (ExchangeFolderSize *fsize,
+ const char *folder_name,
+ gdouble folder_size);
+void exchange_folder_size_remove (ExchangeFolderSize *fsize, const char *folder_name);
+
+gdouble exchange_folder_size_get (ExchangeFolderSize *fsize, const char *folder_name);
+
+void exchange_folder_size_display (EFolder *folder, GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_FOLDER_SIZE_H__ */
diff --git a/plugins/exchange-operations/exchange-folder-tree.glade b/plugins/exchange-operations/exchange-folder-tree.glade
new file mode 100644
index 0000000000..0505681a7b
--- /dev/null
+++ b/plugins/exchange-operations/exchange-folder-tree.glade
@@ -0,0 +1,92 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="folder_tree">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Exchange Folder Tree</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">250</property>
+ <property name="default_height">300</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="folder_tree_hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="folder_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/plugins/exchange-operations/exchange-operations.c b/plugins/exchange-operations/exchange-operations.c
new file mode 100644
index 0000000000..fcee3902d5
--- /dev/null
+++ b/plugins/exchange-operations/exchange-operations.c
@@ -0,0 +1,94 @@
+#include <string.h>
+
+#include "exchange-operations.h"
+
+ExchangeConfigListener *exchange_global_config_listener=NULL;
+
+static void
+free_exchange_listener (void)
+{
+ g_object_unref (exchange_global_config_listener);
+}
+
+int
+e_plugin_lib_enable (EPluginLib *eplib, int enable)
+{
+ if (!exchange_global_config_listener) {
+ exchange_global_config_listener = exchange_config_listener_new ();
+ g_atexit (free_exchange_listener);
+ }
+ g_print ("*** DEBUG: Exchange config listener is initialized ***\n");
+ return 0;
+}
+
+gboolean
+exchange_operations_tokenize_string (char **string, char *token, char delimit)
+{
+ int i=0;
+ char *str=*string;
+ while (*str!=delimit && *str!='\0') {
+ token[i++]=*str++;
+ }
+ while (*str==delimit)
+ str++;
+ token[i]='\0';
+ *string = str;
+ if (i==0)
+ return FALSE;
+ return TRUE;
+}
+
+gboolean
+exchange_operations_cta_add_node_to_tree (GtkTreeStore *store, GtkTreeIter *parent, const char *nuri, const char *ruri)
+{
+ GtkTreeIter iter;
+ char *luri=(char *)nuri;
+ char nodename[80];
+ gchar *readname;
+ gboolean status, found;
+
+ g_print ("TOKENIZER: String passed to tokenizer %s\n", luri);
+ exchange_operations_tokenize_string (&luri, nodename, '/');
+ g_print ("TOKENIZER: Token - %s Residue - %s\n", nodename, luri);
+ if (!nodename[0]) {
+ return TRUE;
+ }
+ if (!strcmp (nodename, "personal") && !parent) {
+ strcpy (nodename, "Personal Folders");
+ }
+
+ found = FALSE;
+ status = gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &iter, parent);
+ while (status) {
+ g_print ("Reading name...\n");
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &readname, -1);
+ g_print ("Name read - %s\n", readname);
+ if (!strcmp (nodename, readname)) {
+ g_print ("Found. Inserting as child.\n");
+ found = TRUE;
+ exchange_operations_cta_add_node_to_tree (store, &iter, luri, ruri);
+ break;
+ }
+ g_free (readname);
+ status = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
+ }
+ if (!found) {
+ g_print ("Not found. Inserting node %s\n", nodename);
+ gtk_tree_store_append (store, &iter, parent);
+ gtk_tree_store_set (store, &iter, 0, nodename, 1, ruri, -1);
+ exchange_operations_cta_add_node_to_tree (store, &iter, luri, ruri);
+ }
+ return TRUE;
+}
+
+ExchangeAccount *
+exchange_operations_get_exchange_account (void) {
+ ExchangeAccount *account;
+ GSList *acclist;
+
+ acclist = exchange_config_listener_get_accounts (exchange_global_config_listener);
+ account = acclist->data; /* FIXME: Need to be changed for handling multiple accounts */
+
+ return account;
+}
+
diff --git a/plugins/exchange-operations/exchange-operations.h b/plugins/exchange-operations/exchange-operations.h
new file mode 100644
index 0000000000..0ac81d9a94
--- /dev/null
+++ b/plugins/exchange-operations/exchange-operations.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2001-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_OPERATIONS_H__
+#define __EXCHANGE_OPERATIONS_H__
+
+#include <gtk/gtk.h>
+
+#include "e-util/e-plugin.h"
+#include "exchange-config-listener.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+extern ExchangeConfigListener *exchange_global_config_listener;
+
+int e_plugin_lib_enable (EPluginLib *eplib, int enable);
+gboolean exchange_operations_tokenize_string (char **string,
+ char *token,
+ char delimit);
+gboolean exchange_operations_cta_add_node_to_tree (GtkTreeStore *store,
+ GtkTreeIter *parent,
+ const char *nuri,
+ const char *ruri);
+ExchangeAccount *exchange_operations_get_exchange_account (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/plugins/exchange-operations/org-gnome-exchange-operations.eplug.in b/plugins/exchange-operations/org-gnome-exchange-operations.eplug.in
new file mode 100644
index 0000000000..b7cee95bcb
--- /dev/null
+++ b/plugins/exchange-operations/org-gnome-exchange-operations.eplug.in
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<e-plugin-list>
+ <e-plugin
+ type="shlib"
+ id="org.gnome.evolution.plugin.exchange-operations"
+ location="@PLUGINDIR@/liborg-gnome-exchange-operations.so"
+ load-on-startup="true"
+ name="Exchange Operations">
+ <author name="Sushma Rai" email="rsushma@novell.com"/>
+ <author name="Praveen Kumar" email="kpraveen@novell.com"/>
+ <description>A plugin that handles a collection of Exchange account specific operations and features.</description>
+
+ <hook class="org.gnome.evolution.mail.config:1.0">
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountEditor"
+ check="org_gnome_exchange_check_options"
+ commit="org_gnome_exchange_commit">
+ <item type="page"
+ path="40.oof"
+ label="Exchange Settings"
+ factory="org_gnome_exchange_settings"/>
+ <item type="item_table"
+ path="10.receive/10.config/20.owa"
+ factory="org_gnome_exchange_owa_url"/>
+ <item type="section"
+ path="10.receive/30.auth/00.exchange_auth"
+ factory="org_gnome_exchange_auth_section"/>
+ </group>
+
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountDruid"
+ check="org_gnome_exchange_check_options">
+ <item type="item_table"
+ path="10.receive/10.config/20.owa"
+ factory="org_gnome_exchange_owa_url"/>
+ </group>
+
+ <group
+ target="account"
+ id="org.gnome.evolution.mail.config.accountWizard"
+ check="org_gnome_exchange_check_options">
+ <item type="item_table"
+ path="10.receive/10.config/20.owa"
+ factory="org_gnome_exchange_owa_url"/>
+ </group>
+ </hook>
+
+ <hook class="org.gnome.evolution.calendar.config:1.0">
+ <group
+ target="source"
+ id="org.gnome.evolution.calendar.calendarProperties"
+ check="e_exchange_calendar_check">
+ <item
+ type="item_table"
+ path="00.general/00.source/40.pcalendar"
+ factory="e_exchange_calendar_pcalendar"/>
+ </group>
+ </hook>
+
+ <hook class="org.gnome.evolution.addressbook.config:1.0">
+ <group
+ target="source"
+ id="org.gnome.evolution.addressbook.addressbookProperties"
+ check="e_exchange_contacts_check">
+ <item
+ type="item"
+ path="00.general/10.display/60.pcontacts"
+ factory="e_exchange_contacts_pcontacts"/>
+ </group>
+ </hook>
+
+ </e-plugin>
+</e-plugin-list>