diff options
Diffstat (limited to 'sediff')
32 files changed, 11605 insertions, 0 deletions
diff --git a/sediff/Makefile.am b/sediff/Makefile.am new file mode 100644 index 0000000..3f53cd3 --- /dev/null +++ b/sediff/Makefile.am @@ -0,0 +1,54 @@ +setoolsdir = @setoolsdir@ + +dist_setools_DATA = sediff_help.txt sediffx.glade \ + sediffx.png sediffx-small.png + +if BUILD_GUI + MAYBE_SEDIFFX = sediffx +endif + +bin_PROGRAMS = sediff $(MAYBE_SEDIFFX) + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ @POLDIFF_CFLAGS@ +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +LDADD = @SELINUX_LIB_FLAG@ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ + +sediff_CFLAGS = $(AM_CFLAGS) +sediffx_CFLAGS = $(AM_CFLAGS) \ + @GTK_CFLAGS@ @PIXBUF_CFLAGS@ @GLADE_CFLAGS@ @GTHREAD_CFLAGS@ + +# need the -rdynamic flag below - glade uses dlopen() upon sediffx callbacks +sediffx_LDFLAGS = $(AM_LDFLAGS) \ + @GTK_LIBS@ @PIXBUF_LIBS@ @GLADE_LIBS@ @GTHREAD_LIBS@ @XML_LIBS@ \ + -rdynamic + +DEPENDENCIES = $(top_builddir)/libpoldiff/src/libpoldiff.so \ + $(top_builddir)/libapol/src/libapol.so \ + $(top_builddir)/libqpol/src/libqpol.so + +sediff_SOURCES = sediff.c + +sediffx_SOURCES = \ + find_dialog.c find_dialog.h \ + open_policies_dialog.c open_policies_dialog.h \ + policy_view.c policy_view.h \ + progress.c progress.h \ + remap_types_dialog.c remap_types_dialog.h \ + result_item.c result_item.h \ + result_item_render.c result_item_render.h \ + results.c results.h \ + select_diff_dialog.c select_diff_dialog.h \ + toplevel.c toplevel.h \ + utilgui.c utilgui.h \ + sediffx.c sediffx.h + +$(top_builddir)/libpoldiff/src/libpoldiff.so: + $(MAKE) -C $(top_builddir)/libpoldiff/src $(notdir $@) + +$(top_builddir)/libapol/src/libapol.so: + $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@) + +$(top_builddir)/libqpol/src/libqpol.so: + $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@) diff --git a/sediff/find_dialog.c b/sediff/find_dialog.c new file mode 100644 index 0000000..7bc3c07 --- /dev/null +++ b/sediff/find_dialog.c @@ -0,0 +1,184 @@ +/** + * @file + * Display a dialog to let the user search through results. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "find_dialog.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <gdk/gdkkeysyms.h> +#include <glade/glade.h> + +struct find_dialog +{ + toplevel_t *top; + GladeXML *xml; + /** offset to start searching from if searching up and the + * start of the offset to select from */ + gint start_offset; + /** the offset to start searching from if searching down and + * the offset to end selecting from */ + gint end_offset; + /** the main window */ + GtkDialog *w; + GtkEntry *entry; + GtkRadioButton *forward, *reverse; +}; + +static void find_dialog_search(find_dialog_t * f) +{ + GtkTextView *view = toplevel_get_text_view(f->top); + GtkTextBuffer *tb = gtk_text_view_get_buffer(view); + GtkTextMark *mark = gtk_text_buffer_get_insert(tb); + GtkTextIter iter, start, end; + const gchar *search_text = gtk_entry_get_text(f->entry); + gboolean text_found; + /* if nothing is selected then start the search from the + * cursor. otherwise, if searching forward then start search + * at the end of the selection, else set start to beginning of + * selection */ + if (!gtk_text_buffer_get_selection_bounds(tb, &start, &end)) { + gtk_text_buffer_get_iter_at_mark(tb, &iter, mark); + } else { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) { + iter = end; + } else { + iter = start; + } + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) { + text_found = gtk_text_iter_forward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL); + if (!text_found) { + /* wrap search */ + gtk_text_buffer_get_start_iter(tb, &iter); + text_found = + gtk_text_iter_forward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL); + } + } else { + text_found = gtk_text_iter_backward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL); + if (!text_found) { + /* wrap search */ + gtk_text_buffer_get_end_iter(tb, &iter); + text_found = + gtk_text_iter_backward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL); + } + } + if (!text_found) { + GString *string = g_string_new(""); + g_string_printf(string, "Text \"%s\" not found.", search_text); + util_message(GTK_WINDOW(f->w), GTK_MESSAGE_INFO, string->str); + g_string_free(string, TRUE); + } else { + gtk_text_view_scroll_to_iter(view, &start, 0.0, FALSE, 0.0, 0.5); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) { + gtk_text_buffer_select_range(tb, &end, &start); + } else { + gtk_text_buffer_select_range(tb, &start, &end); + } + } +} + +static void find_dialog_on_response(GtkDialog * dialog __attribute__ ((unused)), gint response, gpointer user_data) +{ + find_dialog_t *f = (find_dialog_t *) user_data; + if (response == GTK_RESPONSE_CLOSE) { + gtk_widget_hide(GTK_WIDGET(f->w)); + } else if (response == GTK_RESPONSE_OK) { + find_dialog_search(f); + } +} + +static gboolean find_dialog_on_key_press_event(GtkWidget * widget __attribute__ ((unused)), GdkEventKey * event, gpointer user_data) +{ + find_dialog_t *f = (find_dialog_t *) user_data; + if (event->keyval == GDK_Escape) { + gtk_widget_hide(GTK_WIDGET(f->w)); + return TRUE; + } + return FALSE; +} + +static void find_dialog_on_entry_activate(GtkEntry * entry __attribute__ ((unused)), gpointer user_data) +{ + find_dialog_t *f = (find_dialog_t *) user_data; + find_dialog_search(f); +} + +find_dialog_t *find_dialog_create(toplevel_t * top) +{ + find_dialog_t *f; + int error = 0; + + if ((f = calloc(1, sizeof(*f))) == NULL) { + error = errno; + goto cleanup; + } + f->top = top; + f->xml = glade_xml_new(toplevel_get_glade_xml(f->top), "find_dialog", NULL); + + f->w = GTK_DIALOG(glade_xml_get_widget(f->xml, "find_dialog")); + assert(f->w != NULL); + gtk_window_set_transient_for(GTK_WINDOW(f->w), toplevel_get_window(f->top)); + g_signal_connect_swapped(G_OBJECT(f->w), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), f->w); + g_signal_connect(G_OBJECT(f->w), "response", G_CALLBACK(find_dialog_on_response), f); + g_signal_connect(G_OBJECT(f->w), "key-press-event", G_CALLBACK(find_dialog_on_key_press_event), f); + + f->entry = GTK_ENTRY(glade_xml_get_widget(f->xml, "find entry")); + f->forward = GTK_RADIO_BUTTON(glade_xml_get_widget(f->xml, "find forward radio")); + f->reverse = GTK_RADIO_BUTTON(glade_xml_get_widget(f->xml, "find reverse radio")); + assert(f->entry != NULL && f->forward != NULL && f->reverse != NULL); + + /* connect the text entry callback events */ + g_signal_connect(G_OBJECT(f->entry), "activate", G_CALLBACK(find_dialog_on_entry_activate), f); + + cleanup: + if (error != 0) { + find_dialog_destroy(&f); + errno = error; + return NULL; + } + return f; +} + +void find_dialog_destroy(find_dialog_t ** f) +{ + if (f != NULL && *f != NULL) { + free(*f); + *f = NULL; + } +} + +void find_dialog_show(find_dialog_t * f) +{ + gtk_widget_show(GTK_WIDGET(f->w)); + gtk_widget_grab_focus(GTK_WIDGET(f->entry)); + gtk_editable_set_position(GTK_EDITABLE(f->entry), -1); + gtk_editable_select_region(GTK_EDITABLE(f->entry), 0, -1); + gtk_window_present(GTK_WINDOW(f->w)); +} diff --git a/sediff/find_dialog.h b/sediff/find_dialog.h new file mode 100644 index 0000000..a6628b6 --- /dev/null +++ b/sediff/find_dialog.h @@ -0,0 +1,63 @@ +/** + * @file + * Headers for displaying a find dialog. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FIND_DIALOG_H +#define FIND_DIALOG_H + +typedef struct find_dialog find_dialog_t; + +#include "toplevel.h" + +/** + * Create a find dialog object. The dialog will float above the rest + * of sediffx; it allows the user to search for text either forwards + * or backwards in the currently visible text buffer. + * + * @param top Toplevel object that will control the newly opened find + * dialog. + * + * @return An initialized find dialog object, or NULL upon error. The + * caller must call find_dialog_destroy() upon the returned value. + */ +find_dialog_t *find_dialog_create(toplevel_t * top); + +/** + * Destroy the find_dialog object. This does nothing if the pointer + * is set to NULL. + * + * @param f Reference to a find dialog object. Afterwards the + * pointer will be set to NULL. + */ +void find_dialog_destroy(find_dialog_t ** f); + +/** + * (Re)show the find dialog. + * + * @param f Find dialog to show. + */ +void find_dialog_show(find_dialog_t * f); + +#endif diff --git a/sediff/open_policies_dialog.c b/sediff/open_policies_dialog.c new file mode 100644 index 0000000..482b95f --- /dev/null +++ b/sediff/open_policies_dialog.c @@ -0,0 +1,527 @@ +/** + * @file + * Run the dialog to allow the user to open a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "open_policies_dialog.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <apol/util.h> +#include <glade/glade.h> + +struct open_policy_pane +{ + struct open_policy *op; + GtkRadioButton *monolithic_radio, *modular_radio; + GtkLabel *main_label; + + GtkHBox *bottom_hbox; + GtkListStore *module_store; + + GtkEntry *base_entry; + GtkButton *base_browse_button; + + GtkTreeView *module_view; + GtkButton *add_button, *remove_button, *import_button, *export_button; + char *last_module_path; +}; + +struct open_policy +{ + GladeXML *xml; + toplevel_t *top; + GtkDialog *dialog; + GtkButton *ok_button, *rundiff_button; + struct open_policy_pane pane[SEDIFFX_POLICY_NUM]; +}; + +enum module_columns +{ + PATH_COLUMN = 0, NAME_COLUMN, VERSION_COLUMN, NUM_COLUMNS +}; + +/** + * Sort columns in alphabetical order. + */ +static gint open_policy_sort(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data) +{ + GValue value_a = { 0 }, value_b = { + 0}; + const char *name_a, *name_b; + int retval, column_id = GPOINTER_TO_INT(user_data); + + gtk_tree_model_get_value(model, a, column_id, &value_a); + gtk_tree_model_get_value(model, b, column_id, &value_b); + name_a = g_value_get_string(&value_a); + name_b = g_value_get_string(&value_b); + retval = strcmp(name_a, name_b); + g_value_unset(&value_a); + g_value_unset(&value_b); + return retval; +} + +static void open_policy_init_pane(struct open_policy *op, sediffx_policy_e which, const char *suffix) +{ + struct open_policy_pane *pane = op->pane + which; + GString *s = g_string_new(NULL); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + g_string_printf(s, "monolithic radio%s", suffix); + pane->monolithic_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "modular radio%s", suffix); + pane->modular_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "main filename label%s", suffix); + pane->main_label = GTK_LABEL(glade_xml_get_widget(op->xml, s->str)); + assert(pane->monolithic_radio != NULL && pane->modular_radio != NULL && pane->main_label != NULL); + + g_string_printf(s, "base entry%s", suffix); + pane->base_entry = GTK_ENTRY(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "base browse%s", suffix); + pane->base_browse_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); + assert(pane->base_entry != NULL && pane->base_browse_button != NULL); + + g_string_printf(s, "hbox.3%s", suffix); + pane->bottom_hbox = GTK_HBOX(glade_xml_get_widget(op->xml, s->str)); + assert(pane->bottom_hbox != NULL); + + g_string_printf(s, "module view%s", suffix); + pane->module_view = GTK_TREE_VIEW(glade_xml_get_widget(op->xml, s->str)); + assert(pane->module_view != NULL); + pane->module_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + gtk_tree_view_set_model(pane->module_view, GTK_TREE_MODEL(pane->module_store)); + + g_string_printf(s, "module add button%s", suffix); + pane->add_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "module remove button%s", suffix); + pane->remove_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "module list import button%s", suffix); + pane->import_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); + g_string_printf(s, "module list export button%s", suffix); + pane->export_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); + assert(pane->add_button != NULL && pane->remove_button != NULL && + pane->import_button != NULL && pane->export_button != NULL); + + g_string_free(s, TRUE); + + selection = gtk_tree_view_get_selection(pane->module_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + column = gtk_tree_view_column_new_with_attributes("Module", renderer, "text", NAME_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(pane->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), NAME_COLUMN, open_policy_sort, + (gpointer) NAME_COLUMN, NULL); + + column = gtk_tree_view_column_new_with_attributes("Version", renderer, "text", VERSION_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, VERSION_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(pane->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), VERSION_COLUMN, open_policy_sort, + (gpointer) VERSION_COLUMN, NULL); + + column = gtk_tree_view_column_new_with_attributes("Path", renderer, "text", PATH_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, PATH_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(pane->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), PATH_COLUMN, open_policy_sort, + (gpointer) PATH_COLUMN, NULL); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pane->module_store), NAME_COLUMN, GTK_SORT_ASCENDING); +} + +static void open_policy_init_widgets(struct open_policy *op) +{ + op->dialog = GTK_DIALOG(glade_xml_get_widget(op->xml, "PoliciesOpenWindow")); + assert(op->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(op->dialog), toplevel_get_window(op->top)); + op->ok_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "ok button")); + op->rundiff_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "rundiff button")); + assert(op->ok_button != NULL && op->rundiff_button != NULL); + + open_policy_init_pane(op, SEDIFFX_POLICY_ORIG, ""); + open_policy_init_pane(op, SEDIFFX_POLICY_MOD, " 1"); +} + +static void open_policy_on_policy_type_toggle(GtkToggleButton * widget, gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + /* clicking on the radio buttons emit two toggle signals, one for + * the original button and one for the new one. thus only need to + * handle half of all signals */ + if (!gtk_toggle_button_get_active(widget)) { + return; + } + char *prefix; + GString *s = g_string_new(NULL); + if (pane == &(pane->op->pane[SEDIFFX_POLICY_ORIG])) { + prefix = "Original"; + } else { + prefix = "Modified"; + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio))) { + gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), FALSE); + g_string_printf(s, "<b>%s Policy Filename:</b>", prefix); + gtk_label_set_markup(pane->main_label, s->str); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), TRUE); + g_string_printf(s, "<b>%s Base Filename:</b>", prefix); + gtk_label_set_markup(pane->main_label, s->str); + } + g_string_free(s, TRUE); +} + +static void open_policy_on_entry_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + gboolean sens_orig = FALSE, sens_mod = FALSE; + if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_ORIG].base_entry), "") != 0) { + sens_orig = TRUE; + } + if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_MOD].base_entry), "") != 0) { + sens_mod = TRUE; + } + gboolean sens = sens_orig && sens_mod; + gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_ORIG].export_button), sens_orig); + gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_MOD].export_button), sens_mod); + gtk_widget_set_sensitive(GTK_WIDGET(op->ok_button), sens); + gtk_widget_set_sensitive(GTK_WIDGET(op->rundiff_button), sens); +} + +static void open_policy_on_base_browse_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + const char *current_path = gtk_entry_get_text(pane->base_entry); + char *title; + apol_vector_t *paths; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio))) { + title = "Open Monolithic Policy"; + } else { + title = "Open Modular Policy"; + } + if (strcmp(current_path, "") == 0) { + current_path = NULL; + } + if ((paths = util_open_file(GTK_WINDOW(pane->op->dialog), title, current_path, 0)) == NULL) { + return; + } + gtk_entry_set_text(pane->base_entry, apol_vector_get_element(paths, 0)); + apol_vector_destroy(&paths); +} + +/** + * Attempt to load a module and retrieve its name and version. Upon + * success add an entry to the list store. + * + * @return 0 on success, < 0 on error. + */ +static int open_policy_load_module(struct open_policy *op, struct open_policy_pane *pane, const char *path) +{ + const char *module_name, *version_string; + int module_type; + qpol_module_t *module = NULL; + GtkTreeIter iter; + + /* check if modulue was already loaded */ + gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pane->module_store), &iter); + while (iter_valid) { + char *s; + gtk_tree_model_get(GTK_TREE_MODEL(pane->module_store), &iter, PATH_COLUMN, &s, -1); + if (strcmp(s, path) == 0) { + toplevel_ERR(op->top, "Module %s was already added.", path); + return -1; + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(pane->module_store), &iter); + } + if ((qpol_module_create_from_file(path, &module)) < 0) { + toplevel_ERR(op->top, "Error opening module %s: %s", path, strerror(errno)); + return -1; + } + if (qpol_module_get_name(module, &module_name) < 0 || + qpol_module_get_version(module, &version_string) < 0 || qpol_module_get_type(module, &module_type) < 0) { + toplevel_ERR(op->top, "Error reading module %s: %s", path, strerror(errno)); + qpol_module_destroy(&module); + return -1; + } + if (module_type != QPOL_MODULE_OTHER) { + toplevel_ERR(op->top, "%s is not a loadable module.", path); + qpol_module_destroy(&module); + return -1; + } + gtk_list_store_append(pane->module_store, &iter); + gtk_list_store_set(pane->module_store, &iter, PATH_COLUMN, path, NAME_COLUMN, module_name, VERSION_COLUMN, version_string, + -1); + qpol_module_destroy(&module); + return 0; +} + +static void open_policy_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + apol_vector_t *paths; + const char *path = NULL, *prev_path; + size_t i; + if ((prev_path = pane->last_module_path) == NULL) { + prev_path = gtk_entry_get_text(pane->base_entry); + if (strcmp(prev_path, "") == 0) { + prev_path = NULL; + } + } + paths = util_open_file(GTK_WINDOW(pane->op->dialog), "Open Module", prev_path, 1); + if (paths == NULL) { + return; + } + int all_succeed = 1; + for (i = 0; i < apol_vector_get_size(paths); i++) { + path = apol_vector_get_element(paths, i); + if (open_policy_load_module(pane->op, pane, path) < 0) { + all_succeed = 0; + } + } + if (all_succeed) { + assert(path != NULL); + free(pane->last_module_path); + pane->last_module_path = strdup(path); + } + apol_vector_destroy(&paths); +} + +static void open_policy_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view); + GtkTreeIter iter; + char *path; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + return; + } + gtk_tree_model_get(GTK_TREE_MODEL(pane->module_store), &iter, 0, &path, -1); + gtk_list_store_remove(pane->module_store, &iter); +} + +static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data); +static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data); + +static void open_policy_on_selection_change(GtkTreeSelection * selection, gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL); + gtk_widget_set_sensitive(GTK_WIDGET(pane->remove_button), sens); +} + +static void open_policy_init_signals(struct open_policy *op) +{ + sediffx_policy_e i; + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + struct open_policy_pane *pane = op->pane + i; + GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view); + g_signal_connect(pane->monolithic_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane); + g_signal_connect(pane->modular_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane); + g_signal_connect(pane->base_entry, "event-after", G_CALLBACK(open_policy_on_entry_event_after), op); + g_signal_connect(pane->base_browse_button, "clicked", G_CALLBACK(open_policy_on_base_browse_click), pane); + g_signal_connect(selection, "changed", G_CALLBACK(open_policy_on_selection_change), pane); + g_signal_connect(pane->add_button, "clicked", G_CALLBACK(open_policy_on_add_click), pane); + g_signal_connect(pane->remove_button, "clicked", G_CALLBACK(open_policy_on_remove_click), pane); + g_signal_connect(pane->import_button, "clicked", G_CALLBACK(open_policy_on_import_click), pane); + g_signal_connect(pane->export_button, "clicked", G_CALLBACK(open_policy_on_export_click), pane); + } +} + +static void open_policy_init_value(struct open_policy *op, const apol_policy_path_t * path, struct open_policy_pane *pane) +{ + apol_policy_path_type_e path_type = apol_policy_path_get_type(path); + const char *primary_path = apol_policy_path_get_primary(path); + gtk_entry_set_text(pane->base_entry, primary_path); + gtk_list_store_clear(pane->module_store); + if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio), TRUE); + } else if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t i; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pane->modular_radio), TRUE); + for (i = 0; i < apol_vector_get_size(modules); i++) { + char *module_path = apol_vector_get_element(modules, i); + if (open_policy_load_module(op, pane, module_path) < 0) { + break; + } + } + } else { + /* should never get here */ + toplevel_ERR(pane->op->top, "Unknown policy path type %d.", path_type); + } +} + +static void open_policy_init_values(struct open_policy *op, const apol_policy_path_t * orig_path, + const apol_policy_path_t * mod_path) +{ + if (orig_path != NULL) { + open_policy_init_value(op, orig_path, op->pane + SEDIFFX_POLICY_ORIG); + } + if (mod_path != NULL) { + open_policy_init_value(op, mod_path, op->pane + SEDIFFX_POLICY_MOD); + } +} + +/** + * Build the policy path corresponding to the user's inputs on this + * dialog. + * + * @return path for the dialog, or NULL upon error. The caller must + * call apol_policy_path_destroy() afterwards. + */ +static apol_policy_path_t *open_policy_build_path(struct open_policy_pane *pane) +{ + const char *primary_path = gtk_entry_get_text(pane->base_entry); + apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + apol_vector_t *modules = NULL; + apol_policy_path_t *path = NULL; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->modular_radio))) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + GtkTreeIter iter; + if ((modules = apol_vector_create(free)) == NULL) { + toplevel_ERR(pane->op->top, "%s", strerror(errno)); + return NULL; + } + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pane->module_store), &iter)) { + do { + GValue value = { 0 }; + char *module_path; + gtk_tree_model_get_value(GTK_TREE_MODEL(pane->module_store), &iter, PATH_COLUMN, &value); + module_path = g_value_dup_string(&value); + g_value_unset(&value); + if (apol_vector_append(modules, module_path) < 0) { + toplevel_ERR(pane->op->top, "%s", strerror(errno)); + free(module_path); + apol_vector_destroy(&modules); + return NULL; + } + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pane->module_store), &iter)); + } + } + path = apol_policy_path_create(path_type, primary_path, modules); + apol_vector_destroy(&modules); + if (path == NULL) { + toplevel_ERR(pane->op->top, "%s", strerror(errno)); + return NULL; + } + return path; +} + +static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + apol_vector_t *paths = NULL; + apol_policy_path_t *ppath = NULL; + const char *path = NULL, *prev_path; + if ((prev_path = pane->last_module_path) == NULL) { + prev_path = gtk_entry_get_text(pane->base_entry); + if (strcmp(prev_path, "") == 0) { + prev_path = NULL; + } + } + paths = util_open_file(GTK_WINDOW(pane->op->dialog), "Import Policy List", prev_path, 1); + if (paths == NULL) { + return; + } + path = apol_vector_get_element(paths, 0); + ppath = apol_policy_path_create_from_file(path); + if (ppath == NULL) { + toplevel_ERR(pane->op->top, "Error importing policy list %s: %s", path, strerror(errno)); + goto cleanup; + } + open_policy_init_value(pane->op, ppath, pane); + free(pane->last_module_path); + pane->last_module_path = strdup(path); + cleanup: + apol_vector_destroy(&paths); + apol_policy_path_destroy(&ppath); +} + +static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy_pane *pane = (struct open_policy_pane *)user_data; + char *path = util_save_file(GTK_WINDOW(pane->op->dialog), "Export Policy List", NULL); + apol_policy_path_t *ppath = NULL; + if (path == NULL) { + return; + } + ppath = open_policy_build_path(pane); + if (ppath == NULL) { + goto cleanup; + } + if (apol_policy_path_to_file(ppath, path) < 0) { + toplevel_ERR(pane->op->top, "Error exporting policy list %s: %s", path, strerror(errno)); + } + cleanup: + g_free(path); + apol_policy_path_destroy(&ppath); +} + +void open_policies_dialog_run(toplevel_t * top, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path) +{ + struct open_policy op; + gint response; + apol_policy_path_t *input[SEDIFFX_POLICY_NUM]; + + memset(&op, 0, sizeof(op)); + op.top = top; + op.xml = glade_xml_new(toplevel_get_glade_xml(top), "PoliciesOpenWindow", NULL); + op.pane[SEDIFFX_POLICY_ORIG].op = &op; + op.pane[SEDIFFX_POLICY_MOD].op = &op; + + open_policy_init_widgets(&op); + open_policy_init_signals(&op); + open_policy_init_values(&op, orig_path, mod_path); + open_policy_on_entry_event_after(NULL, NULL, &op); + + while (1) { + response = gtk_dialog_run(op.dialog); + if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) { + break; + } + if ((input[SEDIFFX_POLICY_ORIG] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_ORIG])) == NULL || + (input[SEDIFFX_POLICY_MOD] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_MOD])) == NULL) { + continue; + } + if (toplevel_open_policies(op.top, input[SEDIFFX_POLICY_ORIG], input[SEDIFFX_POLICY_MOD]) == 0) { + break; + } + } + gtk_widget_destroy(GTK_WIDGET(op.dialog)); + free(op.pane[SEDIFFX_POLICY_ORIG].last_module_path); + free(op.pane[SEDIFFX_POLICY_MOD].last_module_path); + g_object_unref(op.pane[SEDIFFX_POLICY_ORIG].module_store); + g_object_unref(op.pane[SEDIFFX_POLICY_MOD].module_store); + + if (response == 0) { + /* Run Diff button was clicked */ + toplevel_run_diff(op.top); + } +} diff --git a/sediff/open_policies_dialog.h b/sediff/open_policies_dialog.h new file mode 100644 index 0000000..c00291a --- /dev/null +++ b/sediff/open_policies_dialog.h @@ -0,0 +1,45 @@ +/** + * @file + * Dialog that allows the user to select two policies, each either a + * monolithic policy or a base policy + list of modules. The dialog + * then attempts to open those policies. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPEN_POLICIES_DIALOG_H +#define OPEN_POLICIES_DIALOG_H + +#include "toplevel.h" +#include <apol/policy-path.h> + +/** + * Display and run a dialog that allows the user open two policies, + * each either a monolithic or a modular policy. + * + * @param top Toplevel for the application. + * @param orig_path If not NULL, the default path for the original + * policy. + * @param mod_path If not NULL, the default path for the modified + * policy. + */ +void open_policies_dialog_run(toplevel_t * top, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path); + +#endif diff --git a/sediff/policy_view.c b/sediff/policy_view.c new file mode 100644 index 0000000..bc733a7 --- /dev/null +++ b/sediff/policy_view.c @@ -0,0 +1,391 @@ +/** + * @file + * Routines are responsible calculating a policy's statistics and + * displaying its source. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "policy_view.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <glade/glade.h> + +struct policy_view +{ + toplevel_t *top; + sediffx_policy_e which; + GladeXML *xml; + GtkTextBuffer *stats, *source; + GtkTextView *stats_view, *source_view; + GtkNotebook *notebook; + GtkLabel *line_number; + void *mmap_start; + size_t mmap_length; +}; + +static const char *policy_view_widget_names[SEDIFFX_POLICY_NUM][3] = { + {"toplevel policy_orig stats text", "toplevel policy_orig source text", + "toplevel policy_orig notebook"}, + {"toplevel policy_mod stats text", "toplevel policy_mod source text", + "toplevel policy_mod notebook"} +}; + +/** + * As the user moves the cursor within the source policy text view + * update the bottom label with the line number. Once that view is + * hidden, by flipping to a different tab, then clear the label. + */ +static void policy_view_notebook_on_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused)), gpointer user_data) +{ + policy_view_t *view = (policy_view_t *) user_data; + guint main_pagenum, view_pagenum; + GtkTextMark *mark = NULL; + GtkTextIter iter; + + main_pagenum = toplevel_get_notebook_page(view->top); + view_pagenum = gtk_notebook_get_current_page(view->notebook); + if (main_pagenum == 1 + view->which && view_pagenum == 1) { + mark = gtk_text_buffer_get_insert(view->source); + if (mark != NULL) { + GString *string = g_string_new(""); + gtk_text_buffer_get_iter_at_mark(view->source, &iter, mark); + g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1); + gtk_label_set_text(view->line_number, string->str); + g_string_free(string, TRUE); + } + } else { + gtk_label_set_text(view->line_number, ""); + } +} + +policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which) +{ + policy_view_t *view; + GtkTextTag *mono_tag; + int error = 0; + + if ((view = calloc(1, sizeof(*view))) == NULL) { + error = errno; + goto cleanup; + } + view->top = top; + view->which = which; + + view->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(view->top))); + view->stats = gtk_text_buffer_new(NULL); + view->source = gtk_text_buffer_new(NULL); + mono_tag = gtk_text_buffer_create_tag(view->source, "mono-tag", + "style", PANGO_STYLE_NORMAL, + "weight", PANGO_WEIGHT_NORMAL, "family", "monospace", NULL); + + view->stats_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][0])); + view->source_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][1])); + assert(view->stats_view != NULL && view->source_view != NULL); + gtk_text_view_set_buffer(view->stats_view, view->stats); + gtk_text_view_set_buffer(view->source_view, view->source); + + view->notebook = GTK_NOTEBOOK(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][2])); + view->line_number = GTK_LABEL(glade_xml_get_widget(view->xml, "toplevel line label")); + assert(view->notebook != NULL && view->line_number != NULL); + g_signal_connect_after(G_OBJECT(view->notebook), "event-after", G_CALLBACK(policy_view_notebook_on_event_after), view); + + cleanup: + if (error != 0) { + policy_view_destroy(&view); + errno = error; + return NULL; + } + return view; +} + +void policy_view_destroy(policy_view_t ** view) +{ + if (view != NULL && *view != NULL) { + free(*view); + *view = NULL; + } +} + +/** + * Update the policy stats text buffer (and hence its view) to show + * statistics about the given policy. + * + * @param view View to update. + * @param p New policy whose statistics to get. + * @param path Path to the new policy. + */ +static void policy_view_stats_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path) +{ + GtkTextIter iter; + gchar *contents = NULL; + char *path_desc, *tmp = NULL; + size_t num_classes = 0, + num_commons = 0, + num_perms = 0, + num_types = 0, + num_attribs = 0, + num_allow = 0, num_auditallow = 0, num_dontaudit = 0, + num_type_change = 0, num_type_member, num_type_trans = 0, + num_roles = 0, num_roleallow = 0, num_role_trans = 0, num_users = 0, num_bools = 0; + apol_vector_t *vec = NULL; + qpol_iterator_t *i = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + + util_text_buffer_clear(view->stats); + + if ((path_desc = util_policy_path_to_full_string(path)) == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + tmp = apol_policy_get_version_type_mls_str(p); + contents = g_strdup_printf("Policy: %s\n" "Policy Version & Type: %s\n", path_desc, tmp); + free(path_desc); + free(tmp); + tmp = NULL; + gtk_text_buffer_get_end_iter(view->stats, &iter); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_class_get_by_query(p, NULL, &vec); + num_classes = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_common_get_by_query(p, NULL, &vec); + num_commons = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_perm_get_by_query(p, NULL, &vec); + num_perms = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Classes and Permissions:\n" + "\tObject Classes: %zd\n" + "\tCommon Classes: %zd\n" "\tPermissions: %zd\n", num_classes, num_commons, num_perms); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_type_get_by_query(p, NULL, &vec); + num_types = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_attr_get_by_query(p, NULL, &vec); + num_attribs = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Types and Attributes:\n" + "\tTypes: %zd\n" "\tAttributes: %zd\n", num_types, num_attribs); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &i); + qpol_iterator_get_size(i, &num_allow); + qpol_iterator_destroy(&i); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &i); + qpol_iterator_get_size(i, &num_auditallow); + qpol_iterator_destroy(&i); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &i); + qpol_iterator_get_size(i, &num_dontaudit); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &i); + qpol_iterator_get_size(i, &num_type_change); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &i); + qpol_iterator_get_size(i, &num_type_member); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &i); + qpol_iterator_get_size(i, &num_type_trans); + qpol_iterator_destroy(&i); + + contents = g_strdup_printf("\nNumber of Rules:\n" + "\tallow: %zd\n" + "\tauditallow: %zd\n" + "\tdontaudit %zd\n" + "\tneverallow: not calculated\n" + "\ttype_change: %zd\n" + "\ttype_member: %zd\n" + "\ttype_transition: %zd\n", + num_allow, num_auditallow, num_dontaudit, num_type_change, num_type_member, num_type_trans); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_role_get_by_query(p, NULL, &vec); + num_roles = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + qpol_policy_get_role_allow_iter(q, &i); + qpol_iterator_get_size(i, &num_roleallow); + qpol_iterator_destroy(&i); + + qpol_policy_get_role_trans_iter(q, &i); + qpol_iterator_get_size(i, &num_roleallow); + qpol_iterator_destroy(&i); + + contents = g_strdup_printf("\nNumber of Roles: %zd\n" + "\nNumber of RBAC Rules:\n" + "\tallow: %zd\n" "\trole_transition %zd\n", num_roles, num_roleallow, num_role_trans); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_user_get_by_query(p, NULL, &vec); + num_users = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_bool_get_by_query(p, NULL, &vec); + num_bools = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Users: %zd\n" "\nNumber of Booleans: %zd\n", num_users, num_bools); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); +} + +/** + * Attempt to load the primary policy into this view's source policy + * buffer. If the policy is not a source policy then show an + * appropriate message. Otherwise mmap() the policy's contents to the + * buffer. + * + * @param view View whose source buffer to update. + * @param policy Policy to show, or NULL if none loaded. + * @param path Path to the policy. + */ +static void policy_view_source_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path) +{ + const char *primary_path; + util_text_buffer_clear(view->source); + + /* clear out any old data */ + if (view->mmap_start != NULL) { + munmap(view->mmap_start, view->mmap_length); + view->mmap_start = NULL; + view->mmap_length = 0; + } + if (p == NULL) { + gtk_text_buffer_set_text(view->source, "No policy has been loaded.", -1); + return; + } + primary_path = apol_policy_path_get_primary(path); + if (!qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SOURCE)) { + GString *string = g_string_new(""); + g_string_printf(string, "Policy file %s is not a source policy.", primary_path); + gtk_text_buffer_set_text(view->source, string->str, -1); + g_string_free(string, TRUE); + } else { + /* load the policy by mmap()ing the file */ + struct stat statbuf; + int fd; + + if ((fd = open(primary_path, O_RDONLY)) < 0) { + toplevel_ERR(view->top, "Could not open %s for reading: %s", primary_path, strerror(errno)); + return; + } + if (fstat(fd, &statbuf) < 0) { + toplevel_ERR(view->top, "Could not stat %s: %s", primary_path, strerror(errno)); + close(fd); + return; + } + + view->mmap_length = statbuf.st_size; + if ((view->mmap_start = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + toplevel_ERR(view->top, "Could not mmap %s: %s", primary_path, strerror(errno)); + close(fd); + view->mmap_start = NULL; + return; + } + close(fd); + gtk_text_buffer_set_text(view->source, view->mmap_start, view->mmap_length); + } +} + +void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path) +{ + policy_view_stats_update(view, policy, path); + policy_view_source_update(view, policy, path); +} + +void policy_view_show_policy_line(policy_view_t * view, unsigned long line) +{ + GtkTextTagTable *table = NULL; + GtkTextIter iter, end_iter; + GtkTextMark *mark = NULL; + GString *string = g_string_new(""); + + gtk_notebook_set_current_page(view->notebook, 1); + + /* when moving the buffer we must use marks to scroll because + * goto_line if called before the line height has been + * calculated can produce undesired results, in our case we + * get no scrolling at all */ + table = gtk_text_buffer_get_tag_table(view->source); + gtk_text_buffer_get_start_iter(view->source, &iter); + gtk_text_iter_set_line(&iter, line); + gtk_text_buffer_get_start_iter(view->source, &end_iter); + gtk_text_iter_set_line(&end_iter, line); + while (!gtk_text_iter_ends_line(&end_iter)) { + gtk_text_iter_forward_char(&end_iter); + } + + mark = gtk_text_buffer_create_mark(view->source, "line-position", &iter, TRUE); + assert(mark); + gtk_text_view_scroll_to_mark(view->source_view, mark, 0.0, TRUE, 0.0, 0.5); + + /* destroying the mark and recreating is faster than doing a + * move on a mark that still exists, so we always destroy it + * once we're done */ + gtk_text_buffer_delete_mark(view->source, mark); + gtk_text_view_set_cursor_visible(view->source_view, TRUE); + gtk_text_buffer_place_cursor(view->source, &iter); + gtk_text_buffer_select_range(view->source, &iter, &end_iter); + + gtk_container_set_focus_child(GTK_CONTAINER(view->notebook), GTK_WIDGET(view->source_view)); + + g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1); + gtk_label_set_text(view->line_number, string->str); + g_string_free(string, TRUE); +} + +GtkTextView *policy_view_get_text_view(policy_view_t * view) +{ + gint pagenum = gtk_notebook_get_current_page(view->notebook); + if (pagenum == 0) { + return view->stats_view; + } else { + return view->source_view; + } +} diff --git a/sediff/policy_view.h b/sediff/policy_view.h new file mode 100644 index 0000000..ee2c400 --- /dev/null +++ b/sediff/policy_view.h @@ -0,0 +1,84 @@ +/** + * @file + * Header for routines related to showing the parts of a policy -- + * its statistics and its source, if available. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef POLICY_VIEW_H +#define POLICY_VIEW_H + +#include "sediffx.h" +#include "toplevel.h" + +typedef struct policy_view policy_view_t; + +/** + * Allocate and return an instance of a policy view object. This + * object is responsible for showing the statistics and policy source + * (if available) for a particular policy. + * + * @param top Toplevel object containing view's widgets. + * @param which Which policy to show. + * + * @return An initialized policy_view object, or NULL upon error. The + * caller must call policy_view_destroy() afterwards. + */ +policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which); + +/** + * Deallocate all space associated with the referenced view. This + * does nothing if the pointer is already NULL. + * + * @param view Reference to a policy_view to destroy. Afterwards the + * pointer will be set to NULL. + */ +void policy_view_destroy(policy_view_t ** view); + +/** + * Direct the given policy view to update its display, to reflect the + * policy currently loaded. Note that this should be called prior to + * building the poldiff_t object, for poldiff_create() will take + * ownership of the policy. + * + * @parav view View to update. + */ +void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path); + +/** + * Direct the given policy view to show its source policy tab and then + * scroll to the given line number. Line numbers are zero indexed. + * + * @param view View whose source tab to show. + * @param line Line to show. + */ +void policy_view_show_policy_line(policy_view_t * view, unsigned long line); + +/** + * Get the currently showing text view for the policy view. + * + * @param view View containing text view. + * + * @return Currently visible text view. + */ +GtkTextView *policy_view_get_text_view(policy_view_t * view); + +#endif diff --git a/sediff/progress.c b/sediff/progress.c new file mode 100644 index 0000000..efaa120 --- /dev/null +++ b/sediff/progress.c @@ -0,0 +1,202 @@ +/** + * @file + * Routines to show a progress dialog, indicating that the + * application is doing something. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "progress.h" +#include "utilgui.h" + +#include <gtk/gtk.h> +#include <glib.h> +#include <glib/gprintf.h> + +struct progress +{ + toplevel_t *top; + GtkWidget *progress; + GtkWidget *label1, *label2; + char *s; + int done; + GCond *cond; + GMutex *mutex; +}; + +progress_t *progress_create(toplevel_t * top) +{ + progress_t *p; + GtkWidget *vbox; + + if ((p = calloc(1, sizeof(*p))) == NULL) { + return NULL; + } + p->top = top; + p->progress = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_modal(GTK_WINDOW(p->progress), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(p->progress), toplevel_get_window(top)); + gtk_window_set_default_size(GTK_WINDOW(p->progress), 300, 100); + vbox = gtk_vbox_new(FALSE, 2); + gtk_container_add(GTK_CONTAINER(p->progress), vbox); + p->label1 = gtk_label_new(NULL); + gtk_container_add(GTK_CONTAINER(vbox), p->label1); + p->label2 = gtk_label_new(NULL); + gtk_container_add(GTK_CONTAINER(vbox), p->label2); + gtk_widget_show(p->label1); + gtk_widget_show(p->label2); + gtk_widget_show(vbox); + util_cursor_wait(p->progress); + p->cond = g_cond_new(); + p->mutex = g_mutex_new(); + return p; +} + +void progress_destroy(progress_t ** progress) +{ + if (progress != NULL && *progress != NULL) { + free((*progress)->s); + g_cond_free((*progress)->cond); + g_mutex_free((*progress)->mutex); + free(*progress); + *progress = NULL; + } +} + +void progress_show(progress_t * progress, const char *title) +{ + gtk_label_set_text(GTK_LABEL(progress->label1), title); + gtk_label_set_text(GTK_LABEL(progress->label2), ""); + gtk_widget_show(progress->progress); + gtk_window_deiconify(GTK_WINDOW(progress->progress)); + gtk_window_set_title(GTK_WINDOW(progress->progress), title); + progress->done = 0; +} + +void progress_hide(progress_t * progress) +{ + gtk_widget_hide(progress->progress); +} + +int progress_wait(progress_t * progress) +{ + GTimeVal wait_time = { 0, 50000 }; + g_mutex_lock(progress->mutex); + while (!progress->done) { + if (progress->s != NULL) { + gtk_label_set_text(GTK_LABEL(progress->label2), progress->s); + free(progress->s); + progress->s = NULL; + } + // only process one event -- the policy source could + // still be loading, and this dialog should not block + // until the entire source has been read + gtk_main_iteration_do(FALSE); + g_cond_timed_wait(progress->cond, progress->mutex, &wait_time); + } + g_mutex_unlock(progress->mutex); + if (progress->done < 0) { + toplevel_ERR(progress->top, GTK_LABEL(progress->label2)->label); + return progress->done; + } else if (progress->done > 1) { + toplevel_WARN(progress->top, GTK_LABEL(progress->label2)->label); + return progress->done - 1; + } else { + progress->done = 0; + return 0; + } +} + +void progress_done(progress_t * progress) +{ + g_mutex_lock(progress->mutex); + progress->done = 1; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_warn(progress_t * progress, char *reason, ...) +{ + gchar *s; + va_list ap; + g_mutex_lock(progress->mutex); + if (reason != NULL) { + va_start(ap, reason); + g_vasprintf(&s, reason, ap); + free(progress->s); + progress->s = s; + va_end(ap); + } + progress->done = 2; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_abort(progress_t * progress, char *reason, ...) +{ + gchar *s; + va_list ap; + g_mutex_lock(progress->mutex); + if (reason != NULL) { + va_start(ap, reason); + g_vasprintf(&s, reason, ap); + free(progress->s); + progress->s = s; + va_end(ap); + } + progress->done = -1; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +static void progress_update_label(progress_t * progress, const char *fmt, va_list va_args) +{ + gchar *s = NULL; + g_vasprintf(&s, fmt, va_args); + g_mutex_lock(progress->mutex); + free(progress->s); + progress->s = s; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_update(progress_t * progress, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + progress_update_label(progress, fmt, ap); + va_end(ap); +} + +void progress_poldiff_handle_func(void *arg, const poldiff_t * diff __attribute__ ((unused)), int level + __attribute__ ((unused)), const char *fmt, va_list va_args) +{ + progress_t *progress = arg; + progress_update_label(progress, fmt, va_args); +} + +void progress_apol_handle_func(void *varg, const apol_policy_t * p __attribute__ ((unused)), int level + __attribute__ ((unused)), const char *fmt, va_list argp) +{ + progress_t *progress = varg; + progress_update_label(progress, fmt, argp); +} diff --git a/sediff/progress.h b/sediff/progress.h new file mode 100644 index 0000000..43432d7 --- /dev/null +++ b/sediff/progress.h @@ -0,0 +1,139 @@ +/** + * @file + * Header for showing progress dialogs. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef PROGRESS_H +#define PROGRESS_H + +typedef struct progress progress_t; + +#include "toplevel.h" + +#include <apol/policy.h> +#include <poldiff/poldiff.h> + +/** + * Allocate and return a new progress dialog object. + * + * @param top Toplevel object that will control the progress object. + * + * @return An initialized progress object, or NULL upon error. The + * caller is responsible for calling progress_destroy() afterwards. + */ +progress_t *progress_create(toplevel_t * top); + +/** + * Destroy a progress dialog. Does nothing if the pointer is already + * NULL. + * + * @param prefs Reference to a progress object to destroy. This will + * be set to NULL afterwards. + */ +void progress_destroy(progress_t ** progress); + +/** + * Display a progress dialog. + * + * @param progress Progress dialog to show. + * @param title Title for the progress window. + */ +void progress_show(progress_t * progress, const char *title); + +/** + * Hide the progress dialog. Note that this does not actually destroy + * the object. + * + * @param progress Progress dialog to hide. + */ +void progress_hide(progress_t * progress); + +/* the rest of these are for multi-threaded progress dialog */ + +/** + * Block the current thread until the progress dialog receives a done + * signal via progress_done() or progress_abort(). The dialog will + * periodically awake and update the user interface, based upon + * message received by its handle implementations. + * + * @param progress Progress object to wait against. + * + * @return 0 if the progress object got a progress_done(), < 0 if + * progress_abort(). + */ +int progress_wait(progress_t * progress); + +/** + * Signal to a progress object that this thread is ending + * successfully. This will cause all threads waiting upon the + * progress object to resume. + * + * @param progress Progress object to signal completion. + */ +void progress_done(progress_t * progress); + +/** + * Signal to a progress object that this thread completed with + * warnings. This will cause all threads waiting upon the progress + * object to resume. + * + * @param progress Progress object to signal completion. + * @param reason Explanation for warning, or NULL to use most recently + * written message as the reason. + */ +void progress_warn(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Signal to a progress object that this thread is aborting. This + * will cause all threads waiting upon the progress object to resume. + * + * @param progress Progress object to signal completion. + * @param reason Explanation for abort, or NULL to abort for no + * reason. The most recently written message will be used as the + * reason. + */ +void progress_abort(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Have the progress dialog show a message upon its next refresh. + * + * @param progress Progress object to update. + * @param fmt Format for string to display. + */ +void progress_update(progress_t * progress, char *fmt, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Implementation of libpoldiff's message callback function. This + * will route messages generated by libpoldiff to the progress + * dialog's display. To use this, pass the progress_t object as + * poldiff_create()'s callback_arg parameter. + */ +void progress_poldiff_handle_func(void *arg, const poldiff_t * diff, int level, const char *fmt, va_list va_args); + +/** + * Implementation of a libapol message callback function. This will + * route messages generated by libapol to the progress dialog's + * display. To use this, pass the progress_t object as + * apol_policy_open()'s varg parameter. + */ +void progress_apol_handle_func(void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp); + +#endif diff --git a/sediff/remap_types_dialog.c b/sediff/remap_types_dialog.c new file mode 100644 index 0000000..24c5b21 --- /dev/null +++ b/sediff/remap_types_dialog.c @@ -0,0 +1,564 @@ +/** + * @file + * Displays a dialog that allows users to explicitly remap/remap + * types from one policy to the other. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "remap_types_dialog.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <apol/type-query.h> +#include <apol/util.h> +#include <glade/glade.h> +#include <poldiff/type_map.h> + +struct remap_types +{ + toplevel_t *top; + GladeXML *xml; + poldiff_t *diff; + /** main dialog widget for type remapps*/ + GtkDialog *dialog; + GtkTreeView *view; + GtkCheckButton *show_inferred; + /** drop-down combo boxes that allow user to add new remap */ + GtkComboBoxEntry *combo[SEDIFFX_POLICY_NUM]; + GtkCheckButton *show_unmapped_types; + GtkButton *add, *remove; + GtkListStore *remaps; + GtkTreeModelFilter *filter; + GtkTreeModelSort *sort; + /** non-zero if a type map was added or removed */ + int changed; +}; + +enum +{ + ORIG_NAME_COL = 0, MOD_NAME_COL, ORIG_HIGHLIGHT_VALUE_COL, MOD_HIGHLIGHT_VALUE_COL, ENTRY_COL, NUM_COLUMNS +}; + +static GtkTreeModelFilter *types_filter[SEDIFFX_POLICY_NUM] = { NULL, NULL }; +static GtkListStore *types[SEDIFFX_POLICY_NUM] = { NULL, NULL }; +static gboolean show_only_unmapped = TRUE; + +/** + * Go through all entries in the list store; for those that map the + * current entries for the combo boxes, highlight them. + */ +static void remap_types_highlight_entries(struct remap_types *rt) +{ + const gchar *orig_text = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_ORIG])); + const gchar *mod_text = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_MOD])); + int num_orig_matches = 0, num_mod_matches = 0; + gboolean iter_valid; + GtkTreeIter iter; + poldiff_type_remap_entry_t *entry; + apol_vector_t *v; + size_t idx; + iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rt->remaps), &iter); + while (iter_valid) { + gtk_tree_model_get(GTK_TREE_MODEL(rt->remaps), &iter, ENTRY_COL, &entry, -1); + if (orig_text != NULL && strcmp(orig_text, "") != 0) { + v = poldiff_type_remap_entry_get_original_types(rt->diff, entry); + if (apol_vector_get_index(v, orig_text, apol_str_strcmp, NULL, &idx) == 0) { + gtk_list_store_set(rt->remaps, &iter, ORIG_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_BOLD, -1); + num_orig_matches++; + if (apol_vector_get_size(v) > 1) { + /* this will disallow the + * original type from being + * added again */ + num_orig_matches++; + } + } else { + gtk_list_store_set(rt->remaps, &iter, ORIG_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_NORMAL, -1); + } + apol_vector_destroy(&v); + } + if (mod_text != NULL && strcmp(mod_text, "") != 0) { + v = poldiff_type_remap_entry_get_modified_types(rt->diff, entry); + if (apol_vector_get_index(v, mod_text, apol_str_strcmp, NULL, &idx) == 0) { + gtk_list_store_set(rt->remaps, &iter, MOD_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_BOLD, -1); + num_mod_matches++; + if (apol_vector_get_size(v) > 1) { + /* this will disallow the + * modified type from being + * added again */ + num_mod_matches++; + } + } else { + gtk_list_store_set(rt->remaps, &iter, MOD_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_NORMAL, -1); + } + apol_vector_destroy(&v); + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(rt->remaps), &iter); + } + gtk_widget_set_sensitive(GTK_WIDGET(rt->add), FALSE); + if (orig_text != NULL && strcmp(orig_text, "") != 0 && mod_text != NULL && strcmp(mod_text, "") != 0) { + /* enable the add button if and only if: number of + * orig and mod matches are both 0, or one side is 1 + * and the other is 0 */ + if ((num_orig_matches == 0 && num_mod_matches == 0) || + (num_orig_matches == 1 && num_mod_matches == 0) || (num_orig_matches == 0 && num_mod_matches == 1)) { + gtk_widget_set_sensitive(GTK_WIDGET(rt->add), TRUE); + } + } +} + +/** + * Populate the main tree model with all type remaps currently within + * the poldiff object. + */ +static void remap_types_update_view(struct remap_types *rt) +{ + apol_vector_t *entries = poldiff_type_remap_get_entries(rt->diff); + apol_vector_t *origs = NULL, *mods = NULL; + apol_vector_t *used_origs = NULL, *used_mods = NULL; + char *orig_string = NULL, *mod_string = NULL, *s, *t; + size_t i, j; + GtkTreeIter iter; + if ((used_origs = apol_vector_create(NULL)) == NULL || (used_mods = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(rt->top, "%s", strerror(errno)); + goto cleanup; + } + gtk_list_store_clear(rt->remaps); + for (i = 0; i < apol_vector_get_size(entries); i++) { + poldiff_type_remap_entry_t *e = apol_vector_get_element(entries, i); + if ((origs = poldiff_type_remap_entry_get_original_types(rt->diff, e)) == NULL || + (mods = poldiff_type_remap_entry_get_modified_types(rt->diff, e)) == NULL || + (orig_string = apol_str_join(origs, ", ")) == NULL || (mod_string = apol_str_join(mods, ", ")) == NULL) { + toplevel_ERR(rt->top, "%s", strerror(errno)); + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(origs); j++) { + s = apol_vector_get_element(origs, j); + if (apol_vector_append(used_origs, s) < 0) { + toplevel_ERR(rt->top, "%s", strerror(errno)); + goto cleanup; + } + } + for (j = 0; j < apol_vector_get_size(mods); j++) { + s = apol_vector_get_element(mods, j); + if (apol_vector_append(used_mods, s) < 0) { + toplevel_ERR(rt->top, "%s", strerror(errno)); + goto cleanup; + } + } + gtk_list_store_append(rt->remaps, &iter); + gtk_list_store_set(rt->remaps, &iter, ORIG_NAME_COL, orig_string, MOD_NAME_COL, mod_string, ENTRY_COL, e, -1); + apol_vector_destroy(&origs); + apol_vector_destroy(&mods); + free(orig_string); + free(mod_string); + orig_string = NULL; + mod_string = NULL; + } + + /* mark entries in the 'types' list store that are used. the + * algorithm works only because the types list store was + * sorted by remap_types_update(). */ + apol_vector_sort_uniquify(used_origs, apol_str_strcmp, NULL); + apol_vector_sort_uniquify(used_mods, apol_str_strcmp, NULL); + i = 0; + s = apol_vector_get_element(used_origs, i); + gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter); + while (iter_valid && s != NULL) { + gtk_tree_model_get(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter, 0, &t, -1); + if (strcmp(s, t) == 0) { + gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 1, TRUE, -1); + i++; + s = apol_vector_get_element(used_origs, i); + } else { + gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 1, FALSE, -1); + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter); + } + assert(i >= apol_vector_get_size(used_origs)); + + i = 0; + s = apol_vector_get_element(used_mods, i); + iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter); + while (iter_valid && s != NULL) { + gtk_tree_model_get(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter, 0, &t, -1); + if (strcmp(s, t) == 0) { + gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 1, TRUE, -1); + i++; + s = apol_vector_get_element(used_mods, i); + } else { + gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 1, FALSE, -1); + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter); + } + assert(i >= apol_vector_get_size(used_mods)); + + remap_types_highlight_entries(rt); + gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_ORIG]); + gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_MOD]); + cleanup: + apol_vector_destroy(&used_origs); + apol_vector_destroy(&used_mods); + apol_vector_destroy(&origs); + apol_vector_destroy(&mods); + free(orig_string); + free(mod_string); +} + +static void remap_types_on_show_inferred_toggle(GtkToggleButton * toggle __attribute__ ((unused)), gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + gtk_tree_model_filter_refilter(rt->filter); +} + +static void remap_types_on_show_unmapped_types_toggle(GtkToggleButton * toggle, gpointer user_data __attribute__ ((unused))) +{ + show_only_unmapped = gtk_toggle_button_get_active(toggle); + gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_ORIG]); + gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_MOD]); +} + +static void remap_types_on_selection_change(GtkTreeSelection * selection, gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL); + gtk_widget_set_sensitive(GTK_WIDGET(rt->remove), sens); +} + +static void remap_types_on_combo_change(GtkComboBox * widget __attribute__ ((unused)), gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + remap_types_highlight_entries(rt); +} + +static void remap_types_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + apol_vector_t *orig = NULL, *mod = NULL; + apol_vector_t *old_orig = NULL, *old_mod = NULL; + const gchar *orig_type = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_ORIG])); + const gchar *mod_type = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_MOD])); + + if ((orig = apol_str_split(orig_type, " ")) == NULL || (mod = apol_str_split(mod_type, " ")) == NULL) { + toplevel_ERR(rt->top, "%s", strerror(errno)); + } + if (apol_vector_get_size(orig) > 1 && apol_vector_get_size(mod) > 1) { + toplevel_ERR(rt->top, "%s", "Remappings may be 1 to many or many to 1, but not many to many."); + goto cleanup; + } + + /* check all existing remap entries, to see if the user's + * entries should be appended to an existing entry */ + GtkTreeIter iter; + gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rt->remaps), &iter); + poldiff_type_remap_entry_t *entry = NULL, *e; + /* remap_types_highlight_entries() should have already marked + * which of the existing entries match the user's inputs */ + while (iter_valid) { + gint orig_marked, mod_marked; + gtk_tree_model_get(GTK_TREE_MODEL(rt->remaps), &iter, ORIG_HIGHLIGHT_VALUE_COL, &orig_marked, + MOD_HIGHLIGHT_VALUE_COL, &mod_marked, ENTRY_COL, &e, -1); + assert(orig_marked != PANGO_WEIGHT_BOLD || mod_marked != PANGO_WEIGHT_BOLD); + if (orig_marked == PANGO_WEIGHT_BOLD) { + assert(entry == NULL); + entry = e; + } else if (mod_marked == PANGO_WEIGHT_BOLD) { + assert(entry == NULL); + entry = e; + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(rt->remaps), &iter); + } + if (entry != NULL) { + size_t i; + char *s; + old_orig = poldiff_type_remap_entry_get_original_types(rt->diff, entry); + old_mod = poldiff_type_remap_entry_get_modified_types(rt->diff, entry); + assert(old_orig != NULL && old_mod != NULL); + for (i = 0; i < apol_vector_get_size(old_orig); i++) { + s = strdup(apol_vector_get_element(old_orig, i)); + if (apol_vector_append_unique(orig, s, apol_str_strcmp, NULL) > 0) { + free(s); + } + } + for (i = 0; i < apol_vector_get_size(old_mod); i++) { + s = strdup(apol_vector_get_element(old_mod, i)); + if (apol_vector_append_unique(mod, s, apol_str_strcmp, NULL) > 0) { + free(s); + } + } + poldiff_type_remap_entry_remove(rt->diff, entry); + } + + if (poldiff_type_remap_create(rt->diff, orig, mod) < 0) { + toplevel_ERR(rt->top, "%s", "This was not a valid type remap."); + goto cleanup; + } else { + remap_types_update_view(rt); + rt->changed = 1; + } + cleanup: + apol_vector_destroy(&orig); + apol_vector_destroy(&mod); + apol_vector_destroy(&old_orig); + apol_vector_destroy(&old_mod); +} + +static void remap_types_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + GtkTreeSelection *selection = gtk_tree_view_get_selection(rt->view); + GtkTreeIter iter; + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { + poldiff_type_remap_entry_t *entry; + gtk_tree_model_get(GTK_TREE_MODEL(rt->sort), &iter, ENTRY_COL, &entry, -1); + poldiff_type_remap_entry_remove(rt->diff, entry); + remap_types_update_view(rt); + rt->changed = 1; + } +} + +static gint remap_types_sort_compare(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data) +{ + gint column = GPOINTER_TO_INT(user_data); + char *s, *t; + gtk_tree_model_get(model, a, column, &s, -1); + gtk_tree_model_get(model, b, column, &t, -1); + /* these next two conditionals are needed because while the remap + * list store is being built, a row will temporarily have empty + * strings */ + if (s == NULL) { + s = ""; + } + if (t == NULL) { + t = ""; + } + return strcmp(s, t); +} + +static gboolean remap_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data) +{ + struct remap_types *rt = (struct remap_types *)user_data; + poldiff_type_remap_entry_t *entry; + gtk_tree_model_get(model, iter, ENTRY_COL, &entry, -1); + if (poldiff_type_remap_entry_get_is_inferred(entry)) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt->show_inferred))) { + return TRUE; + } + return FALSE; + } else { + /* explicit maps are always shown */ + return TRUE; + } +} + +static void remap_types_init_widgets(struct remap_types *rt) +{ + GtkTreeSelection *selection; + GtkCellRenderer *orig_renderer, *mod_renderer; + GtkTreeViewColumn *column; + + rt->dialog = GTK_DIALOG(glade_xml_get_widget(rt->xml, "remap_types")); + assert(rt->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(rt->dialog), toplevel_get_window(rt->top)); + rt->view = GTK_TREE_VIEW(glade_xml_get_widget(rt->xml, "remap_types treeview")); + rt->show_inferred = GTK_CHECK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types inferred checkbutton")); + rt->combo[SEDIFFX_POLICY_ORIG] = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(rt->xml, "remap_types orig combo")); + rt->combo[SEDIFFX_POLICY_MOD] = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(rt->xml, "remap_types mod combo")); + rt->show_unmapped_types = GTK_CHECK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types only unmapped checkbutton")); + assert(rt->view != NULL && rt->show_inferred && rt->combo[SEDIFFX_POLICY_ORIG] != NULL + && rt->combo[SEDIFFX_POLICY_MOD] != NULL && rt->show_unmapped_types != NULL); + g_signal_connect(rt->show_inferred, "toggled", G_CALLBACK(remap_types_on_show_inferred_toggle), rt); + g_signal_connect(rt->show_unmapped_types, "toggled", G_CALLBACK(remap_types_on_show_unmapped_types_toggle), rt); + show_only_unmapped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt->show_unmapped_types)); + + rt->add = GTK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types add button")); + rt->remove = GTK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types remove button")); + assert(rt->add != NULL && rt->remove != NULL); + g_signal_connect(rt->add, "clicked", G_CALLBACK(remap_types_on_add_click), rt); + g_signal_connect(rt->remove, "clicked", G_CALLBACK(remap_types_on_remove_click), rt); + + rt->remaps = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER); + rt->filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(rt->remaps), NULL)); + gtk_tree_model_filter_set_visible_func(rt->filter, remap_types_filter_visible, rt, NULL); + rt->sort = GTK_TREE_MODEL_SORT(gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(rt->filter))); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(rt->sort), ORIG_NAME_COL, remap_types_sort_compare, + GINT_TO_POINTER(ORIG_NAME_COL), NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(rt->sort), MOD_NAME_COL, remap_types_sort_compare, + GINT_TO_POINTER(MOD_NAME_COL), NULL); + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(rt->sort), remap_types_sort_compare, + GINT_TO_POINTER(ORIG_NAME_COL), NULL); + gtk_tree_view_set_model(rt->view, GTK_TREE_MODEL(rt->sort)); + + selection = gtk_tree_view_get_selection(rt->view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + g_signal_connect(selection, "changed", G_CALLBACK(remap_types_on_selection_change), rt); + + orig_renderer = gtk_cell_renderer_text_new(); + mod_renderer = gtk_cell_renderer_text_new(); + g_object_set(orig_renderer, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set(mod_renderer, "weight", PANGO_WEIGHT_BOLD, NULL); + column = gtk_tree_view_column_new_with_attributes("Original Policy", orig_renderer, "text", ORIG_NAME_COL, "weight", + ORIG_HIGHLIGHT_VALUE_COL, NULL); + gtk_tree_view_column_set_expand(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, ORIG_NAME_COL); + gtk_tree_view_column_set_sort_indicator(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(rt->view), column); + gtk_tree_view_column_clicked(column); + + column = gtk_tree_view_column_new_with_attributes("Modified Policy", mod_renderer, "text", MOD_NAME_COL, "weight", + MOD_HIGHLIGHT_VALUE_COL, NULL); + gtk_tree_view_column_set_expand(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, MOD_NAME_COL); + gtk_tree_view_column_set_sort_indicator(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(rt->view), column); +} + +static gboolean remap_types_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data); + +/** + * Set up the combo boxes to show only types unique to that policy. + * (The lists of types were calculated by remap_types_update().) + */ +static void remap_types_init_combos(struct remap_types *rt) +{ + sediffx_policy_e i; + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + gtk_combo_box_set_model(GTK_COMBO_BOX(rt->combo[i]), GTK_TREE_MODEL(types_filter[i])); + gtk_combo_box_entry_set_text_column(rt->combo[i], 0); + g_signal_connect(rt->combo[i], "changed", G_CALLBACK(remap_types_on_combo_change), rt); + } +} + +int remap_types_run(toplevel_t * top) +{ + struct remap_types rt; + gint response; + static gboolean prev_show_inferred = FALSE; + static gboolean prev_show_unmapped = TRUE; + + memset(&rt, 0, sizeof(rt)); + rt.top = top; + rt.xml = glade_xml_new(toplevel_get_glade_xml(rt.top), "remap_types", NULL); + rt.diff = toplevel_get_poldiff(rt.top); + + remap_types_init_widgets(&rt); + remap_types_init_combos(&rt); + remap_types_update_view(&rt); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rt.show_inferred), prev_show_inferred); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rt.show_unmapped_types), prev_show_unmapped); + + response = gtk_dialog_run(rt.dialog); + + prev_show_inferred = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt.show_inferred)); + prev_show_unmapped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt.show_unmapped_types)); + gtk_widget_destroy(GTK_WIDGET(rt.dialog)); + g_object_unref(rt.sort); + g_object_unref(rt.filter); + g_object_unref(rt.remaps); + return rt.changed; +} + +static gboolean remap_types_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data + __attribute__ ((unused))) +{ + gboolean in_use; + if (!show_only_unmapped) { + return TRUE; + } + /* otherwise show types that are not being used */ + gtk_tree_model_get(model, iter, 1, &in_use, -1); + return !in_use; +} + +/** + * Alphabetize a list of qpol_type_t, base upon their names. + */ +static int remap_types_qpol_type_cmp(const void *a, const void *b, void *data) +{ + const qpol_type_t *x = a; + const qpol_type_t *y = b; + qpol_policy_t *q = (qpol_policy_t *) data; + const char *s, *t; + qpol_type_get_name(q, x, &s); + qpol_type_get_name(q, y, &t); + return strcmp(s, t); +} + +int remap_types_update(apol_policy_t * orig_policy, apol_policy_t * mod_policy) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_policy); + qpol_policy_t *mq = apol_policy_get_qpol(mod_policy); + apol_vector_t *v = NULL; + sediffx_policy_e which; + size_t i; + const qpol_type_t *t; + const char *type_name; + GtkTreeIter iter; + int error = 0, retval = -1; + + for (which = SEDIFFX_POLICY_ORIG; which < SEDIFFX_POLICY_NUM; which++) { + if (types[which] == NULL) { + types[which] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN); + types_filter[which] = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(types[which]), NULL)); + gtk_tree_model_filter_set_visible_func(types_filter[which], remap_types_types_filter_visible, NULL, NULL); + } else { + gtk_list_store_clear(types[which]); + } + } + + if (apol_type_get_by_query(orig_policy, NULL, &v) < 0) { + error = errno; + goto cleanup; + } + apol_vector_sort(v, remap_types_qpol_type_cmp, oq); + for (i = 0; i < apol_vector_get_size(v); i++) { + t = apol_vector_get_element(v, i); + qpol_type_get_name(oq, t, &type_name); + gtk_list_store_append(types[SEDIFFX_POLICY_ORIG], &iter); + gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 0, type_name, -1); + } + apol_vector_destroy(&v); + + if (apol_type_get_by_query(mod_policy, NULL, &v) < 0) { + error = errno; + goto cleanup; + } + apol_vector_sort(v, remap_types_qpol_type_cmp, mq); + for (i = 0; i < apol_vector_get_size(v); i++) { + t = apol_vector_get_element(v, i); + qpol_type_get_name(mq, t, &type_name); + gtk_list_store_append(types[SEDIFFX_POLICY_MOD], &iter); + gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 0, type_name, -1); + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + if (retval != 0) { + errno = error; + return retval; + } + return retval; +} diff --git a/sediff/remap_types_dialog.h b/sediff/remap_types_dialog.h new file mode 100644 index 0000000..7e52313 --- /dev/null +++ b/sediff/remap_types_dialog.h @@ -0,0 +1,56 @@ +/** + * @file + * Headers for a dialog that allows users to explicitly remap/remap + * types. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REMAP_TYPES_DIALOG_H +#define REMAP_TYPES_DIALOG_H + +#include "toplevel.h" + +/** + * Display and run a dialog that allows the user to add and remove + * type remappings. + * + * @param top Toplevel containing poldiff structure. + * + * @return Non-zero if any mapping was added or removed, zero if there + * were no changes. + */ +int remap_types_run(toplevel_t * top); + +/** + * Notify the remap types dialog that the currently loaded policies + * have changed. This function updates its lists of types from the + * policies. This function must be called at least once prior to + * remap_types_run(). + * + * @param orig_policy Newly loaded original policy. + * @param mod_policy Newly loaded modified policy. + * + * @return 0 on success, < 0 on error. + */ +int remap_types_update(apol_policy_t * orig_policy, apol_policy_t * mod_policy); + +#endif diff --git a/sediff/result_item.c b/sediff/result_item.c new file mode 100644 index 0000000..5d47583 --- /dev/null +++ b/sediff/result_item.c @@ -0,0 +1,1361 @@ +/** + * @file + * Implementation of the result item class. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "result_item.h" +#include "result_item_render.h" +#include "utilgui.h" + +#include <assert.h> + +typedef void (*destructor_fn_t) (result_item_t * item); +typedef void (*policy_changed_fn_t) (result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol); +typedef void (*poldiff_run_fn_t) (result_item_t * item, poldiff_t * diff, int incremental); +typedef GtkTextBuffer *(*get_buffer_fn_t) (result_item_t * item, poldiff_form_e form); +typedef int (*is_render_slow_fn_t) (result_item_t * item, poldiff_form_e form); +typedef void (*get_forms_fn_t) (result_item_t * item, int forms[5]); +typedef void (*set_current_sort_fn_t) (result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir); +typedef void (*print_diff_fn_t) (result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form); + +struct result_item +{ + const char *label; + /** bit value corresponding to polidiff/poldiff.h defines */ + uint32_t bit_pos; + /** if either policy does not support a particular policy + component then this will be zero */ + int supported; + /* protected members below */ + poldiff_t *diff; + size_t stats[5]; + gint offsets[5]; + results_sort_e sorts[5]; + results_sort_dir_e sort_dirs[5]; + /* below are required functions to get poldiff results */ + const apol_vector_t *(*get_vector) (const poldiff_t *); + poldiff_form_e(*get_form) (const void *); + char *(*get_string) (const poldiff_t *, const void *); + /* below is a virtual function table */ + destructor_fn_t destructor; + /** if the result item does not care about the type of + policies are loaded then this can be NULL */ + policy_changed_fn_t policy_changed; + poldiff_run_fn_t poldiff_run; + get_buffer_fn_t get_buffer; + is_render_slow_fn_t is_render_slow; + get_forms_fn_t get_forms; + /** if the result item cannot be sorted then this will be an array + of zeroes */ + set_current_sort_fn_t set_current_sort; + /** data specific to subclasses of result_item */ + union + { + int type_can_modify; + struct + { + int has_line_numbers[SEDIFFX_POLICY_NUM]; + int cached[5]; + GtkTextBuffer *buffers[5]; + apol_vector_t *items[5]; + print_diff_fn_t print_diff; + } multi; + } data; +}; + +/** map from a poldiff_form_e to an integer */ +static const poldiff_form_e form_reverse_map[] = { + -1, 0, 2, 4, 1, 3 +}; + +/******************** single buffer functions ********************/ + +/* below is the implementation of the 'single buffer result item' + class. most policy components are instances of this class.*/ + +static GtkTextBuffer *single_buffer = NULL; + +/** + * For single items, results are always destroyed. (Recalculating + * results is very fast.) + */ +static void result_item_single_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental __attribute__ ((unused))) +{ + item->diff = diff; + memset(item->stats, 0, sizeof(item->stats)); +} + +/** + * For single items, re-use the same buffer each time. + */ +static GtkTextBuffer *result_item_single_get_buffer(result_item_t * item, poldiff_form_e form) +{ + util_text_buffer_clear(single_buffer); + if (form == POLDIFF_FORM_NONE) { + result_item_print_summary(item, single_buffer); + } else { + result_item_print_header(item, single_buffer, form); + result_item_print_diff(item, single_buffer, form); + } + return single_buffer; +} + +/** + * For single items, rendering is (supposed to be) very fast. + */ +static int result_item_single_is_render_slow(result_item_t * item __attribute__ ((unused)), poldiff_form_e form + __attribute__ ((unused))) +{ + return 0; +} + +/** + * For single items, re-use the same buffer each time. This function + * calls a modified rendering functions to be used explicitly by + * rules. + */ +static GtkTextBuffer *result_item_single_get_rule_buffer(result_item_t * item, poldiff_form_e form) +{ + util_text_buffer_clear(single_buffer); + if (form == POLDIFF_FORM_NONE) { + result_item_print_summary(item, single_buffer); + } else { + result_item_print_header(item, single_buffer, form); + result_item_print_rule_diff(item, single_buffer, form); + } + return single_buffer; +} + +static void result_item_single_get_forms(result_item_t * item, int forms[5]) +{ + int i, was_run = poldiff_is_run(item->diff, item->bit_pos); + if (was_run) { + poldiff_get_stats(item->diff, item->bit_pos, item->stats); + } + for (i = 0; i < 5; i++) { + if (!result_item_is_supported(item) || i == form_reverse_map[POLDIFF_FORM_ADD_TYPE] + || i == form_reverse_map[POLDIFF_FORM_REMOVE_TYPE]) { + /* single items do not have add-by-type and + * remove-by-type forms */ + forms[i] = -1; + } else { + forms[i] = was_run; + } + } +} + +/** + * Constructor for the abstract single buffer item class. + */ +static result_item_t *result_item_single_create(GtkTextTagTable * table) +{ + result_item_t *item = calloc(1, sizeof(*item)); + if (item == NULL) { + return item; + } + if (single_buffer == NULL) { + single_buffer = gtk_text_buffer_new(table); + } + item->supported = 1; + item->policy_changed = NULL; + item->poldiff_run = result_item_single_poldiff_run; + item->get_buffer = result_item_single_get_buffer; + item->is_render_slow = result_item_single_is_render_slow; + item->get_forms = result_item_single_get_forms; + return item; +} + +/******************** constructors below ********************/ + +result_item_t *result_item_create_classes(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Classes"; + item->bit_pos = POLDIFF_DIFF_CLASSES; + item->get_vector = poldiff_get_class_vector; + item->get_form = poldiff_class_get_form; + item->get_string = poldiff_class_to_string; + return item; +} + +result_item_t *result_item_create_commons(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Commons"; + item->bit_pos = POLDIFF_DIFF_COMMONS; + item->get_vector = poldiff_get_common_vector; + item->get_form = poldiff_common_get_form; + item->get_string = poldiff_common_to_string; + return item; +} + +/** + * Print an appropriate error message if levels are not supported. + */ +static GtkTextBuffer *result_item_level_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (!result_item_is_supported(item)) { + gtk_text_buffer_set_text(single_buffer, + "Level diffs are not supported because neither policy is a MLS policy.", -1); + return single_buffer; + } else { + return result_item_single_get_buffer(item, form); + } +} + +/** + * Only support sensitivites and levels if either policy (can) has + * them. + */ +static void result_item_level_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + if (!qpol_policy_has_capability(oq, QPOL_CAP_MLS) && !qpol_policy_has_capability(mq, QPOL_CAP_MLS)) { + item->supported = 0; + } else { + item->supported = 1; + } +} + +/* levels require at least one MLS policy */ +result_item_t *result_item_create_levels(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Levels"; + item->bit_pos = POLDIFF_DIFF_LEVELS; + item->get_vector = poldiff_get_level_vector; + item->get_form = poldiff_level_get_form; + item->get_string = poldiff_level_to_string; + item->get_buffer = result_item_level_get_buffer; + item->policy_changed = result_item_level_policy_changed; + return item; +} + +/** + * Print an appropriate error message if categories are not supported. + */ +static GtkTextBuffer *result_item_category_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (!result_item_is_supported(item)) { + gtk_text_buffer_set_text(single_buffer, + "Category diffs are not supported because neither policy is a MLS policy.", -1); + return single_buffer; + } else { + return result_item_single_get_buffer(item, form); + } +} + +/** + * The modified form is always unsupported. + */ +static void result_item_category_get_forms(result_item_t * item, int forms[5]) +{ + result_item_single_get_forms(item, forms); + forms[4] = -1; +} + +/* categories have no modified form; they also require two MLS + policies to be supported */ +result_item_t *result_item_create_categories(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Categories"; + item->bit_pos = POLDIFF_DIFF_CATS; + item->get_vector = poldiff_get_cat_vector; + item->get_form = poldiff_cat_get_form; + item->get_string = poldiff_cat_to_string; + item->get_buffer = result_item_category_get_buffer; + item->policy_changed = result_item_level_policy_changed; /* [sic] */ + item->get_forms = result_item_category_get_forms; + return item; +} + +/** + * Only show modified types if it makes sense -- i.e, when both + * policies have meaningful attribute names. + */ +static void result_item_type_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + if (!qpol_policy_has_capability(oq, QPOL_CAP_ATTRIB_NAMES) || !qpol_policy_has_capability(mq, QPOL_CAP_ATTRIB_NAMES)) { + item->data.type_can_modify = 0; + } else { + item->data.type_can_modify = 1; + } +} + +static void result_item_type_get_forms(result_item_t * item, int forms[5]) +{ + result_item_single_get_forms(item, forms); + if (!item->data.type_can_modify) { + forms[4] = -1; + } +} + +/* the type result item is a subclass of single item. it differs in + that it might not have a modified form */ +result_item_t *result_item_create_types(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Types"; + item->bit_pos = POLDIFF_DIFF_TYPES; + item->get_vector = poldiff_get_type_vector; + item->get_form = poldiff_type_get_form; + item->get_string = poldiff_type_to_string; + item->policy_changed = result_item_type_policy_changed; + item->get_forms = result_item_type_get_forms; + return item; +} + +/** + * Only support attributes when both policies (can) have them. + */ +static void result_item_attribute_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + if (!qpol_policy_has_capability(oq, QPOL_CAP_ATTRIB_NAMES) || !qpol_policy_has_capability(mq, QPOL_CAP_ATTRIB_NAMES)) { + item->supported = 0; + } else { + item->supported = 1; + } +} + +/** + * Print an appropriate error message if attributes are not supported. + */ +static GtkTextBuffer *result_item_attribute_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (!result_item_is_supported(item)) { + gtk_text_buffer_set_text(single_buffer, + "Attribute diffs are not supported because one of the policies does not contain attribute names.", + -1); + return single_buffer; + } else { + return result_item_single_get_buffer(item, form); + } +} + +/* the attribute result item is a subclass of single item. it differs + in that it might not exist if attributes are not supported in + either policy */ +result_item_t *result_item_create_attributes(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Attributes"; + item->bit_pos = POLDIFF_DIFF_ATTRIBS; + item->get_vector = poldiff_get_attrib_vector; + item->get_form = poldiff_attrib_get_form; + item->get_string = poldiff_attrib_to_string; + item->get_buffer = result_item_attribute_get_buffer; + item->policy_changed = result_item_attribute_policy_changed; + return item; +} + +result_item_t *result_item_create_roles(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Roles"; + item->bit_pos = POLDIFF_DIFF_ROLES; + item->get_vector = poldiff_get_role_vector; + item->get_form = poldiff_role_get_form; + item->get_string = poldiff_role_to_string; + return item; +} + +/** + * Construct a string, similar to poldiff_user_to_string(), but with + * the proper color coding enabled. + */ +static void result_item_user_print_modified(result_item_t * item, poldiff_user_t * user, GtkTextBuffer * tb, GtkTextIter * iter) +{ + GString *string = g_string_new(""); + g_string_printf(string, "* %s\n", poldiff_user_get_name(user)); + result_item_print_string(tb, iter, string->str, 1); + + const apol_vector_t *added = poldiff_user_get_added_roles(user); + const apol_vector_t *removed = poldiff_user_get_removed_roles(user); + const apol_vector_t *unmodified = poldiff_user_get_unmodified_roles(user); + size_t i; + char *s; + if (apol_vector_get_size(added) > 0 || apol_vector_get_size(removed) > 0) { + g_string_assign(string, " roles {"); + for (i = 0; i < apol_vector_get_size(unmodified); i++) { + s = (char *)apol_vector_get_element(unmodified, i); + g_string_append_printf(string, " %s", s); + } + for (i = 0; i < apol_vector_get_size(added); i++) { + s = (char *)apol_vector_get_element(added, i); + g_string_append_printf(string, " +%s", s); + } + for (i = 0; i < apol_vector_get_size(removed); i++) { + s = (char *)apol_vector_get_element(removed, i); + g_string_append_printf(string, " -%s", s); + } + g_string_append(string, " }\n"); + result_item_print_string_inline(tb, iter, string->str, 1); + } + + const poldiff_level_t *orig = poldiff_user_get_original_dfltlevel(user); + const poldiff_level_t *mod = poldiff_user_get_modified_dfltlevel(user); + if (orig != NULL) { + result_item_print_string_inline(tb, iter, " level:\n", 1); + s = poldiff_level_to_string_brief(item->diff, orig); + g_string_printf(string, " %s", s); + if (poldiff_level_get_form(orig) != POLDIFF_FORM_MODIFIED) { + result_item_print_string(tb, iter, string->str, 1); + } else { + result_item_print_string_inline(tb, iter, string->str, 1); + } + free(s); + if (mod != NULL) { + s = poldiff_level_to_string_brief(item->diff, mod); + g_string_printf(string, " %s", s); + if (poldiff_level_get_form(mod) != POLDIFF_FORM_MODIFIED) { + result_item_print_string(tb, iter, string->str, 1); + } else { + result_item_print_string_inline(tb, iter, string->str, 1); + } + free(s); + } + } + + const poldiff_range_t *range = poldiff_user_get_range(user); + if (range != NULL) { + result_item_print_modified_range(item, range, tb, iter); + } + result_item_print_string(tb, iter, "\n", 0); + g_string_free(string, TRUE); +} + +/** + * Printing a modified user is special, for it spans multiple lines + * and has inline markers. + */ +static GtkTextBuffer *result_item_user_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (form != POLDIFF_FORM_MODIFIED) { + return result_item_single_get_buffer(item, form); + } else { + util_text_buffer_clear(single_buffer); + result_item_print_header(item, single_buffer, form); + GtkTextIter iter; + const apol_vector_t *v; + size_t i; + poldiff_user_t *user; + + gtk_text_buffer_get_end_iter(single_buffer, &iter); + v = item->get_vector(item->diff); + for (i = 0; i < apol_vector_get_size(v); i++) { + user = (poldiff_user_t *) apol_vector_get_element(v, i); + if (item->get_form(user) == form) { + result_item_user_print_modified(item, user, single_buffer, &iter); + } + } + return single_buffer; + } +} + +/* the user result item is a subclass of a single item. */ +result_item_t *result_item_create_users(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Users"; + item->bit_pos = POLDIFF_DIFF_USERS; + item->get_vector = poldiff_get_user_vector; + item->get_form = poldiff_user_get_form; + item->get_string = poldiff_user_to_string; + item->get_buffer = result_item_user_get_buffer; + return item; +} + +/** + * Only support booleans if either policy (can) has them. + */ +static void result_item_boolean_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + if (!qpol_policy_has_capability(oq, QPOL_CAP_CONDITIONALS) && !qpol_policy_has_capability(mq, QPOL_CAP_CONDITIONALS)) { + item->supported = 0; + } else { + item->supported = 1; + } +} + +result_item_t *result_item_create_booleans(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Booleans"; + item->bit_pos = POLDIFF_DIFF_BOOLS; + item->get_vector = poldiff_get_bool_vector; + item->get_form = poldiff_bool_get_form; + item->get_string = poldiff_bool_to_string; + item->policy_changed = result_item_boolean_policy_changed; + return item; +} + +/* role trans are a subclass of single buffer item, in that they have + a special rendering function for modified items */ +result_item_t *result_item_create_role_allows(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Role Allows"; + item->bit_pos = POLDIFF_DIFF_ROLE_ALLOWS; + item->get_vector = poldiff_get_role_allow_vector; + item->get_form = poldiff_role_allow_get_form; + item->get_string = poldiff_role_allow_to_string; + item->get_buffer = result_item_single_get_rule_buffer; + return item; +} + +static void result_item_role_trans_get_forms(result_item_t * item, int forms[5]) +{ + int i, was_run = poldiff_is_run(item->diff, item->bit_pos); + if (was_run) { + poldiff_get_stats(item->diff, item->bit_pos, item->stats); + } + for (i = 0; i < 5; i++) { + if (!result_item_is_supported(item)) { + forms[i] = -1; + } else { + forms[i] = was_run; + } + } +} + +/* role trans are a subclass of single buffer item, in that they have + a special rendering function for modified items and that they have + add-type and remove-type forms */ +result_item_t *result_item_create_role_trans(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Role Transitions"; + item->bit_pos = POLDIFF_DIFF_ROLE_TRANS; + item->get_vector = poldiff_get_role_trans_vector; + item->get_form = poldiff_role_trans_get_form; + item->get_string = poldiff_role_trans_to_string; + item->get_forms = result_item_role_trans_get_forms; + item->get_buffer = result_item_single_get_rule_buffer; + return item; +} + +/** + * Construct a string, similar to poldiff_range_trans_to_string(), but + * with the proper color coding enabled. + */ +static void result_item_range_trans_print_modified(result_item_t * item, const poldiff_range_trans_t * rt, GtkTextBuffer * tb, + GtkTextIter * iter) +{ + GString *string = g_string_new(""); + char *orig_s = poldiff_range_trans_to_string(item->diff, rt); + char *next_s = orig_s; + const poldiff_range_t *range = poldiff_range_trans_get_range(rt); + + /* first line should always be printed with normal font */ + char *s = strsep(&next_s, "\n"); + g_string_printf(string, "%s\n", s); + result_item_print_string(tb, iter, string->str, 1); + + /* all subsequent lines are printed as normal (yes, this + * discards lines from poldiff_range_trans_to_string() */ + free(orig_s); + result_item_print_modified_range(item, range, tb, iter); +} + +/** + * Printing a modified range_transition is special, for it spans + * multiple lines and has inline markers. + */ +static GtkTextBuffer *result_item_range_trans_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (form != POLDIFF_FORM_MODIFIED) { + return result_item_single_get_buffer(item, form); + } else { + util_text_buffer_clear(single_buffer); + result_item_print_header(item, single_buffer, form); + GtkTextIter iter; + const apol_vector_t *v; + size_t i; + poldiff_range_trans_t *rt; + + gtk_text_buffer_get_end_iter(single_buffer, &iter); + v = item->get_vector(item->diff); + for (i = 0; i < apol_vector_get_size(v); i++) { + rt = (poldiff_range_trans_t *) apol_vector_get_element(v, i); + if (item->get_form(rt) == form) { + result_item_range_trans_print_modified(item, rt, single_buffer, &iter); + } + } + return single_buffer; + } +} + +/* range trans are a subclass of single buffer item, in that they have + a special rendering function for modified items and that they have + add-type and remove-type forms */ +result_item_t *result_item_create_range_trans(GtkTextTagTable * table) +{ + result_item_t *item = result_item_single_create(table); + if (item == NULL) { + return item; + } + item->label = "Range Transitions"; + item->bit_pos = POLDIFF_DIFF_RANGE_TRANS; + item->get_vector = poldiff_get_range_trans_vector; + item->get_form = poldiff_range_trans_get_form; + item->get_string = poldiff_range_trans_to_string; + item->get_forms = result_item_role_trans_get_forms; /* [sic] */ + item->get_buffer = result_item_range_trans_get_buffer; + item->policy_changed = result_item_level_policy_changed; /* [sic] */ + return item; +} + +/******************** AV and Type rules below ********************/ + +/** + * Show line numbers if the policy has them. + */ +static void result_item_multi_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] = qpol_policy_has_capability(oq, QPOL_CAP_LINE_NUMBERS); + item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] = qpol_policy_has_capability(mq, QPOL_CAP_LINE_NUMBERS); +} + +/** + * Clear the cache whenever poldiff is (re-)run. + */ +static void result_item_multi_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental __attribute__ ((unused))) +{ + item->diff = diff; + memset(item->stats, 0, sizeof(item->stats)); + int i; + for (i = 0; i < 5; i++) { + item->data.multi.cached[i] = 0; + util_text_buffer_clear(item->data.multi.buffers[i]); + } +} + +/** + * Render the buffer if it has not yet been cached, then return it. + */ +static GtkTextBuffer *result_item_multi_get_buffer(result_item_t * item, poldiff_form_e form) +{ + GtkTextBuffer *tb; + if (form == POLDIFF_FORM_NONE) { + /* just use the global single_buffer when printing the + * summary */ + util_text_buffer_clear(single_buffer); + result_item_print_summary(item, single_buffer); + tb = single_buffer; + } else { + tb = item->data.multi.buffers[form_reverse_map[form]]; + if (!item->data.multi.cached[form_reverse_map[form]]) { + util_text_buffer_clear(tb); + result_item_print_header(item, tb, form); + item->data.multi.print_diff(item, tb, form); + item->data.multi.cached[form_reverse_map[form]] = 1; + } + } + return tb; +} + +/** + * If the item is cached or if there are less than 50 things to show, + * then rendering is considered to be fast. + */ +static int result_item_multi_is_render_slow(result_item_t * item, poldiff_form_e form) +{ + if (form == POLDIFF_FORM_NONE || item->data.multi.cached[form_reverse_map[form]] + || result_item_get_num_differences(item, form) < 50) { + return 0; + } + return 1; +} + +static void result_item_multi_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort, + results_sort_dir_e dir) +{ + if (item->sorts[form_reverse_map[form]] != sort || item->sort_dirs[form_reverse_map[form]] != dir) { + item->sorts[form_reverse_map[form]] = sort; + item->sort_dirs[form_reverse_map[form]] = dir; + item->data.multi.cached[form_reverse_map[form]] = 0; + } +} + +static void result_item_multi_destructor(result_item_t * item) +{ + size_t i; + for (i = 0; i < 5; i++) { + apol_vector_destroy(&item->data.multi.items[i]); + } +} + +/** + * Constructor for the abstract multi buffer item class. Multi-buffer + * items are capable of sorting and potentially take some time to + * render their buffers. + */ +static result_item_t *result_item_multi_create(GtkTextTagTable * table) +{ + result_item_t *item = calloc(1, sizeof(*item)); + if (item == NULL) { + return item; + } + if (single_buffer == NULL) { + single_buffer = gtk_text_buffer_new(table); + } + item->supported = 1; + item->destructor = result_item_multi_destructor; + item->policy_changed = result_item_multi_policy_changed; + item->poldiff_run = result_item_multi_poldiff_run; + item->get_buffer = result_item_multi_get_buffer; + item->is_render_slow = result_item_multi_is_render_slow; + item->get_forms = result_item_role_trans_get_forms; /* [sic] */ + item->set_current_sort = result_item_multi_set_current_sort; + int i; + for (i = 0; i < 5; i++) { + item->data.multi.buffers[i] = gtk_text_buffer_new(table); + item->sorts[i] = RESULTS_SORT_DEFAULT; + item->sort_dirs[i] = RESULTS_SORT_ASCEND; + } + return item; +} + +struct sort_opts +{ + poldiff_t *diff; + results_sort_e field; + results_sort_dir_e direction; +}; + +static int result_item_avrule_comp(const void *a, const void *b, void *data) +{ + const poldiff_avrule_t *a1 = a; + const poldiff_avrule_t *a2 = b; + struct sort_opts *opts = data; + const char *s1, *s2; + switch (opts->field) { + case RESULTS_SORT_SOURCE: + { + s1 = poldiff_avrule_get_source_type(a1); + s2 = poldiff_avrule_get_source_type(a2); + break; + } + case RESULTS_SORT_TARGET: + { + s1 = poldiff_avrule_get_target_type(a1); + s2 = poldiff_avrule_get_target_type(a2); + break; + } + case RESULTS_SORT_CLASS: + { + s1 = poldiff_avrule_get_object_class(a1); + s2 = poldiff_avrule_get_object_class(a2); + break; + } + case RESULTS_SORT_COND: + { + const qpol_cond_t *q1, *q2; + const apol_policy_t *p1, *p2; + uint32_t w1, w2; + poldiff_avrule_get_cond(opts->diff, a1, &q1, &w1, &p1); + poldiff_avrule_get_cond(opts->diff, a2, &q2, &w2, &p2); + if (q1 != q2) { + return opts->direction * ((char *)q1 - (char *)q2); + } + return opts->direction * (w1 - w2); + break; + } + default: + { + /* shouldn't get here */ + assert(0); + return 0; + } + } + return opts->direction * strcmp(s1, s2); +} + +static apol_vector_t *result_item_avrule_sort(result_item_t * item, poldiff_form_e form) +{ + const apol_vector_t *orig_v; + apol_vector_t *v; + size_t i; + void *elem; + struct sort_opts opts = { item->diff, item->sorts[form_reverse_map[form]], item->sort_dirs[form_reverse_map[form]] }; + + orig_v = item->get_vector(item->diff); + if ((v = apol_vector_create(NULL)) == NULL) { + return NULL; + } + for (i = 0; i < apol_vector_get_size(orig_v); i++) { + elem = apol_vector_get_element(orig_v, i); + if (poldiff_avrule_get_form(elem) == form && apol_vector_append(v, elem) < 0) { + apol_vector_destroy(&v); + return NULL; + } + } + if (opts.field != RESULTS_SORT_DEFAULT) { + apol_vector_sort(v, result_item_avrule_comp, &opts); + } + return v; +} + +static void result_item_avrule_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form) +{ + GtkTextIter iter; + size_t i; + void *elem; + char *s; + GString *string = g_string_new(""); + const apol_vector_t *syn_linenos; + apol_vector_t *rules = result_item_avrule_sort(item, form); + char *orig_prefix; + char *mod_prefix; + + apol_vector_destroy(&item->data.multi.items[form_reverse_map[form]]); + item->data.multi.items[form_reverse_map[form]] = rules; + gtk_text_buffer_get_end_iter(tb, &iter); + for (i = 0; i < apol_vector_get_size(rules); i++) { + elem = apol_vector_get_element(rules, i); + if ((s = poldiff_avrule_to_string(item->diff, elem)) == NULL) { + goto cleanup; + } + result_item_print_string_avrule(tb, &iter, s, 1); + if (form != POLDIFF_FORM_MODIFIED) { + orig_prefix = NULL; + mod_prefix = NULL; + } else { + orig_prefix = "op: "; + mod_prefix = "mp: "; + } + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] && + (syn_linenos = poldiff_avrule_get_orig_line_numbers((poldiff_avrule_t *) elem)) != NULL) { + result_item_print_linenos(tb, &iter, orig_prefix, syn_linenos, "line-pol_orig", string); + } + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] && + (syn_linenos = poldiff_avrule_get_mod_line_numbers((poldiff_avrule_t *) elem)) != NULL) { + result_item_print_linenos(tb, &iter, mod_prefix, syn_linenos, "line-pol_mod", string); + } + free(s); + gtk_text_buffer_insert(tb, &iter, "\n", -1); + } + cleanup: + g_string_free(string, TRUE); +} + +/** + * Only support neverallows if either policy (can) has them. + */ +static void result_item_avrule_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + item->supported = 1; + if (item->bit_pos == POLDIFF_DIFF_AVNEVERALLOW) { + qpol_policy_t *oq = apol_policy_get_qpol(orig_pol); + qpol_policy_t *mq = apol_policy_get_qpol(mod_pol); + int orig_type = -1, mod_type = -1; + // don't use capability to check if neverallows are + // supported, because policies are always loaded + // without them. libpoldiff could then reload the + // policies with neverallow, in which case they would + // become capable (but item->supported still claims to + // be 0) + qpol_policy_get_type(oq, &orig_type); + qpol_policy_get_type(mq, &mod_type); + if (orig_type == QPOL_POLICY_KERNEL_BINARY && mod_type == QPOL_POLICY_KERNEL_BINARY) { + item->supported = 0; + } + } + result_item_multi_policy_changed(item, orig_pol, mod_pol); +} + +/** + * Print an appropriate error message if neverallows are not supported. + */ +static GtkTextBuffer *result_item_avrule_get_buffer(result_item_t * item, poldiff_form_e form) +{ + if (!result_item_is_supported(item)) { + gtk_text_buffer_set_text(single_buffer, + "Neverallow diffs are not supported because both policies are kernel binaries.", -1); + return single_buffer; + } else { + return result_item_multi_get_buffer(item, form); + } +} + +static result_item_t *result_item_create_from_flag(GtkTextTagTable * table, uint32_t flag) +{ + result_item_t *item = result_item_multi_create(table); + if (item == NULL) { + return item; + } + const poldiff_component_record_t *rec = poldiff_get_component_record(flag); + item->label = poldiff_component_record_get_label(rec); + item->bit_pos = flag; + item->get_vector = poldiff_component_record_get_results_fn(rec); + item->get_form = poldiff_component_record_get_form_fn(rec); + item->get_string = poldiff_component_record_get_to_string_fn(rec); + item->get_buffer = result_item_avrule_get_buffer; + item->data.multi.print_diff = result_item_avrule_print_diff; + item->policy_changed = result_item_avrule_policy_changed; + return item; +} + +result_item_t *result_item_create_avrules_allow(GtkTextTagTable * table) +{ + return result_item_create_from_flag(table, POLDIFF_DIFF_AVALLOW); +} + +result_item_t *result_item_create_avrules_auditallow(GtkTextTagTable * table) +{ + return result_item_create_from_flag(table, POLDIFF_DIFF_AVAUDITALLOW); +} + +result_item_t *result_item_create_avrules_dontaudit(GtkTextTagTable * table) +{ + return result_item_create_from_flag(table, POLDIFF_DIFF_AVDONTAUDIT); +} + +result_item_t *result_item_create_avrules_neverallow(GtkTextTagTable * table) +{ + return result_item_create_from_flag(table, POLDIFF_DIFF_AVNEVERALLOW); +} + +static int result_item_terule_comp(const void *a, const void *b, void *data) +{ + const poldiff_terule_t *a1 = a; + const poldiff_terule_t *a2 = b; + struct sort_opts *opts = data; + const char *s1, *s2; + switch (opts->field) { + case RESULTS_SORT_SOURCE: + { + s1 = poldiff_terule_get_source_type(a1); + s2 = poldiff_terule_get_source_type(a2); + break; + } + case RESULTS_SORT_TARGET: + { + s1 = poldiff_terule_get_target_type(a1); + s2 = poldiff_terule_get_target_type(a2); + break; + } + case RESULTS_SORT_CLASS: + { + s1 = poldiff_terule_get_object_class(a1); + s2 = poldiff_terule_get_object_class(a2); + break; + } + case RESULTS_SORT_COND: + { + const qpol_cond_t *q1, *q2; + const apol_policy_t *p1, *p2; + uint32_t w1, w2; + poldiff_terule_get_cond(opts->diff, a1, &q1, &w1, &p1); + poldiff_terule_get_cond(opts->diff, a2, &q2, &w2, &p2); + if (q1 != q2) { + return opts->direction * ((char *)q1 - (char *)q2); + } + return opts->direction * (w1 - w2); + break; + } + default: + { + /* shouldn't get here */ + assert(0); + return 0; + } + } + return opts->direction * strcmp(s1, s2); +} + +static apol_vector_t *result_item_terule_sort(result_item_t * item, poldiff_form_e form) +{ + const apol_vector_t *orig_v; + apol_vector_t *v; + size_t i; + void *elem; + struct sort_opts opts = { item->diff, item->sorts[form_reverse_map[form]], item->sort_dirs[form_reverse_map[form]] }; + orig_v = item->get_vector(item->diff); + if ((v = apol_vector_create(NULL)) == NULL) { + return NULL; + } + for (i = 0; i < apol_vector_get_size(orig_v); i++) { + elem = apol_vector_get_element(orig_v, i); + if (poldiff_terule_get_form(elem) == form && apol_vector_append(v, elem) < 0) { + apol_vector_destroy(&v); + return NULL; + } + } + if (opts.field != RESULTS_SORT_DEFAULT) { + apol_vector_sort(v, result_item_terule_comp, &opts); + } + return v; +} + +static void result_item_terule_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form) +{ + GtkTextIter iter; + size_t i; + void *elem; + char *s; + GString *string = g_string_new(""); + apol_vector_t *syn_linenos; + apol_vector_t *rules = result_item_terule_sort(item, form); + char *orig_prefix; + char *mod_prefix; + + gtk_text_buffer_get_end_iter(tb, &iter); + for (i = 0; i < apol_vector_get_size(rules); i++) { + elem = apol_vector_get_element(rules, i); + if ((s = poldiff_terule_to_string(item->diff, elem)) == NULL) { + goto cleanup; + } + if (form != POLDIFF_FORM_MODIFIED) { + orig_prefix = NULL; + mod_prefix = NULL; + result_item_print_string(tb, &iter, s, 1); + } else { + orig_prefix = "op: "; + mod_prefix = "mp: "; + result_item_print_string_inline(tb, &iter, s, 1); + } + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] && + (syn_linenos = poldiff_terule_get_orig_line_numbers((poldiff_terule_t *) elem)) != NULL) { + result_item_print_linenos(tb, &iter, orig_prefix, syn_linenos, "line-pol_orig", string); + } + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] && + (syn_linenos = poldiff_terule_get_mod_line_numbers((poldiff_terule_t *) elem)) != NULL) { + result_item_print_linenos(tb, &iter, mod_prefix, syn_linenos, "line-pol_mod", string); + } + free(s); + gtk_text_buffer_insert(tb, &iter, "\n", -1); + } + cleanup: + apol_vector_destroy(&rules); + g_string_free(string, TRUE); +} + +static result_item_t *result_item_create_terules_from_flag(GtkTextTagTable * table, uint32_t flag) +{ + result_item_t *item = result_item_multi_create(table); + if (item == NULL) { + return item; + } + const poldiff_component_record_t *rec = poldiff_get_component_record(flag); + item->label = poldiff_component_record_get_label(rec); + item->bit_pos = flag; + item->get_vector = poldiff_component_record_get_results_fn(rec); + item->get_form = poldiff_component_record_get_form_fn(rec); + item->get_string = poldiff_component_record_get_to_string_fn(rec); + item->data.multi.print_diff = result_item_terule_print_diff; + return item; +} + +result_item_t *result_item_create_terules_change(GtkTextTagTable * table) +{ + return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TECHANGE); +} + +result_item_t *result_item_create_terules_member(GtkTextTagTable * table) +{ + return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TEMEMBER); +} + +result_item_t *result_item_create_terules_trans(GtkTextTagTable * table) +{ + return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TETRANS); +} + +/******************** public methods below ********************/ + +void result_item_destroy(result_item_t ** item) +{ + if (item != NULL && *item != NULL) { + if ((*item)->destructor != NULL) { + (*item)->destructor(*item); + } else { + free(*item); + } + *item = NULL; + } +} + +const char *result_item_get_label(const result_item_t * item) +{ + return item->label; +} + +void result_item_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol) +{ + if (item->policy_changed != NULL) { + item->policy_changed(item, orig_pol, mod_pol); + } +} + +GtkTextBuffer *result_item_get_buffer(result_item_t * item, poldiff_form_e form) +{ + return item->get_buffer(item, form); +} + +int result_item_is_render_slow(result_item_t * item, poldiff_form_e form) +{ + return item->is_render_slow(item, form); +} + +void result_item_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental) +{ + item->poldiff_run(item, diff, incremental); +} + +int result_item_is_supported(const result_item_t * item) +{ + return item->supported; +} + +void result_item_get_forms(result_item_t * item, int forms[5]) +{ + item->get_forms(item, forms); +} + +size_t result_item_get_num_differences(result_item_t * item, poldiff_form_e form) +{ + switch (form) { + case POLDIFF_FORM_ADDED: + return item->stats[0]; + case POLDIFF_FORM_REMOVED: + return item->stats[1]; + case POLDIFF_FORM_MODIFIED: + return item->stats[2]; + case POLDIFF_FORM_ADD_TYPE: + return item->stats[3]; + case POLDIFF_FORM_REMOVE_TYPE: + return item->stats[4]; + default: /* should never get here */ + assert(0); + return 0; + } +} + +int result_item_get_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e * sort, results_sort_dir_e * dir) +{ + if (item->set_current_sort == NULL || form == POLDIFF_FORM_NONE) { + return 0; + } + *sort = item->sorts[form_reverse_map[form]]; + *dir = item->sort_dirs[form_reverse_map[form]]; + return 1; +} + +void result_item_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir) +{ + if (item->set_current_sort != NULL && form != POLDIFF_FORM_NONE) { + item->set_current_sort(item, form, sort, dir); + } +} + +void result_item_save_current_line(result_item_t * item, poldiff_form_e form, gint offset) +{ + /* don't care about the summary page */ + if (form != POLDIFF_FORM_NONE) { + item->offsets[form_reverse_map[form]] = offset; + } +} + +gint result_item_get_current_line(result_item_t * item, poldiff_form_e form) +{ + /* don't care about the summary page */ + if (form == POLDIFF_FORM_NONE) { + return 0; + } + return item->offsets[form_reverse_map[form]]; +} + +static void result_item_on_orig_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + toplevel_t *top = (toplevel_t *) user_data; + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menuitem)); + unsigned long line = atoi(gtk_label_get_label(GTK_LABEL(label))) - 1; + toplevel_show_policy_line(top, SEDIFFX_POLICY_ORIG, line); +} + +static void result_item_on_mod_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + toplevel_t *top = (toplevel_t *) user_data; + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menuitem)); + unsigned long line = atoi(gtk_label_get_label(GTK_LABEL(label))) - 1; + toplevel_show_policy_line(top, SEDIFFX_POLICY_MOD, line); +} + +void result_item_inline_link_event(result_item_t * item, toplevel_t * top, GtkWidget * container, GdkEventButton * event, + poldiff_form_e form, int line_num, const char *s) +{ + /* for now, inline links only work for avrule items */ + assert(item->bit_pos & POLDIFF_DIFF_AVRULES); + const char *perm; + poldiff_form_e perm_form = form; + if (*s == '+' || *s == '-' || *s == '*') { + if (*s == '+') { + perm_form = POLDIFF_FORM_ADDED; + } else if (*s == '-') { + perm_form = POLDIFF_FORM_REMOVED; + } + perm = s + 1; + } else { + perm = s; + } + apol_vector_t *rules = item->data.multi.items[form_reverse_map[form]]; + size_t i = line_num - 3; /* subtract 3 because the header consume + * three lines in the text buffer */ + poldiff_avrule_t *rule = apol_vector_get_element(rules, i); + assert(rule != NULL); + + GtkMenu *menu = GTK_MENU(gtk_menu_new()); + GtkWidget *menuitem; + GString *string = g_string_new(""); + int button, event_time; + gtk_menu_set_title(menu, perm); + menuitem = gtk_menu_item_new_with_label(perm); + gtk_widget_set_sensitive(menuitem, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + menuitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + menuitem = gtk_menu_item_new_with_label("Original Policy"); + gtk_widget_set_sensitive(menuitem, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + if (perm_form == POLDIFF_FORM_REMOVED || perm_form == POLDIFF_FORM_REMOVE_TYPE || perm_form == POLDIFF_FORM_MODIFIED) { + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG]) { + apol_vector_t *v = poldiff_avrule_get_orig_line_numbers_for_perm(item->diff, rule, perm); + if (v != NULL && apol_vector_get_size(v) > 0) { + for (i = 0; i < apol_vector_get_size(v); i++) { + unsigned long line = (unsigned long)apol_vector_get_element(v, i); + g_string_printf(string, " %lu", line); + menuitem = gtk_menu_item_new_with_label(string->str); + g_signal_connect(menuitem, "activate", G_CALLBACK(result_item_on_orig_activate), top); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + } else { + menuitem = gtk_menu_item_new_with_label(" line numbers not available"); + gtk_widget_set_sensitive(menuitem, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + + menuitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + menuitem = gtk_menu_item_new_with_label("Modified Policy"); + gtk_widget_set_sensitive(menuitem, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + if (perm_form == POLDIFF_FORM_ADDED || perm_form == POLDIFF_FORM_ADD_TYPE || perm_form == POLDIFF_FORM_MODIFIED) { + if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD]) { + apol_vector_t *v = poldiff_avrule_get_mod_line_numbers_for_perm(item->diff, rule, perm); + if (v != NULL && apol_vector_get_size(v) > 0) { + for (i = 0; i < apol_vector_get_size(v); i++) { + unsigned long line = (unsigned long)apol_vector_get_element(v, i); + g_string_printf(string, " %lu", line); + menuitem = gtk_menu_item_new_with_label(string->str); + g_signal_connect(menuitem, "activate", G_CALLBACK(result_item_on_mod_activate), top); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + } else { + menuitem = gtk_menu_item_new_with_label(" line numbers not available"); + gtk_widget_set_sensitive(menuitem, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + g_string_free(string, TRUE); + if (event != NULL) { + button = event->button; + event_time = event->time; + } else { + button = 0; + event_time = gtk_get_current_event_time(); + } + gtk_menu_attach_to_widget(menu, container, NULL); + gtk_widget_show_all(GTK_WIDGET(menu)); + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, button, event_time); +} + +/******************** friend methods below ********************/ + +poldiff_t *result_item_get_diff(result_item_t * item) +{ + return item->diff; +} + +const apol_vector_t *result_item_get_vector(result_item_t * item) +{ + return item->get_vector(item->diff); +} + +poldiff_form_e result_item_get_form(result_item_t * item, void *elem) +{ + return item->get_form(elem); +} + +char *result_item_get_string(result_item_t * item, void *elem) +{ + return item->get_string(item->diff, elem); +} diff --git a/sediff/result_item.h b/sediff/result_item.h new file mode 100644 index 0000000..466b1f9 --- /dev/null +++ b/sediff/result_item.h @@ -0,0 +1,251 @@ +/** + * @file + * Header for showing a diff result for a single component. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef RESULT_ITEM_H +#define RESULT_ITEM_H + +typedef struct result_item result_item_t; + +#include "results.h" + +#include <gtk/gtk.h> +#include <poldiff/poldiff.h> +#include <poldiff/component_record.h> + +/* constructors for various result items */ + +result_item_t *result_item_create_classes(GtkTextTagTable * table); +result_item_t *result_item_create_commons(GtkTextTagTable * table); +result_item_t *result_item_create_levels(GtkTextTagTable * table); +result_item_t *result_item_create_categories(GtkTextTagTable * table); +result_item_t *result_item_create_types(GtkTextTagTable * table); +result_item_t *result_item_create_attributes(GtkTextTagTable * table); +result_item_t *result_item_create_roles(GtkTextTagTable * table); +result_item_t *result_item_create_users(GtkTextTagTable * table); +result_item_t *result_item_create_booleans(GtkTextTagTable * table); + +result_item_t *result_item_create_avrules_allow(GtkTextTagTable * table); +result_item_t *result_item_create_avrules_auditallow(GtkTextTagTable * table); +result_item_t *result_item_create_avrules_dontaudit(GtkTextTagTable * table); +result_item_t *result_item_create_avrules_neverallow(GtkTextTagTable * table); + +result_item_t *result_item_create_terules_change(GtkTextTagTable * table); +result_item_t *result_item_create_terules_member(GtkTextTagTable * table); +result_item_t *result_item_create_terules_trans(GtkTextTagTable * table); + +result_item_t *result_item_create_role_allows(GtkTextTagTable * table); +result_item_t *result_item_create_role_trans(GtkTextTagTable * table); +result_item_t *result_item_create_range_trans(GtkTextTagTable * table); + +/** + * Deallocate all space associated with a result item, including the + * pointer itself. Does nothing if the pointer is already set to NULL. + * + * @param item Reference to the item to destroy. Afterwards it will + * be set to NULL. + */ +void result_item_destroy(result_item_t ** item); + +/** + * Get the title case label for this result item. The string will be + * used for printing. + * + * @param item Result item to query. + * + * @return Name of this result item. + */ +const char *result_item_get_label(const result_item_t * item); + +/** + * Function to update a result item whenever the policies are changed. + * The result item can then configure its own rendering routine. + * + * @param item Result item to modify based upon the policies. + * @param orig_pol Original policy to diff. + * @param mod_pol Modified policy to diff. + */ +void result_item_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol); + +/** + * Function to update a result item whenever the user (re)runs the + * diff. This will clear the result item's cache as necessary. + * + * @param item Result item to update based upon the poldiff object. + * @param diff Poldiff item that was (re)run. + * @param incremental If non-zero, the diff was incrementally run; + * existing results should not be destroyed. + */ +void result_item_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental); + +/** + * Return a text buffer that contains the rendered results for a + * particular policy component's form. This will re-render the buffer + * as necessary. + * + * @param item Result item whose display to obtain. + * @param form Form to display, or POLDIFF_FORM_NONE if just the + * summary is requested. + * + * @return A text buffer containing the results. + */ +GtkTextBuffer *result_item_get_buffer(result_item_t * item, poldiff_form_e form); + +/** + * If it will take a "significant" amount of time (where "significant" + * is some arbitrary amount) to render a buffer then sediffx will + * display a progress dialog while working. This function returns + * non-zero if it will be significantly long, 0 or not. This function + * will be called prior to result_item_get_buffer(). + * + * @param item Result item to query. + * @param form Form that will be displayed, or POLDIFF_FORM_NONE if + * just the summary is requested. + * + * @return Non-zero if a progress dialog should be displayed, zero if + * not. + */ +int result_item_is_render_slow(result_item_t * item, poldiff_form_e form); + +/** + * Determine if a result item is capable of being run according to the + * given policies. For example, for binary policies prior to version + * 20, it is not possible to have modified types. Note that this does + * not necessarily mean the item has been run yet, for libpoldiff + * supports incremental diffing. + * + * @param item Result item to query. + * @param form Particular form to check if it is capable of being run + * or not. + * + * @return Non-zero if the result item could be run, zero if not. + */ +int result_item_is_supported(const result_item_t * item); + +/** + * Get a list of forms that were actually run. The result is an array + * of 5 integers, each corresponding to the five poldiff forms (added, + * add by type, removed, remove by type, modified). For each form, + * the possible values are: + * + * <dl> + * <dt>less than zero + * <dd>form is not supported + * <dt>zero + * <dd>form was not run + * <dt>greater than zero + * <dd>form was run + * </dl> + * + * @param item Result item to query. + * @param diff Diff structure that was run. + * @param forms Array into which write results. + */ +void result_item_get_forms(result_item_t * item, int forms[5]); + +/** + * Get the number of differences for a particular form. + * + * @param item Result item to query. + * @param form Difference form to query. + * + * @return Number of differences, or zero if the result item is not + * support or was not run. + */ +size_t result_item_get_num_differences(result_item_t * item, poldiff_form_e form); + +/** + * Get the current sorting algorithm and sort direction for the given + * result item. + * + * @param item Result item to query. + * @param form Form whose sort algorithm and direction to get. + * @param sort Reference to where to write the current sorting algorithm. + * @param dir Reference to where to write the current sorting direction. + * + * @return Non-zero if the result item supports sorting, zero if it + * does not. + */ +int result_item_get_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e * sort, results_sort_dir_e * dir); + +/** + * Set the current sorting algorithm and sort direction for the given + * result item. The next time result_item_get_buffer() is called the + * contents of the buffer will be updated as necessary. (This + * function does not update the buffer.) + * + * @param item Result item to modify. + * @param form Form whose sort algorithm and direction to set. + * @param sort New sorting algorithm. + * @param dir New sorting direction. + */ +void result_item_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir); + +/** + * Tell the result item to store a particular line offset for the + * given form. + * + * @param item Result item to modify. + * @param form Particular form's line number to store. + * @param offset Line number to store. + */ +void result_item_save_current_line(result_item_t * item, poldiff_form_e form, gint offset); + +/** + * Return the saved line number for a result item's particular form. + * If a line number has not yet been saved then return 0. + * + * @param item Result item to query. + * @param form Form whose line number to retrieve. + * + * @return The stored line number. + */ +gint result_item_get_current_line(result_item_t * item, poldiff_form_e form); + +/** + * Callback invoked by results_t whenever a inlink-link tag was + * clicked. This will pop a menu that will let the user jump to the + * exact line in the policy that contains that string. + * + * @param item Result item upon which the event occurred. + * @param top Toplevel containing policy sources. + * @param container Containing GTK widget within which the result item + * is being displayed. + * @param event GdkEvent that gives the button used to click on the + * inlink link, or NULL if the event was generated by hitting the Menu + * button. + * @param form Form upon which the event occurred. + * @param line_num Line number of the text buffer where the clicked occurred. + * @param s The string that was clicked. + */ +void result_item_inline_link_event(result_item_t * item, toplevel_t * top, GtkWidget * container, GdkEventButton * event, + poldiff_form_e form, int line_num, const char *s); + +/* these next three functions exist because C has no concept of + * 'friend' like in C++; result_item_render needs access to three + * fields within the result_item. */ +poldiff_t *result_item_get_diff(result_item_t * item); +const apol_vector_t *result_item_get_vector(result_item_t * item); +poldiff_form_e result_item_get_form(result_item_t * item, void *elem); +char *result_item_get_string(result_item_t * item, void *elem); + +#endif diff --git a/sediff/result_item_render.c b/sediff/result_item_render.c new file mode 100644 index 0000000..36fbbfb --- /dev/null +++ b/sediff/result_item_render.c @@ -0,0 +1,421 @@ +/** + * @file + * Common rendering routines for result items. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "result_item_render.h" + +#include <assert.h> + +static const char *form_name_map[] = { + "Added", "Added New Type", "Removed", "Removed Missing Type", "Modified" +}; +static const char *form_name_long_map[] = { + "Added", "Added because of new type", "Removed", "Removed because of missing type", "Modified" +}; +static const char *tag_map[] = { + "added-header", "added-header", "removed-header", "removed-header", "modified-header" +}; +static const poldiff_form_e form_map[] = { + POLDIFF_FORM_ADDED, POLDIFF_FORM_ADD_TYPE, + POLDIFF_FORM_REMOVED, POLDIFF_FORM_REMOVE_TYPE, + POLDIFF_FORM_MODIFIED +}; + +/** + * Show a single diff item string. This will add the appropriate + * color tags based upon the item's first non-space character. + */ +void result_item_print_string(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level) +{ + const char *c; + unsigned int i; + size_t start = 0, end = 0; + static const char *indent = "\t"; + const gchar *tag = NULL; + for (i = 0; i < indent_level; i++) { + gtk_text_buffer_insert(tb, iter, indent, -1); + } + for (c = s; *c && tag == NULL; c++) { + switch (*c) { + case '+': + { + tag = "added"; + break; + } + case '-': + { + tag = "removed"; + break; + } + case ' ': + case '\t': + case '\n': + { + break; + } + default: + { + tag = "modified"; + break; + } + } + } + for (c = s; *c; c++, end++) { + if (*c == '\n' && *(c + 1) != '\0') { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, tag, NULL); + for (i = 0; i < indent_level; i++) { + gtk_text_buffer_insert(tb, iter, indent, -1); + } + start = end + 1; + } + } + if (start < end) { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, tag, NULL); + } +} + +void result_item_print_string_inline(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level) +{ + const char *c = s; + unsigned int i; + size_t start = 0, end = 0; + static const char *indent = "\t"; + const gchar *current_tag = "modified"; + for (i = 0; i < indent_level; i++) { + gtk_text_buffer_insert(tb, iter, indent, -1); + } + for (; *c; c++, end++) { + switch (*c) { + case '+': + { + if (end > 0) { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL); + } + start = end; + current_tag = "added"; + break; + } + case '-': + { + if (end > 0) { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL); + } + start = end; + current_tag = "removed"; + break; + } + case '\n': + { + if (*(c + 1) != '\0') { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, current_tag, NULL); + for (i = 0; i < indent_level; i++) { + gtk_text_buffer_insert(tb, iter, indent, -1); + } + start = end + 1; + } + break; + } + case ' ': + { + if (current_tag != "modified") { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, current_tag, NULL); + start = end + 1; + current_tag = "modified"; + } + break; + } + } + } + if (start < end) { + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL); + } +} + +void result_item_print_string_avrule(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level) +{ + const char *c, *next_c; + const char *tag = NULL, *init_tag; + unsigned int i; + static const char *indent = "\t"; + for (i = 0; i < indent_level; i++) { + gtk_text_buffer_insert(tb, iter, indent, -1); + } + for (c = s; *c && tag == NULL; c++) { + switch (*c) { + case '+': + { + tag = "added"; + break; + } + case '-': + { + tag = "removed"; + break; + } + case ' ': + case '\t': + case '\n': + { + break; + } + default: + { + tag = "modified"; + break; + } + } + } + init_tag = tag; + c = strchr(s, '{'); + assert(c != NULL); + /* advance past the opening brace (which designates start of + * the permissions list) and the following space character */ + c += 2; + gtk_text_buffer_insert_with_tags_by_name(tb, iter, s, c - s, init_tag, NULL); + while (*c != '}') { + assert(*c != '\0'); + next_c = strchr(c, ' '); + assert(next_c != NULL); + switch (*c) { + case '+': + { + tag = "added"; + break; + } + case '-': + { + tag = "removed"; + break; + } + default: + { + tag = init_tag; + } + } + gtk_text_buffer_insert_with_tags_by_name(tb, iter, c, next_c - c, tag, "inline-link", NULL); + gtk_text_buffer_insert_with_tags_by_name(tb, iter, " ", 1, init_tag, NULL); + /* advance past the space */ + c = next_c + 1; + } + gtk_text_buffer_insert_with_tags_by_name(tb, iter, c, -1, init_tag, NULL); +} + +void result_item_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form) +{ + GtkTextIter iter; + const apol_vector_t *v; + size_t i; + void *elem; + char *s = NULL; + + gtk_text_buffer_get_end_iter(tb, &iter); + v = result_item_get_vector(item); + for (i = 0; i < apol_vector_get_size(v); i++) { + elem = apol_vector_get_element(v, i); + if (result_item_get_form(item, elem) == form) { + s = result_item_get_string(item, elem); + result_item_print_string(tb, &iter, s, 1); + free(s); + gtk_text_buffer_insert(tb, &iter, "\n", -1); + } + } +} + +void result_item_print_rule_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form) +{ + GtkTextIter iter; + const apol_vector_t *v; + size_t i; + void *elem; + char *s = NULL; + + gtk_text_buffer_get_end_iter(tb, &iter); + v = result_item_get_vector(item); + for (i = 0; i < apol_vector_get_size(v); i++) { + elem = apol_vector_get_element(v, i); + if (result_item_get_form(item, elem) == form) { + s = result_item_get_string(item, elem); + if (form != POLDIFF_FORM_MODIFIED) { + result_item_print_string(tb, &iter, s, 1); + } else { + result_item_print_string_inline(tb, &iter, s, 1); + } + free(s); + gtk_text_buffer_insert(tb, &iter, "\n", -1); + } + } +} + +void result_item_print_summary(result_item_t * item, GtkTextBuffer * tb) +{ + GtkTextIter iter; + int i, forms[5]; + GString *string = g_string_new(""); + + gtk_text_buffer_get_end_iter(tb, &iter); + g_string_printf(string, "%s:\n", result_item_get_label(item)); + gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, "subheader", NULL); + + int was_run = 0; + result_item_get_forms(item, forms); + for (i = 0; i < 5; i++) { + if (forms[i] > 0) { + g_string_printf(string, "\t%s: %zd\n", + form_name_long_map[i], result_item_get_num_differences(item, form_map[i])); + gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, tag_map[i], NULL); + was_run = 1; + } + } + if (!was_run && result_item_is_supported(item)) { + gtk_text_buffer_set_text(tb, "This component has not yet been diffed.", -1); + } + g_string_free(string, TRUE); +} + +/** + * Show a common header when printing a policy component diff. + */ +void result_item_print_header(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form) +{ + GtkTextIter iter; + int i, forms[5]; + GString *string = g_string_new(""); + char *tag = NULL; + const char *label = result_item_get_label(item); + int add_separator = 0; + + gtk_text_buffer_get_end_iter(tb, &iter); + result_item_get_forms(item, forms); + g_string_printf(string, "%s (", label); + for (i = 0; i < 5; i++) { + if (forms[i] > 0) { + g_string_append_printf(string, "%s%zd %s", + (add_separator ? ", " : ""), + result_item_get_num_differences(item, form_map[i]), form_name_map[i]); + add_separator = 1; + } + } + g_string_append_printf(string, ")\n\n"); + gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, "header", NULL); + + switch (form) { + case POLDIFF_FORM_ADDED: + { + g_string_printf(string, "Added %s:", label); + tag = "added-header"; + break; + } + case POLDIFF_FORM_ADD_TYPE: + { + g_string_printf(string, "Added %s because of new type:", label); + tag = "added-header"; + break; + } + case POLDIFF_FORM_REMOVED: + { + g_string_printf(string, "Removed %s:", label); + tag = "removed-header"; + break; + } + case POLDIFF_FORM_REMOVE_TYPE: + { + g_string_printf(string, "Removed %s because of missing type:", label); + tag = "removed-header"; + break; + } + case POLDIFF_FORM_MODIFIED: + { + g_string_printf(string, "Modified %s:", label); + tag = "modified-header"; + break; + } + default: + { + assert(0); + tag = NULL; + } + } + g_string_append_printf(string, " %zd\n", result_item_get_num_differences(item, form)); + gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, tag, NULL); + g_string_free(string, TRUE); +} + +void result_item_print_linenos(GtkTextBuffer * tb, GtkTextIter * iter, + const gchar * prefix, const apol_vector_t * linenos, const gchar * tag, GString * string) +{ + size_t i; + unsigned long lineno; + gtk_text_buffer_insert(tb, iter, " [", -1); + if (prefix != NULL) { + gtk_text_buffer_insert(tb, iter, prefix, -1); + } + for (i = 0; i < apol_vector_get_size(linenos); i++) { + lineno = (unsigned long)apol_vector_get_element(linenos, i); + if (i > 0) { + gtk_text_buffer_insert(tb, iter, ", ", -1); + } + g_string_printf(string, "%lu", lineno); + gtk_text_buffer_insert_with_tags_by_name(tb, iter, string->str, -1, tag, NULL); + } + gtk_text_buffer_insert(tb, iter, "]", -1); +} + +void result_item_print_modified_range(result_item_t * item, const poldiff_range_t * range, GtkTextBuffer * tb, GtkTextIter * iter) +{ + poldiff_t *diff = result_item_get_diff(item); + char *orig_s = poldiff_range_to_string_brief(diff, range); + char *next_s = orig_s; + GString *string = g_string_new(""); + + /* first line should always be printed with normal font */ + char *s = strsep(&next_s, "\n"); + result_item_print_string(tb, iter, s, 1); + gtk_text_buffer_insert(tb, iter, "\n", -1); + + /* if the next line is minimum category set differences then + * display it */ + if (strncmp(next_s, " minimum categories:", strlen(" minimum categories:")) == 0) { + s = strsep(&next_s, "\n"); + result_item_print_string_inline(tb, iter, s, 1); + gtk_text_buffer_insert(tb, iter, "\n", -1); + } + /* all subsequent lines are printed as normal (yes, this + * discards lines from poldiff_range_to_string_brief() */ + free(orig_s); + apol_vector_t *levels = poldiff_range_get_levels(range); + size_t i; + for (i = 0; i < apol_vector_get_size(levels); i++) { + poldiff_level_t *l = apol_vector_get_element(levels, i); + s = poldiff_level_to_string_brief(diff, l); + g_string_printf(string, " %s", s); + if (poldiff_level_get_form(l) != POLDIFF_FORM_MODIFIED) { + result_item_print_string(tb, iter, string->str, 1); + } else { + result_item_print_string_inline(tb, iter, string->str, 1); + } + free(s); + } + g_string_free(string, TRUE); +} diff --git a/sediff/result_item_render.h b/sediff/result_item_render.h new file mode 100644 index 0000000..e593550 --- /dev/null +++ b/sediff/result_item_render.h @@ -0,0 +1,79 @@ +/** + * @file + * Header for rendering a result item. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef RESULT_ITEM_RENDER_H +#define RESULT_ITEM_RENDER_H + +#include "result_item.h" + +/** + * Show a single diff item string. This will add the appropriate + * color tags based upon the item's first non-space character. + */ +void result_item_print_string(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level); + +/** + * Print a string to a text buffer. Note that this differs from the + * more general results_print_string() because there are inline '+' + * and '-' markers. + */ +void result_item_print_string_inline(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level); + +/** + * Show the results for a single AV rule diff string. + */ +void result_item_print_string_avrule(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level); + +/** + * Show the results for non-rules diff components. + */ +void result_item_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form); + +/** + * Show the results for rules diff components. + */ +void result_item_print_rule_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form); + +/** + * Show a summary of the diff for a particular policy component. + */ +void result_item_print_summary(result_item_t * item, GtkTextBuffer * tb); + +/** + * Show a common header when printing a policy component diff. + */ +void result_item_print_header(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form); + +/** + * Given a vector of unsigned long integers, write to the text buffer + * those line numbers using the given tag. + */ +void result_item_print_linenos(GtkTextBuffer * tb, GtkTextIter * iter, + const gchar * prefix, const apol_vector_t * linenos, const gchar * tag, GString * string); + +/** + * Show the results for a modified range. + */ +void result_item_print_modified_range(result_item_t * item, const poldiff_range_t * range, GtkTextBuffer * tb, GtkTextIter * iter); + +#endif diff --git a/sediff/results.c b/sediff/results.c new file mode 100644 index 0000000..f19689f --- /dev/null +++ b/sediff/results.c @@ -0,0 +1,708 @@ +/** + * @file + * Routines for displaying the results after running poldiff. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "result_item.h" +#include "results.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <glade/glade.h> +#include <qpol/cond_query.h> + +enum +{ + RESULTS_SUMMARY_COLUMN_LABEL = 0, + RESULTS_SUMMARY_COLUMN_FORM, + RESULTS_SUMMARY_COLUMN_ITEM, + RESULTS_SUMMARY_COLUMN_STYLE, + RESULTS_SUMMARY_COLUMN_NUM +}; + +#define NUM_RESULT_ITEMS 19 + +struct results +{ + toplevel_t *top; + GladeXML *xml; + GtkTreeStore *summary_tree; + GtkTreeView *summary_view; + GtkTextBuffer *main_buffer, *key_buffer; + GtkTextView *view; + GtkTextTag *policy_orig_tag, *policy_mod_tag; + GtkLabel *stats; + /** pointer to within items[] of the currently selected result + * item, or NULL if the summary page or none are selected */ + result_item_t *current_item; + /** form that is currently selected, or POLDIFF_FORM_NONE if + * the summary or item's summary page is selected */ + poldiff_form_e current_form; + /** saved cursor's line number for the summary page */ + gint summary_offset; + result_item_t *items[NUM_RESULT_ITEMS]; +}; + +static const poldiff_form_e form_map[] = { + POLDIFF_FORM_ADDED, POLDIFF_FORM_ADD_TYPE, + POLDIFF_FORM_REMOVED, POLDIFF_FORM_REMOVE_TYPE, + POLDIFF_FORM_MODIFIED +}; + +/** + * Array or result_item constructors. Note that the order given below + * governs the order that the items appear in the results summary + * tree. + */ +static result_item_t *(*result_item_constructors[NUM_RESULT_ITEMS]) (GtkTextTagTable *) = { +result_item_create_commons, result_item_create_classes, + result_item_create_levels, result_item_create_categories, + result_item_create_types, result_item_create_attributes, + result_item_create_roles, result_item_create_users, + result_item_create_booleans, + result_item_create_avrules_allow, + result_item_create_avrules_auditallow, + result_item_create_avrules_dontaudit, + result_item_create_avrules_neverallow, + result_item_create_terules_change, + result_item_create_terules_member, + result_item_create_terules_trans, + result_item_create_role_allows, result_item_create_role_trans, result_item_create_range_trans}; + +static void results_summary_on_change(GtkTreeSelection * selection, gpointer user_data); + +static gboolean results_on_inline_link_event(GtkTextTag * tag, GObject * event_object, + GdkEvent * event, const GtkTextIter * iter, gpointer user_data); +static gboolean results_on_line_event(GtkTextTag * tag, GObject * event_object, + GdkEvent * event, const GtkTextIter * iter, gpointer user_data); +static gboolean results_on_popup_menu(GtkWidget * widget, gpointer user_data); +static gboolean results_on_text_view_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data); +/** + * Callback whenever the user double-clicks a row in the summary tree. + */ +static void results_summary_on_row_activate(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column, + gpointer user_data); + +/** + * Build a GTK tree store to hold the summary table of contents; then + * add that (empty) tree to the tree view. + */ +static void results_create_summary(results_t * r) +{ + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + r->summary_tree = gtk_tree_store_new(RESULTS_SUMMARY_COLUMN_NUM, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_BOOLEAN); + r->summary_view = GTK_TREE_VIEW(glade_xml_get_widget(r->xml, "toplevel summary view")); + assert(r->summary_view != NULL); + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_title(col, "Differences"); + gtk_tree_view_append_column(r->summary_view, col); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_set_attributes(col, renderer, + "text", RESULTS_SUMMARY_COLUMN_LABEL, + "strikethrough", RESULTS_SUMMARY_COLUMN_STYLE, NULL);; + gtk_tree_view_set_model(r->summary_view, GTK_TREE_MODEL(r->summary_tree)); + + selection = gtk_tree_view_get_selection(r->summary_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(results_summary_on_change), r); + g_signal_connect(r->summary_view, "row-activated", G_CALLBACK(results_summary_on_row_activate), r); +} + +results_t *results_create(toplevel_t * top) +{ + results_t *r; + int i; + GtkTextTagTable *tag_table; + GtkTextAttributes *attr; + GtkTextView *text_view; + gint size; + PangoTabArray *tabs; + + if ((r = calloc(1, sizeof(*r))) == NULL) { + return NULL; + } + r->top = top; + r->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(r->top))); + results_create_summary(r); + + tag_table = gtk_text_tag_table_new(); + r->main_buffer = gtk_text_buffer_new(tag_table); + gtk_text_buffer_create_tag(r->main_buffer, "header", "style", PANGO_STYLE_ITALIC, "weight", PANGO_WEIGHT_BOLD, "family", + NULL, NULL); + gtk_text_buffer_create_tag(r->main_buffer, "subheader", "weight", PANGO_WEIGHT_BOLD, "underline", PANGO_UNDERLINE_SINGLE, + "family", NULL, NULL); + gtk_text_buffer_create_tag(r->main_buffer, "removed-header", "foreground", "red", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(r->main_buffer, "added-header", "foreground", "dark green", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(r->main_buffer, "modified-header", "foreground", "dark blue", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(r->main_buffer, "removed", "foreground", "red", NULL); + gtk_text_buffer_create_tag(r->main_buffer, "added", "foreground", "dark green", NULL); + gtk_text_buffer_create_tag(r->main_buffer, "modified", "foreground", "dark blue", NULL); + GtkTextTag *inline_tag = gtk_text_buffer_create_tag(r->main_buffer, "inline-link", NULL); + g_signal_connect_after(G_OBJECT(inline_tag), "event", G_CALLBACK(results_on_inline_link_event), r); + r->policy_orig_tag = gtk_text_buffer_create_tag(r->main_buffer, "line-pol_orig", + "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_signal_connect_after(G_OBJECT(r->policy_orig_tag), "event", G_CALLBACK(results_on_line_event), r); + r->policy_mod_tag = gtk_text_buffer_create_tag(r->main_buffer, "line-pol_mod", + "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_signal_connect_after(G_OBJECT(r->policy_mod_tag), "event", G_CALLBACK(results_on_line_event), r); + + r->view = GTK_TEXT_VIEW(glade_xml_get_widget(r->xml, "toplevel results view")); + assert(r->view != NULL); + g_signal_connect(G_OBJECT(r->view), "popup-menu", G_CALLBACK(results_on_popup_menu), r); + g_signal_connect(G_OBJECT(r->view), "motion-notify-event", G_CALLBACK(results_on_text_view_motion), r); + attr = gtk_text_view_get_default_attributes(r->view); + size = pango_font_description_get_size(attr->font); + tabs = pango_tab_array_new_with_positions(4, + FALSE, + PANGO_TAB_LEFT, 3 * size, + PANGO_TAB_LEFT, 6 * size, PANGO_TAB_LEFT, 9 * size, PANGO_TAB_LEFT, 12 * size); + gtk_text_view_set_tabs(r->view, tabs); + gtk_text_view_set_buffer(r->view, r->main_buffer); + + r->key_buffer = gtk_text_buffer_new(tag_table); + text_view = GTK_TEXT_VIEW(glade_xml_get_widget(r->xml, "toplevel key view")); + assert(text_view != NULL); + gtk_text_view_set_buffer(text_view, r->key_buffer); + + PangoFontDescription *font_desc = pango_font_description_new(); + pango_font_description_set_family_static(font_desc, "monospace"); + gtk_widget_modify_font(GTK_WIDGET(r->view), font_desc); + gtk_widget_modify_font(GTK_WIDGET(text_view), font_desc); + pango_font_description_free(font_desc); + + r->stats = GTK_LABEL((glade_xml_get_widget(r->xml, "toplevel stats label"))); + assert(r->stats != NULL); + gtk_label_set_text(r->stats, ""); + + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + if ((r->items[i] = result_item_constructors[i] (tag_table)) == NULL) { + results_destroy(&r); + return NULL; + } + } + return r; +} + +void results_destroy(results_t ** r) +{ + if (r != NULL && *r != NULL) { + int i; + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + result_item_destroy(&((*r)->items[i])); + } + free(*r); + *r = NULL; + } +} + +void results_open_policies(results_t * r, apol_policy_t * orig, apol_policy_t * mod) +{ + int i; + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + result_item_policy_changed(r->items[i], orig, mod); + } +} + +void results_clear(results_t * r) +{ + gtk_tree_store_clear(r->summary_tree); + gtk_text_view_set_buffer(r->view, r->main_buffer); + util_text_buffer_clear(r->main_buffer); + util_text_buffer_clear(r->key_buffer); + gtk_label_set_text(r->stats, ""); + r->current_item = NULL; + r->current_form = POLDIFF_FORM_NONE; + r->summary_offset = 0; +} + +/** + * Update the summary tree and summary buffer to reflect the number of + * items added/removed/modified. + */ +static void results_update_summary(results_t * r) +{ + GtkTreeIter topiter, childiter; + GtkTextIter iter; + size_t sum_diffs; + int i, j, forms[5]; + GString *s = g_string_new(""); + + gtk_tree_store_append(r->summary_tree, &topiter, NULL); + gtk_tree_store_set(r->summary_tree, &topiter, + RESULTS_SUMMARY_COLUMN_LABEL, "Summary", + RESULTS_SUMMARY_COLUMN_FORM, POLDIFF_FORM_NONE, RESULTS_SUMMARY_COLUMN_ITEM, NULL, -1); + gtk_text_buffer_get_start_iter(r->main_buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, "Policy Difference Statistics\n", -1, "header", NULL); + static const char *form_name_short_map[] = { + "Added", "Added Type", "Removed", "Removed Type", "Modified" + }; + static const char *form_name_long_map[] = { + "Added", "Added because of new type", "Removed", "Removed because of missing type", "Modified" + }; + static const char *tag_map[] = { + "added-header", "added-header", "removed-header", "removed-header", "modified-header" + }; + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + const char *label = result_item_get_label(r->items[i]); + gtk_tree_store_append(r->summary_tree, &topiter, NULL); + gtk_tree_store_set(r->summary_tree, &topiter, + RESULTS_SUMMARY_COLUMN_FORM, POLDIFF_FORM_NONE, RESULTS_SUMMARY_COLUMN_ITEM, r->items[i], -1); + if (result_item_is_supported(r->items[i])) { + result_item_get_forms(r->items[i], forms); + sum_diffs = 0; + g_string_printf(s, "\n%s:\n", label); + gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, s->str, -1, "subheader", NULL); + int was_run = 0; + for (j = 0; j < 5; j++) { + if (forms[j] > 0) { + size_t num_diffs; + num_diffs = result_item_get_num_differences(r->items[i], form_map[j]); + g_string_printf(s, "\t%s: %zd\n", form_name_long_map[j], num_diffs); + gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, s->str, -1, tag_map[j], + NULL); + sum_diffs += num_diffs; + gtk_tree_store_append(r->summary_tree, &childiter, &topiter); + g_string_printf(s, "%s (%zd)", form_name_short_map[j], num_diffs); + gtk_tree_store_set(r->summary_tree, &childiter, + RESULTS_SUMMARY_COLUMN_LABEL, s->str, + RESULTS_SUMMARY_COLUMN_FORM, form_map[j], + RESULTS_SUMMARY_COLUMN_ITEM, r->items[i], -1); + was_run = 1; + } + } + if (!was_run) { + g_string_printf(s, "%s (\?\?\?)", label); + } else { + g_string_printf(s, "%s (%zd)", label, sum_diffs); + } + gtk_tree_store_set(r->summary_tree, &topiter, + RESULTS_SUMMARY_COLUMN_LABEL, s->str, RESULTS_SUMMARY_COLUMN_STYLE, FALSE, -1); + } else { + /* item is not supported at all */ + g_string_printf(s, "%s (N/A)", label); + gtk_tree_store_set(r->summary_tree, &topiter, + RESULTS_SUMMARY_COLUMN_LABEL, s->str, RESULTS_SUMMARY_COLUMN_STYLE, FALSE, -1); + } + } + + g_string_free(s, TRUE); +} + +/** + * Show the legend of the symbols used in results displays. + */ +static void results_populate_key_buffer(results_t * r) +{ + GString *string = g_string_new(""); + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(r->key_buffer, &iter); + + g_string_printf(string, " Added(+):\n Items added in\n modified policy.\n\n"); + gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "added", NULL); + g_string_printf(string, " Removed(-):\n Items removed\n from original\n policy.\n\n"); + gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "removed", NULL); + g_string_printf(string, " Modified(*):\n Items modified\n from original\n policy to\n modified policy."); + gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "modified", NULL); + g_string_free(string, TRUE); +} + +/** + * Populate the status bar with summary info of our diff. + */ +static void results_update_stats(results_t * r) +{ + GString *string = g_string_new(""); + int i, j, forms[5]; + size_t diffs[5] = { 0, 0, 0, 0, 0 }; + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + if (result_item_is_supported(r->items[i])) { + const char *label; + result_item_get_forms(r->items[i], forms); + label = result_item_get_label(r->items[i]); + for (j = 0; j < 5; j++) { + if (forms[j] > 0) { + diffs[j] += result_item_get_num_differences(r->items[i], form_map[j]); + } + } + } + } + g_string_printf(string, "Total Differences: %zd", diffs[0] + diffs[1] + diffs[2] + diffs[3] + diffs[4]); + gtk_label_set_text(r->stats, string->str); + g_string_free(string, TRUE); +} + +void results_update(results_t * r) +{ + int i, j, forms[5], was_diff_run = 0; + poldiff_t *diff = toplevel_get_poldiff(r->top); + + results_clear(r); + + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + result_item_poldiff_run(r->items[i], diff, 0); + } + /* only show diff-relevant buffers if a diff was actually run */ + for (i = 0; i < NUM_RESULT_ITEMS; i++) { + if (result_item_is_supported(r->items[i])) { + result_item_get_forms(r->items[i], forms); + for (j = 0; j < 5; j++) { + if (forms[j] > 0) { + was_diff_run = 1; + break; + } + } + } + } + if (was_diff_run) { + results_update_summary(r); + results_populate_key_buffer(r); + results_update_stats(r); + + /* select the summary item */ + GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view); + GtkTreeIter iter; + gtk_tree_model_get_iter_first(GTK_TREE_MODEL(r->summary_tree), &iter); + gtk_tree_selection_select_iter(selection, &iter); + } +} + +void results_switch_to_page(results_t * r) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view); + GtkTreeIter iter; + gboolean sens = FALSE; + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { + int f; + result_item_t *item; + results_sort_e sort; + results_sort_dir_e dir; + gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &f, + RESULTS_SUMMARY_COLUMN_ITEM, &item, -1); + poldiff_form_e form = (poldiff_form_e) f; + if (item != NULL && result_item_get_current_sort(item, form, &sort, &dir)) { + sens = TRUE; + } + } + toplevel_set_sort_menu_sensitivity(r->top, sens); +} + +struct run_datum +{ + results_t *r; + progress_t *progress; + GtkTextBuffer *tb; +}; + +static gpointer results_get_slow_buffer_runner(gpointer data) +{ + struct run_datum *run = (struct run_datum *)data; + results_t *r = run->r; + run->tb = result_item_get_buffer(r->current_item, r->current_form); + progress_done(run->progress); + return NULL; +} + +/** + * Spawn a thread that will get the result_item buffer as given by + * r->current_item and r->current_form. This will display a progress + * dialog while waiting. + */ +static GtkTextBuffer *results_get_slow_buffer(results_t * r, const char *action) +{ + struct run_datum run; + run.r = r; + run.progress = toplevel_get_progress(r->top); + util_cursor_wait(GTK_WIDGET(toplevel_get_window(r->top))); + GString *s = g_string_new(""); + g_string_printf(s, "Rendering %s", result_item_get_label(r->current_item)); + progress_show(run.progress, s->str); + g_string_free(s, TRUE); + progress_update(run.progress, "%s", action); + g_thread_create(results_get_slow_buffer_runner, &run, FALSE, NULL); + progress_wait(run.progress); + util_cursor_clear(GTK_WIDGET(toplevel_get_window(r->top))); + progress_hide(run.progress); + return run.tb; +} + +/** + * Callback invoked when the user selects an entry from the summary + * tree. + */ +static void results_summary_on_change(GtkTreeSelection * selection, gpointer user_data) +{ + results_t *r = (results_t *) user_data; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { + int form; + GdkRectangle rect; + GtkTextIter textiter; + GtkTextMark *mark; + gint offset; + GtkTextBuffer *tb; + results_sort_e sort; + results_sort_dir_e dir; + gboolean sens = FALSE; + + gtk_text_view_get_visible_rect(r->view, &rect); + gtk_text_view_get_iter_at_location(r->view, &textiter, rect.x, rect.y); + offset = gtk_text_iter_get_offset(&textiter); + if (r->current_item == NULL) { + r->summary_offset = offset; + } else { + result_item_save_current_line(r->current_item, r->current_form, offset); + } + gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &form, + RESULTS_SUMMARY_COLUMN_ITEM, &r->current_item, -1); + r->current_form = (poldiff_form_e) form; + if (r->current_item == NULL) { + tb = r->main_buffer; + offset = r->summary_offset; + } else { + int render_is_slow = result_item_is_render_slow(r->current_item, r->current_form); + if (result_item_get_current_sort(r->current_item, r->current_form, &sort, &dir)) { + sens = TRUE; + toplevel_set_sort_menu_selection(r->top, sort, dir); + } + if (render_is_slow) { + tb = results_get_slow_buffer(r, "Displaying Items"); + } else { + tb = result_item_get_buffer(r->current_item, r->current_form); + } + offset = result_item_get_current_line(r->current_item, r->current_form); + } + + gtk_text_view_set_buffer(r->view, tb); + + /* restore saved location. use marks to ensure that + * we go to this position even if it hasn't been + * drawn. */ + gtk_text_buffer_get_start_iter(tb, &textiter); + gtk_text_iter_set_offset(&textiter, offset); + mark = gtk_text_buffer_create_mark(tb, "location-mark", &textiter, FALSE); + gtk_text_view_scroll_to_mark(r->view, mark, 0.0, TRUE, 0.0, 0.0); + gtk_text_buffer_delete_mark(tb, mark); + + toplevel_set_sort_menu_sensitivity(r->top, sens); + } +} + +static void results_summary_on_row_activate(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column + __attribute__ ((unused)), gpointer user_data __attribute__ ((unused))) +{ + gboolean expanded = gtk_tree_view_row_expanded(tree_view, path); + if (!expanded) { + gtk_tree_view_expand_row(tree_view, path, 1); + } else { + gtk_tree_view_collapse_row(tree_view, path); + } +} + +/** + * Callback invoked when the user clicks on an inline link. This will + * spawn a pop-up menu where the user and get more information on the + * clicked string (which is hopefully an AV rule's permission). + */ +static gboolean results_on_inline_link_event(GtkTextTag * tag, GObject * event_object + __attribute__ ((unused)), GdkEvent * event, const GtkTextIter * iter, + gpointer user_data) +{ + results_t *r = (results_t *) user_data; + if (event->type == GDK_BUTTON_PRESS) { + GtkTextIter *start = gtk_text_iter_copy(iter); + while (!gtk_text_iter_begins_tag(start, tag)) + gtk_text_iter_backward_char(start); + GtkTextIter *end = gtk_text_iter_copy(start); + while (!gtk_text_iter_ends_tag(end, tag)) + gtk_text_iter_forward_char(end); + gint line = gtk_text_iter_get_line(start); + char *s = gtk_text_iter_get_slice(start, end); + result_item_inline_link_event(r->current_item, r->top, GTK_WIDGET(r->view), (GdkEventButton *) event, + r->current_form, line, s); + return TRUE; + } + return FALSE; +} + +/** + * Callback invoked when the user clicks on a line number tag. This + * will flip to the appropriate policy's source page and jump to that + * line. + */ +static gboolean results_on_line_event(GtkTextTag * tag, GObject * event_object __attribute__ ((unused)), + GdkEvent * event, const GtkTextIter * iter, gpointer user_data) +{ + results_t *r = (results_t *) user_data; + sediffx_policy_e which_pol = -1; + unsigned long line; + GtkTextIter *start, *end; + if (event->type == GDK_BUTTON_PRESS) { + start = gtk_text_iter_copy(iter); + + while (!gtk_text_iter_starts_word(start)) + gtk_text_iter_backward_char(start); + end = gtk_text_iter_copy(start); + while (!gtk_text_iter_ends_word(end)) + gtk_text_iter_forward_char(end); + + /* the line # in policy starts with 1, in the buffer it + * starts at 0 */ + line = atoi(gtk_text_iter_get_slice(start, end)) - 1; + if (tag == r->policy_orig_tag) { + which_pol = SEDIFFX_POLICY_ORIG; + } else if (tag == r->policy_mod_tag) { + which_pol = SEDIFFX_POLICY_MOD; + } else { + /* should never get here */ + assert(0); + } + toplevel_show_policy_line(r->top, which_pol, line); + return TRUE; + } + return FALSE; +} + +static gboolean results_on_popup_menu(GtkWidget * widget, gpointer user_data) +{ + results_t *r = (results_t *) user_data; + GtkTextView *textview = GTK_TEXT_VIEW(widget); + gint ex, ey, x, y; + GtkTextBuffer *buffer; + GtkTextIter iter; + GSList *tags, *tagp; + gboolean hovering = FALSE; + gdk_window_at_pointer(&ex, &ey); + gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y); + buffer = gtk_text_view_get_buffer(textview); + gtk_text_view_get_iter_at_location(textview, &iter, x, y); + tags = gtk_text_iter_get_tags(&iter); + for (tagp = tags; tagp != NULL; tagp = tagp->next) { + if (strcmp(GTK_TEXT_TAG(tagp->data)->name, "inline-link") == 0) { + hovering = TRUE; + break; + } + } + if (hovering) { + GtkTextIter *start = gtk_text_iter_copy(&iter); + while (!gtk_text_iter_begins_tag(start, GTK_TEXT_TAG(tagp->data))) + gtk_text_iter_backward_char(start); + GtkTextIter *end = gtk_text_iter_copy(start); + while (!gtk_text_iter_ends_tag(end, GTK_TEXT_TAG(tagp->data))) + gtk_text_iter_forward_char(end); + gint line = gtk_text_iter_get_line(start); + char *s = gtk_text_iter_get_slice(start, end); + g_slist_free(tags); + result_item_inline_link_event(r->current_item, r->top, widget, NULL, r->current_form, line, s); + return TRUE; + } + g_slist_free(tags); + return FALSE; +} + +/** + * Set the cursor to a hand when user scrolls over a line number in + * when displaying te diff. + */ +static gboolean results_on_text_view_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data __attribute__ ((unused))) +{ + GtkTextBuffer *buffer; + GtkTextView *textview; + GdkCursor *cursor; + GtkTextIter iter; + GSList *tags, *tagp; + gint x, ex, ey, y; + int hovering = 0; + + textview = GTK_TEXT_VIEW(widget); + + if (event->is_hint) { + gdk_window_get_pointer(event->window, &ex, &ey, NULL); + } else { + ex = event->x; + ey = event->y; + } + + gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y); + buffer = gtk_text_view_get_buffer(textview); + gtk_text_view_get_iter_at_location(textview, &iter, x, y); + tags = gtk_text_iter_get_tags(&iter); + for (tagp = tags; tagp != NULL; tagp = tagp->next) { + if (strncmp(GTK_TEXT_TAG(tagp->data)->name, "line", 4) == 0 || + strcmp(GTK_TEXT_TAG(tagp->data)->name, "inline-link") == 0) { + hovering = TRUE; + break; + } + } + if (hovering) { + cursor = gdk_cursor_new(GDK_HAND2); + gdk_window_set_cursor(event->window, cursor); + gdk_cursor_unref(cursor); + gdk_flush(); + } else { + gdk_window_set_cursor(event->window, NULL); + } + g_slist_free(tags); + return FALSE; +} + +void results_sort(results_t * r, results_sort_e field, results_sort_dir_e direction) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view); + GtkTreeIter iter; + int f; + result_item_t *item; +#ifndef NDEBUG + results_sort_e sort; + results_sort_dir_e dir; +#endif + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + return; + } + gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &f, + RESULTS_SUMMARY_COLUMN_ITEM, &item, -1); + poldiff_form_e form = (poldiff_form_e) f; + assert(item != NULL && item == r->current_item && form == r->current_form + && result_item_get_current_sort(item, form, &sort, &dir) != 0); + result_item_set_current_sort(item, form, field, direction); + if (result_item_is_render_slow(r->current_item, form)) { + results_get_slow_buffer(r, "Sorting Items"); + } else { + result_item_get_buffer(item, form); + } +} + +GtkTextView *results_get_text_view(results_t * r) +{ + return r->view; +} diff --git a/sediff/results.h b/sediff/results.h new file mode 100644 index 0000000..3d781b6 --- /dev/null +++ b/sediff/results.h @@ -0,0 +1,121 @@ +/** + * @file + * Header for showing diff results. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef RESULTS_H +#define RESULTS_H + +typedef enum results_sort +{ + RESULTS_SORT_DEFAULT = 0, + RESULTS_SORT_SOURCE, RESULTS_SORT_TARGET, + RESULTS_SORT_CLASS, RESULTS_SORT_COND +} results_sort_e; + +typedef enum results_sort_dir +{ + RESULTS_SORT_DESCEND = -1, RESULTS_SORT_ASCEND = 1 +} results_sort_dir_e; + +#include "toplevel.h" + +#include <gtk/gtk.h> +#include <poldiff/poldiff.h> + +typedef struct results results_t; + +/** + * Allocate and return a results object. This object is responsible + * for showing the results of a poldiff run, and all sorting of + * results. + * + * @param top Toplevel object to contain the results object. + * + * @return Results object, or NULL upon error. The caller is + * responsible for calling results_destroy() afterwards. + */ +results_t *results_create(toplevel_t * top); + +/** + * Destroy the results object. This does nothing if the pointer is + * set to NULL. + * + * @param r Reference to a results object. Afterwards the pointer + * will be set to NULL. + */ +void results_destroy(results_t ** r); + +/** + * Notify the results object that the policies have been changed. + * + * @param r Results object to notify. + * @param orig The (possibly newly loaded) original policy to diff. + * @param mod The (possibly newly loaded) modified policy to diff. + */ +void results_open_policies(results_t * r, apol_policy_t * orig, apol_policy_t * mod); + +/** + * Clear all text from the results object. This should be done prior + * to running a new diff. + * + * @param r Results object to clear. + */ +void results_clear(results_t * r); + +/** + * Update the results display to match the most recent poldiff run. + * + * @param r Results object to update. + */ +void results_update(results_t * r); + +/** + * Called whenever the user switches to the results page. This + * function is responsible for setting up its menus and other widgets. + * + * @param r Results object to update. + */ +void results_switch_to_page(results_t * r); + +/** + * Sort the currently visible result buffer (which hopefully is the TE + * rules diff) using the given criteria. + * + * @param r Results object to sort. + * @param field Component of a rule to sort against. + * @param direction Direction of sort, either RESULTS_SORT_ASCEND or + * RESULTS_SORT_DESCEND. + */ +void results_sort(results_t * r, results_sort_e field, results_sort_dir_e direction); + +/** + * Get the currently showing text view for the results object. + * + * @param r Results object containing text view. + * + * @return Currently visible text view. + */ +GtkTextView *results_get_text_view(results_t * r); + +#endif diff --git a/sediff/sediff.c b/sediff/sediff.c new file mode 100644 index 0000000..6022775 --- /dev/null +++ b/sediff/sediff.c @@ -0,0 +1,653 @@ +/** + * @file + * Command line frontend for computing a semantic policy difference. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <poldiff/poldiff.h> +#include <poldiff/component_record.h> +#include <apol/policy.h> +#include <apol/vector.h> +#include <stdio.h> +#include <errno.h> +#include <getopt.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC" + +enum opt_values +{ + DIFF_LEVEL = 256, DIFF_CATEGORY, + DIFF_AUDITALLOW, DIFF_DONTAUDIT, DIFF_NEVERALLOW, + DIFF_TYPE_CHANGE, DIFF_TYPE_MEMBER, DIFF_TYPE_TRANS, + DIFF_ROLE_TRANS, DIFF_ROLE_ALLOW, DIFF_RANGE_TRANS, + OPT_STATS +}; + +/* command line options struct */ +static struct option const longopts[] = { + {"class", no_argument, NULL, 'c'}, + {"level", no_argument, NULL, DIFF_LEVEL}, + {"category", no_argument, NULL, DIFF_CATEGORY}, + {"type", no_argument, NULL, 't'}, + {"attribute", no_argument, NULL, 'a'}, + {"role", no_argument, NULL, 'r'}, + {"user", no_argument, NULL, 'u'}, + {"bool", no_argument, NULL, 'b'}, + {"allow", no_argument, NULL, 'A'}, + {"auditallow", no_argument, NULL, DIFF_AUDITALLOW}, + {"dontaudit", no_argument, NULL, DIFF_DONTAUDIT}, + {"neverallow", no_argument, NULL, DIFF_NEVERALLOW}, + {"type_change", no_argument, NULL, DIFF_TYPE_CHANGE}, + {"type_trans", no_argument, NULL, DIFF_TYPE_TRANS}, + {"type_member", no_argument, NULL, DIFF_TYPE_MEMBER}, + {"role_trans", no_argument, NULL, DIFF_ROLE_TRANS}, + {"role_allow", no_argument, NULL, DIFF_ROLE_ALLOW}, + {"range_trans", no_argument, NULL, DIFF_RANGE_TRANS}, + {"stats", no_argument, NULL, OPT_STATS}, + {"quiet", no_argument, NULL, 'q'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +static void usage(const char *prog_name, int brief) +{ + printf("Usage: %s [OPTIONS] ORIGINAL_POLICY ; MODIFIED_POLICY\n\n", prog_name); + if (brief) { + printf("\tTry %s --help for more help.\n\n", prog_name); + return; + } + printf("Semantically differentiate two policies. By default, all supported\n"); + printf("policy elements sans neverallows are examined. The following options\n"); + printf("are available:\n\n"); + printf(" -c, --class object class and common permission definitions\n"); + printf(" --level MLS level definitions\n"); + printf(" --category MLS category definitions\n"); + printf(" -t, --type type definitions\n"); + printf(" -a, --attribute attribute definitions\n"); + printf(" -r, --role role definitions\n"); + printf(" -u, --user user definitions\n"); + printf(" -b, --bool boolean definitions and default values\n"); + printf(" -A, --allow allow rules\n"); + printf(" --auditallow auditallow rules\n"); + printf(" --dontaudit dontaudit rules\n"); + printf(" --neverallow neverallow rules\n"); + printf(" --type_change type_change rules\n"); + printf(" --type_member type_member rules\n"); + printf(" --type_trans type_transition rules\n"); + printf(" --role_trans role_transition rules\n"); + printf(" --role_allow role allow rules\n"); + printf(" --range_trans range_transition rules\n"); + printf("\n"); + printf(" -q, --quiet suppress status output for elements with no differences\n"); + printf(" --stats print only statistics\n"); + printf(" -h, --help print this help text and exit\n"); + printf(" -V, --version print version information and exit\n\n"); +} + +static void print_diff_string(const char *str, unsigned int indent_level) +{ + const char *c = str; + unsigned int i; + static const char *indent = " "; + + for (i = 0; i < indent_level; i++) + printf("%s", indent); + for (; *c; c++) { + if (*c == '\n') { + if (*(c + 1) == '\0') + break; + printf("%c", *c); + for (i = 0; i < indent_level; i++) + printf("%s", indent); + } else if (*c == '\t') { + printf("%s", indent); + } else { + printf("%c", *c); + } + } +} + +static void print_rule_section(const poldiff_t * diff, const poldiff_component_record_t * rec, const apol_vector_t * v, + poldiff_form_e form) +{ + int i; + char *str = NULL; + const void *item1; + + for (i = 0; i < apol_vector_get_size(v); i++) { + item1 = apol_vector_get_element(v, i); + if (poldiff_component_record_get_form_fn(rec) (item1) == form) { + if ((str = poldiff_component_record_get_to_string_fn(rec) (diff, item1)) == NULL) { + return; + } + print_diff_string(str, 1); + printf("\n"); + free(str); + str = NULL; + } + } +} + +#define PRINT_ADDED_REMOVED 1 +#define PRINT_MODIFIED 2 +#define PRINT_ALL 4 + +static void print_rule_diffs(const poldiff_t * diff, const poldiff_component_record_t * rec, int stats_only, const char *name, + uint32_t flags, apol_vector_comp_func sort_by) +{ + const apol_vector_t *internal_v = NULL; + apol_vector_t *v = NULL; + size_t stats[5] = { 0, 0, 0, 0, 0 }; + + if (!rec || !diff) + return; + + poldiff_component_record_get_stats_fn(rec) (diff, stats); + if (flags == PRINT_ADDED_REMOVED) { + printf("%s (Added %zd, Removed %zd)\n", name, stats[0], stats[1]); + } else if (flags == PRINT_MODIFIED) { + printf("%s (Added %zd, Removed %zd, Modified %zd)\n", name, stats[0], stats[1], stats[2]); + } else if (flags == PRINT_ALL) { + printf("%s (Added %zd, Added New Type %zd, Removed %zd, Removed Missing Type %zd, Modified %zd)\n", name, stats[0], + stats[3], stats[1], stats[4], stats[2]); + } else { + fprintf(stderr, "Error, unhandled flag type\n"); + } + + if (stats_only) + return; + if ((internal_v = poldiff_component_record_get_results_fn(rec) (diff)) == NULL) { + return; + } + if (!(v = apol_vector_create_from_vector(internal_v, NULL, NULL, NULL))) { + perror("Error printig results"); + return; + } + + if (sort_by) { + apol_vector_sort(v, sort_by, NULL); + } + + printf(" Added %s: %zd\n", name, stats[0]); + print_rule_section(diff, rec, v, POLDIFF_FORM_ADDED); + + if (flags == PRINT_ALL) { + printf(" Added %s because of new type: %zd\n", name, stats[3]); + print_rule_section(diff, rec, v, POLDIFF_FORM_ADD_TYPE); + } + + printf(" Removed %s: %zd\n", name, stats[1]); + print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVED); + + if (flags == PRINT_ALL) { + printf(" Removed %s because of missing type: %zd\n", name, stats[4]); + print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVE_TYPE); + } + if (flags == PRINT_MODIFIED || flags == PRINT_ALL) { + printf(" Modified %s: %zd\n", name, stats[2]); + print_rule_section(diff, rec, v, POLDIFF_FORM_MODIFIED); + } + printf("\n"); + apol_vector_destroy(&v); + return; +} + +static void print_class_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CLASSES), stats_only, "Classes", PRINT_MODIFIED, NULL); + return; +} + +static void print_bool_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_BOOLS), stats_only, "Booleans", PRINT_MODIFIED, NULL); + return; +} + +static void print_common_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_COMMONS), stats_only, "Commons", PRINT_MODIFIED, NULL); + return; +} + +static void print_level_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_LEVELS), stats_only, "Levels", PRINT_MODIFIED, NULL); + return; +} + +static void print_cat_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CATS), stats_only, "Categories", PRINT_MODIFIED, NULL); + return; +} + +static void print_role_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLES), stats_only, "Roles", PRINT_MODIFIED, NULL); + return; +} + +static void print_user_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_USERS), stats_only, "Users", PRINT_MODIFIED, NULL); + return; +} + +static void print_avallow_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVALLOW), stats_only, "AV-Allow Rules", PRINT_ALL, NULL); +} + +static void print_avauditallow_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVAUDITALLOW), stats_only, "AV-Audit Allow Rules", + PRINT_ALL, NULL); +} + +static void print_avdontaudit_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVDONTAUDIT), stats_only, "AV-Don't Audit Rules", + PRINT_ALL, NULL); +} + +static void print_avneverallow_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVNEVERALLOW), stats_only, "AV-Never Allow Rules", + PRINT_ALL, NULL); +} + +static void print_role_allow_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_ALLOWS), stats_only, "Role Allow Rules", + PRINT_MODIFIED, NULL); +} + +static void print_role_trans_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_TRANS), stats_only, "Role Transitions", PRINT_ALL, + NULL); +} + +static void print_range_trans_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_RANGE_TRANS), stats_only, "Range Transitions", + PRINT_MODIFIED, NULL); +} + +/** compare the names for two poldiff_type_t objects. + * used to sort items prior to display. */ +static int type_name_cmp(const void *a, const void *b, void *user_data __attribute__ ((unused))) +{ + poldiff_type_t *ta = (poldiff_type_t *) a; + poldiff_type_t *tb = (poldiff_type_t *) b; + if (ta == NULL || tb == NULL) + return -1; + return strcmp(poldiff_type_get_name(ta), poldiff_type_get_name(tb)); +} + +static void print_type_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TYPES), stats_only, "Types", PRINT_MODIFIED, + type_name_cmp); +} + +static void print_attrib_diffs(const poldiff_t * diff, int stats_only) +{ + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ATTRIBS), stats_only, "Attributes", PRINT_MODIFIED, NULL); +} + +static size_t get_diff_total(const poldiff_t * diff, uint32_t flags) +{ + size_t total = 0; + uint32_t i; + size_t stats[5] = { 0, 0, 0, 0, 0 }; + + if (!diff || !flags) + return 0; + + /* for all 32 bits possible in flags */ + for (i = 0x80000000; i; i = i >> 1) { + if (flags & i) { + poldiff_get_stats(diff, i, stats); + total += (stats[0] + stats[1] + stats[2] + stats[3] + stats[4]); + } + } + + return total; +} + +static void print_diff(const poldiff_t * diff, uint32_t flags, int stats, int quiet) +{ + if (flags & POLDIFF_DIFF_CLASSES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CLASSES))) { + print_class_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_COMMONS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_COMMONS))) { + print_common_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_LEVELS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_LEVELS))) { + print_level_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_CATS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CATS))) { + print_cat_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_TYPES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TYPES))) { + print_type_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_ATTRIBS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ATTRIBS))) { + print_attrib_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_ROLES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLES))) { + print_role_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_USERS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_USERS))) { + print_user_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_BOOLS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_BOOLS))) { + print_bool_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_AVALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVALLOW))) { + print_avallow_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_AVAUDITALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVAUDITALLOW))) { + print_avauditallow_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_AVDONTAUDIT && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVDONTAUDIT))) { + print_avdontaudit_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_AVNEVERALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVNEVERALLOW))) { + print_avneverallow_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_TECHANGE && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TECHANGE))) { + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TECHANGE), stats, "TE type_change", PRINT_ALL, + NULL); + } + if (flags & POLDIFF_DIFF_TEMEMBER && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TEMEMBER))) { + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TEMEMBER), stats, "TE type_member", PRINT_ALL, + NULL); + } + if (flags & POLDIFF_DIFF_TETRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TETRANS))) { + print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TETRANS), stats, "TE type_trans", PRINT_ALL, NULL); + } + if (flags & POLDIFF_DIFF_ROLE_ALLOWS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_ALLOWS))) { + print_role_allow_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_ROLE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_TRANS))) { + print_role_trans_diffs(diff, stats); + } + if (flags & POLDIFF_DIFF_RANGE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_RANGE_TRANS))) { + print_range_trans_diffs(diff, stats); + } +} + +int main(int argc, char **argv) +{ + int optc = 0, quiet = 0, stats = 0, default_all = 0; + uint32_t flags = 0; + apol_policy_t *orig_policy = NULL, *mod_policy = NULL; + apol_policy_path_type_e orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + char *orig_base_path = NULL; + apol_vector_t *orig_module_paths = NULL; + apol_policy_path_t *orig_pol_path = NULL; + apol_policy_path_type_e mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + char *mod_base_path = NULL; + apol_vector_t *mod_module_paths = NULL; + apol_policy_path_t *mod_pol_path = NULL; + poldiff_t *diff = NULL; + size_t total = 0; + + while ((optc = getopt_long(argc, argv, "ctarubANDLMCRqhV", longopts, NULL)) != -1) { + switch (optc) { + case 0: + break; + case 'c': + flags |= (POLDIFF_DIFF_CLASSES | POLDIFF_DIFF_COMMONS); + break; + case DIFF_LEVEL: + flags |= POLDIFF_DIFF_LEVELS; + break; + case DIFF_CATEGORY: + flags |= POLDIFF_DIFF_CATS; + break; + case 't': + flags |= POLDIFF_DIFF_TYPES; + break; + case 'a': + flags |= POLDIFF_DIFF_ATTRIBS; + break; + case 'r': + flags |= POLDIFF_DIFF_ROLES; + break; + case 'u': + flags |= POLDIFF_DIFF_USERS; + break; + case 'b': + flags |= POLDIFF_DIFF_BOOLS; + break; + case 'A': + flags |= POLDIFF_DIFF_AVALLOW; + break; + case DIFF_AUDITALLOW: + flags |= POLDIFF_DIFF_AVAUDITALLOW; + break; + case DIFF_DONTAUDIT: + flags |= POLDIFF_DIFF_AVDONTAUDIT; + break; + case DIFF_NEVERALLOW: + flags |= POLDIFF_DIFF_AVNEVERALLOW; + break; + case DIFF_TYPE_CHANGE: + flags |= POLDIFF_DIFF_TECHANGE; + break; + case DIFF_TYPE_MEMBER: + flags |= POLDIFF_DIFF_TEMEMBER; + break; + case DIFF_TYPE_TRANS: + flags |= POLDIFF_DIFF_TETRANS; + break; + case DIFF_ROLE_ALLOW: + flags |= POLDIFF_DIFF_ROLE_ALLOWS; + break; + case DIFF_ROLE_TRANS: + flags |= POLDIFF_DIFF_ROLE_TRANS; + break; + case DIFF_RANGE_TRANS: + flags |= POLDIFF_DIFF_RANGE_TRANS; + break; + case OPT_STATS: + stats = 1; + break; + case 'q': + quiet = 1; + break; + case 'h': + usage(argv[0], 0); + exit(0); + case 'V': + printf("sediff %s\n%s\n", VERSION, COPYRIGHT_INFO); + exit(0); + default: + usage(argv[0], 1); + exit(1); + } + } + + if (!flags) { + flags = POLDIFF_DIFF_ALL & ~POLDIFF_DIFF_AVNEVERALLOW; + default_all = 1; + } + + if (argc - optind < 2) { + usage(argv[0], 1); + exit(1); + } + + if (!strcmp(";", argv[optind])) { + ERR(NULL, "%s", "Missing path to original policy."); + goto err; + } + orig_base_path = argv[optind++]; + orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + if (!(orig_module_paths = apol_vector_create(NULL))) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + for (; argc - optind; optind++) { + if (!strcmp(";", argv[optind])) { + optind++; + break; + } + if (apol_vector_append(orig_module_paths, (void *)argv[optind])) { + ERR(NULL, "Error loading module %s", argv[optind]); + goto err; + } + orig_path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } + if (apol_file_is_policy_path_list(orig_base_path) > 0) { + orig_pol_path = apol_policy_path_create_from_file(orig_base_path); + if (!orig_pol_path) { + ERR(NULL, "%s", "invalid policy list"); + goto err; + } + } else { + orig_pol_path = apol_policy_path_create(orig_path_type, orig_base_path, orig_module_paths); + if (!orig_pol_path) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + } + apol_vector_destroy(&orig_module_paths); + + if (argc - optind == 0) { + ERR(NULL, "%s", "Missing path to modified policy."); + goto err; + } + + mod_base_path = argv[optind++]; + mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + if (!(mod_module_paths = apol_vector_create(NULL))) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + for (; argc - optind; optind++) { + if (apol_vector_append(mod_module_paths, (void *)argv[optind])) { + ERR(NULL, "Error loading module %s", argv[optind]); + goto err; + } + mod_path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } + if (apol_file_is_policy_path_list(mod_base_path) > 0) { + mod_pol_path = apol_policy_path_create_from_file(mod_base_path); + if (!mod_pol_path) { + ERR(NULL, "%s", "invalid policy list"); + goto err; + } + } else { + mod_pol_path = apol_policy_path_create(mod_path_type, mod_base_path, mod_module_paths); + if (!mod_pol_path) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + } + apol_vector_destroy(&mod_module_paths); + + int policy_opt = 0; + if (!(flags & POLDIFF_DIFF_AVNEVERALLOW)) { + policy_opt |= QPOL_POLICY_OPTION_NO_NEVERALLOWS; + } + if (!(flags & POLDIFF_DIFF_RULES)) { + policy_opt |= QPOL_POLICY_OPTION_NO_RULES; + } + orig_policy = apol_policy_create_from_policy_path(orig_pol_path, policy_opt, NULL, NULL); + if (!orig_policy) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + mod_policy = apol_policy_create_from_policy_path(mod_pol_path, policy_opt, NULL, NULL); + if (!mod_policy) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + + qpol_policy_t *orig_qpol = apol_policy_get_qpol(orig_policy); + qpol_policy_t *mod_qpol = apol_policy_get_qpol(mod_policy); + /* we disable attribute diffs if either policy does not + * support attribute names because the fake attribute names + * won't make sense */ + if ((flags & POLDIFF_DIFF_ATTRIBS) + && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_ATTRIB_NAMES)) + || !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_ATTRIB_NAMES)))) { + flags &= ~POLDIFF_DIFF_ATTRIBS; + WARN(NULL, "%s", "Attribute diffs are not supported for current policies."); + } + + /* we disable MLS diffs if both policies do not support MLS + * but do not warn if it was implicitly requested for two + * non-MLS policies */ + if ((flags & POLDIFF_DIFF_MLS) + && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_MLS)) && !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_MLS)))) { + flags &= ~(POLDIFF_DIFF_MLS); + if (!default_all) { + WARN(NULL, "%s", "MLS diffs are not supported for current policies."); + } + } + + /* default callback for error handling is sufficient here */ + if (!(diff = poldiff_create(orig_policy, mod_policy, NULL, NULL))) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + /* poldiff now owns the policies */ + orig_policy = mod_policy = NULL; + + if (poldiff_run(diff, flags)) { + goto err; + } + + print_diff(diff, flags, stats, quiet); + + total = get_diff_total(diff, flags); + + apol_policy_path_destroy(&orig_pol_path); + apol_policy_path_destroy(&mod_pol_path); + poldiff_destroy(&diff); + + if (total) + return 1; + else + return 0; + + err: + apol_policy_destroy(&orig_policy); + apol_policy_destroy(&mod_policy); + apol_policy_path_destroy(&orig_pol_path); + apol_policy_path_destroy(&mod_pol_path); + apol_vector_destroy(&orig_module_paths); + apol_vector_destroy(&mod_module_paths); + poldiff_destroy(&diff); + return 1; +} diff --git a/sediff/sediff_help.txt b/sediff/sediff_help.txt new file mode 100644 index 0000000..8929528 --- /dev/null +++ b/sediff/sediff_help.txt @@ -0,0 +1,259 @@ +Semantic Policy Difference Tool for Security Enhanced Linux + + +Overview: +--------- +The sediff and sediffx programs are policy analysis tools that take +two policies and compare them, showing a list of the differences. The +former is a command-line only program while the latter is a GTK+ +application. They can compare source, monolithic binary, and modular +binary policies; they can also compare different versions of policy. +The two programs support source policies versions 12 and higher, +monolithic binary versions 15 and higher, and modular binary versions +5 and higher. + + +Limitations: +------------ +The programs currently compare the following policy elements: + - commons and object classes + - levels and categories + - types and attributes + - roles + - users + - booleans + - access vector rules (allow, neverallow, etc.) + - type rules (type_transition, type_member, etc.) + - role allow rules + - role transition rules + - range transition rules + + +What is a Semantic Diff? +------------------------ +The challenge with comparing two policies is that a straightforward +textual comparison is of little value. What one needs is the ability +to determine semantically how two policies differ. For example, one +could not simply grep for allow rules with a given type, and then +compare them to a similar list from another policy. Many factors +affect the semantic meaning of the rules. For example, multiple rules +can allow differing sets of permissions. Attributes can allow +permissions to or from a type. What was a type in one policy could +become an alias in another. + +What sediff and sediffx do are analyze each policy semantically. We +define "semantically" as how the kernel security server uses a policy +to make enforcement decisions. This approach also allows binary and +source policies to be compared, as well as different versions of +policies. + +NOTE: The one semantic assumption sediff and sediffx make is that when +an identifier (e.g., a type name) has the same string value in each +policy, then it represents the same semantic meaning in both policies. + + +sediff and sediffx Commands: +---------------------------- +Policies may be differentiated upon the command line (see "sediff +--help") or in a graphical environment (see "sediffx --help"). The +sediffx tool is recommended because it gives additional details about +policy differences and affords type remappings. The remainder of this +document focuses on sediffx. + + +Understanding sediffx's Results: +-------------------------------- +After calculating differences between two policies, the GUI shows the +compared policy components in the top-left frame. Besides each policy +component is a number representing the total number of differences for +that component. Select a policy component to show detailed results in +the right-hand window. + +NOTE: All differences are shown from the perspective of the first +policy given (i.e., "original policy") to the second ("modified +policy"). There are five types of differences shown: + + - Added (+): A policy component was added by the second policy (in + modified policy but not original policy). + + - Removed (-): A policy component was removed by the second policy + (in original policy but not modified policy). + + - Modified (*): A policy component was present in both policies, but + is different in the modified policy. + +Where appropriate, two other differences are possible: + + - Added because of new type (+): This policy component could not + exist in the original policy because that policy does not declare + a necessary type. + + - Removed because of missing type (-): This policy component could + not exist in the modified policy because that policy no longer + declares a necessary type. + + +Supported Policy Areas Differences: +----------------------------------- +Below is an explanation of the difference for each supported policy +area: + + Commons: + -------- + Classes can be added, removed, or modified. Modified means that + the list of permissions associated with a common is different. + + Classes: + -------- + Classes are compared much like commons. They too may be added, + removed, or modified. + + Levels: + ------- + If either policy is MLS then levels will be compared. Levels can be + added or removed; a modified level means that the categories + assigned to its corresponding sensitivity has changed. Be aware + that levels' aliases are ignored by the diff algorithm. + + Categories: + ----------- + If either policy is MLS then categories will be compared. They can + be added or removed; there is no such thing as a "modified" + category. Be aware that categories' aliases are ignored by the + diff algorithm. + + Types: + ------ + Types can be added, removed, or modified. Modified means that the + attributes associated with a type are different between the two + policies. + + Attributes: + ----------- + Attributes are compared like types. They can be added, removed, + or modified. Modified means that the types associated with the + attributes are different (types can be added or removed from the + attribute). + + Roles: + ------ + Roles can be added, removed, or modified. Modified means that the + types associated with a role are different between the two policies. + Types can be added or removed from a role. + + Users: + ------ + Users can be added, removed, or modified. Modified means that the + roles associated with a user are different between the two policies. + Roles can be added or removed from a user. In addition, if either + policy is MLS then the users' ranges and default levels are also + compared. + + Booleans: + --------- + Booleans can be added, removed, or modified. If comparing a version + 15 or earlier policy with a version 16 or later policy, all the + booleans will be added or removed, for booleans were introduced in + version 16. Modified means that the default value is different + between the two policies. + + AV Rules: + --------- + Finding differences in access vector rules (allow, neverallow, etc.) + consumes the majority of time when diffing two policies. The rule + comparison is truly semantic. All issues of redundancy and + duplication, as well as indirect access through attributes are + resolved. All rules are keyed by the "source-target-class" (STC) + triple. In addition, conditional rules are distinguished from + non-conditional rules. Thus, for example, two rules with the same + STC will not be compared if one is non-conditional and the other is + conditional, or if both are conditional but conditioned on two + different conditional expressions. For conditional rules, the + conditional expression is compared to ensure that conditional rules + are meaningfully compared. In the results pane, conditional rules + are displayed with their associated conditional expression and if + the rule was in conditional's TRUE or FALSE branch. + + NOTE: For conditional rules, the default and current values of + the booleans are ignored. Conditional expressions are compared + as if all booleans were in the same state. + + Rules can be added, removed, or modified. Added means the STC + triple for that rule is not present in the original policy but in + the modified one. Removed means the STC triple is present in the + original but not modified policy. Modified means that the + permissions for the rule are different between the policies. + + When source policies are compared, hyperlinked line numbers are + shown that takes the user to the policy's source where the rule was + defined. If there were more than one source rules that contributed + to a STC triple for a given rule, then all relevant source rules are + linked. Furthermore, the user may click upon an individual + permission to obtain a list of lines that contributed just that + permission. + + Type Rules: + ----------- + Type rules are type_transition, type_member, and type_change. They + are differentiated much like AV rules in that their STCs are used as + keys. For type rules, modified means that the default type is + different between the policies. + + Role Allows: + ------------ + Role allow rules determine if a role is allowed to transition to + another role. Diffing a role allow involves taking the source role + and checking to see if there are corresponding rules in the other + policy with the same source role. A modified role allow means the + same source exist in both policies but target roles differ. + + Role Transitions: + ----------------- + Role transitions are keyed against both the source role and target + type. If a role transition exists in both policies but has a + different default role then it is marked as modified. + + Range Transitions: + ------------------ + Range transitions have a STC much like AV rules. A modified range + transition indicates a difference in the rules' target ranges. This + could be either a difference in level or in minimal category set. + + +Policy Tabs: +------------ +Each policy has a tab on the main window labeled Policy #: followed by +the policy file name. Under these tabs are a policy statistics tab +and a source tab. + + Policy Statistics Tab: + ---------------------- + The policy statistics tab displays a summary of that policy's + contents. + + Source Tab: + ----------- + If the policy is a source policy, this tab displays the source of + that policy. + + +Remapping Types: +---------------- +The diff algorithm implicitly treats a type with the same name across +both policies as the same semantic item. This includes a name that +was a type in one policy but became an alias in the other. There may +be instances where the operator has special knowledge of the remaining +unmapped types. From sediffx's main interface, select "Remap Types" +from the Tools menu to open a dialog box. Add additional remappings +between types as necessary. + +There are times when a one-to-one mapping is not sufficient for +analysis purposes. Occasionally a type is "split" into two or more +types; conversely multiple types are "joined" into a single type. For +example, a policy has the type "games_t" for a number of programs. At +some point in the future the policy writer decides to give NetHack its +own type, "nethack_t". To represent this type split in sediffx, go to +the Remap Types dialog. There should already be an inferred mapping +from the original policy's games_t to the modified policy. Add a new +mapping from games_t to nethack_t. The mappings list will be updated +to show that games_t now maps to both games_t and nethack_t. diff --git a/sediff/sediffx-small.png b/sediff/sediffx-small.png Binary files differnew file mode 100644 index 0000000..21354b8 --- /dev/null +++ b/sediff/sediffx-small.png diff --git a/sediff/sediffx-small.xcf b/sediff/sediffx-small.xcf Binary files differnew file mode 100644 index 0000000..d936489 --- /dev/null +++ b/sediff/sediffx-small.xcf diff --git a/sediff/sediffx.c b/sediff/sediffx.c new file mode 100644 index 0000000..44cf686 --- /dev/null +++ b/sediff/sediffx.c @@ -0,0 +1,317 @@ +/** + * @file + * Main program for running sediffx in a GTK+ environment. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "sediffx.h" +#include "toplevel.h" + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <glade/glade.h> +#include <gtk/gtk.h> + +struct sediffx +{ + apol_policy_path_t *paths[SEDIFFX_POLICY_NUM]; + apol_policy_t *policies[SEDIFFX_POLICY_NUM]; + toplevel_t *top; + poldiff_t *poldiff; + uint32_t flags; +}; + +static struct option const longopts[] = { + {"run-diff", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +void sediffx_set_policy(sediffx_t * s, sediffx_policy_e which, apol_policy_t * policy, apol_policy_path_t * path) +{ + poldiff_destroy(&s->poldiff); + if (policy != NULL) { + apol_policy_destroy(&s->policies[which]); + s->policies[which] = policy; + if (path != s->paths[which]) { + apol_policy_path_destroy(&s->paths[which]); + } + s->paths[which] = path; + } else { + apol_policy_destroy(&s->policies[which]); + apol_policy_path_destroy(&s->paths[which]); + } +} + +const apol_policy_path_t *sediffx_get_policy_path(sediffx_t * sediffx, const sediffx_policy_e which) +{ + return sediffx->paths[which]; +} + +poldiff_t *sediffx_get_poldiff(sediffx_t * s, poldiff_handle_fn_t fn, void *arg) +{ + if (s->poldiff != NULL) { + return s->poldiff; + } + if (s->policies[SEDIFFX_POLICY_ORIG] == NULL || s->policies[SEDIFFX_POLICY_MOD] == NULL) { + return NULL; + } + s->poldiff = poldiff_create(s->policies[SEDIFFX_POLICY_ORIG], s->policies[SEDIFFX_POLICY_MOD], fn, arg); + if (s->poldiff != NULL) { + /* poldiff_create() took ownership of the policies */ + s->policies[SEDIFFX_POLICY_ORIG] = NULL; + s->policies[SEDIFFX_POLICY_MOD] = NULL; + } + return s->poldiff; +} + +void sediffx_set_poldiff_run_flags(sediffx_t * s, uint32_t flags) +{ + s->flags = flags; +} + +uint32_t sediffx_get_poldiff_run_flags(sediffx_t * s) +{ + return s->flags; +} + +static void print_version_info(void) +{ + printf("sediffx %s\n%s\n", VERSION, COPYRIGHT_INFO); +} + +static void usage(const char *program_name, int brief) +{ + printf("Usage: %s [-d] [ORIGINAL_POLICY ; MODIFIED_POLICY]\n\n", program_name); + if (brief) { + printf("\tTry %s --help for more help.\n\n", program_name); + return; + } + printf("Semantically differentiate two policies. All supported policy elements\n"); + printf("are examined. The following options are available:\n"); + printf("\n"); + printf(" -d, --diff-now load policies and diff immediately\n"); + printf(" -h, --help print this help text and exit\n"); + printf(" -V, --version print version information and exit\n\n"); +} + +struct delayed_main_data +{ + apol_policy_path_t *orig_path, *mod_path; + int run_diff; + toplevel_t *top; +}; + +/* + * We don't want to do the heavy work of loading and displaying + * the diff before the main loop has started because it will freeze + * the gui for too long. To solve this, the function is called from an + * idle callback set-up in main. + */ +static gboolean delayed_main(gpointer data) +{ + struct delayed_main_data *dmd = (struct delayed_main_data *)data; + if (toplevel_open_policies(dmd->top, dmd->orig_path, dmd->mod_path) == 0 && dmd->run_diff) { + toplevel_run_diff(dmd->top); + } + return FALSE; +} + +static void sediffx_destroy(sediffx_t ** sediffx) +{ + if (sediffx != NULL && *sediffx != NULL) { + int i; + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + apol_policy_path_destroy(&((*sediffx)->paths[i])); + apol_policy_destroy(&((*sediffx)->policies[i])); + } + poldiff_destroy(&((*sediffx)->poldiff)); + free(*sediffx); + *sediffx = NULL; + } +} + +static void sediffx_parse_command_line(int argc, char **argv, apol_policy_path_t ** orig_path, apol_policy_path_t ** mod_path, + int *run_diff) +{ + int optc; + *orig_path = NULL; + *mod_path = NULL; + *run_diff = 0; + while ((optc = getopt_long(argc, argv, "dhV", longopts, NULL)) != -1) { + switch (optc) { + case 0: + break; + case 'd': /* run the diff only for gui */ + *run_diff = 1; + break; + case 'h': /* help */ + usage(argv[0], 0); + exit(EXIT_SUCCESS); + case 'V': /* version */ + print_version_info(); + exit(EXIT_SUCCESS); + default: + usage(argv[0], 1); + exit(EXIT_FAILURE); + } + } + + if (argc - optind == 0) { + /* here we have found no missing arguments, but + * perhaps the user specified -d with no files */ + if (*run_diff) { + usage(argv[0], 0); + exit(EXIT_FAILURE); + } + return; + } else if (argc - optind == 1) { + usage(argv[0], 1); + exit(EXIT_FAILURE); + } + if (argc - optind == 2) { + /* sediffx with file names, old syntax */ + if (strcmp(argv[optind], ";") == 0 || strcmp(argv[optind + 1], ";") == 0) { + usage(argv[0], 1); + exit(EXIT_FAILURE); + } + *orig_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, argv[optind], NULL); + *mod_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, argv[optind + 1], NULL); + if (*orig_path == NULL || *mod_path == NULL) { + ERR(NULL, "%s", strerror(errno)); + exit(EXIT_FAILURE); + } + return; + } + + /* module lists */ + char *orig_base_path = NULL; + apol_vector_t *orig_module_paths = NULL; + char *mod_base_path = NULL; + apol_vector_t *mod_module_paths = NULL; + apol_policy_path_type_e orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + apol_policy_path_type_e mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + + orig_base_path = argv[optind++]; + if (!(orig_module_paths = apol_vector_create(NULL))) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + for (; argc - optind; optind++) { + if (!strcmp(";", argv[optind])) { + optind++; + break; + } + if (apol_vector_append(orig_module_paths, (void *)argv[optind])) { + ERR(NULL, "Error loading module %s", argv[optind]); + goto err; + } + orig_path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } + if (apol_file_is_policy_path_list(orig_base_path) > 0) { + *orig_path = apol_policy_path_create_from_file(orig_base_path); + if (*orig_path == NULL) { + ERR(NULL, "%s", "invalid policy list"); + goto err; + } + } else { + *orig_path = apol_policy_path_create(orig_path_type, orig_base_path, orig_module_paths); + if (*orig_path == NULL) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + } + apol_vector_destroy(&orig_module_paths); + + if (argc - optind == 0) { + ERR(NULL, "%s", "Missing path to modified policy."); + goto err; + } + + mod_base_path = argv[optind++]; + if (!(mod_module_paths = apol_vector_create(NULL))) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + for (; argc - optind; optind++) { + if (apol_vector_append(mod_module_paths, (void *)argv[optind])) { + ERR(NULL, "Error loading module %s", argv[optind]); + goto err; + } + mod_path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } + if (apol_file_is_policy_path_list(mod_base_path) > 0) { + *mod_path = apol_policy_path_create_from_file(mod_base_path); + if (*mod_path == NULL) { + ERR(NULL, "%s", "invalid policy list"); + goto err; + } + } else { + *mod_path = apol_policy_path_create(mod_path_type, mod_base_path, mod_module_paths); + if (*mod_path == NULL) { + ERR(NULL, "%s", strerror(errno)); + goto err; + } + } + apol_vector_destroy(&mod_module_paths); + return; + err: + apol_policy_path_destroy(orig_path); + apol_policy_path_destroy(mod_path); + apol_vector_destroy(&orig_module_paths); + apol_vector_destroy(&mod_module_paths); +} + +int main(int argc, char **argv) +{ + sediffx_t *app; + apol_policy_path_t *orig_path, *mod_path; + int run_diff; + + if (!g_thread_supported()) + g_thread_init(NULL); + + gtk_init(&argc, &argv); + sediffx_parse_command_line(argc, argv, &orig_path, &mod_path, &run_diff); + glade_init(); + if (!g_thread_supported()) + g_thread_init(NULL); + if ((app = calloc(1, sizeof(*app))) == NULL || (app->top = toplevel_create(app)) == NULL) { + ERR(NULL, "%s", strerror(errno)); + sediffx_destroy(&app); + exit(EXIT_FAILURE); + } + if (orig_path != NULL && mod_path != NULL) { + struct delayed_main_data dmd = { orig_path, mod_path, run_diff, app->top }; + g_idle_add(&delayed_main, &dmd); + } + gtk_main(); + + sediffx_destroy(&app); + exit(EXIT_SUCCESS); +} diff --git a/sediff/sediffx.glade b/sediff/sediffx.glade new file mode 100644 index 0000000..baeea93 --- /dev/null +++ b/sediff/sediffx.glade @@ -0,0 +1,3619 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="toplevel"> + <property name="visible">True</property> + <property name="title" translatable="yes">Semantic Policy Difference Tool </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">800</property> + <property name="default_height">600</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="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <signal name="destroy" handler="toplevel_on_destroy" object="toplevel" last_modification_time="Wed, 10 Jan 2007 20:20:42 GMT"/> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkMenuBar" id="main_menubar"> + <property name="visible">True</property> + <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property> + <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property> + + <child> + <widget class="GtkMenuItem" id="file menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="file menu"> + + <child> + <widget class="GtkImageMenuItem" id="Open"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Open Policies</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_open_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:14 GMT"/> + <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image201"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator1"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="Quit"> + <property name="visible">True</property> + <property name="label">gtk-quit</property> + <property name="use_stock">True</property> + <signal name="activate" handler="toplevel_on_quit_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:08 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="edit menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="edit menu"> + <signal name="activate_current" handler="toplevel_on_edit_menu_activate" object="toplevel" last_modification_time="Tue, 16 Jan 2007 16:29:46 GMT"/> + + <child> + <widget class="GtkImageMenuItem" id="Copy"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label">gtk-copy</property> + <property name="use_stock">True</property> + <signal name="activate" handler="toplevel_on_copy_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:02 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator2"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="Select All"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Select _All</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_select_all_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:56 GMT"/> + <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="tools menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Tools</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="tools menu"> + + <child> + <widget class="GtkImageMenuItem" id="Find"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">_Find Text</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_find_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:50 GMT"/> + <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image202"> + <property name="visible">True</property> + <property name="stock">gtk-find</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="Run Diff"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">_Run Diff</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_run_diff_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:44 GMT"/> + <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image203"> + <property name="visible">True</property> + <property name="stock">gtk-execute</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="Remap Types"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Remap _Types</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_remap_types_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:37 GMT"/> + <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image204"> + <property name="visible">True</property> + <property name="stock">gtk-preferences</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator3"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="sort menu item"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Sort Rules</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="sort menu"> + + <child> + <widget class="GtkRadioMenuItem" id="Default Sort"> + <property name="visible">True</property> + <property name="label" translatable="yes">Default Sort</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <signal name="activate" handler="toplevel_on_default_sort_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:04:31 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="source type menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Source Type</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="source type menu"> + + <child> + <widget class="GtkRadioMenuItem" id="Ascending source type"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ascending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_source_type_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:38 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="Descending source type"> + <property name="visible">True</property> + <property name="label" translatable="yes">Descending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_source_type_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:45 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="target type menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Target Type</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="target type menu"> + + <child> + <widget class="GtkRadioMenuItem" id="Ascending target type"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ascending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_target_type_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:53 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="Descending target type"> + <property name="visible">True</property> + <property name="label" translatable="yes">Descending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_target_type_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:59 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="object class menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Object Class</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="object class menu"> + + <child> + <widget class="GtkRadioMenuItem" id="Ascending object class"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ascending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_class_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:05 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="Descending object class"> + <property name="visible">True</property> + <property name="label" translatable="yes">Descending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_class_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:10 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="conditional menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Conditional</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="conditional menu"> + + <child> + <widget class="GtkRadioMenuItem" id="Ascending conditional"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ascending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_conditional_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:17 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="Descending conditional"> + <property name="visible">True</property> + <property name="label" translatable="yes">Descending</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">Default Sort</property> + <signal name="activate" handler="toplevel_on_conditional_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:23 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="help menu item"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="help menu"> + + <child> + <widget class="GtkImageMenuItem" id="Help"> + <property name="visible">True</property> + <property name="label">gtk-help</property> + <property name="use_stock">True</property> + <signal name="activate" handler="toplevel_on_help_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 20:29:03 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator4"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="About sediffx"> + <property name="visible">True</property> + <property name="label" translatable="yes">_About sediffx</property> + <property name="use_underline">True</property> + <signal name="activate" handler="toplevel_on_about_sediffx_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 20:28:55 GMT"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image213"> + <property name="visible">True</property> + <property name="stock">gtk-about</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolbar" id="toolbar"> + <property name="visible">True</property> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> + <property name="tooltips">True</property> + <property name="show_arrow">True</property> + + <child> + <widget class="GtkToolButton" id="open policies button"> + <property name="visible">True</property> + <property name="label" translatable="yes">Open Policies</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-open</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">True</property> + <signal name="clicked" handler="toplevel_on_open_policies_button_click" object="toplevel" last_modification_time="Tue, 16 Jan 2007 23:30:33 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="run diff button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Run Diff</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-execute</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="toplevel_on_run_diff_button_click" object="toplevel" last_modification_time="Wed, 10 Jan 2007 21:33:48 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="remap types button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Remap Types</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-preferences</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="toplevel_on_remap_types_button_click" object="toplevel" last_modification_time="Tue, 09 Jan 2007 23:49:40 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkNotebook" id="toplevel main notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkHPaned" id="hpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">170</property> + + <child> + <widget class="GtkVPaned" id="vpaned2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">300</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow18"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="toplevel summary view"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow11"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkViewport" id="viewport3"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + + <child> + <widget class="GtkVBox" id="vbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label18"> + <property name="visible">True</property> + <property name="label" translatable="yes">Difference Key</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow16"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel key view"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">False</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">False</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">491</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow8"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel results view"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK</property> + <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="label" translatable="yes">Differences</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkNotebook" id="toplevel policy_orig notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow12"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel policy_orig stats text"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">False</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label13"> + <property name="visible">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Policy Statistics</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow13"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel policy_orig source text"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label14"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Source</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="label" translatable="yes">Original Policy</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkNotebook" id="toplevel policy_mod notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow14"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel policy_mod stats text"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">False</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="label" translatable="yes">Policy Statistics</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow15"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="toplevel policy_mod source text"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label16"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Source</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="label" translatable="yes">Modified Policy</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox10"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="toplevel line label"> + <property name="width_request">82</property> + <property name="visible">True</property> + <property name="label" translatable="yes"></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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">4</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="toplevel stats label"> + <property name="visible">True</property> + <property name="label" translatable="yes"></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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">4</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="find_dialog"> + <property name="title" translatable="yes">Sediff Find</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</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="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</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="find close button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</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">-7</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="find find button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-find</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">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox16"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkEntry" id="find entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox9"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkRadioButton" id="find forward radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Forward</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> + + <child> + <widget class="GtkRadioButton" id="find reverse radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Reverse</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> + <property name="group">find forward radio</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> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="remap_types"> + <property name="title" translatable="yes">Remap Types</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">True</property> + <property name="default_width">500</property> + <property name="default_height">450</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="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox3"> + <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_area3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="closebutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</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">-7</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="vbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkFrame" id="frame3"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + + <child> + <widget class="GtkAlignment" id="alignment6"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox14"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkHBox" id="hbox15"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow17"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="remap_types treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">True</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox3"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkButton" id="remap_types remove button"> + <property name="visible">True</property> + <property name="sensitive">False</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">5</property> + <property name="expand">False</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> + + <child> + <widget class="GtkCheckButton" id="remap_types inferred checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show inferred mappings</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> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label25"> + <property name="visible">True</property> + <property name="label" translatable="yes">Remap Types</property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame2"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox15"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkHBox" id="hbox14"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox7"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label23"> + <property name="visible">True</property> + <property name="label" translatable="yes">Original Policy</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">3</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBoxEntry" id="remap_types orig combo"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="has_frame">True</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">5</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> + + <child> + <widget class="GtkVBox" id="vbox8"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label24"> + <property name="visible">True</property> + <property name="label" translatable="yes">Modified Policy</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">3</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBoxEntry" id="remap_types mod combo"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="has_frame">True</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">5</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> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox2"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_SPREAD</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkButton" id="remap_types add button"> + <property name="visible">True</property> + <property name="sensitive">False</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> + </widget> + <packing> + <property name="padding">5</property> + <property name="expand">False</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> + + <child> + <widget class="GtkCheckButton" id="remap_types only unmapped checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show only unmapped types</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</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> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label23"> + <property name="visible">True</property> + <property name="label" translatable="yes">Add New Remap</property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</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="PoliciesOpenWindow"> + <property name="title" translatable="yes">Open Policies</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">True</property> + <property name="default_width">690</property> + <property name="default_height">360</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="focus_on_map">True</property> + <property name="urgency_hint">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_area6"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancel button"> + <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="ok button"> + <property name="visible">True</property> + <property name="sensitive">False</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> + + <child> + <widget class="GtkButton" id="rundiff button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">0</property> + <signal name="clicked" handler="sediff_open_dialog_on_open_and_diff_button_clicked" last_modification_time="Wed, 02 Nov 2005 16:20:30 GMT"/> + + <child> + <widget class="GtkAlignment" id="alignment11"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox21"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image214"> + <property name="visible">True</property> + <property name="stock">gtk-execute</property> + <property name="icon_size">4</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="GtkLabel" id="label29"> + <property name="visible">True</property> + <property name="label" translatable="yes">Run Diff</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </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="hbox22"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkVBox" id="vbox"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">10</property> + + <child> + <widget class="GtkVBox" id="vbox.1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Original Policy Type:</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_END</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment.1.2"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">10</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox.1.2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkRadioButton" id="monolithic radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Monolithic policy</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> + + <child> + <widget class="GtkRadioButton" id="modular radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Modular policy</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> + <property name="group">monolithic radio</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </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">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox.2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="main filename label"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Original Policy Filename:</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_END</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">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="hbox.2.2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkEntry" id="base entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="base browse"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment.2.2.1"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox.2.2.1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image.2.2.1.1"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">4</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="GtkLabel" id="label.2.2.1.1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Browse</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </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> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox.3"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow.3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="module view"> + <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> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox.3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="module 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="module remove button"> + <property name="visible">True</property> + <property name="sensitive">False</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> + + <child> + <widget class="GtkButton" id="module list import button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment16"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox27"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image218"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">4</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="GtkLabel" id="label35"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Import</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkButton" id="module list export button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment15"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox26"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image217"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + <property name="icon_size">4</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="GtkLabel" id="label34"> + <property name="visible">True</property> + <property name="label" translatable="yes">E_xport</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</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> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVSeparator" id="vseparator1"> + <property name="visible">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">10</property> + + <child> + <widget class="GtkVBox" id="vbox11"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label30"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Modified Policy Type:</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_END</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment12"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">10</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox12"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkRadioButton" id="monolithic radio 1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Monolithic policy</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> + + <child> + <widget class="GtkRadioButton" id="modular radio 1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Modular policy</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> + <property name="group">monolithic radio 1</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </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">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox13"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="main filename label 1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Modified Policy Filename:</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_END</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">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="hbox23"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkEntry" id="base entry 1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="base browse 1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment13"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox24"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image215"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">4</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="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="label" translatable="yes">Browse</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </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> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox.3 1"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow19"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="module view 1"> + <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> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox4"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="module add button 1"> + <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="module remove button 1"> + <property name="visible">True</property> + <property name="sensitive">False</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> + + <child> + <widget class="GtkButton" id="module list import button 1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment17"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox28"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image219"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">4</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="GtkLabel" id="label36"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Import</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkButton" id="module list export button 1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment18"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox29"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image220"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + <property name="icon_size">4</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="GtkLabel" id="label37"> + <property name="visible">True</property> + <property name="label" translatable="yes">E_xport</property> + <property name="use_underline">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</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> + <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="select_components"> + <property name="visible">True</property> + <property name="title" translatable="yes" context="yes">Run Diff</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">False</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_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox4"> + <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_area7"> + <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="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="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="vbox16"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">4</property> + + <child> + <widget class="GtkLabel" id="label38"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes"><b>Select Components to Diff</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox17"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label39"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Basic Components:</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">20</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment20"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">8</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox18"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="commons checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Commons</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> + + <child> + <widget class="GtkCheckButton" id="classes checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Classes</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> + + <child> + <widget class="GtkCheckButton" id="users checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Users</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> + + <child> + <widget class="GtkCheckButton" id="bools checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Booleans</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> + </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> + + <child> + <widget class="GtkVBox" id="vbox21"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label41"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Type Enforcement:</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment22"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">8</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox22"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="attribs checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Attributes</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> + + <child> + <widget class="GtkCheckButton" id="types checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Types</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> + + <child> + <widget class="GtkCheckButton" id="allow checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Access vector rules: allow</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> + + <child> + <widget class="GtkCheckButton" id="auditallow checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Access vector rules: auditallow</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> + + <child> + <widget class="GtkCheckButton" id="dontaudit checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Access vector rules: dontaudit</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> + + <child> + <widget class="GtkCheckButton" id="neverallow checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Access vector rules: neverallow</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> + <property name="tooltip">This option may dramatically increase run time.</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="type_change checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Type rules: type__change</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> + + <child> + <widget class="GtkCheckButton" id="type_member checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Type rules: type__member</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> + + <child> + <widget class="GtkCheckButton" id="type_transition checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Type rules: type__transition</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> + </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> + + <child> + <widget class="GtkVBox" id="vbox23"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label42"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">RBAC:</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment23"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">8</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox24"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="roles checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Roles</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> + + <child> + <widget class="GtkCheckButton" id="roleallows checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Role allows</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> + + <child> + <widget class="GtkCheckButton" id="roletrans checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Role transitions</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> + </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> + + <child> + <widget class="GtkVBox" id="vbox19"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label40"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">MLS:</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> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment21"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">8</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox20"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="levels checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Levels</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> + + <child> + <widget class="GtkCheckButton" id="cats checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Categories</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> + + <child> + <widget class="GtkCheckButton" id="rangetrans checkbutton"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Range transitions</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> + </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> + + <child> + <widget class="GtkHButtonBox" id="hbuttonbox1"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_SPREAD</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkButton" id="select all button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="label" translatable="yes" context="yes">Select All</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="select none button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="label" translatable="yes" context="yes">Select None</property> + <property name="use_underline">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">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/sediff/sediffx.h b/sediff/sediffx.h new file mode 100644 index 0000000..01d4cf8 --- /dev/null +++ b/sediff/sediffx.h @@ -0,0 +1,108 @@ +/** + * @file + * Headers for main sediffx program. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SEDIFFX_H +#define SEDIFFX_H + +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <poldiff/poldiff.h> + +typedef struct sediffx sediffx_t; + +/** enumeration of which policy to affect -- the original policy (used + to be called "policy 1") or the modified policy ("policy 2") */ +typedef enum sediffx_policy +{ + SEDIFFX_POLICY_ORIG = 0, SEDIFFX_POLICY_MOD, SEDIFFX_POLICY_NUM +} sediffx_policy_e; + +#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC" + +/** + * Set one of the policies for sediffx. This will invalidate any + * currently executed poldiff_t objects. + * + * @param s sediffx object to query. + * @param which Which policy to set. + * @param policy New policy file for sediffx. If NULL then no policy + * is opened. Afterwards sediffx takes ownership of the policy. + * @param path If policy is not NULL, then the path that was used to + * open the policy. + */ +void sediffx_set_policy(sediffx_t * s, sediffx_policy_e which, apol_policy_t * policy, apol_policy_path_t * path); + +/** + * Return the policy path for the policy given. If the policy has not + * yet been loaded then return NULL. + * + * @param s sediffx object to query. + * @param which Which policy path to get. + * + * @return Path to the policy, or NULL if none set. + */ +const apol_policy_path_t *sediffx_get_policy_path(sediffx_t * s, const sediffx_policy_e which); + +/** + * Return the currently active poldiff object. If one is not yet + * created or if a policy has changed since the last time this + * function was called, then build a new one and return it. Note that + * this does not actually call poldiff_run(); it is up to the caller + * of this function to do that. + * + * @param s sediffx object to query. + * @param fn If a poldiff object is being created, a valid callback + * function to receive poldiff messages. + * @param arg Arbitrary argument to poldiff callback handler. + * + * @return Poldiff object for currently loaded policies, or NULL upon + * error. + */ +poldiff_t *sediffx_get_poldiff(sediffx_t * s, poldiff_handle_fn_t fn, void *arg); + +/** + * Set the flags that were used to run a poldiff. This function + * should be called immediately proceeding a call to poldiff_run(). + * + * @param s sediffx object that contained the poldiff object that ran. + * @param flags Flags for that were used during the run. + * + * @see sediffx_get_poldiff_run_flags + */ +void sediffx_set_poldiff_run_flags(sediffx_t * s, uint32_t flags); + +/** + * Get the flags that were used to run a poldiff. + * + * @param s sediffx object to query. + * + * @return poldiff run flags, or 0 in none set. + * + * @see sediffx_set_poldiff_run_flags + */ +uint32_t sediffx_get_poldiff_run_flags(sediffx_t * s); + +#endif diff --git a/sediff/sediffx.png b/sediff/sediffx.png Binary files differnew file mode 100644 index 0000000..db4f83b --- /dev/null +++ b/sediff/sediffx.png diff --git a/sediff/sediffx.xcf b/sediff/sediffx.xcf Binary files differnew file mode 100644 index 0000000..ded254f --- /dev/null +++ b/sediff/sediffx.xcf diff --git a/sediff/select_diff_dialog.c b/sediff/select_diff_dialog.c new file mode 100644 index 0000000..881dbe5 --- /dev/null +++ b/sediff/select_diff_dialog.c @@ -0,0 +1,134 @@ +/** + * @file + * Run the dialog to allow the user to select components. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "select_diff_dialog.h" + +#include <assert.h> +#include <glade/glade.h> + +struct component +{ + const char *name; + const uint32_t bit; +}; + +static const struct component comps[] = { + {"attribs checkbutton", POLDIFF_DIFF_ATTRIBS}, + {"allow checkbutton", POLDIFF_DIFF_AVALLOW}, + {"auditallow checkbutton", POLDIFF_DIFF_AVAUDITALLOW}, + {"dontaudit checkbutton", POLDIFF_DIFF_AVDONTAUDIT}, + {"neverallow checkbutton", POLDIFF_DIFF_AVNEVERALLOW}, + {"bools checkbutton", POLDIFF_DIFF_BOOLS}, + {"cats checkbutton", POLDIFF_DIFF_CATS}, + {"classes checkbutton", POLDIFF_DIFF_CLASSES}, + {"commons checkbutton", POLDIFF_DIFF_COMMONS}, + {"levels checkbutton", POLDIFF_DIFF_LEVELS}, + {"rangetrans checkbutton", POLDIFF_DIFF_RANGE_TRANS}, + {"roles checkbutton", POLDIFF_DIFF_ROLES}, + {"roleallows checkbutton", POLDIFF_DIFF_ROLE_ALLOWS}, + {"roletrans checkbutton", POLDIFF_DIFF_ROLE_TRANS}, + {"users checkbutton", POLDIFF_DIFF_USERS}, + {"type_change checkbutton", POLDIFF_DIFF_TECHANGE}, + {"type_member checkbutton", POLDIFF_DIFF_TEMEMBER}, + {"type_transition checkbutton", POLDIFF_DIFF_TETRANS}, + {"types checkbutton", POLDIFF_DIFF_TYPES}, + {NULL, 0} +}; + +static uint32_t prev_selection = POLDIFF_DIFF_ALL & ~POLDIFF_DIFF_AVNEVERALLOW; + +static void select_diff_on_select_all_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + GladeXML *xml = (GladeXML *) user_data; + size_t i; + const struct component *c; + for (i = 0; comps[i].name != NULL; i++) { + c = comps + i; + GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name)); + gtk_toggle_button_set_active(cb, TRUE); + } +} + +static void select_diff_on_select_none_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + GladeXML *xml = (GladeXML *) user_data; + size_t i; + const struct component *c; + for (i = 0; comps[i].name != NULL; i++) { + c = comps + i; + GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name)); + gtk_toggle_button_set_active(cb, FALSE); + } +} + +int select_diff_dialog_run(toplevel_t * top) +{ + GladeXML *xml = glade_xml_new(toplevel_get_glade_xml(top), "select_components", NULL); + GtkDialog *dialog = GTK_DIALOG(glade_xml_get_widget(xml, "select_components")); + assert(dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(dialog), toplevel_get_window(top)); + + size_t i; + const struct component *c; + for (i = 0; comps[i].name != NULL; i++) { + c = comps + i; + GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name)); + assert(cb != NULL); + gtk_toggle_button_set_active(cb, c->bit & prev_selection ? TRUE : FALSE); + } + + GtkButton *b = GTK_BUTTON(glade_xml_get_widget(xml, "select all button")); + assert(b != NULL); + g_signal_connect(b, "clicked", G_CALLBACK(select_diff_on_select_all_click), xml); + b = GTK_BUTTON(glade_xml_get_widget(xml, "select none button")); + assert(b != NULL); + g_signal_connect(b, "clicked", G_CALLBACK(select_diff_on_select_none_click), xml); + + uint32_t result = 0; + while (result == 0) { + result = 0; + if (gtk_dialog_run(dialog) != GTK_RESPONSE_OK) { + gtk_widget_destroy(GTK_WIDGET(dialog)); + return 0; + } + + for (i = 0; comps[i].name != NULL; i++) { + c = comps + i; + GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name)); + if (gtk_toggle_button_get_active(cb)) { + result |= c->bit; + } + } + + if (result == 0) { + toplevel_ERR(top, "%s", "At least one component must be selected."); + } + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); + prev_selection = result; + return result; +} diff --git a/sediff/select_diff_dialog.h b/sediff/select_diff_dialog.h new file mode 100644 index 0000000..227ab04 --- /dev/null +++ b/sediff/select_diff_dialog.h @@ -0,0 +1,42 @@ +/** + * @file + * Dialog that allows the user to select which policy components to + * diff. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SELECT_DIFF_DIALOG_H +#define SELECT_DIFF_DIALOG_H + +#include "toplevel.h" + +/** + * Display and run a dialog that allows the user to select which + * policy components to diff. + * + * @param top Toplevel for the application. + * + * @return Bitmap of which components to diff; the bits correspond to + * those defined in poldiff/poldiff.h + */ +int select_diff_dialog_run(toplevel_t * top); + +#endif diff --git a/sediff/toplevel.c b/sediff/toplevel.c new file mode 100644 index 0000000..db6d1f5 --- /dev/null +++ b/sediff/toplevel.c @@ -0,0 +1,728 @@ +/** + * @file + * Implementation for sediffx's main toplevel window. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "find_dialog.h" +#include "open_policies_dialog.h" +#include "policy_view.h" +#include "remap_types_dialog.h" +#include "sediffx.h" +#include "select_diff_dialog.h" +#include "toplevel.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <apol/util.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +struct toplevel +{ + sediffx_t *s; + progress_t *progress; + find_dialog_t *find; + results_t *results; + policy_view_t *views[SEDIFFX_POLICY_NUM]; + GladeXML *xml; + /** filename for glade file */ + char *xml_filename; + /** toplevel window widget */ + GtkWindow *w; + /** toplevel notebook widget */ + GtkNotebook *notebook; +}; + +/** + * Enable/disable all items (menus and buttons) that depend upon if a + * policy is loaded. + * + * @param top Toplevel object containing menu widgets. + * @param TRUE to enable items, FALSE to disable. + */ +static void toplevel_enable_policy_items(toplevel_t * top, gboolean sens) +{ + static const char *items[] = { + "Copy", "Select All", + "Find", "Run Diff", "Remap Types", + "run diff button", "remap types button", + NULL + }; + size_t i; + const char *s; + for (i = 0, s = items[0]; s != NULL; s = items[++i]) { + GtkWidget *w = glade_xml_get_widget(top->xml, s); + assert(w != NULL); + gtk_widget_set_sensitive(w, sens); + } +} + +/** + * Update the toplevel's title bar to list the policies currently + * opened. + * + * @param top Toplevel to modify. + */ +static void toplevel_update_title_bar(toplevel_t * top) +{ + const apol_policy_path_t *paths[SEDIFFX_POLICY_NUM]; + char *types[SEDIFFX_POLICY_NUM] = { "Policy", "Policy" }, *s; + const char *primaries[SEDIFFX_POLICY_NUM] = { NULL, NULL }; + sediffx_policy_e i; + + paths[SEDIFFX_POLICY_ORIG] = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG); + paths[SEDIFFX_POLICY_MOD] = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD); + + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + if (paths[i] == NULL) { + primaries[i] = "No Policy"; + } else { + if (apol_policy_path_get_type(paths[i]) == APOL_POLICY_PATH_TYPE_MODULAR) { + types[i] = "Base"; + } + primaries[i] = apol_policy_path_get_primary(paths[i]); + } + } + if (asprintf(&s, "sediffx - [%s file: %s] [%s file: %s]", + types[SEDIFFX_POLICY_ORIG], primaries[SEDIFFX_POLICY_ORIG], + types[SEDIFFX_POLICY_MOD], primaries[SEDIFFX_POLICY_MOD]) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + return; + } + gtk_window_set_title(top->w, s); + free(s); +} + +/** + * Initialize the application icons for the program. These icons are + * the ones shown by the window manager within title bars and pagers. + * The last icon listed in the array will be displayed in the About + * dialog. + * + * @param top Toplevel whose icon to set. All child windows will + * inherit these icons. + */ +static void init_icons(toplevel_t * top) +{ + static const char *icon_names[] = { "sediffx-small.png", "sediffx.png" }; + GdkPixbuf *icon; + char *path; + GList *icon_list = NULL; + size_t i; + for (i = 0; i < sizeof(icon_names) / sizeof(icon_names[0]); i++) { + if ((path = apol_file_find_path(icon_names[i])) == NULL) { + continue; + } + icon = gdk_pixbuf_new_from_file(path, NULL); + free(path); + if (icon == NULL) { + continue; + } + icon_list = g_list_append(icon_list, icon); + } + gtk_window_set_default_icon_list(icon_list); + gtk_window_set_icon_list(top->w, icon_list); +} + +static void toplevel_on_switch_page(GtkNotebook * notebook __attribute__ ((unused)), GtkNotebookPage * page + __attribute__ ((unused)), guint page_num, gpointer user_data) +{ + toplevel_t *top = (toplevel_t *) user_data; + if (page_num != 0) { + toplevel_set_sort_menu_sensitivity(top, FALSE); + } else { + results_switch_to_page(top->results); + } +} + +toplevel_t *toplevel_create(sediffx_t * s) +{ + toplevel_t *top; + int error = 0; + sediffx_policy_e i; + if ((top = calloc(1, sizeof(*top))) == NULL) { + error = errno; + goto cleanup; + } + top->s = s; + if ((top->xml_filename = apol_file_find_path("sediffx.glade")) == NULL || + (top->xml = glade_xml_new(top->xml_filename, "toplevel", NULL)) == NULL) { + fprintf(stderr, "Could not open sediffx.glade.\n"); + error = EIO; + goto cleanup; + } + top->w = GTK_WINDOW(glade_xml_get_widget(top->xml, "toplevel")); + top->notebook = GTK_NOTEBOOK(glade_xml_get_widget(top->xml, "toplevel main notebook")); + assert(top->w != NULL && top->notebook != NULL); + init_icons(top); + g_object_set_data(G_OBJECT(top->w), "toplevel", top); + gtk_widget_show(GTK_WIDGET(top->w)); + g_signal_connect(G_OBJECT(top->notebook), "switch-page", G_CALLBACK(toplevel_on_switch_page), top); + + /* initialize sub-windows, now that glade XML file has been + * read */ + if ((top->find = find_dialog_create(top)) == NULL || + (top->progress = progress_create(top)) == NULL || (top->results = results_create(top)) == NULL) { + error = errno; + goto cleanup; + } + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + if ((top->views[i] = policy_view_create(top, i)) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + error = errno; + goto cleanup; + } + } + + glade_xml_signal_autoconnect(top->xml); + + cleanup: + if (error != 0) { + toplevel_destroy(&top); + errno = error; + return NULL; + } + return top; +} + +void toplevel_destroy(toplevel_t ** top) +{ + if (top != NULL && *top != NULL) { + sediffx_policy_e i; + find_dialog_destroy(&(*top)->find); + progress_destroy(&(*top)->progress); + results_destroy(&(*top)->results); + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + policy_view_destroy(&(*top)->views[i]); + } + free((*top)->xml_filename); + free(*top); + *top = NULL; + } +} + +struct policy_run_datum +{ + toplevel_t *top; + apol_policy_path_t *paths[2]; + apol_policy_t *policies[2]; + int result; +}; + +/** + * Thread that loads and parses a policy file. It will write to + * progress_seaudit_handle_func() its status during the load. + * + * @param data Pointer to a struct policy_run_datum, for control + * information. + */ +static gpointer toplevel_open_policy_runner(gpointer data) +{ + struct policy_run_datum *run = (struct policy_run_datum *)data; + sediffx_policy_e i; + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + apol_policy_path_t *path = run->paths[i]; + char *title = util_policy_path_to_string(path); + if (title == NULL) { + run->result = -1; + progress_abort(run->top->progress, "%s", strerror(errno)); + return NULL; + } + progress_update(run->top->progress, "Opening %s", title); + free(title); + run->policies[i] = + apol_policy_create_from_policy_path(path, QPOL_POLICY_OPTION_NO_RULES, progress_apol_handle_func, + run->top->progress); + // poldiff_run() will rebuild the policies as needed + if (run->policies[i] == NULL) { + run->result = -1; + progress_abort(run->top->progress, NULL); + return NULL; + } + } + run->result = 0; + progress_done(run->top->progress); + return NULL; +} + +int toplevel_open_policies(toplevel_t * top, apol_policy_path_t * orig_path, apol_policy_path_t * mod_path) +{ + struct policy_run_datum run; + memset(&run, 0, sizeof(run)); + run.top = top; + run.paths[0] = orig_path; + run.paths[1] = mod_path; + sediffx_policy_e i; + + util_cursor_wait(GTK_WIDGET(top->w)); + progress_show(top->progress, "Loading Policies"); + g_thread_create(toplevel_open_policy_runner, &run, FALSE, NULL); + progress_wait(top->progress); + progress_hide(top->progress); + util_cursor_clear(GTK_WIDGET(top->w)); + if (run.result < 0) { + apol_policy_path_destroy(&run.paths[0]); + apol_policy_path_destroy(&run.paths[1]); + return run.result; + } + if (remap_types_update(run.policies[SEDIFFX_POLICY_ORIG], run.policies[SEDIFFX_POLICY_MOD]) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + apol_policy_path_destroy(&run.paths[0]); + apol_policy_path_destroy(&run.paths[1]); + return -1; + } + results_open_policies(top->results, run.policies[SEDIFFX_POLICY_ORIG], run.policies[SEDIFFX_POLICY_MOD]); + for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { + policy_view_update(top->views[i], run.policies[i], run.paths[i]); + sediffx_set_policy(top->s, i, run.policies[i], run.paths[i]); + } + results_update(top->results); + toplevel_enable_policy_items(top, TRUE); + toplevel_update_title_bar(top); + return 0; +} + +struct run_datum +{ + toplevel_t *top; + uint32_t run_flags; + int result; +}; + +static gpointer toplevel_run_diff_runner(gpointer data) +{ + struct run_datum *run = data; + poldiff_t *diff = sediffx_get_poldiff(run->top->s, progress_poldiff_handle_func, run->top->progress); + if (diff == NULL) { + run->result = -1; + progress_abort(run->top->progress, "Could not get a poldiff object: %s", strerror(errno)); + return NULL; + } + run->result = poldiff_run(diff, run->run_flags); + sediffx_set_poldiff_run_flags(run->top->s, run->run_flags); + if (run->run_flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) { + poldiff_enable_line_numbers(diff); + } + if (run->result < 0) { + progress_abort(run->top->progress, NULL); + } else { + progress_done(run->top->progress); + } + return NULL; +} + +void toplevel_run_diff(toplevel_t * top) +{ + struct run_datum r; + + r.run_flags = select_diff_dialog_run(top); + if (r.run_flags == 0) { + return; + } + r.top = top; + r.result = 0; + + results_clear(top->results); + util_cursor_wait(GTK_WIDGET(top->w)); + progress_show(top->progress, "Running Diff"); + g_thread_create(toplevel_run_diff_runner, &r, FALSE, NULL); + progress_wait(top->progress); + progress_hide(top->progress); + util_cursor_clear(GTK_WIDGET(top->w)); + if (r.result == 0) { + results_update(top->results); + } +} + +void toplevel_show_policy_line(toplevel_t * top, sediffx_policy_e which, unsigned long line) +{ + gtk_notebook_set_current_page(top->notebook, 1 + which); + policy_view_show_policy_line(top->views[which], line); +} + +void toplevel_set_sort_menu_sensitivity(toplevel_t * top, gboolean sens) +{ + GtkWidget *w = glade_xml_get_widget(top->xml, "sort menu item"); + assert(w != NULL); + gtk_widget_set_sensitive(w, sens); +} + +void toplevel_set_sort_menu_selection(toplevel_t * top, results_sort_e field, results_sort_dir_e dir) +{ + static const char *menu_items[][2] = { + {"Default Sort", "Default Sort"}, + {"Ascending source type", "Descending source type"}, + {"Ascending target type", "Descending target type"}, + {"Ascending object class", "Descending object class"}, + {"Ascending conditional", "Descending conditonal"} + }; + int direction = 1; + if (dir == RESULTS_SORT_ASCEND) { + direction = 0; + } + GtkWidget *w = glade_xml_get_widget(top->xml, menu_items[field][direction]); + assert(w != NULL); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), TRUE); +} + +char *toplevel_get_glade_xml(toplevel_t * top) +{ + return top->xml_filename; +} + +gint toplevel_get_notebook_page(toplevel_t * top) +{ + return gtk_notebook_get_current_page(top->notebook); +} + +progress_t *toplevel_get_progress(toplevel_t * top) +{ + return top->progress; +} + +GtkWindow *toplevel_get_window(toplevel_t * top) +{ + return top->w; +} + +GtkTextView *toplevel_get_text_view(toplevel_t * top) +{ + gint pagenum = gtk_notebook_get_current_page(top->notebook); + switch (pagenum) { + case 0: + return results_get_text_view(top->results); + case 1: + return policy_view_get_text_view(top->views[SEDIFFX_POLICY_ORIG]); + case 2: + return policy_view_get_text_view(top->views[SEDIFFX_POLICY_MOD]); + } + /* should never get here */ + assert(0); + return NULL; +} + +poldiff_t *toplevel_get_poldiff(toplevel_t * top) +{ + return sediffx_get_poldiff(top->s, progress_poldiff_handle_func, top->progress); +} + +uint32_t toplevel_get_poldiff_run_flags(toplevel_t * top) +{ + return sediffx_get_poldiff_run_flags(top->s); +} + +/** + * Pop-up a dialog with a line of text and wait for the user to + * dismiss the dialog. + * + * @param top Toplevel window; this message dialog will be centered + * upon it. + * @param msg_type Type of message being displayed. + * @param fmt Format string to print, using syntax of printf(3). + */ +static void toplevel_message(toplevel_t * top, GtkMessageType msg_type, const char *fmt, va_list ap) +{ + GtkWidget *dialog; + char *msg; + if (vasprintf(&msg, fmt, ap) < 0) { + ERR(NULL, "%s", strerror(errno)); + return; + } + dialog = gtk_message_dialog_new(top->w, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg); + free(msg); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +void toplevel_ERR(toplevel_t * top, const char *format, ...) +{ + va_list(ap); + va_start(ap, format); + toplevel_message(top, GTK_MESSAGE_ERROR, format, ap); + va_end(ap); +} + +void toplevel_WARN(toplevel_t * top, const char *format, ...) +{ + va_list(ap); + va_start(ap, format); + toplevel_message(top, GTK_MESSAGE_WARNING, format, ap); + va_end(ap); +} + +/******************** menu callbacks below ********************/ + +void toplevel_on_open_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + const apol_policy_path_t *orig_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG); + const apol_policy_path_t *mod_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD); + open_policies_dialog_run(top, orig_path, mod_path); +} + +void toplevel_on_quit_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + top->w = NULL; + gtk_main_quit(); +} + +void toplevel_on_edit_menu_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + GtkTextView *view = toplevel_get_text_view(top); + GtkTextBuffer *txt = gtk_text_view_get_buffer(view); + GtkWidget *copy = glade_xml_get_widget(top->xml, "Copy"); + GtkWidget *select_all = glade_xml_get_widget(top->xml, "Select All"); + GtkTextIter start, end; + assert(copy != NULL && select_all != NULL); + + /* check to see if anything has been selected and set copy + * button up */ + if (gtk_text_buffer_get_selection_bounds(txt, &start, &end)) { + gtk_widget_set_sensitive(copy, TRUE); + } else { + gtk_widget_set_sensitive(copy, FALSE); + } + /* check to see if there is anything currently in this buffer + * that can be selected */ + gtk_text_buffer_get_start_iter(txt, &start); + gtk_text_buffer_get_end_iter(txt, &end); + if (gtk_text_iter_get_offset(&start) == gtk_text_iter_get_offset(&end)) { + gtk_widget_set_sensitive(select_all, FALSE); + } else { + gtk_widget_set_sensitive(select_all, TRUE); + } +} + +void toplevel_on_copy_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + GtkClipboard *clipboard = gtk_clipboard_get(NULL); + GtkTextView *view = toplevel_get_text_view(top); + GtkTextBuffer *txt = gtk_text_view_get_buffer(view); + if (gtk_text_buffer_get_selection_bounds(txt, NULL, NULL)) { + gtk_text_buffer_copy_clipboard(txt, clipboard); + } +} + +void toplevel_on_select_all_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + GtkTextView *view = toplevel_get_text_view(top); + GtkTextBuffer *txt = gtk_text_view_get_buffer(view); + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(txt, &start); + gtk_text_buffer_get_end_iter(txt, &end); + gtk_text_buffer_select_range(txt, &start, &end); +} + +void toplevel_on_find_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + find_dialog_show(top->find); +} + +void toplevel_on_run_diff_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + toplevel_run_diff(top); +} + +void toplevel_on_remap_types_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + remap_types_run(top); +} + +void toplevel_on_default_sort_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_DEFAULT, RESULTS_SORT_ASCEND); + } +} + +void toplevel_on_source_type_asc_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_SOURCE, RESULTS_SORT_ASCEND); + } +} + +void toplevel_on_source_type_des_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_SOURCE, RESULTS_SORT_DESCEND); + } +} + +void toplevel_on_target_type_asc_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_TARGET, RESULTS_SORT_ASCEND); + } +} + +void toplevel_on_target_type_des_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_TARGET, RESULTS_SORT_DESCEND); + } +} + +void toplevel_on_class_asc_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_CLASS, RESULTS_SORT_ASCEND); + } +} + +void toplevel_on_class_des_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_CLASS, RESULTS_SORT_DESCEND); + } +} + +void toplevel_on_conditional_asc_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_COND, RESULTS_SORT_ASCEND); + } +} + +void toplevel_on_conditional_des_activate(gpointer user_data, GtkMenuItem * menuitem) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { + results_sort(top->results, RESULTS_SORT_COND, RESULTS_SORT_DESCEND); + } +} + +void toplevel_on_help_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + GtkWidget *window; + GtkWidget *scroll; + GtkWidget *text_view; + GtkTextBuffer *buffer; + char *help_text = NULL; + size_t len; + int rt; + char *dir; + + window = gtk_dialog_new_with_buttons("sediffx Help", + GTK_WINDOW(top->w), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE); + g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window); + scroll = gtk_scrolled_window_new(NULL, NULL); + text_view = gtk_text_view_new(); + gtk_window_set_default_size(GTK_WINDOW(window), 520, 300); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll); + gtk_container_add(GTK_CONTAINER(scroll), text_view); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_NONE); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); + if ((dir = apol_file_find_path("sediff_help.txt")) == NULL) { + toplevel_ERR(top, "Cannot find help file."); + return; + } + rt = apol_file_read_to_buffer(dir, &help_text, &len); + free(dir); + if (rt != 0) { + free(help_text); + return; + } + gtk_text_buffer_set_text(buffer, help_text, len); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_widget_show(text_view); + gtk_widget_show(scroll); + gtk_widget_show(window); +} + +void toplevel_on_about_sediffx_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); +#ifdef GTK_2_8 + gtk_show_about_dialog(top->w, + "comments", "Policy Semantic Difference Tool for Security Enhanced Linux", + "copyright", COPYRIGHT_INFO, + "name", "sediffx", "version", VERSION, "website", "http://oss.tresys.com/projects/setools", NULL); +#else + GtkWidget *w = gtk_message_dialog_new(top->w, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + "%s %s\n%s\n%s\n%s", + "sediffx", VERSION, + "Policy Semantic Difference Tool for Security Enhanced Linux", + COPYRIGHT_INFO, + "http://oss.tresys.com/projects/setools"); + gtk_dialog_run(GTK_DIALOG(w)); + gtk_widget_destroy(w); +#endif +} + +void toplevel_on_open_policies_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + const apol_policy_path_t *orig_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG); + const apol_policy_path_t *mod_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD); + open_policies_dialog_run(top, orig_path, mod_path); +} + +void toplevel_on_run_diff_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + toplevel_run_diff(top); +} + +void toplevel_on_remap_types_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + remap_types_run(top); +} + +void toplevel_on_destroy(gpointer user_data, GtkObject * object __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + top->w = NULL; + gtk_main_quit(); +} diff --git a/sediff/toplevel.h b/sediff/toplevel.h new file mode 100644 index 0000000..14074a7 --- /dev/null +++ b/sediff/toplevel.h @@ -0,0 +1,202 @@ +/** + * @file + * Headers for main toplevel window. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Brandon Whalen bwhalen@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +typedef struct toplevel toplevel_t; + +#include "progress.h" +#include "results.h" +#include "sediffx.h" +#include <apol/policy-path.h> +#include <gtk/gtk.h> +#include <poldiff/poldiff.h> + +/** + * Allocate and return an instance of the toplevel window object. + * This will create the window, set up the menus and icons, then + * display the window. + * + * @param s Main sediffx object that will control the toplevel. + * + * @return An initialized toplevel object, or NULL upon error. The + * caller must call toplevel_destroy() afterwards. + */ +toplevel_t *toplevel_create(sediffx_t * s); + +/** + * Destroy the toplevel window. This function will recursively + * destroy all other windows. This does nothing if the pointer is set + * to NULL. + * + * @param top Reference to a toplevel object. Afterwards the pointer + * will be set to NULL. + */ +void toplevel_destroy(toplevel_t ** top); + +/** + * Open the policy files. Upon success destroy the existing policies + * and current poldiff objects. + * + * @param top Toplevel object, used for UI control. + * @param orig_path Path to the original policy. This function takes + * ownership of this object. + * @param mod_path Path to the modified policy. This function takes + * ownership of this object. + * + * @return 0 on successful open, < 0 on error. + */ +int toplevel_open_policies(toplevel_t * top, apol_policy_path_t * orig_path, apol_policy_path_t * mod_path); + +/** + * Run the current poldiff object. Afterwards this function will + * notify the results object to update its view. + * + * @param top Toplevel object whose poldiff to run. + */ +void toplevel_run_diff(toplevel_t * top); + +/** + * Switch to the given policy's source tab, if not already visible, + * and then scroll the view to show the given line. Policy line + * numbers are zero-indexed. + * + * @param top Toplevel object containing policy source tabs. + * @param which Which policy's source tab to show. + * @param line Line to show. + */ +void toplevel_show_policy_line(toplevel_t * top, sediffx_policy_e which, unsigned long line); + +/** + * Enable or disable the toplevel's sort menu. The sort menu should + * be enabled only when showing the differences for TE rules; + * otherwise it should be disabled. + * + * @param top Toplevel object containing sort menu. + * @param sens New sensitivity for the menu. + */ +void toplevel_set_sort_menu_sensitivity(toplevel_t * top, gboolean sens); + +/** + * Set the current sort menu selection to the given field and + * direction. + * + * @param top Toplevel object containing sort menu. + * @param field Sort field to select. + * @param dir Sort direction to select. + */ +void toplevel_set_sort_menu_selection(toplevel_t * top, results_sort_e field, results_sort_dir_e dir); + +/** + * Return the filename containing sediffx's glade file. + * + * @param top Toplevel containing glade XML declarations. + * + * @return Name of the glade file. Do not modify this string. + */ +char *toplevel_get_glade_xml(toplevel_t * top); + +/** + * Return the current page number for the toplevel main notebook. + * + * @param top Toplevel to query. + * + * @return Page number for the currently showing tab. + */ +gint toplevel_get_notebook_page(toplevel_t * top); + +/** + * Return the progress object, so that sub-windows may also show the + * threaded progress object. + * + * @param top Toplevel containing progress object. + * + * @return Progress object. Do not free() this pointer. + */ +progress_t *toplevel_get_progress(toplevel_t * top); + +/** + * Return the main application window. Sub-windows should be set + * transient to this window. + * + * @param top Toplevel containing main window. + * + * @return Main window. + */ +GtkWindow *toplevel_get_window(toplevel_t * top); + +/** + * Get the currently showing text view. This depends upon which + * toplevel notebook page is showing. + * + * @param top Toplevel containing text views. + * + * @return Currently visible text view. + */ +GtkTextView *toplevel_get_text_view(toplevel_t * top); + +/** + * Retrieve the currently active poldiff object. If policies have not + * yet been loaded then this returns NULL. Note that the poldiff + * object will not be run yet; for that call toplevel_run_diff(). + * + * @param top Toplevel containing poldiff object. + * + * @return poldiff object, or NULL if none availble or upon error. + */ +poldiff_t *toplevel_get_poldiff(toplevel_t * top); + +/** + * Get the flags that were used the most recently run poldiff. + * + * @param top Toplevel object to query. + * + * @return poldiff run flags, or 0 in none set. + */ +uint32_t toplevel_get_poldiff_run_flags(toplevel_t * top); + +/** + * Pop-up an error dialog with a line of text and wait for the user to + * dismiss the dialog. + * + * @param top Toplevel window; this message dialog will be centered + * upon it. + * @param format Format string to print, using syntax of printf(3). + */ +void toplevel_ERR(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Pop-up a warning dialog with a line of text and wait for the user + * to dismiss the dialog. + * + * @param top Toplevel window; this message dialog will be centered + * upon it. + * @param format Format string to print, using syntax of printf(3). + */ +void toplevel_WARN(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3))); + +#endif diff --git a/sediff/utilgui.c b/sediff/utilgui.c new file mode 100644 index 0000000..04e1e05 --- /dev/null +++ b/sediff/utilgui.c @@ -0,0 +1,168 @@ +/** + * @file + * Miscellaneous helper functions for GTK+ applications. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "utilgui.h" +#include <string.h> +#include <apol/util.h> + +void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg) +{ + GtkWidget *dialog; + dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +void util_cursor_wait(GtkWidget * widget) +{ + GdkCursor *cursor; + if (widget->window != NULL) { + cursor = gdk_cursor_new(GDK_WATCH); + gdk_window_set_cursor(widget->window, cursor); + gdk_cursor_unref(cursor); + } +} + +/** + * WARNING: this is sort of a hack + * + * If we reset the pointer at the end of a callback, it gets reset too + * soon (i.e. before all of the pending events have been processed. To + * avoid this, this function is put in an idle handler by + * util_cursor_clear(). + */ +static gboolean pointer_reset(gpointer data) +{ + gdk_window_set_cursor(GTK_WIDGET(data)->window, NULL); + return FALSE; +} + +void util_cursor_clear(GtkWidget * widget) +{ + g_idle_add(&pointer_reset, widget); +} + +void util_text_buffer_clear(GtkTextBuffer * txt) +{ + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(txt, &start); + gtk_text_buffer_get_end_iter(txt, &end); + gtk_text_buffer_remove_all_tags(txt, &start, &end); + gtk_text_buffer_delete(txt, &start, &end); +} + +apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple) +{ + GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + apol_vector_t *paths = NULL; + if (init_path != NULL) { + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path); + } + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + GSList *f; + paths = apol_vector_create(g_free); + for (f = files; f != NULL; f = f->next) { + apol_vector_append(paths, f->data); + } + g_slist_free(files); + } + gtk_widget_destroy(dialog); + return paths; +} + +char *util_save_file(GtkWindow * parent, const char *title, const char *init_path) +{ + GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + char *path = NULL; +#ifdef GTK_2_8 + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); +#endif + if (init_path != NULL) { + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path); + } else { + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled"); + } + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + gtk_widget_destroy(dialog); + return path; +} + +char *util_policy_path_to_string(const apol_policy_path_t * path) +{ + char *s; + const char *primary_path = apol_policy_path_get_primary(path); + if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + return strdup(primary_path); + } else { + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t num_modules = apol_vector_get_size(modules); + if (asprintf(&s, "%s + %zd module%s", primary_path, num_modules, num_modules == 1 ? "" : "s") < 0) { + return NULL; + } + return s; + } +} + +char *util_policy_path_to_full_string(const apol_policy_path_t * path) +{ + const char *primary_path = apol_policy_path_get_primary(path); + if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + return strdup(primary_path); + } else { + char *s = NULL, *t = NULL; + size_t len = 0; + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t num_modules = apol_vector_get_size(modules); + if (apol_str_appendf(&s, &len, "%s + %zd module%s", primary_path, num_modules, num_modules == 1 ? "" : "s") < 0) { + return NULL; + } + if (num_modules > 0) { + if ((t = apol_str_join(modules, "\n\t")) == NULL || apol_str_appendf(&s, &len, "\n\t%s", t) < 0) { + free(t); + free(s); + return NULL; + } + } + return s; + } +} + +const gchar *util_combo_box_get_active_text(GtkComboBox * w) +{ +#ifdef GTK_2_8 + return gtk_combo_box_get_active_text(w); +#else + return gtk_entry_get_text(GTK_ENTRY(GTK_BIN(w)->child)); +#endif +} diff --git a/sediff/utilgui.h b/sediff/utilgui.h new file mode 100644 index 0000000..ad91514 --- /dev/null +++ b/sediff/utilgui.h @@ -0,0 +1,125 @@ +/** + * @file + * Miscellaneous helper functions for GTK+ applications. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UTILGUI_H +#define UTILGUI_H + +#include <apol/policy-path.h> +#include <gtk/gtk.h> + +/** + * Pop-up a dialog with a line of text and wait for the user to + * dismiss the dialog. + * + * @param parent Parent window; this message dialog will be centered + * upon the parent. + * @param msg_type Type of message being displayed. + * @param msg Text of message to display. + */ +void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg); +/** + * Set the cursor over a widget to the watch cursor. + * + * @param widget Widget whose cursor to set. + */ +void util_cursor_wait(GtkWidget * widget); + +/** + * Clear the cursor over a widget, setting it to the default arrow. + * + * @param widget Widget whose cursor to set. + */ +void util_cursor_clear(GtkWidget * widget); + +/** + * Given some arbitrary GtkTextBuffer, remove all of its text and + * attributes. This will not delete the buffer's tag table. + * + * @param txt Text buffer to clear. + */ +void util_text_buffer_clear(GtkTextBuffer * txt); + +/** + * Allow the user select an existing file. Run the dialog and return + * the selected filename. + * + * @param parent Parent window; this dialog will be centered upon the + * parent. + * @param title Name of the dialog. + * @param init_path If not NULL, the default filename. + * @param multiple If true, allow the user to select multiple files. + * Otherwise only one file at a time may be chosen. + * + * @return Name of the file selected, or NULL if no file was selected. + * The caller must free the returned value with g_free(). + */ +apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple); + +/** + * Allow the user select an existing file or enter a new file for + * writing. Run the dialog and return the selected filename. + * + * @param parent Parent window; this dialog will be centered upon the + * parent. + * @param title Name of the dialog. + * @param init_path If not NULL, the default filename. + * + * @return Name of the file selected, or NULL if no file was selected. + * The caller must free the returned value with g_free(). + */ +char *util_save_file(GtkWindow * parent, const char *title, const char *init_path); + +/** + * Given a policy path, return a newly allocated string that briefly + * describes the path. This string is suitable for showing to the + * user. + * + * @param path Policy path to describe. + * + * @return String describing the path, or NULL upon error. The caller + * must free the string afterwards. + */ +char *util_policy_path_to_string(const apol_policy_path_t * path); + +/** + * Given a policy path, return a newly allocated string that fully + * describes the path. This string is suitable for showing to the + * user. + * + * @param path Policy path to describe. + * + * @return String describing the path, or NULL upon error. The caller + * must free the string afterwards. + */ +char *util_policy_path_to_full_string(const apol_policy_path_t * path); + +/** + * Get the active text from a GtkComboBox. + * + * Whereas GTK 2.6 has gtk_combo_box_get_active_text(), GTK 2.4 + * (another supported platform) does not. + */ +const gchar *util_combo_box_get_active_text(GtkComboBox * w); + +#endif |