/* -*- 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* ExchangeDelegates: Exchange delegate handling. * * FIXME: make this instant-apply */ #ifdef HAVE_CONFIG_H #include #endif #include #include "exchange-delegates.h" #include "exchange-delegates-user.h" #include "exchange-user-dialog.h" #include "exchange-operations.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { const char *uri; E2kSecurityDescriptor *sd; gboolean changed; } ExchangeDelegatesFolder; typedef struct { ExchangeAccount *account; char *self_dn; GladeXML *xml; GtkWidget *dialog, *parent; GtkListStore *model; GtkWidget *table; GByteArray *creator_entryid; GPtrArray *users, *added_users, *removed_users; gboolean loaded_folders; ExchangeDelegatesFolder folder[EXCHANGE_DELEGATES_LAST]; ExchangeDelegatesFolder freebusy_folder; } ExchangeDelegates; extern const char *exchange_delegates_user_folder_names[]; const char *exchange_localfreebusy_path = "NON_IPM_SUBTREE/Freebusy%20Data/LocalFreebusy.EML"; static void set_perms_for_user (ExchangeDelegatesUser *user, gpointer user_data); static void set_sd_for_href (ExchangeDelegates *delegates, const char *href, E2kSecurityDescriptor *sd) { int i; for (i = 0; i < EXCHANGE_DELEGATES_LAST; i++) { if (!delegates->folder[i].uri) continue; if (!strcmp (href, delegates->folder[i].uri)) { delegates->folder[i].sd = sd; return; } } /* else, it's the freebusy folder */ delegates->freebusy_folder.uri = g_strdup (href); delegates->freebusy_folder.sd = sd; } /* Given an array of ExchangeDelegatesUser containing display names * and entryids, and an array of E2kSecurityDescriptors containing * SIDs (which contain display names), add SIDs to the delegates. In * the easy case, we can just match the SIDs up with their * corresponding user by display name. However, there are two things * that can go wrong: * * 1. Some users may have been removed from the SDs * 2. Two users may have the same display name * * In both cases, we fall back to using the GC. */ static gboolean fill_in_sids (ExchangeDelegates *delegates) { int u, u2, sd, needed_sids; ExchangeDelegatesUser *user, *user2; GList *sids, *s; E2kSid *sid; E2kGlobalCatalog *gc; E2kGlobalCatalogStatus status; E2kGlobalCatalogEntry *entry; gboolean ok = TRUE; needed_sids = 0; /* Mark users with duplicate names and count the number of * non-duplicate names. */ for (u = 0; u < delegates->users->len; u++) { user = delegates->users->pdata[u]; if (user->sid == (E2kSid *)-1) continue; for (u2 = u + 1; u2 < delegates->users->len; u2++) { user2 = delegates->users->pdata[u2]; if (!strcmp (user->display_name, user2->display_name)) user->sid = user2->sid = (E2kSid *)-1; } if (!user->sid) needed_sids++; } /* Scan security descriptors trying to match SIDs until we're * not expecting to find any more. */ for (sd = 0; sd < EXCHANGE_DELEGATES_LAST && needed_sids; sd++) { sids = e2k_security_descriptor_get_sids (delegates->folder[sd].sd); for (s = sids; s && needed_sids; s = s->next) { sid = s->data; for (u = 0; u < delegates->users->len; u++) { user = delegates->users->pdata[u]; if (user->sid) continue; if (!strcmp (user->display_name, e2k_sid_get_display_name (sid))) { user->sid = sid; g_object_ref (sid); needed_sids--; } } } g_list_free (sids); } /* Now for each user whose SID hasn't yet been found, look it up. */ gc = exchange_account_get_global_catalog (delegates->account); for (u = 0; u < delegates->users->len; u++) { user = delegates->users->pdata[u]; if (user->sid && user->sid != (E2kSid *)-1) continue; status = e2k_global_catalog_lookup ( gc, NULL, /* FIXME: cancellable */ E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN, e2k_entryid_to_dn (user->entryid), E2K_GLOBAL_CATALOG_LOOKUP_SID, &entry); if (status != E2K_GLOBAL_CATALOG_OK) { user->sid = NULL; ok = FALSE; continue; } user->sid = entry->sid; g_object_ref (user->sid); e2k_global_catalog_entry_free (gc, entry); } return ok; } static const char *sd_props[] = { E2K_PR_EXCHANGE_SD_BINARY, E2K_PR_EXCHANGE_SD_XML }; static const int n_sd_props = sizeof (sd_props) / sizeof (sd_props[0]); /* Read the folder security descriptors and match them up with the * list of delegates. */ static gboolean get_folder_security (ExchangeDelegates *delegates) { GPtrArray *hrefs; E2kContext *ctx; E2kHTTPStatus status; E2kResultIter *iter; E2kResult *result; xmlNode *xml_form; GByteArray *binary_form; ExchangeDelegatesUser *user; guint32 perms; int i, u; /* If we've been here before, just return the success or * failure result from last time. */ if (delegates->freebusy_folder.uri) return delegates->loaded_folders; if (!exchange_account_get_global_catalog (delegates->account)) { e_error_run (GTK_WINDOW (delegates->table), ERROR_DOMAIN ":delegates-no-gcs-error", NULL); 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_error_run (GTK_WINDOW (delegates->table), ERROR_DOMAIN ":delegates-perm-read-error", NULL); return FALSE; } if (!fill_in_sids (delegates)) { delegates->loaded_folders = FALSE; e_error_run (GTK_WINDOW (delegates->table), ERROR_DOMAIN ":perm-deter-error", NULL); 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_error_run (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-error", email, NULL); 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_error_run (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-own-error", NULL); 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_error_run (GTK_WINDOW (parent_window), ERROR_DOMAIN ":delegate-existing", user->display_name, NULL); g_object_unref (user); exchange_delegates_user_edit (delegates->account, match, parent_window); return; } } if (!exchange_delegates_user_edit (delegates->account, 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->account, delegates->users->pdata[row], parent_window); } const char * email_look_up (const char *delegate_legacy, ExchangeAccount *account) { E2kGlobalCatalog *gc; E2kGlobalCatalogEntry *entry; E2kGlobalCatalogStatus status; const char *email_id; gc = exchange_account_get_global_catalog (account); if (!gc) return NULL; status = e2k_global_catalog_lookup ( gc, NULL, E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN, delegate_legacy, 0, &entry); email_id = g_strdup (entry->email); e2k_global_catalog_entry_free (gc, entry); return email_id; } 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->account, 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; E2kGlobalCatalogStatus 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) { E2kGlobalCatalogEntry *entry; 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_error_run (GTK_WINDOW (delegates->table), ERROR_DOMAIN ":delegate-fail-error", error, NULL); 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); }