diff options
Diffstat (limited to 'seaudit/toplevel.c')
-rw-r--r-- | seaudit/toplevel.c | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/seaudit/toplevel.c b/seaudit/toplevel.c new file mode 100644 index 0000000..d901a99 --- /dev/null +++ b/seaudit/toplevel.c @@ -0,0 +1,1179 @@ +/** + * @file + * Implementation for the main toplevel window. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@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 "message_view.h" +#include "open_policy_window.h" +#include "policy_view.h" +#include "preferences_view.h" +#include "report_window.h" +#include "toplevel.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <apol/util.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <seaudit/parse.h> + +struct toplevel +{ + seaudit_t *s; + policy_view_t *pv; + progress_t *progress; + /** vector of message_view_t that are in the toplevel's notebook */ + apol_vector_t *views; + GladeXML *xml; + /** filename for glade file */ + char *xml_filename; + /** toplevel window widget */ + GtkWindow *w; + GtkNotebook *notebook; + /** non-zero if the log file should be polled for changes */ + int do_monitor_log; + /** event id for the monitor callback */ + guint monitor_id; + /** serial number for models created, such that new models + * will be named Untitled <number> */ + int next_model_number; + /** filename for most recently opened view */ + char *view_filename; +}; + +/** + * Given a view, return its index within the toplevel notebook pages. + * + * @param top Toplevel containing the notebook. + * @param view View to look up. + * + * @return Index of the view (zero-indexed), or -1 if not found. + */ +static gint toplevel_notebook_find_view(toplevel_t * top, message_view_t * view) +{ + gint num_pages = gtk_notebook_get_n_pages(top->notebook); + while (num_pages >= 1) { + GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, num_pages - 1); + GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child); + message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object"); + if (v == view) { + return num_pages - 1; + } + num_pages--; + } + return -1; +} + +/** + * Return the view on the page that is currently raised, or NULL if + * there are no views. + */ +static message_view_t *toplevel_get_current_view(toplevel_t * top) +{ + gint current = gtk_notebook_get_current_page(top->notebook); + if (current >= 0) { + GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, current); + GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child); + return g_object_get_data(G_OBJECT(tab), "view-object"); + } + return NULL; +} + +static void toplevel_on_notebook_switch_page(GtkNotebook * notebook __attribute__ ((unused)), GtkNotebookPage * page + __attribute__ ((unused)), guint pagenum __attribute__ ((unused)), toplevel_t * top) +{ + toplevel_update_selection_menu_item(top); + toplevel_update_status_bar(top); +} + +/** + * Callback invoked when a tab close button is clicked. + */ +static void toplevel_on_tab_close(GtkButton * button, toplevel_t * top) +{ + /* disallow the close if this is the last tab */ + if (top->views == NULL || apol_vector_get_size(top->views) <= 1) { + return; + } else { + message_view_t *view = g_object_get_data(G_OBJECT(button), "view-object"); + gint idx = toplevel_notebook_find_view(top, view); + size_t i; + assert(idx >= 0); + gtk_notebook_remove_page(top->notebook, idx); + apol_vector_get_index(top->views, view, NULL, NULL, &i); + message_view_destroy(&view); + apol_vector_remove(top->views, i); + } +} + +/** + * Create a new view associated with the given model, then create a + * tab to place that view. The newly created tab will then be raised. + * + * @param top Toplevel containing notebook to which add the view and tab. + * @param model Model from which to create a view. + * @param filename Initial filename for the view. + */ +static void toplevel_add_new_view(toplevel_t * top, seaudit_model_t * model, const char *filename) +{ + message_view_t *view; + GtkWidget *tab, *button, *label, *image; + gint idx; + if ((view = message_view_create(top, model, filename)) == NULL) { + return; + } + if (apol_vector_append(top->views, view) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + message_view_destroy(&view); + return; + } + tab = gtk_hbox_new(FALSE, 5); + g_object_set_data(G_OBJECT(tab), "view-object", view); + button = gtk_button_new(); + g_object_set_data(G_OBJECT(button), "view-object", view); + image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + gtk_container_add(GTK_CONTAINER(button), image); + gtk_widget_set_size_request(image, 8, 8); + g_signal_connect(G_OBJECT(button), "pressed", G_CALLBACK(toplevel_on_tab_close), top); + label = gtk_label_new(seaudit_model_get_name(model)); + g_object_set_data(G_OBJECT(tab), "label", label); + gtk_box_pack_start(GTK_BOX(tab), label, TRUE, TRUE, 5); + gtk_box_pack_end(GTK_BOX(tab), button, FALSE, FALSE, 5); + gtk_widget_show(label); + gtk_widget_show(button); + gtk_widget_show(image); + idx = gtk_notebook_append_page(top->notebook, message_view_get_view(view), tab); + gtk_notebook_set_current_page(top->notebook, idx); +} + +/** + * Create a new model for the currently loaded log file (which could + * be NULL), then create a view that watches that model. + */ +static void toplevel_add_new_model(toplevel_t * top) +{ + seaudit_log_t *log = seaudit_get_log(top->s); + char *model_name = NULL; + seaudit_model_t *model = NULL; + if (asprintf(&model_name, "Untitled %d", top->next_model_number) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + return; + } + model = seaudit_model_create(model_name, log); + free(model_name); + if (model == NULL) { + toplevel_ERR(top, "%s", strerror(errno)); + return; + } else { + top->next_model_number++; + toplevel_add_new_view(top, model, NULL); + } +} + +/** + * Callback whenever an item from the recent logs submenu is activated. + */ +static void toplevel_on_open_recent_log_activate(GtkWidget * widget, gpointer user_data) +{ + GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget)); + const char *path = gtk_label_get_text(GTK_LABEL(label)); + toplevel_t *top = (toplevel_t *) user_data; + toplevel_open_log(top, path); +} + +/** + * Update the entries within recent logs submenu to match those in the + * preferences object. + */ +static void toplevel_set_recent_logs_submenu(toplevel_t * top) +{ + GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentLog")); + apol_vector_t *paths = preferences_get_recent_logs(toplevel_get_prefs(top)); + GtkWidget *submenu, *submenu_item; + size_t i; + + gtk_menu_item_remove_submenu(recent); + submenu = gtk_menu_new(); + for (i = 0; i < apol_vector_get_size(paths); i++) { + char *path = (char *)apol_vector_get_element(paths, i); + submenu_item = gtk_menu_item_new_with_label(path); + gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item); + gtk_widget_show(submenu_item); + g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_log_activate), top); + } + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu); +} + +/** + * Callback whenever an item from the recent policies submenu is + * activated. + */ +static void toplevel_on_open_recent_policy_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + apol_policy_path_t *path = g_object_get_data(G_OBJECT(menuitem), "path"); + toplevel_t *top = (toplevel_t *) user_data; + apol_policy_path_t *dup_path = apol_policy_path_create_from_policy_path(path); + if (dup_path == NULL) { + toplevel_ERR(top, "%s", strerror(errno)); + return; + } + toplevel_open_policy(top, dup_path); +} + +/** + * Update the entries within recent policies submenu to match those in + * the preferences object. + */ +static void toplevel_set_recent_policies_submenu(toplevel_t * top) +{ + GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentPolicy")); + apol_vector_t *paths = preferences_get_recent_policies(toplevel_get_prefs(top)); + GtkWidget *submenu, *submenu_item; + size_t i; + + GtkTooltipsData *tips_data = gtk_tooltips_data_get(GTK_WIDGET(recent)); + assert(tips_data != NULL); + GtkTooltips *tooltips = tips_data->tooltips; + + gtk_menu_item_remove_submenu(recent); + submenu = gtk_menu_new(); + for (i = 0; i < apol_vector_get_size(paths); i++) { + apol_policy_path_t *path = apol_vector_get_element(paths, i); + char *menu_label = NULL; + const char *primary_path = apol_policy_path_get_primary(path); + GString *tip = g_string_new(NULL); + if ((menu_label = util_policy_path_to_string(path)) == NULL) { + toplevel_ERR(top, "%s", strerror(errno)); + g_string_free(tip, TRUE); + break; + } + submenu_item = gtk_menu_item_new_with_label(menu_label); + free(menu_label); + if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + g_string_append_printf(tip, "monolithic policy: %s", primary_path); + } else { + char *s = NULL; + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t num_modules = apol_vector_get_size(modules); + g_string_append_printf(tip, "base policy: %s", primary_path); + if (num_modules > 0) { + if ((s = apol_str_join(modules, "\n ")) == NULL) { + toplevel_ERR(top, "%s", strerror(errno)); + break; + } + g_string_append_printf(tip, "\n %s", s); + free(s); + } + } + gtk_tooltips_set_tip(tooltips, GTK_WIDGET(submenu_item), tip->str, ""); + g_string_free(tip, TRUE); + g_object_set_data(G_OBJECT(submenu_item), "path", path); + gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item); + gtk_widget_show(submenu_item); + g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_policy_activate), top); + } + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu); +} + +/** + * Enable/disable all items (menus and buttons) that depend upon if a + * log is loaded. + * + * @param top Toplevel object containing menu items. + * @param TRUE to enable items, FALSE to disable. + */ +static void toplevel_enable_log_items(toplevel_t * top, gboolean sens) +{ + static const char *items[] = { + "NewView", "OpenView", "SaveView", "SaveViewAs", "ModifyView", + "ExportAll", "ExportSelected", "ViewMessage", + "CreateReport", "MonitorLog", "ClearView", + "ModifyViewButton", "MonitorLogButton", "ClearViewButton", + 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); + } +} + +/** + * Enable/disable all items (menus and buttons) that depend upon if a + * policy is loaded. + * + * @param top Toplevel object containing widgets. + * @param TRUE to enable items, FALSE to disable. + */ +static void toplevel_enable_policy_items(toplevel_t * top, gboolean sens) +{ + static const char *items[] = { + "FindTERules", "FindTERulesButton", + 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 log and policy files + * opened. + * + * @param top Toplevel to modify. + */ +static void toplevel_update_title_bar(toplevel_t * top) +{ + char *log_path = seaudit_get_log_path(top->s); + apol_policy_path_t *policy_path = seaudit_get_policy_path(top->s); + char *policy_type_str = "Policy"; + const char *primary_path; + char *s; + + if (log_path == NULL) { + log_path = "No Log"; + } + if (policy_path == NULL) { + primary_path = "No Policy"; + } else { + if (apol_policy_path_get_type(policy_path) == APOL_POLICY_PATH_TYPE_MODULAR) { + policy_type_str = "Base"; + } + primary_path = apol_policy_path_get_primary(policy_path); + } + if (asprintf(&s, "seaudit - [Log file: %s] [%s file: %s]", log_path, policy_type_str, primary_path) < 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[] = { "seaudit-small.png", "seaudit.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 message_view_free(void *elem) +{ + message_view_t *view = elem; + message_view_destroy(&view); +} + +toplevel_t *toplevel_create(seaudit_t * s) +{ + toplevel_t *top; + GtkWidget *vbox; + int error = 0; + + if ((top = calloc(1, sizeof(*top))) == NULL || (top->views = apol_vector_create(message_view_free)) == NULL) { + error = errno; + goto cleanup; + } + top->s = s; + top->next_model_number = 1; + + if ((top->xml_filename = apol_file_find_path("seaudit.glade")) == NULL || + (top->xml = glade_xml_new(top->xml_filename, "TopLevel", NULL)) == NULL) { + fprintf(stderr, "Could not open seaudit.glade.\n"); + error = EIO; + goto cleanup; + } + top->w = GTK_WINDOW(glade_xml_get_widget(top->xml, "TopLevel")); + g_object_set_data(G_OBJECT(top->w), "toplevel", top); + init_icons(top); + top->notebook = GTK_NOTEBOOK(gtk_notebook_new()); + g_signal_connect_after(G_OBJECT(top->notebook), "switch-page", G_CALLBACK(toplevel_on_notebook_switch_page), top); + vbox = glade_xml_get_widget(top->xml, "NotebookVBox"); + gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(top->notebook)); + gtk_widget_show(GTK_WIDGET(top->notebook)); + gtk_widget_show(GTK_WIDGET(top->w)); + toplevel_set_recent_logs_submenu(top); + toplevel_set_recent_policies_submenu(top); + + glade_xml_signal_autoconnect(top->xml); + + /* create initial blank tab for the notebook */ + toplevel_add_new_model(top); + + /* initialize sub-windows, now that glade XML file has been + * read */ + if ((top->pv = policy_view_create(top)) == NULL || (top->progress = progress_create(top)) == NULL) { + error = errno; + goto cleanup; + } + cleanup: + if (error != 0) { + toplevel_destroy(&top); + errno = error; + return NULL; + } + return top; +} + +void toplevel_destroy(toplevel_t ** top) +{ + if (top != NULL && *top != NULL) { + if ((*top)->monitor_id > 0) { + g_source_remove((*top)->monitor_id); + } + policy_view_destroy(&(*top)->pv); + apol_vector_destroy(&(*top)->views); + free((*top)->xml_filename); + g_free((*top)->view_filename); + progress_destroy(&(*top)->progress); + if ((*top)->w != NULL) { + gtk_widget_destroy(GTK_WIDGET((*top)->w)); + } + free(*top); + *top = NULL; + } +} + +struct log_run_datum +{ + toplevel_t *top; + FILE *file; + const char *filename; + seaudit_log_t *log; + int result; +}; + +/** + * Update the seaudit log, then refresh all views as necessary. Note + * that this only works in a single-threaded environment; otherwise + * there are two possible race conditions: + * - monitor is disabled while this function is being executed + * - a new log file is loaded while the function is being executed + * + * But what happens if this function is scheduled and then a new log + * is opened? In toplevel_open_log(), do_monitor_log is temporarily + * disabled because that function is threaded. It is then re-enabled + * afterwards. + * + * To make this function fully thread-safe requires making this entire + * function synchronized, and then employ locking every time + * do_monitor_log and monitor_id are set. + */ +static gboolean toplevel_monitor_log_timer(gpointer data) +{ + toplevel_t *top = (toplevel_t *) data; + if (top->do_monitor_log) { + int retval; + gint i = gtk_notebook_get_n_pages(top->notebook) - 1; + uint delay; + retval = seaudit_parse_log(top->s); + if (retval < 0) { + GtkCheckMenuItem *w; + toplevel_ERR(top, "Error while monitoring log: %s", strerror(errno)); + w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog")); + top->monitor_id = 0; + gtk_check_menu_item_set_active(w, 0); + return FALSE; + } + while (i >= 0) { + GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i); + GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child); + message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object"); + message_view_update_rows(v); + i--; + } + + /* reschedule another timer callback */ + delay = preferences_get_real_time_interval(toplevel_get_prefs(top)); + top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top); + } else { + top->monitor_id = 0; + } + return FALSE; +} + +/** + * Enable or disable the log monitoring feature. While enabled, the + * log file will be periodically polled; new lines will be parsed and + * inserted into the seaudit log object. All models and their views + * will then be notified of the changes. + * + * @param top Toplevel object whose widgets to update. + */ +static void toplevel_monitor_log(toplevel_t * top) +{ + GtkLabel *label = GTK_LABEL(glade_xml_get_widget(top->xml, "MonitorLogLabel")); + assert(label != NULL); + if (top->do_monitor_log) { + gtk_label_set_markup(label, "Monitor Status: <span foreground=\"green\">ON</span>"); + if (top->monitor_id == 0) { + uint delay = preferences_get_real_time_interval(toplevel_get_prefs(top)); + top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top); + } + } else { + if (top->monitor_id > 0) { + g_source_remove(top->monitor_id); + top->monitor_id = 0; + } + gtk_label_set_markup(label, "Monitor Status: <span foreground=\"red\">OFF</span>"); + } +} + +/** + * Thread that loads and parses a log file. It will write to + * progress_seaudit_handle_func() its status during the load. Note + * that the file handle is not closed upon completion; it is left open + * so that subsequent calls to seaudit_log_parse(), such as + * forreal-time monitoring. + * + * @param data Pointer to a struct log_run_datum, for control + * information. + */ +static gpointer toplevel_open_log_runner(gpointer data) +{ + struct log_run_datum *run = (struct log_run_datum *)data; + progress_update(run->top->progress, "Parsing %s", run->filename); + if ((run->file = fopen(run->filename, "r")) == NULL) { + progress_update(run->top->progress, "Could not open %s for reading.", run->filename); + run->result = -1; + goto cleanup; + } + if ((run->log = seaudit_log_create(progress_seaudit_handle_func, run->top->progress)) == NULL) { + progress_update(run->top->progress, "%s", strerror(errno)); + run->result = -1; + goto cleanup; + } + run->result = seaudit_log_parse(run->log, run->file); + cleanup: + if (run->result < 0) { + if (run->file != NULL) { + fclose(run->file); + } + run->file = NULL; + seaudit_log_destroy(&run->log); + progress_abort(run->top->progress, NULL); + } else if (run->result > 0) { + progress_warn(run->top->progress, NULL); + } else { + progress_done(run->top->progress); + } + return NULL; +} + +/** + * Destroy all views and their notebook tabs. + */ +static void toplevel_destroy_views(toplevel_t * top) +{ + gint num_pages = gtk_notebook_get_n_pages(top->notebook); + while (num_pages >= 1) { + message_view_t *view = apol_vector_get_element(top->views, num_pages - 1); + gtk_notebook_remove_page(top->notebook, num_pages - 1); + message_view_destroy(&view); + apol_vector_remove(top->views, num_pages - 1); + num_pages--; + } +} + +void toplevel_open_log(toplevel_t * top, const char *filename) +{ + struct log_run_datum run = { top, NULL, filename, NULL, 0 }; + int was_monitor_running; + GtkCheckMenuItem *w; + + /* disable monitoring during the threaded part of this code */ + was_monitor_running = top->do_monitor_log; + top->do_monitor_log = 0; + toplevel_monitor_log(top); + + util_cursor_wait(GTK_WIDGET(top->w)); + progress_show(top->progress, "Opening Log"); + g_thread_create(toplevel_open_log_runner, &run, FALSE, NULL); + progress_wait(top->progress); + progress_hide(top->progress); + util_cursor_clear(GTK_WIDGET(top->w)); + + if (run.result < 0) { + top->do_monitor_log = was_monitor_running; + toplevel_monitor_log(top); + return; + } + + toplevel_destroy_views(top); + top->next_model_number = 1; + seaudit_set_log(top->s, run.log, run.file, filename); + toplevel_set_recent_logs_submenu(top); + toplevel_enable_log_items(top, TRUE); + toplevel_add_new_model(top); + toplevel_update_title_bar(top); + toplevel_update_status_bar(top); + toplevel_update_selection_menu_item(top); + top->do_monitor_log = preferences_get_real_time_at_startup(toplevel_get_prefs(top)); + + w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog")); + + gtk_check_menu_item_set_active(w, top->do_monitor_log); + /* call this again because the check item could have already + * been active, thus its handler would not run */ + toplevel_monitor_log(top); +} + +struct policy_run_datum +{ + toplevel_t *top; + apol_policy_path_t *path; + apol_policy_t *policy; + 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; + progress_update(run->top->progress, "Opening policy."); + run->policy = + apol_policy_create_from_policy_path(run->path, QPOL_POLICY_OPTION_NO_NEVERALLOWS, progress_apol_handle_func, + run->top->progress); + if (run->policy == 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_policy(toplevel_t * top, apol_policy_path_t * path) +{ + struct policy_run_datum run = { top, path, NULL, 0 }; + + util_cursor_wait(GTK_WIDGET(top->w)); + progress_show(top->progress, apol_policy_path_get_primary(path)); + 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(&path); + return run.result; + } + seaudit_set_policy(top->s, run.policy, path); + toplevel_set_recent_policies_submenu(top); + toplevel_enable_policy_items(top, TRUE); + toplevel_update_title_bar(top); + toplevel_update_status_bar(top); + policy_view_update(top->pv, path); + return 0; +} + +void toplevel_update_status_bar(toplevel_t * top) +{ + apol_policy_t *policy = seaudit_get_policy(top->s); + GtkLabel *policy_version = (GtkLabel *) glade_xml_get_widget(top->xml, "PolicyVersionLabel"); + GtkLabel *log_num = (GtkLabel *) glade_xml_get_widget(top->xml, "LogNumLabel"); + GtkLabel *log_dates = (GtkLabel *) glade_xml_get_widget(top->xml, "LogDateLabel"); + seaudit_log_t *log = toplevel_get_log(top); + + if (policy == NULL) { + gtk_label_set_text(policy_version, "Policy: No policy"); + } else { + char *policy_str = apol_policy_get_version_type_mls_str(policy); + if (policy_str == NULL) { + toplevel_ERR(top, "%s", strerror(errno)); + } else { + char *s; + if (asprintf(&s, "Policy: %s", policy_str) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + } else { + gtk_label_set_text(policy_version, s); + free(s); + } + free(policy_str); + } + } + + if (log == NULL) { + gtk_label_set_text(log_num, "Log Messages: No log"); + gtk_label_set_text(log_dates, "Dates: No log"); + } else { + message_view_t *view = toplevel_get_current_view(top); + size_t num_messages = seaudit_get_num_log_messages(top->s); + size_t num_view_messages; + const struct tm *first = seaudit_get_log_first(top->s); + const struct tm *last = seaudit_get_log_last(top->s); + assert(view != NULL); + num_view_messages = message_view_get_num_log_messages(view); + char *s, t1[256], t2[256]; + if (asprintf(&s, "Log Messages: %zd/%zd", num_view_messages, num_messages) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + } else { + gtk_label_set_text(log_num, s); + free(s); + } + if (first == NULL || last == NULL) { + gtk_label_set_text(log_dates, "Dates: No messages"); + } else { + strftime(t1, 256, "%b %d %H:%M:%S", first); + strftime(t2, 256, "%b %d %H:%M:%S", last); + if (asprintf(&s, "Dates: %s - %s", t1, t2) < 0) { + toplevel_ERR(top, "%s", strerror(errno)); + } else { + gtk_label_set_text(log_dates, s); + free(s); + } + } + } +} + +void toplevel_update_tabs(toplevel_t * top) +{ + gint i = gtk_notebook_get_n_pages(top->notebook) - 1; + while (i >= 0) { + GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i); + GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child); + GtkWidget *label = g_object_get_data(G_OBJECT(tab), "label"); + message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object"); + seaudit_model_t *model = message_view_get_model(v); + const char *name = seaudit_model_get_name(model); + gtk_label_set_text(GTK_LABEL(label), name); + i--; + } +} + +void toplevel_update_selection_menu_item(toplevel_t * top) +{ + static const char *items[] = { + "ExportSelected", "ViewMessage", + NULL + }; + message_view_t *view = toplevel_get_current_view(top); + gboolean sens = FALSE; + size_t i; + const char *s; + if (view != NULL) { + sens = message_view_is_message_selected(view); + } + for (i = 0, s = items[0]; s != NULL; s = items[++i]) { + GtkWidget *w = glade_xml_get_widget(top->xml, s); + assert(s != NULL); + gtk_widget_set_sensitive(w, sens); + } +} + +preferences_t *toplevel_get_prefs(toplevel_t * top) +{ + return seaudit_get_prefs(top->s); +} + +seaudit_log_t *toplevel_get_log(toplevel_t * top) +{ + return seaudit_get_log(top->s); +} + +apol_vector_t *toplevel_get_log_users(toplevel_t * top) +{ + return seaudit_get_log_users(top->s); +} + +apol_vector_t *toplevel_get_log_roles(toplevel_t * top) +{ + return seaudit_get_log_roles(top->s); +} + +apol_vector_t *toplevel_get_log_types(toplevel_t * top) +{ + return seaudit_get_log_types(top->s); +} + +apol_vector_t *toplevel_get_log_mls_lvl(toplevel_t * top) +{ + return seaudit_get_log_mls_lvl(top->s); +} + +apol_vector_t *toplevel_get_log_mls_clr(toplevel_t * top) +{ + return seaudit_get_log_mls_clr(top->s); +} + +apol_vector_t *toplevel_get_log_classes(toplevel_t * top) +{ + return seaudit_get_log_classes(top->s); +} + +apol_policy_t *toplevel_get_policy(toplevel_t * top) +{ + return seaudit_get_policy(top->s); +} + +char *toplevel_get_glade_xml(toplevel_t * top) +{ + return top->xml_filename; +} + +progress_t *toplevel_get_progress(toplevel_t * top) +{ + return top->progress; +} + +GtkWindow *toplevel_get_window(toplevel_t * top) +{ + return top->w; +} + +void toplevel_find_terules(toplevel_t * top, seaudit_message_t * message) +{ + policy_view_find_terules(top->pv, message); +} + +/** + * 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); +} + +/************* below are callbacks for the toplevel menu items *************/ + +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(); +} + +void toplevel_on_open_log_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + apol_vector_t *paths = util_open_file(top->w, "Open Log", seaudit_get_log_path(top->s), 0); + if (paths != NULL) { + toplevel_open_log(top, apol_vector_get_element(paths, 0)); + apol_vector_destroy(&paths); + } +} + +void toplevel_on_open_policy_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + open_policy_window_run(top, seaudit_get_policy_path(top->s), NULL); +} + +void toplevel_on_preferences_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (preferences_view_run(top, seaudit_get_log_path(top->s), seaudit_get_policy_path(top->s))) { + size_t i; + for (i = 0; i < apol_vector_get_size(top->views); i++) { + message_view_t *v = apol_vector_get_element(top->views, i); + message_view_update_visible_columns(v); + } + } +} + +void toplevel_on_quit_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + top->w = NULL; + gtk_main_quit(); +} + +void toplevel_on_new_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + toplevel_add_new_model(top); +} + +void toplevel_on_open_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + apol_vector_t *paths = util_open_file(top->w, "Open View", top->view_filename, 0); + seaudit_model_t *model = NULL; + if (paths == NULL) { + return; + } + free(top->view_filename); + top->view_filename = strdup(apol_vector_get_element(paths, 0)); + apol_vector_destroy(&paths); + if (top->view_filename == NULL || + (model = seaudit_model_create_from_file(top->view_filename)) == NULL || + seaudit_model_append_log(model, seaudit_get_log(top->s)) < 0) { + toplevel_ERR(top, "Error opening view: %s", strerror(errno)); + seaudit_model_destroy(&model); + } else { + toplevel_add_new_view(top, model, top->view_filename); + } +} + +void toplevel_on_save_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_save(view); +} + +void toplevel_on_save_viewas_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_saveas(view); +} + +void toplevel_on_modify_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_modify(view); +} + +void toplevel_on_export_all_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_export_all_messages(view); +} + +void toplevel_on_export_selected_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_export_selected_messages(view); +} + +void toplevel_on_view_entire_message_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_entire_message(view); +} + +void toplevel_on_find_terules_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + toplevel_find_terules(top, NULL); +} + +void toplevel_on_create_report_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + report_window_run(top, view); +} + +void toplevel_on_monitor_log_activate(gpointer user_data, GtkMenuItem * widget) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { + top->do_monitor_log = 1; + } else { + top->do_monitor_log = 0; + } + toplevel_monitor_log(top); +} + +void toplevel_on_clear_view_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_clear(view); +} + +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("seaudit 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)); + dir = apol_file_find_path("seaudit_help.txt"); + if (!dir) { + 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_seaudit_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", "Audit Log Analysis Tool for Security Enhanced Linux", + "copyright", COPYRIGHT_INFO, + "name", "seaudit", "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", + "seaudit", VERSION, + "Audit Log Analysis 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_find_terules_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_find_terules(top, NULL); +} + +void toplevel_on_modify_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_modify(view); +} + +void toplevel_on_monitor_log_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + GtkCheckMenuItem *w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog")); + gboolean old_state; + assert(w != NULL); + old_state = gtk_check_menu_item_get_active(w); + gtk_check_menu_item_set_active(w, !old_state); +} + +void toplevel_on_clear_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused))) +{ + toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel"); + message_view_t *view = toplevel_get_current_view(top); + assert(view != NULL); + message_view_clear(view); +} |