aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarfraaz Ahmed <asarfraaz@novell.com>2005-05-21 16:55:07 +0800
committerAhmed Sarfraaz <sarfraaz@src.gnome.org>2005-05-21 16:55:07 +0800
commitf3074e998b560938b0c904aabfb317d5764e71d6 (patch)
treecd6147ea3094b7cd10f3361a5e387677831f2c3f
parentcedf3ee1ff16d652986fa86509ec2d4fb5ac0f58 (diff)
downloadgsoc2013-evolution-f3074e998b560938b0c904aabfb317d5764e71d6.tar.gz
gsoc2013-evolution-f3074e998b560938b0c904aabfb317d5764e71d6.tar.zst
gsoc2013-evolution-f3074e998b560938b0c904aabfb317d5764e71d6.zip
Added a bunch of files to create exchange plugins for UI changes
2005-05-21 Sarfraaz Ahmed <asarfraaz@novell.com> Added a bunch of files to create exchange plugins for UI changes * exchange-change-password.[ch]/.glade * exchange-delegates.[ch]/.glade * exchange-delegate-user.[ch] * exchange-folder-size.[ch] * exchange-folder-tree.glade svn path=/trunk/; revision=29398
-rw-r--r--plugins/exchange-account-setup/ChangeLog9
-rw-r--r--plugins/exchange-account-setup/exchange-change-password.c134
-rw-r--r--plugins/exchange-account-setup/exchange-change-password.glade243
-rw-r--r--plugins/exchange-account-setup/exchange-change-password.h21
-rw-r--r--plugins/exchange-account-setup/exchange-delegates-user.c332
-rw-r--r--plugins/exchange-account-setup/exchange-delegates-user.h70
-rw-r--r--plugins/exchange-account-setup/exchange-delegates.c972
-rw-r--r--plugins/exchange-account-setup/exchange-delegates.glade628
-rw-r--r--plugins/exchange-account-setup/exchange-delegates.h21
-rw-r--r--plugins/exchange-account-setup/exchange-folder-size.c343
-rw-r--r--plugins/exchange-account-setup/exchange-folder-size.h56
-rw-r--r--plugins/exchange-account-setup/exchange-folder-tree.glade92
12 files changed, 2921 insertions, 0 deletions
diff --git a/plugins/exchange-account-setup/ChangeLog b/plugins/exchange-account-setup/ChangeLog
index c3d909b3f2..3dfd7ec4f5 100644
--- a/plugins/exchange-account-setup/ChangeLog
+++ b/plugins/exchange-account-setup/ChangeLog
@@ -1,3 +1,12 @@
+2005-05-21 Sarfraaz Ahmed <asarfraaz@novell.com>
+
+ Added a bunch of files to create exchange plugins for UI changes
+ * exchange-change-password.[ch]/.glade
+ * exchange-delegates.[ch]/.glade
+ * exchange-delegate-user.[ch]
+ * exchange-folder-size.[ch]
+ * exchange-folder-tree.glade
+
2005-05-16 Not Zed <NotZed@Ximian.com>
* exchange-account-setup.c: moved e-error to e-util
diff --git a/plugins/exchange-account-setup/exchange-change-password.c b/plugins/exchange-account-setup/exchange-change-password.c
new file mode 100644
index 0000000000..0e1e13265b
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-change-password.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* Copyright (C) 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.
+ */
+
+/* exchange-change-password: Change Password code */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "exchange-change-password.h"
+#include <exchange-account.h>
+#include <e2k-utils.h>
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtklabel.h>
+
+#define FILENAME EVOLUTION_GLADEDIR "/exchange-change-password.glade"
+#define ROOTNODE "pass_dialog"
+#define STARTNODE "pass_vbox"
+
+static void
+entry_changed (GtkEntry *entry, gpointer user_data)
+{
+ GladeXML *xml = user_data;
+ GtkEntry *new_entry, *confirm_entry;
+ GtkWidget *ok_button;
+ const char *text;
+
+ new_entry = GTK_ENTRY (glade_xml_get_widget (xml, "new_pass_entry"));
+ confirm_entry = GTK_ENTRY (glade_xml_get_widget (xml, "confirm_pass_entry"));
+ ok_button = glade_xml_get_widget (xml, "okbutton1");
+
+ text = gtk_entry_get_text (new_entry);
+ if (!text || !*text) {
+ gtk_widget_set_sensitive (ok_button, FALSE);
+ return;
+ }
+
+ text = gtk_entry_get_text (confirm_entry);
+ if (!text || !*text) {
+ gtk_widget_set_sensitive (ok_button, FALSE);
+ return;
+ }
+
+ gtk_widget_set_sensitive (ok_button, TRUE);
+}
+
+/**
+ * exchange_get_new_password:
+ * @existing_password: The user's current password
+ * @voluntary: %TRUE if the user has chosen "Change Password",
+ * %FALSE if their old password has expired.
+ *
+ * Prompt the user for a new password.
+ */
+char *
+exchange_get_new_password (const char *existing_password, gboolean voluntary)
+{
+ GladeXML *xml;
+ GtkWidget *top_widget;
+ GtkEntry *cur_entry, *new_entry, *confirm_entry;
+ GtkResponseType response;
+ GtkLabel *top_label;
+ char *new_pass;
+
+ xml = glade_xml_new (FILENAME, ROOTNODE, NULL);
+ top_widget = glade_xml_get_widget (xml, ROOTNODE);
+
+ cur_entry = GTK_ENTRY (glade_xml_get_widget (xml, "current_pass_entry"));
+ new_entry = GTK_ENTRY (glade_xml_get_widget (xml, "new_pass_entry"));
+ g_signal_connect (new_entry, "changed",
+ G_CALLBACK (entry_changed), xml);
+ confirm_entry = GTK_ENTRY (glade_xml_get_widget (xml, "confirm_pass_entry"));
+ g_signal_connect (confirm_entry, "changed",
+ G_CALLBACK (entry_changed), xml);
+ entry_changed (NULL, xml);
+
+ top_label = GTK_LABEL (glade_xml_get_widget (xml, "pass_label"));
+ if (voluntary)
+ gtk_widget_hide (GTK_WIDGET (top_label));
+
+run_dialog_again:
+ response = gtk_dialog_run (GTK_DIALOG (top_widget));
+ if (response == GTK_RESPONSE_OK) {
+ const char *cur_pass, *new_pass1, *new_pass2;
+
+ cur_pass = gtk_entry_get_text (cur_entry);
+ new_pass1 = gtk_entry_get_text (new_entry);
+ new_pass2 = gtk_entry_get_text (confirm_entry);
+
+ if (existing_password) {
+ if (strcmp (cur_pass, existing_password) != 0) {
+ /* User entered a wrong existing
+ * password. Prompt him again.
+ */
+ gtk_label_set_text (top_label, _("The current password does not match the existing password for your account. Please enter the correct password"));
+ gtk_widget_show (GTK_WIDGET (top_label));
+ goto run_dialog_again;
+ }
+ }
+
+ if (strcmp (new_pass1, new_pass2) != 0) {
+ gtk_label_set_text (top_label, _("The two passwords do not match. Please re-enter the passwords."));
+ gtk_widget_show (GTK_WIDGET (top_label));
+ goto run_dialog_again;
+ }
+
+ new_pass = g_strdup (new_pass1);
+ } else
+ new_pass = NULL;
+
+ gtk_widget_destroy (top_widget);
+ g_object_unref (xml);
+
+ return new_pass;
+}
diff --git a/plugins/exchange-account-setup/exchange-change-password.glade b/plugins/exchange-account-setup/exchange-change-password.glade
new file mode 100644
index 0000000000..fa49da6270
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-change-password.glade
@@ -0,0 +1,243 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="pass_dialog">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Change Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Your current password has expired. Please change your password now.</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.52</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">6</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="current_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Current Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="new_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="confirm_pass_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Confirm Password:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="new_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="confirm_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="current_pass_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_padding">12</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/plugins/exchange-account-setup/exchange-change-password.h b/plugins/exchange-account-setup/exchange-change-password.h
new file mode 100644
index 0000000000..155fff897b
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-change-password.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_CHANGE_PASSWORD_H__
+#define __EXCHANGE_CHANGE_PASSWORD_H__
+
+#include <exchange/exchange-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+char *exchange_get_new_password (const char *existing_password,
+ gboolean voluntary);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_CHANGE_PASSWORD_H__ */
diff --git a/plugins/exchange-account-setup/exchange-delegates-user.c b/plugins/exchange-account-setup/exchange-delegates-user.c
new file mode 100644
index 0000000000..38166747f4
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-delegates-user.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeDelegatesUser: A single delegate */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "exchange-delegates-user.h"
+
+#include "e2k-global-catalog.h"
+#include "e2k-marshal.h"
+#include "e2k-sid.h"
+#include "e2k-utils.h"
+
+#include <e-util/e-dialog-utils.h>
+#include <e-util/e-dialog-widgets.h>
+#include <e-util/e-gtk-utils.h>
+#include <glade/glade.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkmenushell.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtktogglebutton.h>
+
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtkoptionmenu.h>
+
+#include <string.h>
+
+#define EXCHANGE_DELEGATES_USER_SEPARATOR -2
+#define EXCHANGE_DELEGATES_USER_CUSTOM -3
+/* Can't use E2K_PERMISSIONS_ROLE_CUSTOM, because it's -1, which
+ * means "end of list" to e_dialog_option_menu_get/set
+ */
+
+static const int exchange_perm_map[] = {
+ E2K_PERMISSIONS_ROLE_NONE,
+ E2K_PERMISSIONS_ROLE_REVIEWER,
+ E2K_PERMISSIONS_ROLE_AUTHOR,
+ E2K_PERMISSIONS_ROLE_EDITOR,
+
+ EXCHANGE_DELEGATES_USER_SEPARATOR,
+ EXCHANGE_DELEGATES_USER_CUSTOM,
+
+ -1
+};
+
+const char *exchange_delegates_user_folder_names[] = {
+ "calendar", "tasks", "inbox", "contacts"
+};
+static const char *widget_names[] = {
+ "calendar_perms", "task_perms", "inbox_perms", "contact_perms",
+};
+
+
+enum {
+ EDITED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class = NULL;
+
+static void
+finalize (GObject *object)
+{
+ ExchangeDelegatesUser *user = EXCHANGE_DELEGATES_USER (object);
+
+ if (user->display_name)
+ g_free (user->display_name);
+ if (user->dn)
+ g_free (user->dn);
+ if (user->entryid)
+ g_byte_array_free (user->entryid, TRUE);
+ if (user->sid)
+ g_object_unref (user->sid);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ object_class->finalize = finalize;
+
+ /* signals */
+ signals[EDITED] =
+ g_signal_new ("edited",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ExchangeDelegatesUserClass, edited),
+ NULL, NULL,
+ e2k_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+}
+
+E2K_MAKE_TYPE (exchange_delegates_user, ExchangeDelegatesUser, class_init, NULL, PARENT_TYPE)
+
+static inline gboolean
+is_delegate_role (E2kPermissionsRole role)
+{
+ return (role == E2K_PERMISSIONS_ROLE_NONE ||
+ role == E2K_PERMISSIONS_ROLE_REVIEWER ||
+ role == E2K_PERMISSIONS_ROLE_AUTHOR ||
+ role == E2K_PERMISSIONS_ROLE_EDITOR);
+}
+
+static void
+set_perms (GtkWidget *omenu, E2kPermissionsRole role)
+{
+ if (!is_delegate_role (role)) {
+ GtkWidget *menu, *item;
+
+ menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (omenu));
+
+ item = gtk_menu_item_new ();
+ gtk_widget_set_sensitive (item, FALSE);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label (_("Custom"));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ gtk_widget_show_all (menu);
+
+ role = EXCHANGE_DELEGATES_USER_CUSTOM;
+ }
+
+ e_dialog_option_menu_set (omenu, role, exchange_perm_map);
+}
+
+static void
+parent_window_destroyed (gpointer dialog, GObject *where_parent_window_was)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * exchange_delegates_user_edit:
+ * @user: a delegate
+ * @parent_window: parent window for the editor dialog
+ *
+ * Brings up a dialog to edit @user's permissions as a delegate.
+ * An %edited signal will be emitted if anything changed.
+ *
+ * Return value: %TRUE for "OK", %FALSE for "Cancel".
+ **/
+gboolean
+exchange_delegates_user_edit (ExchangeDelegatesUser *user,
+ GtkWidget *parent_window)
+{
+ GladeXML *xml;
+ GtkWidget *dialog, *table, *label, *menu, *check;
+ char *title;
+ int button, i;
+ E2kPermissionsRole role;
+ gboolean modified;
+
+ g_return_val_if_fail (EXCHANGE_IS_DELEGATES_USER (user), FALSE);
+ g_return_val_if_fail (E2K_IS_SID (user->sid), FALSE);
+
+ /* Grab the Glade widgets */
+ xml = glade_xml_new (
+ CONNECTOR_GLADEDIR "/exchange-delegates.glade",
+ "delegate_permissions", PACKAGE);
+ g_return_val_if_fail (xml, FALSE);
+
+ title = g_strdup_printf (_("Permissions for %s"), user->display_name);
+
+ dialog = glade_xml_get_widget (xml, "delegate_permissions");
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), parent_window);
+
+ table = glade_xml_get_widget (xml, "toplevel_table");
+ gtk_widget_reparent (table, GTK_DIALOG (dialog)->vbox);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 6);
+
+ label = glade_xml_get_widget (xml, "delegate_label");
+ gtk_label_set_text (GTK_LABEL (label), title);
+ g_free (title);
+
+ /* Set up the permissions */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ menu = glade_xml_get_widget (xml, widget_names[i]);
+ set_perms (menu, user->role[i]);
+ }
+ check = glade_xml_get_widget (xml, "see_private_checkbox");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ user->see_private);
+
+ /* Run the dialog, while watching its parent. */
+ g_object_weak_ref (G_OBJECT (parent_window),
+ parent_window_destroyed, dialog);
+ g_object_add_weak_pointer (G_OBJECT (parent_window),
+ (void **)&parent_window);
+ button = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (parent_window) {
+ g_object_remove_weak_pointer (G_OBJECT (parent_window),
+ (void **)&parent_window);
+ g_object_weak_unref (G_OBJECT (parent_window),
+ parent_window_destroyed, dialog);
+ }
+
+ if (button != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return FALSE;
+ }
+
+ /* And update */
+ modified = FALSE;
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ menu = glade_xml_get_widget (xml, widget_names[i]);
+ role = e_dialog_option_menu_get (menu, exchange_perm_map);
+
+ if (is_delegate_role (user->role[i]) &&
+ user->role[i] != role) {
+ user->role[i] = role;
+ modified = TRUE;
+ }
+ }
+ check = glade_xml_get_widget (xml, "see_private_checkbox");
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)) !=
+ user->see_private) {
+ user->see_private = !user->see_private;
+ modified = TRUE;
+ }
+
+ g_object_unref (xml);
+ gtk_widget_destroy (dialog);
+
+ if (modified)
+ g_signal_emit (user, signals[EDITED], 0);
+
+ return TRUE;
+}
+
+/**
+ * exchange_delegates_user_new:
+ * @display_name: the delegate's (UTF8) display name
+ *
+ * Return value: a new delegate user with default permissions (but
+ * with most of the internal data blank).
+ **/
+ExchangeDelegatesUser *
+exchange_delegates_user_new (const char *display_name)
+{
+ ExchangeDelegatesUser *user;
+ int i;
+
+ user = g_object_new (EXCHANGE_TYPE_DELEGATES_USER, NULL);
+ user->display_name = g_strdup (display_name);
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (i == EXCHANGE_DELEGATES_CALENDAR ||
+ i == EXCHANGE_DELEGATES_TASKS)
+ user->role[i] = E2K_PERMISSIONS_ROLE_EDITOR;
+ else
+ user->role[i] = E2K_PERMISSIONS_ROLE_NONE;
+ }
+
+ return user;
+}
+
+/**
+ * exchange_delegates_user_new_from_gc:
+ * @gc: the global catalog object
+ * @email: email address of the new delegate
+ * @creator_entryid: The value of the PR_CREATOR_ENTRYID property
+ * on the LocalFreebusy file.
+ *
+ * Return value: a new delegate user with default permissions and
+ * internal data filled in from the global catalog.
+ **/
+ExchangeDelegatesUser *
+exchange_delegates_user_new_from_gc (E2kGlobalCatalog *gc,
+ const char *email,
+ GByteArray *creator_entryid)
+{
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+ ExchangeDelegatesUser *user;
+ guint8 *p;
+
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL, email,
+ (E2K_GLOBAL_CATALOG_LOOKUP_SID |
+ E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN),
+ &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK)
+ return NULL;
+
+ user = exchange_delegates_user_new (e2k_sid_get_display_name (entry->sid));
+ user->dn = g_strdup (entry->dn);
+ user->sid = entry->sid;
+ g_object_ref (user->sid);
+
+ user->entryid = g_byte_array_new ();
+ p = creator_entryid->data + creator_entryid->len - 2;
+ while (p > creator_entryid->data && *p)
+ p--;
+ g_byte_array_append (user->entryid, creator_entryid->data,
+ p - creator_entryid->data + 1);
+ g_byte_array_append (user->entryid, entry->legacy_exchange_dn,
+ strlen (entry->legacy_exchange_dn));
+ g_byte_array_append (user->entryid, "", 1);
+
+ return user;
+}
diff --git a/plugins/exchange-account-setup/exchange-delegates-user.h b/plugins/exchange-account-setup/exchange-delegates-user.h
new file mode 100644
index 0000000000..2b5b291e13
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-delegates-user.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_DELEGATES_USER_H__
+#define __EXCHANGE_DELEGATES_USER_H__
+
+#include <exchange-types.h>
+#include <e2k-security-descriptor.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EXCHANGE_TYPE_DELEGATES_USER (exchange_delegates_user_get_type ())
+#define EXCHANGE_DELEGATES_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_TYPE_DELEGATES_USER, ExchangeDelegatesUser))
+#define EXCHANGE_DELEGATES_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_TYPE_DELEGATES_USER, ExchangeDelegatesUserClass))
+#define EXCHANGE_IS_DELEGATES_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_TYPE_DELEGATES_USER))
+#define EXCHANGE_IS_DELEGATES_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_TYPE_DELEGATES_USER))
+
+
+typedef struct _ExchangeDelegatesUser ExchangeDelegatesUser;
+typedef struct _ExchangeDelegatesUserPrivate ExchangeDelegatesUserPrivate;
+typedef struct _ExchangeDelegatesUserClass ExchangeDelegatesUserClass;
+
+enum {
+ EXCHANGE_DELEGATES_CALENDAR,
+ EXCHANGE_DELEGATES_TASKS,
+ EXCHANGE_DELEGATES_INBOX,
+ EXCHANGE_DELEGATES_CONTACTS,
+ EXCHANGE_DELEGATES_LAST
+};
+
+struct _ExchangeDelegatesUser {
+ GObject parent;
+
+ char *display_name, *dn;
+ GByteArray *entryid;
+
+ E2kSid *sid;
+ E2kPermissionsRole role[EXCHANGE_DELEGATES_LAST];
+ gboolean see_private;
+};
+
+struct _ExchangeDelegatesUserClass {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*edited) (ExchangeDelegatesUser *, gpointer);
+};
+
+
+
+GType exchange_delegates_user_get_type (void);
+
+ExchangeDelegatesUser *exchange_delegates_user_new (const char *display_name);
+ExchangeDelegatesUser *exchange_delegates_user_new_from_gc (E2kGlobalCatalog *gc,
+ const char *email,
+ GByteArray *creator_entryid);
+
+gboolean exchange_delegates_user_edit (ExchangeDelegatesUser *user,
+ GtkWidget *parent_window);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_DELEGATES_USER_H__ */
diff --git a/plugins/exchange-account-setup/exchange-delegates.c b/plugins/exchange-account-setup/exchange-delegates.c
new file mode 100644
index 0000000000..c868eb64b9
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-delegates.c
@@ -0,0 +1,972 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeDelegates: Exchange delegate handling.
+ *
+ * FIXME: make this instant-apply
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "exchange-delegates.h"
+#include "exchange-delegates-user.h"
+#include "exchange-account.h"
+#include "e2k-propnames.h"
+#include "e2k-security-descriptor.h"
+#include "e2k-sid.h"
+#include "e2k-uri.h"
+#include "e2k-user-dialog.h"
+#include "e2k-utils.h"
+
+#include <e-util/e-dialog-utils.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+
+typedef struct {
+ const char *uri;
+ E2kSecurityDescriptor *sd;
+ gboolean changed;
+} ExchangeDelegatesFolder;
+
+typedef struct {
+ ExchangeAccount *account;
+ char *self_dn;
+
+ GladeXML *xml;
+ GtkWidget *dialog, *parent;
+
+ GtkListStore *model;
+ GtkWidget *table;
+
+ GByteArray *creator_entryid;
+ GPtrArray *users, *added_users, *removed_users;
+ gboolean loaded_folders;
+ ExchangeDelegatesFolder folder[EXCHANGE_DELEGATES_LAST];
+ ExchangeDelegatesFolder freebusy_folder;
+} ExchangeDelegates;
+
+extern const char *exchange_delegates_user_folder_names[];
+
+const char *exchange_localfreebusy_path = "NON_IPM_SUBTREE/Freebusy%20Data/LocalFreebusy.EML";
+
+static void set_perms_for_user (ExchangeDelegatesUser *user, gpointer user_data);
+
+static void
+set_sd_for_href (ExchangeDelegates *delegates,
+ const char *href,
+ E2kSecurityDescriptor *sd)
+{
+ int i;
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (!delegates->folder[i].uri)
+ continue;
+
+ if (!strcmp (href, delegates->folder[i].uri)) {
+ delegates->folder[i].sd = sd;
+ return;
+ }
+ }
+
+ /* else, it's the freebusy folder */
+ delegates->freebusy_folder.uri = g_strdup (href);
+ delegates->freebusy_folder.sd = sd;
+}
+
+/* Given an array of ExchangeDelegatesUser containing display names
+ * and entryids, and an array of E2kSecurityDescriptors containing
+ * SIDs (which contain display names), add SIDs to the delegates. In
+ * the easy case, we can just match the SIDs up with their
+ * corresponding user by display name. However, there are two things
+ * that can go wrong:
+ *
+ * 1. Some users may have been removed from the SDs
+ * 2. Two users may have the same display name
+ *
+ * In both cases, we fall back to using the GC.
+ */
+static gboolean
+fill_in_sids (ExchangeDelegates *delegates)
+{
+ int u, u2, sd, needed_sids;
+ ExchangeDelegatesUser *user, *user2;
+ GList *sids, *s;
+ E2kSid *sid;
+ E2kGlobalCatalog *gc;
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+ gboolean ok = TRUE;
+
+ needed_sids = 0;
+
+ /* Mark users with duplicate names and count the number of
+ * non-duplicate names.
+ */
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid == (E2kSid *)-1)
+ continue;
+ for (u2 = u + 1; u2 < delegates->users->len; u2++) {
+ user2 = delegates->users->pdata[u2];
+ if (!strcmp (user->display_name, user2->display_name))
+ user->sid = user2->sid = (E2kSid *)-1;
+ }
+ if (!user->sid)
+ needed_sids++;
+ }
+
+ /* Scan security descriptors trying to match SIDs until we're
+ * not expecting to find any more.
+ */
+ for (sd = 0; sd < EXCHANGE_DELEGATES_LAST && needed_sids; sd++) {
+ sids = e2k_security_descriptor_get_sids (delegates->folder[sd].sd);
+ for (s = sids; s && needed_sids; s = s->next) {
+ sid = s->data;
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid)
+ continue;
+ if (!strcmp (user->display_name,
+ e2k_sid_get_display_name (sid))) {
+ user->sid = sid;
+ g_object_ref (sid);
+ needed_sids--;
+ }
+ }
+ }
+ g_list_free (sids);
+ }
+
+ /* Now for each user whose SID hasn't yet been found, look it up. */
+ gc = exchange_account_get_global_catalog (delegates->account);
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ if (user->sid && user->sid != (E2kSid *)-1)
+ continue;
+
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ e2k_entryid_to_dn (user->entryid),
+ E2K_GLOBAL_CATALOG_LOOKUP_SID, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK) {
+ user->sid = NULL;
+ ok = FALSE;
+ continue;
+ }
+ user->sid = entry->sid;
+ g_object_ref (user->sid);
+ e2k_global_catalog_entry_free (gc, entry);
+ }
+
+ return ok;
+}
+
+static const char *sd_props[] = {
+ E2K_PR_EXCHANGE_SD_BINARY,
+ E2K_PR_EXCHANGE_SD_XML
+};
+static const int n_sd_props = sizeof (sd_props) / sizeof (sd_props[0]);
+
+/* Read the folder security descriptors and match them up with the
+ * list of delegates.
+ */
+static gboolean
+get_folder_security (ExchangeDelegates *delegates)
+{
+ GPtrArray *hrefs;
+ E2kContext *ctx;
+ E2kHTTPStatus status;
+ E2kResultIter *iter;
+ E2kResult *result;
+ xmlNode *xml_form;
+ GByteArray *binary_form;
+ ExchangeDelegatesUser *user;
+ guint32 perms;
+ int i, u;
+
+ /* If we've been here before, just return the success or
+ * failure result from last time.
+ */
+ if (delegates->freebusy_folder.uri)
+ return delegates->loaded_folders;
+
+ if (!exchange_account_get_global_catalog (delegates->account)) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("No Global Catalog server configured for this account.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ ctx = exchange_account_get_context (delegates->account);
+
+ hrefs = g_ptr_array_new ();
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ delegates->folder[i].uri = exchange_account_get_standard_uri (
+ delegates->account, exchange_delegates_user_folder_names[i]);
+ if (delegates->folder[i].uri) {
+ g_ptr_array_add (hrefs, (char *)e2k_uri_relative (
+ delegates->account->home_uri,
+ delegates->folder[i].uri));
+ }
+ }
+ g_ptr_array_add (hrefs, (char *)exchange_localfreebusy_path);
+
+ iter = e2k_context_bpropfind_start (
+ ctx, NULL, delegates->account->home_uri,
+ (const char **)hrefs->pdata, hrefs->len,
+ sd_props, n_sd_props);
+ g_ptr_array_free (hrefs, TRUE);
+
+ while ((result = e2k_result_iter_next (iter))) {
+ xml_form = e2k_properties_get_prop (result->props,
+ E2K_PR_EXCHANGE_SD_XML);
+ binary_form = e2k_properties_get_prop (result->props,
+ E2K_PR_EXCHANGE_SD_BINARY);
+
+ if (xml_form && binary_form) {
+ set_sd_for_href (delegates, result->href,
+ e2k_security_descriptor_new (xml_form, binary_form));
+ }
+ }
+ status = e2k_result_iter_free (iter);
+
+ if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Could not read folder permissions.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ if (!fill_in_sids (delegates)) {
+ delegates->loaded_folders = FALSE;
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Could not determine folder permissions for delegates.\nUnable to edit delegates."));
+ return FALSE;
+ }
+
+ /* Fill in delegate structures from the security descriptors */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ for (u = 0; u < delegates->users->len; u++) {
+ user = delegates->users->pdata[u];
+ perms = e2k_security_descriptor_get_permissions (
+ delegates->folder[i].sd, user->sid);
+ user->role[i] = e2k_permissions_role_find (perms);
+ }
+ }
+
+ delegates->loaded_folders = TRUE;
+ return TRUE;
+}
+
+
+static const char *delegation_props[] = {
+ PR_DELEGATES_DISPLAY_NAMES,
+ PR_DELEGATES_ENTRYIDS,
+ PR_DELEGATES_SEE_PRIVATE,
+ PR_CREATOR_ENTRYID
+};
+static const int n_delegation_props = sizeof (delegation_props) / sizeof (delegation_props[0]);
+
+/* Fetch the list of delegates from the freebusy message. */
+static gboolean
+get_user_list (ExchangeDelegates *delegates)
+{
+ E2kContext *ctx;
+ E2kResultIter *iter;
+ E2kResult *result;
+ GPtrArray *display_names, *entryids, *privflags;
+ GByteArray *entryid;
+ ExchangeDelegatesUser *user;
+ int i;
+
+ ctx = exchange_account_get_context (delegates->account);
+ iter = e2k_context_bpropfind_start (ctx, NULL,
+ delegates->account->home_uri,
+ &exchange_localfreebusy_path, 1,
+ delegation_props, n_delegation_props);
+ result = e2k_result_iter_next (iter);
+ if (!result || !E2K_HTTP_STATUS_IS_SUCCESSFUL (result->status)) {
+ e2k_result_iter_free (iter);
+ return FALSE;
+ }
+
+ delegates->users = g_ptr_array_new ();
+ delegates->added_users = g_ptr_array_new ();
+ delegates->removed_users = g_ptr_array_new ();
+
+ display_names = e2k_properties_get_prop (result->props, PR_DELEGATES_DISPLAY_NAMES);
+ entryids = e2k_properties_get_prop (result->props, PR_DELEGATES_ENTRYIDS);
+ privflags = e2k_properties_get_prop (result->props, PR_DELEGATES_SEE_PRIVATE);
+
+ entryid = e2k_properties_get_prop (result->props, PR_CREATOR_ENTRYID);
+ delegates->creator_entryid = g_byte_array_new ();
+ g_byte_array_append (delegates->creator_entryid, entryid->data, entryid->len);
+
+ if (!display_names || !entryids || !privflags) {
+ e2k_result_iter_free (iter);
+ return TRUE;
+ }
+
+ for (i = 0; i < display_names->len && i < entryids->len && i < privflags->len; i++) {
+ user = exchange_delegates_user_new (display_names->pdata[i]);
+ user->see_private = privflags->pdata[i] && atoi (privflags->pdata[i]);
+ entryid = entryids->pdata[i];
+ user->entryid = g_byte_array_new ();
+ g_byte_array_append (user->entryid, entryid->data, entryid->len);
+
+ g_signal_connect (user, "edited", G_CALLBACK (set_perms_for_user), delegates);
+
+ g_ptr_array_add (delegates->users, user);
+ }
+
+ e2k_result_iter_free (iter);
+ return TRUE;
+}
+
+/* Add or remove a delegate. Everyone must be in one of three states:
+ * 1. only in users (because they started and ended there)
+ * 2. in users and added_users (because they weren't in
+ * users to begin with, but got added)
+ * 3. only in removed_users (because they were in users to
+ * begin with and got removed).
+ * If you're added and then removed, or removed and then added, you have
+ * to end up in state 1. That's what this is for.
+ */
+static void
+add_remove_user (ExchangeDelegatesUser *user,
+ GPtrArray *to_array, GPtrArray *from_array)
+{
+ ExchangeDelegatesUser *match;
+ int i;
+
+ for (i = 0; i < from_array->len; i++) {
+ match = from_array->pdata[i];
+ if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (match->sid),
+ e2k_sid_get_binary_sid (user->sid))) {
+ g_ptr_array_remove_index_fast (from_array, i);
+ g_object_unref (match);
+ return;
+ }
+ }
+
+ g_ptr_array_add (to_array, user);
+ g_object_ref (user);
+}
+
+static void
+set_perms_for_user (ExchangeDelegatesUser *user, gpointer user_data)
+{
+ ExchangeDelegates *delegates = user_data;
+ int i, role;
+ guint32 perms;
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ perms = e2k_permissions_role_get_perms (user->role[i]);
+ e2k_security_descriptor_set_permissions (delegates->folder[i].sd,
+ user->sid, perms);
+ }
+ role = user->role[EXCHANGE_DELEGATES_CALENDAR];
+ if (role == E2K_PERMISSIONS_ROLE_AUTHOR)
+ role = E2K_PERMISSIONS_ROLE_EDITOR;
+ perms = e2k_permissions_role_get_perms (role);
+ e2k_security_descriptor_set_permissions (delegates->freebusy_folder.sd,
+ user->sid, perms);
+}
+
+static void
+add_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ E2kGlobalCatalog *gc;
+ GtkWidget *dialog, *parent_window;
+ const char *delegate_exchange_dn;
+ char *email;
+ ExchangeDelegatesUser *user, *match;
+ int response, u;
+ GtkTreeIter iter;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ dialog = e2k_user_dialog_new (parent_window,
+ _("Delegate To:"), _("Delegate To"));
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return;
+ }
+ email = e2k_user_dialog_get_user (E2K_USER_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ if (email == NULL)
+ return;
+
+ user = exchange_delegates_user_new_from_gc (gc, email,
+ delegates->creator_entryid);
+ if (!user) {
+ e_notice (parent_window, GTK_MESSAGE_ERROR,
+ _("Could not make %s a delegate"), email);
+ g_free (email);
+ return;
+ }
+ g_free (email);
+
+ delegate_exchange_dn = e2k_entryid_to_dn (user->entryid);
+ if (delegate_exchange_dn && !g_ascii_strcasecmp (delegate_exchange_dn, delegates->account->legacy_exchange_dn)) {
+ g_object_unref (user);
+ e_notice (parent_window, GTK_MESSAGE_ERROR,
+ _("You cannot make yourself your own delegate"));
+ return;
+ }
+
+ for (u = 0; u < delegates->users->len; u++) {
+ match = delegates->users->pdata[u];
+ if (e2k_sid_binary_sid_equal (e2k_sid_get_binary_sid (user->sid),
+ e2k_sid_get_binary_sid (match->sid))) {
+ e_notice (parent_window, GTK_MESSAGE_INFO,
+ _("%s is already a delegate"),
+ user->display_name);
+ g_object_unref (user);
+ exchange_delegates_user_edit (match, parent_window);
+ return;
+ }
+ }
+
+ if (!exchange_delegates_user_edit (user, parent_window)) {
+ g_object_unref (user);
+ return;
+ }
+ set_perms_for_user (user, delegates);
+ g_signal_connect (user, "edited",
+ G_CALLBACK (set_perms_for_user), delegates);
+
+ add_remove_user (user, delegates->added_users, delegates->removed_users);
+ g_ptr_array_add (delegates->users, user);
+
+ /* Add the user to the table */
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, user->display_name,
+ -1);
+}
+
+static int
+get_selected_row (GtkWidget *tree_view, GtkTreeIter *iter)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ int *indices, row;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+ if (!gtk_tree_selection_get_selected (selection, &model, iter))
+ return -1;
+
+ path = gtk_tree_model_get_path (model, iter);
+ indices = gtk_tree_path_get_indices (path);
+ row = indices[0];
+ gtk_tree_path_free (path);
+
+ return row;
+}
+
+static void
+edit_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ GtkWidget *parent_window;
+ GtkTreeIter iter;
+ int row;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ row = get_selected_row (delegates->table, &iter);
+ g_return_if_fail (row >= 0 && row < delegates->users->len);
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ exchange_delegates_user_edit (delegates->users->pdata[row],
+ parent_window);
+}
+
+static gboolean
+table_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ GtkWidget *parent_window;
+ GtkTreeIter iter;
+ int row;
+
+ if (event->type != GDK_2BUTTON_PRESS)
+ return FALSE;
+
+ row = get_selected_row (delegates->table, &iter);
+ if (row < 0 || row >= delegates->users->len)
+ return FALSE;
+
+ if (!get_folder_security (delegates))
+ return FALSE;
+
+ parent_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ exchange_delegates_user_edit (delegates->users->pdata[row],
+ parent_window);
+ return TRUE;
+}
+
+static void
+remove_button_clicked_cb (GtkWidget *widget, gpointer data)
+{
+ ExchangeDelegates *delegates = data;
+ ExchangeDelegatesUser *user;
+ GtkWidget *dialog;
+ int row, btn, i;
+ GtkTreeIter iter;
+
+ if (!get_folder_security (delegates))
+ return;
+
+ row = get_selected_row (delegates->table, &iter);
+ g_return_if_fail (row >= 0 && row < delegates->users->len);
+
+ user = delegates->users->pdata[row];
+
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("Remove the delegate %s?"),
+ user->display_name);
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), widget);
+
+ btn = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ if (btn != GTK_RESPONSE_YES)
+ return;
+
+ add_remove_user (user, delegates->removed_users, delegates->added_users);
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ e2k_security_descriptor_remove_sid (delegates->folder[i].sd,
+ user->sid);
+ }
+ e2k_security_descriptor_remove_sid (delegates->freebusy_folder.sd,
+ user->sid);
+
+ /* Remove the user from the table */
+ gtk_list_store_remove (delegates->model, &iter);
+ g_ptr_array_remove_index (delegates->users, row);
+ g_object_unref (user);
+}
+
+
+static gboolean
+proppatch_sd (E2kContext *ctx, ExchangeDelegatesFolder *folder)
+{
+ GByteArray *binsd;
+ E2kProperties *props;
+ const char *href = "";
+ E2kResultIter *iter;
+ E2kResult *result;
+ E2kHTTPStatus status;
+
+ binsd = e2k_security_descriptor_to_binary (folder->sd);
+ if (!binsd)
+ return FALSE;
+
+ props = e2k_properties_new ();
+ e2k_properties_set_binary (props, E2K_PR_EXCHANGE_SD_BINARY, binsd);
+
+ iter = e2k_context_bproppatch_start (ctx, NULL, folder->uri,
+ &href, 1, props, FALSE);
+ e2k_properties_free (props);
+
+ result = e2k_result_iter_next (iter);
+ if (result) {
+ status = result->status;
+ e2k_result_iter_free (iter);
+ } else
+ status = e2k_result_iter_free (iter);
+
+ return E2K_HTTP_STATUS_IS_SUCCESSFUL (status);
+}
+
+static gboolean
+get_user_dn (E2kGlobalCatalog *gc, ExchangeDelegatesUser *user)
+{
+ E2kGlobalCatalogEntry *entry;
+ E2kGlobalCatalogStatus status;
+ const char *exchange_dn;
+
+ exchange_dn = e2k_entryid_to_dn (user->entryid);
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ exchange_dn, 0, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK)
+ return FALSE;
+
+ user->dn = g_strdup (entry->dn);
+ e2k_global_catalog_entry_free (gc, entry);
+ return TRUE;
+}
+
+static void
+delegates_apply (ExchangeDelegates *delegates)
+{
+ ExchangeDelegatesUser *user;
+ E2kGlobalCatalog *gc;
+ E2kContext *ctx;
+ GPtrArray *display_names, *entryids, *privflags;
+ GByteArray *entryid_dup;
+ char *error = NULL;
+ E2kProperties *props;
+ int i, status;
+
+ if (!delegates->loaded_folders)
+ return;
+
+ /* We can't do this atomically/transactionally, so we need to
+ * make sure that if we fail at any step, things are still in
+ * a semi-consistent state. So we do:
+ *
+ * 1. Remove old delegates from AD
+ * 2. Update LocalFreebusy.EML (the canonical list of delegates)
+ * 3. Add new delegates to AD
+ * 4. Update security descriptors
+ *
+ * If step 1 fails, nothing is changed.
+ *
+ * If step 2 fails, delegates who should have been removed
+ * will have been removed from AD but nothing else, so they
+ * will still show up as being delegates and the user can try
+ * to remove them again later.
+ *
+ * If step 3 fails, delegates who should have been added will
+ * not be in AD, but will be listed as delegates, so the user
+ * can remove them and try adding them again later.
+ *
+ * If step 4 fails, the user can still correct the folder
+ * permissions by hand.
+ */
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+ if (!gc) {
+ error = g_strdup (_("Could not access Active Directory"));
+ goto done;
+ }
+
+ if ((delegates->removed_users || delegates->added_users) && !delegates->self_dn) {
+ E2kGlobalCatalog *gc;
+ E2kGlobalCatalogStatus status;
+ E2kGlobalCatalogEntry *entry;
+
+ gc = exchange_account_get_global_catalog (delegates->account);
+ status = e2k_global_catalog_lookup (
+ gc, NULL, /* FIXME: cancellable */
+ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN,
+ delegates->account->legacy_exchange_dn, 0, &entry);
+ if (status != E2K_GLOBAL_CATALOG_OK) {
+ error = g_strdup (_("Could not find self in Active Directory"));
+ goto done;
+ }
+
+ delegates->self_dn = g_strdup (entry->dn);
+ e2k_global_catalog_entry_free (gc, entry);
+ }
+
+ /* 1. Remove old delegates from AD */
+ while (delegates->removed_users && delegates->removed_users->len) {
+ user = delegates->removed_users->pdata[0];
+ if (!user->dn && !get_user_dn (gc, user)) {
+ error = g_strdup_printf (
+ _("Could not find delegate %s in Active Directory"),
+ user->display_name);
+ goto done;
+ }
+
+ /* FIXME: cancellable */
+ status = e2k_global_catalog_remove_delegate (gc, NULL,
+ delegates->self_dn,
+ user->dn);
+ if (status != E2K_GLOBAL_CATALOG_OK &&
+ status != E2K_GLOBAL_CATALOG_NO_DATA) {
+ error = g_strdup_printf (
+ _("Could not remove delegate %s"),
+ user->display_name);
+ goto done;
+ }
+
+ g_object_unref (user);
+ g_ptr_array_remove_index_fast (delegates->removed_users, 0);
+ }
+
+ /* 2. Update LocalFreebusy.EML */
+ ctx = exchange_account_get_context (delegates->account);
+
+ if (delegates->users->len) {
+ display_names = g_ptr_array_new ();
+ entryids = g_ptr_array_new ();
+ privflags = g_ptr_array_new ();
+
+ for (i = 0; i < delegates->users->len; i++) {
+ user = delegates->users->pdata[i];
+ g_ptr_array_add (display_names, g_strdup (user->display_name));
+ entryid_dup = g_byte_array_new ();
+ g_byte_array_append (entryid_dup, user->entryid->data,
+ user->entryid->len);
+ g_ptr_array_add (entryids, entryid_dup);
+ g_ptr_array_add (privflags, g_strdup_printf ("%d", user->see_private));
+ }
+
+ props = e2k_properties_new ();
+ e2k_properties_set_string_array (
+ props, PR_DELEGATES_DISPLAY_NAMES, display_names);
+ e2k_properties_set_binary_array (
+ props, PR_DELEGATES_ENTRYIDS, entryids);
+ e2k_properties_set_int_array (
+ props, PR_DELEGATES_SEE_PRIVATE, privflags);
+ } else if (delegates->removed_users) {
+ props = e2k_properties_new ();
+ e2k_properties_remove (props, PR_DELEGATES_DISPLAY_NAMES);
+ e2k_properties_remove (props, PR_DELEGATES_ENTRYIDS);
+ e2k_properties_remove (props, PR_DELEGATES_SEE_PRIVATE);
+ } else
+ props = NULL;
+
+ if (props) {
+ E2kResultIter *iter;
+ E2kResult *result;
+
+ iter = e2k_context_bproppatch_start (
+ ctx, NULL, delegates->account->home_uri,
+ &exchange_localfreebusy_path, 1,
+ props, FALSE);
+ e2k_properties_free (props);
+
+ result = e2k_result_iter_next (iter);
+ if (result) {
+ status = result->status;
+ e2k_result_iter_free (iter);
+ } else
+ status = e2k_result_iter_free (iter);
+
+ if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status)) {
+ error = g_strdup (_("Could not update list of delegates."));
+ goto done;
+ }
+ }
+
+ /* 3. Add new delegates to AD */
+ while (delegates->added_users && delegates->added_users->len) {
+ user = delegates->added_users->pdata[0];
+ /* An added user must have come from the GC so
+ * we know user->dn is set.
+ */
+ /* FIXME: cancellable */
+ status = e2k_global_catalog_add_delegate (gc, NULL,
+ delegates->self_dn,
+ user->dn);
+ if (status != E2K_GLOBAL_CATALOG_OK &&
+ status != E2K_GLOBAL_CATALOG_EXISTS) {
+ error = g_strdup_printf (
+ _("Could not add delegate %s"),
+ user->display_name);
+ goto done;
+ }
+ g_ptr_array_remove_index_fast (delegates->added_users, 0);
+ g_object_unref (user);
+ }
+
+ /* 4. Update security descriptors */
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++)
+ proppatch_sd (ctx, &delegates->folder[i]);
+ proppatch_sd (ctx, &delegates->freebusy_folder);
+
+ done:
+ if (error) {
+ e_notice (delegates->table, GTK_MESSAGE_ERROR,
+ _("Failed to update delegates:\n%s"), error);
+ g_free (error);
+ }
+}
+
+static void parent_destroyed (gpointer user_data, GObject *ex_parent);
+
+static void
+delegates_destroy (ExchangeDelegates *delegates)
+{
+ int i;
+
+ g_object_unref (delegates->account);
+
+ if (delegates->parent) {
+ g_object_weak_unref (G_OBJECT (delegates->parent),
+ parent_destroyed, delegates);
+ }
+ if (delegates->dialog)
+ gtk_widget_destroy (delegates->dialog);
+
+ if (delegates->model)
+ g_object_unref (delegates->model);
+
+ if (delegates->self_dn)
+ g_free (delegates->self_dn);
+ if (delegates->creator_entryid)
+ g_byte_array_free (delegates->creator_entryid, TRUE);
+
+ if (delegates->users) {
+ for (i = 0; i < delegates->users->len; i++)
+ g_object_unref (delegates->users->pdata[i]);
+ g_ptr_array_free (delegates->users, TRUE);
+ }
+ if (delegates->added_users) {
+ for (i = 0; i < delegates->added_users->len; i++)
+ g_object_unref (delegates->added_users->pdata[i]);
+ g_ptr_array_free (delegates->added_users, TRUE);
+ }
+ if (delegates->removed_users) {
+ for (i = 0; i < delegates->removed_users->len; i++)
+ g_object_unref (delegates->removed_users->pdata[i]);
+ g_ptr_array_free (delegates->removed_users, TRUE);
+ }
+
+ for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) {
+ if (delegates->folder[i].sd)
+ g_object_unref (delegates->folder[i].sd);
+ }
+ if (delegates->freebusy_folder.sd)
+ g_object_unref (delegates->freebusy_folder.sd);
+ if (delegates->freebusy_folder.uri)
+ g_free ((char *)delegates->freebusy_folder.uri);
+
+ if (delegates->xml)
+ g_object_unref (delegates->xml);
+
+ g_free (delegates);
+}
+
+
+static void
+dialog_response (GtkDialog *dialog, int response, gpointer user_data)
+{
+ ExchangeDelegates *delegates = user_data;
+
+ if (response == GTK_RESPONSE_OK)
+ delegates_apply (delegates);
+ delegates_destroy (delegates);
+}
+
+static void
+parent_destroyed (gpointer user_data, GObject *ex_parent)
+{
+ ExchangeDelegates *delegates = user_data;
+
+ gtk_widget_destroy (delegates->dialog);
+ delegates_destroy (delegates);
+}
+
+void
+exchange_delegates (ExchangeAccount *account, GtkWidget *parent)
+{
+ ExchangeDelegates *delegates;
+ GtkWidget *button;
+ ExchangeDelegatesUser *user;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ int i;
+
+ g_return_if_fail (GTK_IS_WIDGET (parent));
+ g_return_if_fail (EXCHANGE_IS_ACCOUNT (account));
+
+ delegates = g_new0 (ExchangeDelegates, 1);
+ delegates->account = g_object_ref (account);
+
+ delegates->xml = glade_xml_new (CONNECTOR_GLADEDIR "/exchange-delegates.glade", NULL, NULL);
+ g_return_if_fail (delegates->xml != NULL);
+
+ delegates->dialog = glade_xml_get_widget (delegates->xml, "delegates");
+ g_return_if_fail (delegates->dialog != NULL);
+
+ g_signal_connect (delegates->dialog, "response",
+ G_CALLBACK (dialog_response), delegates);
+
+ e_dialog_set_transient_for (GTK_WINDOW (delegates->dialog), parent);
+ delegates->parent = parent;
+ g_object_weak_ref (G_OBJECT (parent), parent_destroyed, delegates);
+
+ /* Set up the buttons */
+ button = glade_xml_get_widget (delegates->xml, "add_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (add_button_clicked_cb), delegates);
+ button = glade_xml_get_widget (delegates->xml, "edit_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (edit_button_clicked_cb), delegates);
+ button = glade_xml_get_widget (delegates->xml, "remove_button");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (remove_button_clicked_cb), delegates);
+
+ /* Set up the table */
+ delegates->model = gtk_list_store_new (1, G_TYPE_STRING);
+ delegates->table = glade_xml_get_widget (delegates->xml, "delegates_table");
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Name"), gtk_cell_renderer_text_new (), "text", 0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (delegates->table),
+ column);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (delegates->table),
+ GTK_TREE_MODEL (delegates->model));
+
+ /* Get list of delegate users */
+ if (get_user_list (delegates)) {
+ for (i = 0; i < delegates->users->len; i++) {
+ user = delegates->users->pdata[i];
+
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, user->display_name,
+ -1);
+ }
+ g_signal_connect (delegates->table,
+ "button_press_event",
+ G_CALLBACK (table_click_cb), delegates);
+ } else {
+ button = glade_xml_get_widget (delegates->xml, "add_button");
+ gtk_widget_set_sensitive (button, FALSE);
+ button = glade_xml_get_widget (delegates->xml, "edit_button");
+ gtk_widget_set_sensitive (button, FALSE);
+ button = glade_xml_get_widget (delegates->xml, "remove_button");
+ gtk_widget_set_sensitive (button, FALSE);
+
+ gtk_list_store_append (delegates->model, &iter);
+ gtk_list_store_set (delegates->model, &iter,
+ 0, _("Error reading delegates list."),
+ -1);
+ }
+
+ gtk_widget_show (delegates->dialog);
+}
diff --git a/plugins/exchange-account-setup/exchange-delegates.glade b/plugins/exchange-account-setup/exchange-delegates.glade
new file mode 100644
index 0000000000..4b2e0e7695
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-delegates.glade
@@ -0,0 +1,628 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="delegates">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Delegates</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">These users will be able to send mail on your behalf
+and access your folders with the permissions you give them.</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="delegate_hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkTreeView" id="delegates_table">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="add_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="edit_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="remove_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="delegate_permissions">
+ <property name="title" translatable="yes">Delegate Permissions</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="delegate_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Permissions for</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="folders_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="calendar_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">C_alendar:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">calendar_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="task_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Tasks:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">task_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="inbox_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Inbox:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">inbox_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="contact_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_ntacts:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">contact_perms</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="calendar_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget1">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="task_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget6">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="inbox_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget11">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget12">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget14">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="contact_perms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget16">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget17">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget18">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reviewer (read-only)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget19">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author (read, create)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget20">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Editor (read, create, edit)</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="see_private_checkbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Delegate can see private items</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/plugins/exchange-account-setup/exchange-delegates.h b/plugins/exchange-account-setup/exchange-delegates.h
new file mode 100644
index 0000000000..be8f4f89e3
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-delegates.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_DELEGATES_H__
+#define __EXCHANGE_DELEGATES_H__
+
+#include <exchange-types.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+void exchange_delegates (ExchangeAccount *account, GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_DELEGATES_CONTROL_H__ */
diff --git a/plugins/exchange-account-setup/exchange-folder-size.c b/plugins/exchange-account-setup/exchange-folder-size.c
new file mode 100644
index 0000000000..694155f54d
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-folder-size.c
@@ -0,0 +1,343 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Copyright (C) 2002-2004 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* ExchangeFolderSize: Display the folder tree with the folder sizes */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <e-util/e-dialog-utils.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+
+#include "exchange-hierarchy-webdav.h"
+#include "e-folder-exchange.h"
+#include "exchange-folder-size.h"
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class = NULL;
+
+typedef struct {
+ char *folder_name;
+ gdouble folder_size;
+} folder_info;
+
+
+struct _ExchangeFolderSizePrivate {
+
+ GHashTable *table;
+ GtkListStore *model;
+ GHashTable *row_refs;
+};
+
+enum {
+ COLUMN_NAME,
+ COLUMN_SIZE,
+ NUM_COLUMNS
+};
+
+static gboolean
+free_fsize_table (gpointer key, gpointer value, gpointer data)
+{
+ folder_info *f_info = (folder_info *) value;
+
+ g_free (key);
+ g_free (f_info->folder_name);
+ g_free (f_info);
+ return TRUE;
+}
+
+static gboolean
+free_row_refs (gpointer key, gpointer value, gpointer user_data)
+{
+ g_free (key);
+ gtk_tree_row_reference_free (value);
+ return TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+ ExchangeFolderSize *fsize = EXCHANGE_FOLDER_SIZE (object);
+
+ g_hash_table_foreach_remove (fsize->priv->table, free_fsize_table, NULL);
+ g_hash_table_destroy (fsize->priv->table);
+ g_hash_table_foreach_remove (fsize->priv->row_refs, free_row_refs, NULL);
+ g_hash_table_destroy (fsize->priv->row_refs);
+ if (fsize->priv->model)
+ g_object_unref (fsize->priv->model);
+ g_free (fsize->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* override virtual methods */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+}
+
+static void
+init (GObject *object)
+{
+ ExchangeFolderSize *fsize = EXCHANGE_FOLDER_SIZE (object);
+
+ fsize->priv = g_new0 (ExchangeFolderSizePrivate, 1);
+ fsize->priv->table = g_hash_table_new (g_str_hash, g_str_equal);
+ fsize->priv->model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_DOUBLE);
+ fsize->priv->row_refs = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+E2K_MAKE_TYPE (exchange_folder_size, ExchangeFolderSize, class_init, init, PARENT_TYPE)
+
+/**
+ * exchange_folder_size_new:
+ * @display_name: the delegate's (UTF8) display name
+ *
+ * Return value: a foldersize object with the table initialized
+ **/
+ExchangeFolderSize *
+exchange_folder_size_new (void)
+{
+ ExchangeFolderSize *fsize;
+
+ fsize = g_object_new (EXCHANGE_TYPE_FOLDER_SIZE, NULL);
+
+ return fsize;
+}
+
+void
+exchange_folder_size_update (ExchangeFolderSize *fsize,
+ const char *folder_name,
+ gdouble folder_size)
+{
+ folder_info *f_info, *cached_info;
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ GtkTreeRowReference *row;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize));
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ if (cached_info->folder_size == folder_size) {
+ return;
+ } else {
+ cached_info->folder_size = folder_size;
+ row = g_hash_table_lookup (priv->row_refs, folder_name);
+ path = gtk_tree_row_reference_get_path (row);
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fsize->priv->model), &iter, path)) {
+ gtk_list_store_set (fsize->priv->model, &iter,
+ COLUMN_NAME, cached_info->folder_name,
+ COLUMN_SIZE, cached_info->folder_size,
+ -1);
+ }
+ gtk_tree_path_free (path);
+ return;
+ }
+ } else {
+ f_info = g_new0(folder_info, 1);
+ f_info->folder_name = g_strdup (folder_name);
+ f_info->folder_size = folder_size;
+ g_hash_table_insert (folder_size_table, f_info->folder_name, f_info);
+
+ gtk_list_store_append (fsize->priv->model, &iter);
+ gtk_list_store_set (fsize->priv->model, &iter,
+ COLUMN_NAME, f_info->folder_name,
+ COLUMN_SIZE, f_info->folder_size,
+ -1);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsize->priv->model), &iter);
+ row = gtk_tree_row_reference_new (GTK_TREE_MODEL (fsize->priv->model), path);
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (fsize->priv->row_refs, g_strdup (folder_name), row);
+ }
+}
+
+void
+exchange_folder_size_remove (ExchangeFolderSize *fsize,
+ const char *folder_name)
+{
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ folder_info *cached_info;
+ GtkTreeRowReference *row;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize));
+ g_return_if_fail (folder_name != NULL);
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ row = g_hash_table_lookup (priv->row_refs, folder_name);
+ path = gtk_tree_row_reference_get_path (row);
+ g_hash_table_remove (folder_size_table, folder_name);
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fsize->priv->model), &iter, path)) {
+ gtk_list_store_remove (fsize->priv->model, &iter);
+ }
+ g_hash_table_remove (priv->row_refs, row);
+ gtk_tree_path_free (path);
+ }
+}
+
+gdouble
+exchange_folder_size_get (ExchangeFolderSize *fsize,
+ const char *folder_name)
+{
+ ExchangeFolderSizePrivate *priv;
+ GHashTable *folder_size_table;
+ folder_info *cached_info;
+
+ g_return_val_if_fail (EXCHANGE_IS_FOLDER_SIZE (fsize), -1);
+
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ cached_info = g_hash_table_lookup (folder_size_table, folder_name);
+ if (cached_info) {
+ return cached_info->folder_size;
+ }
+ return -1;
+}
+
+static void
+format_size_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkCellRendererText *cell = (GtkCellRendererText *)renderer;
+ gdouble folder_size;
+ char * new_text;
+
+ gtk_tree_model_get(model, iter, COLUMN_SIZE, &folder_size, -1);
+
+ if (folder_size)
+ new_text = g_strdup_printf ("%.2f", folder_size);
+ else
+ new_text = g_strdup ("0");
+
+ g_object_set (cell, "text", new_text, NULL);
+ g_free (new_text);
+}
+
+static void
+parent_destroyed (gpointer dialog, GObject *ex_parent)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+void
+exchange_folder_size_display (EFolder *folder, GtkWidget *parent)
+{
+ ExchangeFolderSizePrivate *priv;
+ ExchangeFolderSize *fsize;
+ ExchangeHierarchy *hier;
+ GtkTreeViewColumn *column;
+ GtkTreeSortable *sortable;
+ GtkCellRenderer *cell;
+ GHashTable *folder_size_table;
+ GladeXML *xml;
+ GtkWidget *dialog, *table;
+ GList *l;
+ char *col_name;
+ int response;
+
+ g_return_if_fail (GTK_IS_WIDGET (parent));
+
+ hier = e_folder_exchange_get_hierarchy (folder);
+ if (!hier)
+ return;
+ /* FIXME: This should be a more generic query and not just
+ specifically for webdav */
+ fsize = exchange_hierarchy_webdav_get_folder_size (EXCHANGE_HIERARCHY_WEBDAV (hier));
+ if (!fsize)
+ return;
+ priv = fsize->priv;
+ folder_size_table = priv->table;
+
+ if (!g_hash_table_size (folder_size_table))
+ return;
+
+ xml = glade_xml_new (CONNECTOR_GLADEDIR "/exchange-folder-tree.glade", NULL, NULL);
+ g_return_if_fail (xml != NULL);
+ dialog = glade_xml_get_widget (xml, "folder_tree");
+ table = glade_xml_get_widget (xml, "folder_treeview");
+
+ e_dialog_set_transient_for (GTK_WINDOW (dialog), parent);
+ /* fsize->parent = parent; */
+ g_object_weak_ref (G_OBJECT (parent), parent_destroyed, dialog);
+
+ /* Set up the table */
+ sortable = GTK_TREE_SORTABLE (priv->model);
+ gtk_tree_sortable_set_sort_column_id (sortable, COLUMN_SIZE, GTK_SORT_DESCENDING);
+
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Folder Name"), gtk_cell_renderer_text_new (), "text", COLUMN_NAME, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (table),
+ column);
+
+ col_name = g_strdup_printf ("%s (KB)", _("Folder Size"));
+ column = gtk_tree_view_column_new_with_attributes (
+ col_name, gtk_cell_renderer_text_new (), "text", COLUMN_SIZE, NULL);
+ g_free (col_name);
+
+ l = gtk_tree_view_column_get_cell_renderers (column);
+ cell = (GtkCellRenderer *)l->data;
+ gtk_tree_view_column_set_cell_data_func (column, cell, format_size_func, NULL, NULL );
+ g_list_free (l);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (table),
+ column);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (table),
+ GTK_TREE_MODEL (priv->model));
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ g_object_unref (xml);
+}
diff --git a/plugins/exchange-account-setup/exchange-folder-size.h b/plugins/exchange-account-setup/exchange-folder-size.h
new file mode 100644
index 0000000000..2d673902a1
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-folder-size.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* Copyright (C) 2002-2004 Novell, Inc. */
+
+#ifndef __EXCHANGE_FOLDER_SIZE_H__
+#define __EXCHANGE_FOLDER_SIZE_H__
+
+#include <exchange-types.h>
+#include <e2k-security-descriptor.h>
+#include <e-folder.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EXCHANGE_TYPE_FOLDER_SIZE (exchange_folder_size_get_type ())
+#define EXCHANGE_FOLDER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXCHANGE_TYPE_FOLDER_SIZE, ExchangeFolderSize))
+#define EXCHANGE_FOLDER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXCHANGE_TYPE_FOLDER_SIZE, ExchangeFolderSizeClass))
+#define EXCHANGE_IS_FOLDER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXCHANGE_TYPE_FOLDER_SIZE))
+#define EXCHANGE_IS_FOLDER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EXCHANGE_TYPE_FOLDER_SIZE))
+
+
+typedef struct _ExchangeFolderSize ExchangeFolderSize;
+typedef struct _ExchangeFolderSizePrivate ExchangeFolderSizePrivate;
+typedef struct _ExchangeFolderSizeClass ExchangeFolderSizeClass;
+
+struct _ExchangeFolderSize {
+ GObject parent;
+
+ ExchangeFolderSizePrivate *priv;
+};
+
+struct _ExchangeFolderSizeClass {
+ GObjectClass parent_class;
+
+};
+
+GType exchange_folder_size_get_type (void);
+
+ExchangeFolderSize *exchange_folder_size_new (void);
+
+void exchange_folder_size_update (ExchangeFolderSize *fsize,
+ const char *folder_name,
+ gdouble folder_size);
+void exchange_folder_size_remove (ExchangeFolderSize *fsize, const char *folder_name);
+
+gdouble exchange_folder_size_get (ExchangeFolderSize *fsize, const char *folder_name);
+
+void exchange_folder_size_display (EFolder *folder, GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EXCHANGE_FOLDER_SIZE_H__ */
diff --git a/plugins/exchange-account-setup/exchange-folder-tree.glade b/plugins/exchange-account-setup/exchange-folder-tree.glade
new file mode 100644
index 0000000000..0505681a7b
--- /dev/null
+++ b/plugins/exchange-account-setup/exchange-folder-tree.glade
@@ -0,0 +1,92 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="folder_tree">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Exchange Folder Tree</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">250</property>
+ <property name="default_height">300</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="folder_tree_hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="folder_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>