summaryrefslogtreecommitdiffstats
path: root/sediff
diff options
context:
space:
mode:
Diffstat (limited to 'sediff')
-rw-r--r--sediff/Makefile.am54
-rw-r--r--sediff/find_dialog.c184
-rw-r--r--sediff/find_dialog.h63
-rw-r--r--sediff/open_policies_dialog.c527
-rw-r--r--sediff/open_policies_dialog.h45
-rw-r--r--sediff/policy_view.c391
-rw-r--r--sediff/policy_view.h84
-rw-r--r--sediff/progress.c202
-rw-r--r--sediff/progress.h139
-rw-r--r--sediff/remap_types_dialog.c564
-rw-r--r--sediff/remap_types_dialog.h56
-rw-r--r--sediff/result_item.c1361
-rw-r--r--sediff/result_item.h251
-rw-r--r--sediff/result_item_render.c421
-rw-r--r--sediff/result_item_render.h79
-rw-r--r--sediff/results.c708
-rw-r--r--sediff/results.h121
-rw-r--r--sediff/sediff.c653
-rw-r--r--sediff/sediff_help.txt259
-rw-r--r--sediff/sediffx-small.pngbin0 -> 281 bytes
-rw-r--r--sediff/sediffx-small.xcfbin0 -> 1639 bytes
-rw-r--r--sediff/sediffx.c317
-rw-r--r--sediff/sediffx.glade3619
-rw-r--r--sediff/sediffx.h108
-rw-r--r--sediff/sediffx.pngbin0 -> 607 bytes
-rw-r--r--sediff/sediffx.xcfbin0 -> 5882 bytes
-rw-r--r--sediff/select_diff_dialog.c134
-rw-r--r--sediff/select_diff_dialog.h42
-rw-r--r--sediff/toplevel.c728
-rw-r--r--sediff/toplevel.h202
-rw-r--r--sediff/utilgui.c168
-rw-r--r--sediff/utilgui.h125
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
new file mode 100644
index 0000000..21354b8
--- /dev/null
+++ b/sediff/sediffx-small.png
Binary files differ
diff --git a/sediff/sediffx-small.xcf b/sediff/sediffx-small.xcf
new file mode 100644
index 0000000..d936489
--- /dev/null
+++ b/sediff/sediffx-small.xcf
Binary files differ
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">&lt;b&gt;Original Policy Type:&lt;/b&gt;</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">&lt;b&gt;Original Policy Filename:&lt;/b&gt;</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">&lt;b&gt;Modified Policy Type:&lt;/b&gt;</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">&lt;b&gt;Modified Policy Filename:&lt;/b&gt;</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">&lt;b&gt;Select Components to Diff&lt;/b&gt;</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
new file mode 100644
index 0000000..db4f83b
--- /dev/null
+++ b/sediff/sediffx.png
Binary files differ
diff --git a/sediff/sediffx.xcf b/sediff/sediffx.xcf
new file mode 100644
index 0000000..ded254f
--- /dev/null
+++ b/sediff/sediffx.xcf
Binary files differ
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