From d12d5fd082bd480bd9b635dbdae72d439730273a Mon Sep 17 00:00:00 2001 From: Sarfraaz Ahmed Date: Mon, 13 Jun 2005 12:39:20 +0000 Subject: Added a new plugin directory for renaming exchange-account-setup as 2005-06-12 Sarfraaz Ahmed * plugins/exchange-operations : Added a new plugin directory for renaming exchange-account-setup as exchange-operations. svn path=/trunk/; revision=29498 --- ChangeLog | 5 + plugins/exchange-operations/ChangeLog | 241 +++++ plugins/exchange-operations/Makefile.am | 37 + .../exchange-operations/exchange-account-setup.c | 844 +++++++++++++++ .../exchange-operations/exchange-ask-password.c | 357 +++++++ .../exchange-change-password.glade | 243 +++++ .../exchange-operations/exchange-change-password.h | 21 + .../exchange-operations/exchange-config-listener.c | 1095 ++++++++++++++++++++ .../exchange-operations/exchange-config-listener.h | 58 ++ .../exchange-operations/exchange-delegates-user.c | 332 ++++++ .../exchange-operations/exchange-delegates-user.h | 70 ++ plugins/exchange-operations/exchange-delegates.c | 973 +++++++++++++++++ .../exchange-operations/exchange-delegates.glade | 628 +++++++++++ plugins/exchange-operations/exchange-delegates.h | 21 + plugins/exchange-operations/exchange-folder-size.c | 343 ++++++ plugins/exchange-operations/exchange-folder-size.h | 56 + .../exchange-operations/exchange-folder-tree.glade | 92 ++ plugins/exchange-operations/exchange-operations.c | 94 ++ plugins/exchange-operations/exchange-operations.h | 34 + .../org-gnome-exchange-operations.eplug.in | 75 ++ 20 files changed, 5619 insertions(+) create mode 100644 plugins/exchange-operations/ChangeLog create mode 100644 plugins/exchange-operations/Makefile.am create mode 100644 plugins/exchange-operations/exchange-account-setup.c create mode 100644 plugins/exchange-operations/exchange-ask-password.c create mode 100644 plugins/exchange-operations/exchange-change-password.glade create mode 100644 plugins/exchange-operations/exchange-change-password.h create mode 100644 plugins/exchange-operations/exchange-config-listener.c create mode 100644 plugins/exchange-operations/exchange-config-listener.h create mode 100644 plugins/exchange-operations/exchange-delegates-user.c create mode 100644 plugins/exchange-operations/exchange-delegates-user.h create mode 100644 plugins/exchange-operations/exchange-delegates.c create mode 100644 plugins/exchange-operations/exchange-delegates.glade create mode 100644 plugins/exchange-operations/exchange-delegates.h create mode 100644 plugins/exchange-operations/exchange-folder-size.c create mode 100644 plugins/exchange-operations/exchange-folder-size.h create mode 100644 plugins/exchange-operations/exchange-folder-tree.glade create mode 100644 plugins/exchange-operations/exchange-operations.c create mode 100644 plugins/exchange-operations/exchange-operations.h create mode 100644 plugins/exchange-operations/org-gnome-exchange-operations.eplug.in diff --git a/ChangeLog b/ChangeLog index ae45baa857..c31c3c2c3a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-06-12 Sarfraaz Ahmed + + * plugins/exchange-operations : Added a new plugin directory for + renaming exchange-account-setup as exchange-operations. + 2005-06-10 Sarfraaz Ahmed * 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 + + * Initial commit to rename Exchange-account-setup as Exchange + Operations. + +2005-06-10 Sarfraaz Ahmed + + * 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 + + 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 + + * exchange-account-setup.c: moved e-error to e-util + +2005-03-18 Sushma Rai + + * 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 + + * exchange-account-setup.c (exchange_authtype_changed): Setting the + authentication mechanism to both transport and source urls. + +2005-03-05 Sushma Rai + + * 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 + + * org-gnome-exchange-account-setup.eplug.in: add account wizard item + +2005-02-27 Sushma Rai + + * 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 + + * 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 + + * org-gnome-exchange-account-setup.eplug.in: Added author and + fixed description. + +2005-02-21 Not Zed + + * 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 + + * 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 + + ** 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 + + * exchange-account-setup.c (create_page): Fixed empty + string being marked for translation problem. #71644 + +2005-01-23 Sushma Rai + + * 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 + + * 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 + + * 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 + + * exchange-ask-password.c (validate_exchange_user): Reading the return + value of user validation function. Fixes #71385 + +2005-01-18 Sushma Rai + + * 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 + + * 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 + + * 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 + + * Makefile.am: Linking to camel libs. Fixes plugin loading problem + due to undefined camel symbol, during evolution startup. + +2005-01-13 Sushma Rai + + * org-gnome-exchange-account-setup.eplug.in: Combined + all the plugins into one. + +2005-01-12 Sushma Rai + + * exchange-ask-password.c: (validate_exchange_user): + Added one more error condition check. + +2005-01-12 Sushma Rai + + * 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 + + * 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 + + * Makefile.am: fix LDFLAGS variable name. + +2005-01-10 Sushma Rai + + * exchange-ask-password.c: (validate_exchange_user): + Corrected argument order. + +2005-01-10 Sushma Rai + + * 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 + + * 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 + + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ("%s", _("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("%s", _("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 + * 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 +#include +#include +#include +#include +#include +#include +#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 @@ + + + + + + + True + Change Password + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_SPREAD + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + Your current password has expired. Please change your password now. + False + False + GTK_JUSTIFY_CENTER + True + False + 0.52 + 0.5 + 0 + 6 + + + 0 + False + False + + + + + + 6 + True + 3 + 2 + False + 6 + 6 + + + + True + Current Password: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + New Password: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + Confirm Password: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + True + True + False + 0 + + True + * + False + + + 1 + 2 + 1 + 2 + + + + + + + True + True + True + False + 0 + + True + * + False + + + 1 + 2 + 2 + 3 + + + + + + + True + True + True + False + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + 12 + + + + + + 0 + True + True + + + + + + + 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 + +#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 +#endif + +#include "exchange-config-listener.h" + +#include +#include +#include +#include +#include +// SURF : #include "mail-stub-listener.h" + +// SURF : #include + +#include +#include +#include + +#include +#include + +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; ipriv; + + 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 + +#include "exchange-types.h" +#include "e-util/e-account-list.h" +#include +#include + +#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 +#endif + +#include "exchange-delegates-user.h" + +#include "e2k-global-catalog.h" +#include "e2k-marshal.h" +#include "e2k-sid.h" +#include "e2k-utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef GTK_DISABLE_DEPRECATED +#include + +#include + +#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 +#include +#include + +#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 +#endif + +#include + +#include "exchange-delegates.h" +#include "exchange-delegates-user.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 @@ + + + + + + + True + Delegates + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + False + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + 6 + True + False + 6 + + + + True + These users will be able to send mail on your behalf +and access your folders with the permissions you give them. + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + False + 6 + + + + True + True + True + False + False + False + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_START + 6 + + + + True + True + True + gtk-add + True + GTK_RELIEF_NORMAL + True + + + + + + True + True + True + _Edit + True + GTK_RELIEF_NORMAL + True + + + + + + True + True + True + gtk-remove + True + GTK_RELIEF_NORMAL + True + + + + + 0 + False + True + GTK_PACK_END + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + Delegate Permissions + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + False + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + 12 + True + False + 12 + + + + True + Permissions for + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + 4 + 2 + False + 6 + 6 + + + + True + C_alendar: + True + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + calendar_perms + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + _Tasks: + True + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + task_perms + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + _Inbox: + True + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + inbox_perms + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + Co_ntacts: + True + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + contact_perms + + + 0 + 1 + 3 + 4 + fill + + + + + + + True + True + 0 + + + + True + + + + True + None + True + + + + + + True + Reviewer (read-only) + True + + + + + + True + Author (read, create) + True + + + + + + True + Editor (read, create, edit) + True + + + + + + + 1 + 2 + 0 + 1 + + + + + + + True + True + 0 + + + + True + + + + True + None + True + + + + + + True + Reviewer (read-only) + True + + + + + + True + Author (read, create) + True + + + + + + True + Editor (read, create, edit) + True + + + + + + + 1 + 2 + 1 + 2 + + + + + + + True + True + 0 + + + + True + + + + True + None + True + + + + + + True + Reviewer (read-only) + True + + + + + + True + Author (read, create) + True + + + + + + True + Editor (read, create, edit) + True + + + + + + + 1 + 2 + 2 + 3 + + + + + + + True + True + 0 + + + + True + + + + True + None + True + + + + + + True + Reviewer (read-only) + True + + + + + + True + Author (read, create) + True + + + + + + True + Editor (read, create, edit) + True + + + + + + + 1 + 2 + 3 + 4 + + + + + + 0 + False + False + + + + + + True + True + _Delegate can see private items + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + 0 + True + True + + + + + + + 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 +#include + +#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 +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 @@ + + + + + + + True + Exchange Folder Tree + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 250 + 300 + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + 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 + +#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 + +#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 @@ + + + + + + A plugin that handles a collection of Exchange account specific operations and features. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3