diff options
Diffstat (limited to 'plugins/exchange-account-setup/exchange-delegates.c')
-rw-r--r-- | plugins/exchange-account-setup/exchange-delegates.c | 972 |
1 files changed, 972 insertions, 0 deletions
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); +} |