|
Server : Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8e-fips-rhel5 DAV/2 PHP/5.2.17 System : Linux localhost 2.6.18-419.el5 #1 SMP Fri Feb 24 22:47:42 UTC 2017 x86_64 User : nobody ( 99) PHP Version : 5.2.17 Disable Function : NONE Directory : /proc/21573/root/usr/lib/python2.4/site-packages/setroubleshoot/ |
Upload File : |
# Authors: John Dennis <jdennis@redhat.com>
#
# Copyright (C) 2006,2007,2008 Red Hat, Inc.
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
__all__ = ['BrowserApplet',
]
# This import must come before importing gtk to silence warnings
from setroubleshoot.gui_utils import *
import os
import sys
import pygtk
pygtk.require("2.0")
import gtk
import pango
import re
import time
import traceback
from types import *
import gobject
import gnome.ui
import gtkhtml2
from setroubleshoot.log import *
from setroubleshoot.analyze import *
from setroubleshoot.config import get_config
from setroubleshoot.email_dialog import *
from setroubleshoot.errcode import *
from setroubleshoot.signature import *
from setroubleshoot.util import *
from setroubleshoot.html_util import *
from setroubleshoot.rpc import *
from setroubleshoot.rpc_interfaces import *
#------------------------------------------------------------------------------
NUM_COLUMNS = 7
(PYOBJECT_COLUMN, FILTER_TYPE_COLUMN, DATE_COLUMN, HOST_COLUMN, COUNT_COLUMN, CATEGORY_COLUMN,
SUMMARY_COLUMN) = range(NUM_COLUMNS)
tv_column_info = {
FILTER_TYPE_COLUMN: {'name' : 'Filter', 'title' : _('Quiet'),},
DATE_COLUMN: {'name' : 'Date', 'title' : _('Date'),},
HOST_COLUMN: {'name' : 'Host', 'title' : _('Host'),},
COUNT_COLUMN: {'name' : 'Count', 'title' : _('Count'),},
CATEGORY_COLUMN: {'name' : 'Category', 'title' : _('Category'),},
SUMMARY_COLUMN: {'name' : 'Summary', 'title' : _('Summary'),},
}
#------------------------------------------------------------------------------
def get_siginfo_from_model_path(model, path):
if type(path) is StringType:
iter = model.get_iter_from_string(path)
else:
iter = model.get_iter(path)
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
return siginfo
def get_model_path_from_local_id(model, local_id):
iter = model.get_iter_first()
while iter:
row_siginfo = model.get_value(iter, PYOBJECT_COLUMN)
if row_siginfo.local_id == local_id:
return model.get_path(iter)
iter = model.iter_next(iter)
return None
def get_local_id_from_model_path(model, path):
iter = model.get_iter(path)
if iter is None:
return None
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
return siginfo.local_id
def validate_siginfo(siginfo):
timestamp = None
if siginfo.first_seen_date is None:
if timestamp is None:
timestamp = TimeStamp()
siginfo.first_seen_date = timestamp
if siginfo.last_seen_date is None:
if timestamp is None:
timestamp = TimeStamp()
siginfo.last_seen_date = timestamp
def convert_paths_to_local_ids(model, paths):
local_ids = []
if model is None:
return local_ids
for path in paths:
local_id = get_local_id_from_model_path(model, path)
if local_id is None:
log_gui_data.error("unable to lookup local_id of row %s in model", path)
continue
local_ids.append(local_id)
return local_ids
def convert_local_ids_to_paths(model, local_ids):
paths = []
if model is None:
return paths
for local_id in local_ids:
path = get_model_path_from_local_id(model, local_id)
if path is None:
log_gui_data.error("unable to lookup path for local_id %s in model", local_id)
continue
paths.append(path)
return paths
def dump_tv_columns(treeview):
column_infos = []
i = 0
for tv_column in treeview.get_columns():
title = tv_column.get_title()
sort_column_id = tv_column.get_sort_column_id()
sort_order = tv_column.get_sort_order()
sort_indicator = tv_column.get_sort_indicator()
order = {None:'None', gtk.SORT_DESCENDING:'DESCENDING', gtk.SORT_ASCENDING:'ASCENDING'}[sort_order]
column_info = "%d:%s, order=%s indicator=%s" % (i, title, order, sort_indicator)
column_infos.append(column_info)
i += 1
print '\n'.join(column_infos)
print
return True
def get_iconset_from_name(icon_name):
icon_source = gtk.IconSource()
icon_source.set_icon_name(icon_name)
iconset = gtk.IconSet()
iconset.add_source(icon_source)
return iconset
def load_stock_icons(icon_name_list):
factory = gtk.IconFactory()
for icon_name in icon_name_list:
iconset = get_iconset_from_name(icon_name)
factory.add(icon_name, iconset)
factory.add_default()
#------------------------------------------------------------------------------
class AlertListView(gobject.GObject):
__gsignals__ = {
'row-changed':
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)),
'load-data':
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)),
'properties-changed':
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
'async-error': # callback(method, errno, strerror)
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)),
}
column_types = (gobject.TYPE_PYOBJECT,# siginfo
gobject.TYPE_BOOLEAN, # filter
gobject.TYPE_PYOBJECT,# date
gobject.TYPE_STRING, # host
gobject.TYPE_INT, # count
gobject.TYPE_STRING, # category
gobject.TYPE_STRING) # summary
def __init__(self, username, browser):
gobject.GObject.__init__(self)
self.username = username
self.browser = browser
self.scrolled_window = gtk.ScrolledWindow()
self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.view = gtk.TreeView()
self.scrolled_window.add(self.view)
self.tv_columns = [None] * NUM_COLUMNS
self.selection = None
self.sort_column_id = DATE_COLUMN
self.sort_order = gtk.SORT_DESCENDING
self.hide_deleted = False
self.hide_quiet = False
self.last_selected_row = 0
self.base_model = None
self.filter_model = None
self.sort_model = None
self.view_model = None
self.row_inserted_signal = SignalConnection('row-inserted')
self.row_deleted_signal = SignalConnection('row-deleted')
self.row_changed_signal = SignalConnection('row-changed')
self.properties_changed_signal = SignalConnection('properties-changed')
self.load_data_signal = SignalConnection('load-data')
self.async_error_signal = SignalConnection('async-error')
self.button_press_event = SignalConnection('button_press_event')
self.button_press_event.connect(self.view, self.browser.on_button_press_event)
#
# Create each column and intitialize it's cell renderers, etc.
#
# --- siginfo ---
self.tv_columns[PYOBJECT_COLUMN] = tv_column = gtk.TreeViewColumn()
tv_column.set_visible(False)
self.view.append_column(tv_column)
# --- filter ---
self.tv_columns[FILTER_TYPE_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[FILTER_TYPE_COLUMN]['title'])
cell = gtk.CellRendererToggle()
cell.set_property('activatable', True)
cell.connect( 'toggled', self.on_filter_toggle)
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.filter_type_cell_data, FILTER_TYPE_COLUMN)
tv_column.set_sort_column_id(FILTER_TYPE_COLUMN)
self.view.append_column(tv_column)
# --- date ----
self.tv_columns[DATE_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[DATE_COLUMN]['title'])
cell = gtk.CellRendererText()
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.date_cell_data, DATE_COLUMN)
tv_column.set_sort_column_id(DATE_COLUMN)
self.view.append_column(tv_column)
# --- host ---
self.tv_columns[HOST_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[HOST_COLUMN]['title'])
cell = gtk.CellRendererText()
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.text_cell_data, HOST_COLUMN)
tv_column.set_sort_column_id(HOST_COLUMN)
#tv_column.set_visible(False)
self.view.append_column(tv_column)
# --- count ---
self.tv_columns[COUNT_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[COUNT_COLUMN]['title'])
cell = gtk.CellRendererText()
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.count_cell_data, COUNT_COLUMN)
tv_column.set_sort_column_id(COUNT_COLUMN)
self.view.append_column(tv_column)
# --- category ---
self.tv_columns[CATEGORY_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[CATEGORY_COLUMN]['title'])
cell = gtk.CellRendererText()
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.text_cell_data, CATEGORY_COLUMN)
tv_column.set_sort_column_id(CATEGORY_COLUMN)
self.view.append_column(tv_column)
# --- summary ---
self.tv_columns[SUMMARY_COLUMN] = tv_column = gtk.TreeViewColumn(tv_column_info[SUMMARY_COLUMN]['title'])
cell = gtk.CellRendererText()
tv_column.pack_start(cell)
tv_column.set_cell_data_func(cell, self.text_cell_data, SUMMARY_COLUMN)
tv_column.set_sort_column_id(SUMMARY_COLUMN)
self.view.append_column(tv_column)
#
# Some final properties
#
# alternate row color for easy reading
self.view.set_rules_hint(True)
# Set up the selection objects
self.selection = self.view.get_selection()
self.selection.set_mode(gtk.SELECTION_MULTIPLE)
self.scrolled_window.show_all()
def on_sort_column_changed(self, treesortable, treeview):
sort_column_id, sort_order = treesortable.get_sort_column_id()
if debug:
if sort_column_id is None:
title = None
else:
tv_column = treeview.get_column(sort_column_id)
title = tv_column.get_title()
order = {None:'None', gtk.SORT_DESCENDING:'DESCENDING', gtk.SORT_ASCENDING:'ASCENDING'}[sort_order]
log_gui.debug('on_sort_column_changed: %s %s', title, order)
self.sort_column_id = sort_column_id
self.sort_order = sort_order
def set_model(self, base_model):
if debug:
log_gui.debug('set_model:')
if base_model is None:
self.base_model = None
self.filter_model = None
self.sort_model = None
self.view_model = None
else:
self.base_model = base_model
# create an intermediate model to filter rows with
self.filter_model = self.base_model.filter_new()
self.filter_model.set_visible_func(self.visible_row_filter)
self.filter_model.set_modify_func(self.column_types, self.get_row_col_model_data)
# create an intermediate model to sort the filtered rows with
self.sort_model = gtk.TreeModelSort(self.filter_model)
self.sort_model.connect('sort-column-changed', self.on_sort_column_changed, self.view)
self.sort_model.set_sort_func(DATE_COLUMN, self.sort_date_column, DATE_COLUMN)
self.view.set_model(self.sort_model)
self.view_model = self.sort_model
self.sort_model.set_sort_column_id(self.sort_column_id, self.sort_order)
self.row_inserted_signal.connect(self.view_model, self.on_model_row_inserted)
self.row_deleted_signal.connect (self.view_model, self.on_model_row_deleted)
self.row_changed_signal.connect (self.view_model, self.on_model_row_changed)
def bind_data(self, alert_data):
if debug:
if alert_data is None:
name = None
else:
name = alert_data.name
log_gui.debug("view.bind_data: %s", name)
self.alert_data = alert_data
self.set_model(alert_data.model)
self.properties_changed_signal.connect(self.alert_data, self.on_data_properties_changed)
self.load_data_signal.connect(self.alert_data, self.on_load_data)
# --- Row Handling ---
def get_row_col_model_data(self, model, iter, column):
value = None
# Convert the filter iter to the corresponding child model iter.
child_model_iter = model.convert_iter_to_child_iter(iter)
if child_model_iter is None:
return None
child_model = model.get_model()
siginfo = child_model.get_value(child_model_iter, 0)
if siginfo is None:
return None
if column == PYOBJECT_COLUMN:
value = siginfo
elif column == COUNT_COLUMN:
value = siginfo.report_count
elif column == DATE_COLUMN:
value = siginfo.last_seen_date
elif column == HOST_COLUMN:
value = siginfo.host
elif column == CATEGORY_COLUMN:
value = default_text(siginfo.category)
elif column == SUMMARY_COLUMN:
value = html_to_text(siginfo.solution.summary, 1024)
return value
def visible_row_filter(self, model, iter):
visible = True
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
if siginfo is not None:
user_data = siginfo.get_user_data(self.username)
if user_data.delete_flag and self.hide_deleted:
visible = False
if user_data.filter.filter_type == FILTER_ALWAYS and self.hide_quiet:
visible = False
return visible
def on_filter_toggle(self, cell, path):
if self.view_model is None:
return
model = self.view_model
iter = model.get_iter_from_string(path)
siginfo = get_siginfo_from_model_path(model, path)
user_data = siginfo.get_user_data(self.username)
if user_data.filter.filter_type == FILTER_NEVER:
filter_type = FILTER_ALWAYS
else:
filter_type = FILTER_NEVER
database = self.alert_data.database
database.set_filter(siginfo.sig, self.username, filter_type, '')
return True # return True ==> handled, False ==> propagate
def on_model_row_inserted(self, model, path, iter):
if debug:
pass
#log_gui_data.debug("model_row_inserted: %s", path)
self.emit('row-changed', self.view_model, 'add', iter)
def on_model_row_changed(self, model, path, iter):
if debug:
pass
#log_gui_data.debug("model_row_changed: %s", path)
self.emit('row-changed', self.view_model, 'modify', iter)
def on_model_row_deleted(self, model, path):
if debug:
pass
#log_gui_data.debug("model_row_deleted: %s", path)
self.emit('row-changed', self.view_model, 'delete', None)
# --- Column Handling ---
def sort_date_column(self, model, iter1, iter2, column_index):
if debug:
log_gui.debug('sort_date_column:')
timestamp1 = model.get_value(iter1, column_index)
timestamp2 = model.get_value(iter2, column_index)
if timestamp1 is None or timestamp2 is None:
return 0
return cmp(timestamp1, timestamp2)
def count_cell_data(self, column, cell, model, iter, column_index):
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
text_attributes = self.get_text_attributes(siginfo)
cell.set_property("attributes", text_attributes)
# FIXME: this should be a global property, not set each time
cell.set_property("xalign", 1.0)
text = model.get_value(iter, column_index)
cell.set_property('text', text)
return
def date_cell_data(self, column, cell, model, iter, column_index):
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
text_attributes = self.get_text_attributes(siginfo)
cell.set_property("attributes", text_attributes)
timestamp = model.get_value(iter, column_index)
if timestamp is None:
return
cell.set_property('text', timestamp.format())
return
def text_cell_data(self, column, cell, model, iter, column_index):
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
text_attributes = self.get_text_attributes(siginfo)
cell.set_property("attributes", text_attributes)
text = model.get_value(iter, column_index)
cell.set_property('text', text)
return
def filter_type_cell_data(self, column, cell, model, iter, column_index):
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
user_data = siginfo.get_user_data(self.username)
if user_data.filter.filter_type == FILTER_NEVER:
cell.set_property('active', False)
else:
cell.set_property('active', True)
return
def restore_selection(self):
if debug:
log_gui.debug('restore_selection:')
if self.selection is None or self.view_model is None:
return
self.selection.unselect_all()
n_paths = len(self.view_model)
if n_paths == 0:
new_row = 0
else:
new_row = min(self.last_selected_row, n_paths-1)
if debug:
log_gui.debug('restore_selection: new_row=%s', new_row)
self.selection.select_path((new_row,))
self.view.scroll_to_cell((new_row,))
def get_selected_siginfos(self):
model, selected_paths = self.selection.get_selected_rows()
siginfos = []
for path in selected_paths:
siginfo = get_siginfo_from_model_path(model, path)
siginfos.append(siginfo)
return siginfos
# --- Signals ---
def on_data_properties_changed(self, alert_data, properties):
self.emit('properties-changed', alert_data, properties)
def on_load_data(self, alert_data, state, errno, strerror):
self.emit('load-data', alert_data, state, errno, strerror)
def on_async_error(self, alert_data, method, errno, strerror):
if debug:
log_program.debug("%s.on_async_error(%s, %d, %s)", self.__class__.__name__, method, errno, strerror)
self.emit('async-error', alert_data, method, errno, strerror)
# --- Utilities ---
def get_text_attributes(self, siginfo):
user_data = siginfo.get_user_data(self.username)
text_attributes = pango.AttrList()
if not user_data.seen_flag:
text_attributes.insert(pango.AttrWeight(pango.WEIGHT_HEAVY, 0, -1))
if user_data.delete_flag:
text_attributes.insert(pango.AttrStrikethrough(True, 0, 1000))
return text_attributes
#------------------------------------------------------------------------------
class AlertData(gobject.GObject):
__gsignals__ = {
'load-data':
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)),
'properties-changed':
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
'async-error': # callback(method, errno, strerror)
(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)),
}
def __init__(self, name, database):
gobject.GObject.__init__(self)
self.name = name
self.database = database
self.database_properties = SEDatabaseProperties()
# create the base model, it holds a siginfo
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT)
self.clear()
self.get_properties()
database.connect('signatures_updated', self.signatures_updated)
def clear(self):
if debug:
log_gui.debug("AlertData (%s) clear: cur row count = %d", self.name, len(self.model))
self.model.clear()
def set_properties(self, properties):
if debug:
log_gui.debug("set_properties: properties=%s", properties)
if properties is None:
self.database_properties = SEDatabaseProperties()
else:
self.database_properties = properties
self.emit('properties-changed', self.database_properties)
def get_properties(self):
if self.database is None: return
async_rpc = self.database.get_properties()
async_rpc.add_callback(self.get_properties_callback)
async_rpc.add_errback(self.get_properties_errback)
def get_properties_callback(self, properties):
if debug:
log_gui.debug("get_properties_callback: properties=%s", properties)
self.set_properties(properties)
def get_properties_errback(self, method, errno, strerror):
log_rpc.error('database bind: %s', strerror)
self.set_properties(None)
def signatures_updated(self, database, type, item):
if debug:
log_gui_data.debug("signatures_updated() %s: type=%s, item=%s alert_list size=%s",
self.name, type, item, len(self.model))
def new_siginfo_callback(sigs):
if debug:
log_gui_data.debug("new_siginfo_callback(), type=%s %s", type, str(sigs))
for siginfo in sigs.signature_list:
self.insert_siginfo_into_model(siginfo)
if type == 'add' or type == 'modify':
async_rpc = self.database.query_alerts(item)
async_rpc.add_callback(new_siginfo_callback)
elif type == 'delete':
iter = self.get_iter_from_local_id(item)
if iter is not None:
self.model.remove(iter)
else:
raise ProgramError(ERR_UNKNOWN_VALUE, "signatures_updated: type = %s not recognized" % type)
def get_iter_from_local_id(self, local_id, model=None):
if model is None:
model = self.model
iter = model.get_iter_first()
while iter:
row_siginfo = model.get_value(iter, PYOBJECT_COLUMN)
if row_siginfo.local_id == local_id:
return iter
iter = model.iter_next(iter)
return None
def insert_siginfo_into_model(self, siginfo):
iter = self.get_iter_from_local_id(siginfo.local_id)
if iter is None:
if debug:
log_gui_data.debug("insert_siginfo_into_model(): new")
iter = self.new_model_row(siginfo)
else:
if debug:
log_gui_data.debug("insert_siginfo_into_model(): replace")
self.update_model_row(iter, siginfo)
def update_model_row(self, iter, siginfo):
validate_siginfo(siginfo)
self.model.set(iter, PYOBJECT_COLUMN, siginfo)
def new_model_row(self, siginfo):
validate_siginfo(siginfo)
iter = self.model.append((siginfo,))
return iter
def load_model_data_from_sigs(self, sigs):
self.model.clear()
for siginfo in sigs.signature_list:
self.new_model_row(siginfo)
def load_data(self):
self.clear()
if debug:
log_gui.debug("load_data(): database=%s", self.name)
self.get_properties()
if self.database is not None:
criteria = '*'
self.emit('load-data', 'start', 0, '')
async_rpc = self.database.query_alerts(criteria)
async_rpc.add_callback(self.query_alerts_callback)
async_rpc.add_errback(self.query_alerts_error)
def query_alerts_callback(self, sigs):
if debug:
log_gui.debug("query_alerts_callback():")
self.sigs = sigs
self.load_model_data_from_sigs(sigs)
self.emit('load-data', 'end', 0, '')
def query_alerts_error(self, method, errno, strerror):
log_gui.error("query_alerts: [%d] %s", errno, strerror)
self.emit('load-data', 'end', errno, strerror)
def get_siginfos(self, criteria=None):
siginfos = []
iter = self.model.get_iter_first()
while iter:
siginfo = self.model.get_value(iter, PYOBJECT_COLUMN)
siginfos.append(siginfo)
iter = self.model.iter_next(iter)
return siginfos
gobject.type_register(AlertData)
#------------------------------------------------------------------------------
class AlertViewData(object):
def __init__(self, name, username, browser):
self.name = name
self.username = username
self.list_view = AlertListView(username, browser)
self.alert_data = None
def bind_data(self, alert_data):
self.alert_data = alert_data
self.list_view.bind_data(alert_data)
#-----------------------------------------------------
class ScanLogfileDialog(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, title=_('Scan Log File'))
#buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
self.analyzer = LogfileAnalyzer()
self.analyzer.connect('progress', self.on_progress)
self.analyzer.connect('state-changed', self.on_analyzer_state_change)
icon_name = get_config('general','icon_name')
self.set_icon_name(icon_name)
self.set_default_size(400, 100)
self.cancel_response_button = self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
self.cancel_response_button.connect('clicked', self.run_analysis, False)
self.ok_response_button = self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
self.ok_response_button.set_sensitive(False)
self.file_hbox = gtk.HBox()
self.action_hbox = gtk.HBox()
self.progress_bar = gtk.ProgressBar()
self.vbox.pack_start(self.file_hbox, False)
self.vbox.pack_start(self.action_hbox, False)
self.vbox.pack_start(self.progress_bar, False)
self.file_entry = gtk.Entry()
self.file_entry.connect('activate', self.on_filepath_activate)
self.file_entry.connect('changed', self.on_filepath_changed)
self.file_chooser_button = gtk.Button(stock=gtk.STOCK_OPEN)
self.file_chooser_button.connect('clicked', self.run_file_chooser)
self.file_hbox.pack_start(self.file_entry, True)
self.file_hbox.pack_start(self.file_chooser_button, False)
self.run_analysis_button = gtk.Button(stock=gtk.STOCK_EXECUTE)
self.run_analysis_button.connect('clicked', self.run_analysis, True)
self.run_analysis_button.set_sensitive(False)
self.cancel_analysis_button = gtk.Button(stock=gtk.STOCK_STOP)
self.cancel_analysis_button.connect('clicked', self.run_analysis, False)
self.cancel_analysis_button.set_sensitive(False)
self.action_hbox.pack_start(self.run_analysis_button, True)
self.action_hbox.pack_start(self.cancel_analysis_button, True)
self.set_position(gtk.WIN_POS_CENTER)
self.set_keep_above(True)
self.show_all()
self.set_widget_state('pending')
filepath = property(lambda self: self.file_entry.get_text())
def on_progress(self, analyzer, progress):
self.progress_bar.set_fraction(progress)
if progress == 1.0:
self.cancel_analysis_button.set_sensitive(False)
def on_analyzer_state_change(self, analyzer, state):
self.set_widget_state(state)
if state == 'stopped':
if analyzer.strerror:
display_error(analyzer.strerror)
def scan_file(self, filepath):
if debug:
log_avc.debug('%s.scan_file(%s)', self.__class__.__name__, filepath)
self.analyzer.cancelled = False
self.analyzer.open(self.filepath)
self.progress_bar.set_text(self.analyzer.friendly_name)
self.analyzer.run()
def set_widget_state(self, state):
if state == 'pending':
self.ok_response_button.set_sensitive(False)
self.cancel_response_button.set_sensitive(True)
self.file_chooser_button.set_sensitive(True)
self.file_entry.set_sensitive(True)
self.run_analysis_button.set_sensitive(True)
self.cancel_analysis_button.set_sensitive(False)
elif state == 'stopped':
if not self.analyzer.errno:
self.ok_response_button.set_sensitive(True)
else:
self.ok_response_button.set_sensitive(False)
self.cancel_response_button.set_sensitive(True)
self.file_chooser_button.set_sensitive(False)
self.file_entry.set_sensitive(False)
self.run_analysis_button.set_sensitive(False)
self.cancel_analysis_button.set_sensitive(False)
elif state == 'running':
self.ok_response_button.set_sensitive(False)
self.cancel_response_button.set_sensitive(True)
self.file_chooser_button.set_sensitive(False)
self.file_entry.set_sensitive(False)
self.run_analysis_button.set_sensitive(False)
self.cancel_analysis_button.set_sensitive(True)
else:
raise ValueError("unknown state (%s)" % state)
def run_analysis(self, widget, start):
if debug:
log_avc.debug('%s.run_analysis() start=%s', self.__class__.__name__, start)
if start:
self.scan_file(self.filepath)
else:
self.analyzer.cancelled = True
def on_filepath_changed(self, widget):
if self.filepath:
self.run_analysis_button.set_sensitive(True)
else:
self.run_analysis_button.set_sensitive(False)
def on_filepath_activate(self, widget):
self.run_analysis(widget, True)
def run_file_chooser(self, widget):
result = None
file_open = gtk.FileChooserDialog(self.get_title(), action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
if file_open.run() == gtk.RESPONSE_OK:
self.file_entry.set_text(file_open.get_filename())
file_open.destroy()
def get_database(self):
if not self.analyzer.errno:
return self.analyzer.database
else:
return None
#------------------------------------------------------------------------------
class StatusMessage(object):
LOW_PRIORITY = 0
MEDIUM_PRIORITY = 1
HIGH_PRIORITY = 2
message_priorities = [LOW_PRIORITY, MEDIUM_PRIORITY, HIGH_PRIORITY]
map_priority_to_string = {
LOW_PRIORITY : 'LOW',
MEDIUM_PRIORITY : 'MEDIUM',
HIGH_PRIORITY : 'HIGH',
}
INFO_TYPE = 0
ERROR_TYPE = 1
message_types = [INFO_TYPE, ERROR_TYPE]
map_type_to_string = {
INFO_TYPE : 'INFO',
ERROR_TYPE : 'ERROR',
}
time_fmt = '%I:%M.%S %p'
def __init__(self, owner, type, priority, text, time_to_live=None, message_name=None):
self.owner = owner
self.type = type
self.priority = priority
self.text = text
self.start_time = time.time()
self.time_to_live = time_to_live
self.message_name = message_name
def __str__(self):
time_str = time.strftime(self.time_fmt, time.localtime(self.start_time))
if self.time_to_live is not None:
expiration_time = self.start_time + self.time_to_live
time_str += ' - %s' % (time.strftime(self.time_fmt, time.localtime(expiration_time)))
text = "[%s,%s,%s, %s] %s" % (self.map_type_to_string[self.type],
self.map_priority_to_string[self.priority],
time_str, self.message_name,
self.text)
return text
def update_text(self, text, time_to_live=None):
self.text = text
if time_to_live is not None:
self.start_time = time.time()
self.time_to_live = time_to_live
self.owner.update_message_text(self)
#------------------------------------------------------------------------------
class StatusMessageManager(object):
'''Messages are inserted into a priority list, the message
with the highest priority is the one currently displayed.
Messages may be either expiring (timeout) or non-expiring.
There is at most one non-expiring message of any given
type. The next non-expiring message written replaces the
previous non-expiring message of the same type.
Expiring messages persist in the message list until they
expire or are manually removed.
Any message may be removed at any time by calling
remove_message(). It is not an error to remove a message not
present in the message list.
The currently displayed message is updated whenever the
message list is updated which occures when a message is added,
removed, modified, or times out.
Setting a message does not guarantee it will be displayed, a
higher priority message may obscure it. In such a case it
might be displayed later if the obscuring message times out or
is manually removed.
'''
def __init__(self, name, set_message_func):
self.name = name
self.set_message = set_message_func
self.expire_timeout_id = None
self.messages = []
def _expire_timeout_callback(self):
if debug:
log_gui.debug("_expire_timeout_callback (%s)", self.name)
self.expire_timeout_id = None
self._prune_expiring_messages()
self._display_current_message()
return False
def _prune_non_expiring_messages(self):
if debug:
self.dump_message_list("_prune_non_expiring_messages start (%s)" % (self.name))
message_at_priority_level = {}
# Note iterate over copy of message list (e.g. [:]) to avoid problems with modification during iteration
for message in self.messages[:]:
if message.time_to_live is None:
if message_at_priority_level.get(message.priority):
self.messages.remove(message)
else:
message_at_priority_level[message.priority] = message
if debug:
self.dump_message_list("_prune_non_expiring_messages end (%s)" % (self.name))
def _prune_expiring_messages(self):
now = time.time()
if debug:
self.dump_message_list("_prune_expiring_messages start (%s) now=%s" % (self.name, time.strftime(StatusMessage.time_fmt, time.localtime(now))))
if self.expire_timeout_id is not None:
gobject.source_remove(self.expire_timeout_id)
self.expire_timeout_id = None
maximum_expiration_time = 0.0
soonest_message_to_expire = None
# Note iterate over copy of message list (e.g. [:]) to avoid problems with modification during iteration
for message in self.messages[:]:
if message.time_to_live is not None:
message_expiration_time = message.start_time + message.time_to_live
if message_expiration_time <= now:
self.messages.remove(message)
else:
if message_expiration_time > maximum_expiration_time:
maximum_expiration_time = message_expiration_time
soonest_message_to_expire = message
if soonest_message_to_expire is not None:
seconds_to_next_expire = (soonest_message_to_expire.start_time + soonest_message_to_expire.time_to_live) - now
if debug:
log_gui.debug("expire next (%s) in %.1f: %s", self.name, seconds_to_next_expire, soonest_message_to_expire)
self.expire_timeout_id = gobject.timeout_add(int(seconds_to_next_expire*1000), self._expire_timeout_callback)
else:
if debug:
log_gui.debug("NO expiring messages")
if debug:
self.dump_message_list("_prune_expiring_messages end (%s)" % (self.name))
def get_message_by_name(self, message_name):
for message in self.messages:
if message.message_name == message_name:
return message
return None
def dump_message_list(self, header=None):
if header is not None:
log_gui.debug(header)
for message in self.messages:
log_gui.debug(message)
def _message_sort_func(self, msg2, msg1):
# sort descending by swapping input parameters
relation = cmp(msg1.priority, msg2.priority)
if relation: return relation
relation = cmp(msg1.start_time, msg2.start_time)
return relation
def _add_message(self, message):
if message.message_name is not None:
existing_named_message = self.get_message_by_name(message.message_name)
if existing_named_message != message:
self.remove_message(existing_named_message)
if not message in self.messages:
self.messages.append(message)
self.messages.sort(self._message_sort_func)
if debug:
self.dump_message_list("_add_message (%s): %s" % (self.name, message))
self._prune_non_expiring_messages()
self._prune_expiring_messages()
self._display_current_message()
def _display_current_message(self):
if debug:
self.dump_message_list("_display_current_message (%s)" % (self.name))
if len(self.messages) > 0:
message = self.messages[0]
self.set_message(message.text)
else:
self.set_message(None)
def update_message_text(self, message):
self._add_message(message)
def remove_message(self, message):
if message in self.messages:
self.messages.remove(message)
self._display_current_message()
if debug:
self.dump_message_list("remove_message (%s): %s" % (self.name, message))
def remove_message_by_name(self, message_name):
message = self.get_message_by_name(message_name)
if message is not None:
self.remove_message(message)
def remove_all_messages(self):
if debug:
self.dump_message_list("remove_all_messages (%s)" % (self.name))
# Note iterate over copy of message list (e.g. [:]) to avoid problems with modification during iteration
for message in self.messages[:]:
self.remove_message(message)
def new_message(self, type, priority, text, time_to_live=None, message_name=None):
message = StatusMessage(self, type, priority, text, time_to_live, message_name)
self._add_message(message)
return message
#------------------------------------------------------------------------------
class BrowserStatusBar(gtk.VBox):
# These should sum to 1.0
visit_msg_proportion = 0.20
alert_count_proportion = 0.10
status_msg_proportion = 0.55
progress_proportion = 0.15
def __init__(self):
gtk.VBox.__init__(self)
self.status_hbox = gtk.HBox()
# Error bar
self.error_frame = gtk.Frame()
self.error_hbox = gtk.HBox()
error_icon = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_MENU)
self.error_hbox.pack_start(error_icon, expand=False)
self.error_msg = gtk.Label("error")
self.error_msg.set_ellipsize(pango.ELLIPSIZE_END)
self.error_msg.set_alignment(0.0, 0.5)
self.error_hbox.pack_start(self.error_msg, expand=True)
self.error_frame.add(self.error_hbox)
self.connect_icon = gtk.Image()
self.connect_icon.set_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.connect_icon_frame = gtk.Frame()
self.connect_icon_frame.add(self.connect_icon)
self.status_hbox.add(self.connect_icon_frame)
self.visit_msg = gtk.Label("visiting")
self.visit_msg.set_ellipsize(pango.ELLIPSIZE_END)
self.visit_msg.set_alignment(0.0, 0.5)
self.visit_msg_frame = gtk.Frame()
self.visit_msg_frame.add(self.visit_msg)
self.status_hbox.add(self.visit_msg_frame)
self.alert_count = gtk.Label("count")
self.alert_count.set_ellipsize(pango.ELLIPSIZE_END)
self.alert_count.set_alignment(0.0, 0.5)
self.alert_count_frame = gtk.Frame()
self.alert_count_frame.add(self.alert_count)
self.status_hbox.add(self.alert_count_frame)
self.status_msg = gtk.Label("status")
self.status_msg.set_ellipsize(pango.ELLIPSIZE_END)
self.status_msg.set_alignment(0.0, 0.5)
self.status_msg_frame = gtk.Frame()
self.status_msg_frame.add(self.status_msg)
self.status_hbox.add(self.status_msg_frame)
self.progress = gtk.ProgressBar()
self.progress.set_ellipsize(pango.ELLIPSIZE_END)
self.progress_frame = gtk.Frame()
self.progress_frame.add(self.progress)
self.status_hbox.add(self.progress_frame)
self.pack_start(self.error_frame, expand=False)
self.pack_start(self.status_hbox, expand=False)
self.show_all()
self.error_frame.hide() # error is initially hidden
self.status = StatusMessageManager('statusbar status', self.set_status_message)
self.error = StatusMessageManager('statusbar error', self.set_error_message)
def do_size_allocate(self, allocation):
if debug:
log_gui.debug("statusbar.do_size_allocate: (%d,%d)(%dx%d)",
allocation.x, allocation.y, allocation.width, allocation.height)
max_x = allocation.x + allocation.width
# If the error bar is visible, allocate space for it
if self.error_frame.flags() & gtk.VISIBLE:
child_rect = gtk.gdk.Rectangle(allocation.x, allocation.y, allocation.width, allocation.height/2)
self.error_frame.size_allocate(child_rect)
child_rect.y += child_rect.height
else:
child_rect = gtk.gdk.Rectangle(allocation.x, allocation.y, allocation.width, allocation.height)
# Compute size for connection icon
icon_width, icon_height = self.connect_icon_frame.size_request()
# Total space available for fixed sized items
fixed_width_total = icon_width
fixed_width_total = min(allocation.width, fixed_width_total)
fixed_width_remaining = fixed_width_total
# Total space available remaining for scaled items
scaled_width_total = allocation.width - fixed_width_total
scaled_width_remaining = scaled_width_total
# Move left to right laying out items:
# [connection icon][visit msg][status msg][progress bar]
# Connection Icon
child_rect.width = min(fixed_width_total, icon_width)
self.connect_icon_frame.size_allocate(child_rect)
child_rect.x += child_rect.width
fixed_width_remaining = fixed_width_remaining - child_rect.width
# Visit Message
child_rect.width = int(scaled_width_total * self.visit_msg_proportion)
if (child_rect.x + child_rect.width) > max_x:
child_rect.width = max_x - child_rect.x
self.visit_msg_frame.size_allocate(child_rect)
scaled_width_remaining = scaled_width_remaining - child_rect.width
child_rect.x += child_rect.width
# Alert Count
child_rect.width = int(scaled_width_total * self.alert_count_proportion)
if (child_rect.x + child_rect.width) > max_x:
child_rect.width = max_x - child_rect.x
self.alert_count_frame.size_allocate(child_rect)
scaled_width_remaining = scaled_width_remaining - child_rect.width
child_rect.x += child_rect.width
# Status Message
child_rect.width = int(scaled_width_total * self.status_msg_proportion)
if (child_rect.x + child_rect.width) > max_x:
child_rect.width = max_x - child_rect.x
self.status_msg_frame.size_allocate(child_rect)
scaled_width_remaining = scaled_width_remaining - child_rect.width
child_rect.x += child_rect.width
# Progress Bar
# Note, we don't compute the proportionate space for the last scaled item
# because of potential rounding errors, we just use whats left
child_rect.width = scaled_width_remaining
if child_rect.x + child_rect.width > max_x:
child_rect.width = max(0, max_x - child_rect.x)
self.progress_frame.size_allocate(child_rect)
def set_status_message(self, text):
if debug:
log_gui.debug("set_status_message: %s", text)
if text is None:
self.status_msg.set_text('')
else:
self.status_msg.set_text(text)
def set_error_message(self, text):
if debug:
log_gui.debug("set_error_message: %s", text)
if text is None:
self.error_frame.hide()
else:
text = '<span foreground="#FF0000">%s</span>' % text
self.error_msg.set_markup(text)
self.error_frame.show()
def set_connected_state(self, is_connected):
if is_connected:
self.connect_icon.set_from_stock(gtk.STOCK_CONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
else:
self.connect_icon.set_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
def set_visit_message(self, msg):
if msg is None:
self.visit_message = ''
else:
self.visit_message = msg
self.visit_msg.set_text(self.visit_message)
gobject.type_register(BrowserStatusBar)
#------------------------------------------------------------------------------
class SignalConnection(object):
def __init__(self, signal, emitter=None, id=None):
self.signal = signal
self.emitter = emitter
self.id = id
def connect(self, emitter, handler):
self.disconnect()
self.emitter = emitter
if self.emitter is not None:
self.id = self.emitter.connect(self.signal, handler)
def disconnect(self):
if self.emitter is not None and self.id is not None:
self.emitter.disconnect(self.id)
self.emitter = None
self.id = None
#------------------------------------------------------------------------------
class BrowserApplet(object):
VISIT_AUDIT = 0
VISIT_LOGFILE = 1
map_visit_num_to_name = {VISIT_AUDIT : 'audit', VISIT_LOGFILE : 'logfile'}
# --- Initialization ---
def __init__(self, username=None, server=None):
self.username = username
self.server = server
self.displayed_siginfo = None
self.mark_seen_timeout_event_id = None
self.update_alert_view_idle_event_id = None
self.restore_selection_idle_event_id = None
self.alert_display = None
self.window_delete_hides = True
self.load_in_progress = False
self.list_view = None
self.toolbar_visible = False
self.view_data_collection = {}
self.audit_data = AlertData('audit', self.server)
self.audit_view_data = AlertViewData('audit', username, self)
self.audit_view_data.bind_data(self.audit_data)
self.set_visit_data('audit', self.audit_view_data)
self.logfile_view_data = AlertViewData('logfile', username, self)
self.set_visit_data('logfile', self.logfile_view_data)
self.default_save_folder = get_user_home_dir()
self.default_save_filename = 'selinux_alert.txt'
self.clipboard = gtk.Clipboard()
self.print_settings = None
# How long an alert must be displayed before it's marked as having been seen
self.seen_after_displayed_seconds = 3
program_name = _('setroubleshoot browser')
program_version = '1.0'
gnome.init(program_name, program_version)
self.window_name = _("SETroubleshoot Browser")
self.connection_state_change_signal = SignalConnection('connection_state_changed')
self.connection_pending_retry_signal = SignalConnection('pending_retry')
self.properties_changed_signal = SignalConnection('properties-changed')
self.load_data_signal = SignalConnection('load-data')
self.async_error_signal = SignalConnection('async-error')
self.alert_list_changed_signal = SignalConnection('row-changed')
self.selection_changed_signal = SignalConnection('changed')
self.init_widgets()
window_state = os.getenv('SEALERT_WINDOW_STATE')
if debug:
log_gui.debug("read SEALERT_WINDOW_STATE from environment (%s)", window_state)
if window_state is None:
window_state = 'hidden'
self.window_state = parse_window_state(window_state)
self.update_window_state(self.window_state)
window_geometry = os.getenv('SEALERT_WINDOW_GEOMETRY')
if debug:
log_gui.debug("read SEALERT_WINDOW_GEOMETRY from environment (%s)", window_geometry)
if window_geometry is not None:
self.set_geometry(window_geometry)
self.update_connection_state(server, server.connection_state)
self.connection_state_change_signal.connect(self.server, self.on_connection_state_change)
self.connection_pending_retry_signal.connect(self.server.connection_retry, self.on_connection_pending_retry)
self.async_error_signal.connect(self.server, self.on_async_error)
self.do_visit('audit')
if (self.server.connection_state.flags & ConnectionState.OPEN) and len(self.audit_data.model) == 0:
self.audit_data.load_data()
def create_ui(self):
load_stock_icons(['stock_mail-send', 'connect_creating'])
toggle_column_visibility = "\n".join([" <menuitem action='Toggle%sColumn'/>" % \
tv_column_info[column]['name'] for column in range(1, NUM_COLUMNS)])
ui_string = """<ui>
<menubar name='Menubar'>
<menu action='FileMenu'>
<menuitem action='ConnectTo'/>
<menuitem action='ScanLogfile'/>
<separator/>
<menuitem action='SaveAs'/>
<menuitem action='Print'/>
<separator/>
<menuitem action='EditEmailList'/>
<separator/>
<menuitem action='Close'/>
</menu>
<menu action='EditMenu'>
<menuitem action='SelectAll'/>
<menuitem action='SelectNone'/>
<separator/>
<menuitem action='Copy'/>
<menuitem action='CopyAlert'/>
<separator/>
<menuitem action='MarkDelete'/>
<menuitem action='Undelete'/>
<menuitem action='Expunge'/>
</menu>
<menu action='ViewMenu'>
<menuitem action='HideDeleted'/>
<menuitem action='HideQuiet'/>
<menuitem action='ToggleToolbar'/>
<separator/>
<menuitem action='ViewAudit'/>
<menuitem action='ViewLogfile'/>
<separator/>
<menu action='ColumnVisibilityMenu'>
%s
</menu>
</menu>
<menu action='HelpMenu'>
<menuitem action='Help'/>
<menuitem action='About'/>
</menu>
</menubar>
<popup name='PopupMenu'>
<menuitem action='MarkDelete'/>
<menuitem action='Undelete'/>
<menuitem action='Expunge'/>
<separator/>
<menuitem action='CopyAlert'/>
<separator/>
<menuitem action='SaveAs'/>
<menuitem action='Print'/>
</popup>
<toolbar name='Toolbar'>
<toolitem action='Print'/>
<toolitem action='ConnectTo'/>
<toolitem action='ScanLogfile'/>
<separator/>
<toolitem action='ViewAudit'/>
<toolitem action='ViewLogfile'/>
</toolbar>
</ui>""" % toggle_column_visibility
self.action_group = gtk.ActionGroup('WindowActions')
# File Menu
action = gtk.Action('FileMenu', _('_File'), None, None)
self.action_group.add_action(action)
# View Menu
action = gtk.Action('ViewMenu', _('_View'), None, None)
self.action_group.add_action(action)
# Edit Menu
action = gtk.Action('EditMenu', _('_Edit'), None, None)
self.action_group.add_action(action)
# Help Menu
action = gtk.Action('HelpMenu', _('_Help'), None, None)
self.action_group.add_action(action)
# Column Visibility Menu
action = gtk.Action('ColumnVisibilityMenu', _('_Column Visibility'), None, None)
self.action_group.add_action(action)
action = gtk.Action('ConnectTo', _('Connect To...'), _('Connect to setroubleshoot server, browse alert results'), 'connect_creating')
action.connect('activate', self.on_connect_to)
self.action_group.add_action_with_accel(action, None)
action = gtk.Action('ScanLogfile', _('Scan Logfile...'), _('Scan a log file, browse alert results'), gtk.STOCK_OPEN)
action.connect('activate', self.on_open_logfile)
self.action_group.add_action_with_accel(action, None)
action = gtk.Action('SaveAs', _('Save _As...'), _('Save selected alerts in file'), gtk.STOCK_SAVE_AS)
action.connect('activate', self.on_save_as)
self.action_group.add_action_with_accel(action, '<shift><control>S')
action = gtk.Action('Print', _('Print...'), _('Print selected alerts'), gtk.STOCK_PRINT)
action.connect('activate', self.on_print)
self.action_group.add_action_with_accel(action, '<control>P')
action = gtk.Action('EditEmailList', _('Edit Email Alert List...'), _('Edit list of email addresses which receive alerts'), 'stock_mail-send')
action.connect('activate', self.on_edit_email_alert_list)
self.action_group.add_action_with_accel(action, None)
action = gtk.Action('Close', _('_Close'), _('Close the window'), gtk.STOCK_CLOSE)
action.connect('activate', self.on_close)
self.action_group.add_action_with_accel(action, '<control>W')
action = gtk.Action('SelectAll', _('Select _All'), _('Select all alerts'), gtk.STOCK_SELECT_ALL)
action.connect('activate', self.on_select_all)
self.action_group.add_action_with_accel(action, '<control>A')
action = gtk.Action('SelectNone', _('Select _None'), _('Remove all selections'), None)
action.connect('activate', self.on_select_none)
self.action_group.add_action_with_accel(action, None)
action = gtk.Action('Copy', _('Copy'), _('Copy selected text in detail pane'), gtk.STOCK_COPY)
action.connect('activate', self.on_copy)
self.action_group.add_action_with_accel(action, '<control>C')
action = gtk.Action('CopyAlert', _('Copy Alert'), _('Copy selected alerts in entirety to clipboard'), gtk.STOCK_COPY)
action.connect('activate', self.on_copy_alert)
self.action_group.add_action_with_accel(action, '<shift><control>C')
action = gtk.Action('MarkDelete', _('Mark _Delete'), _('Mark for deletion'), gtk.STOCK_DELETE)
action.connect('activate', self.on_delete)
self.action_group.add_action_with_accel(action, 'Delete')
action = gtk.Action('Undelete', _('_Undelete'), _('Clear deletion flag'), gtk.STOCK_UNDELETE)
action.connect('activate', self.on_undelete)
self.action_group.add_action_with_accel(action, '<shift><control>D')
action = gtk.Action('Expunge', _('Remove Marked Deleted'), _('Permanently delete alerts marked for deletion'), gtk.STOCK_REMOVE)
action.connect('activate', self.on_expunge)
self.action_group.add_action_with_accel(action, '<control>E')
action = gtk.Action('Help', _('Help'), _('Show help information'), gtk.STOCK_HELP)
action.connect('activate', self.on_user_help)
self.action_group.add_action_with_accel(action, 'F1')
action = gtk.Action('About', _('About'), _('About'), gtk.STOCK_ABOUT)
action.connect('activate', self.on_about)
self.action_group.add_action_with_accel(action, None)
action = gtk.RadioAction('ViewAudit', _('View Audit Alerts'), _('View alerts from audit system'), None, self.VISIT_AUDIT)
self.visit_radio_action = action
self.action_group.add_action_with_accel(action, None)
action = gtk.RadioAction('ViewLogfile', _('View Logfile Scan'), _('View alerts from last log file scan'), None, self.VISIT_LOGFILE)
action.set_group(self.visit_radio_action)
action.set_sensitive(False)
self.action_group.add_action_with_accel(action, None)
self.visit_radio_action.connect('changed', self.on_visit_change)
action = gtk.ToggleAction('HideDeleted', _('Hide deleted'), _('Toggle hide deleted alerts'), None)
action.connect('activate', self.on_hide_deleted)
action.set_active(False)
self.action_group.add_action_with_accel(action, None)
action = gtk.ToggleAction('HideQuiet', _('Hide quiet'), _('Toggle hide quiet alerts'), None)
action.connect('activate', self.on_hide_quiet)
action.set_active(False)
self.action_group.add_action_with_accel(action, None)
action = gtk.ToggleAction('ToggleToolbar', _('Show Toolbar'), _('Toggle the toolbar on/off'), None)
action.connect('activate', self.on_toggle_toolbar)
self.action_group.add_action_with_accel(action, None)
# Column Visibility
for column in range(1, NUM_COLUMNS):
column_name = tv_column_info[column]['name']
column_title = tv_column_info[column]['title']
action = gtk.ToggleAction('Toggle%sColumn' % column_name, _('Show %s Column') % column_title, _('Show/Hide %s Column') % column_title, None)
action.connect('activate', self.on_toggle_column_visibility, column)
action.set_active(True)
self.action_group.add_action_with_accel(action, None)
self.ui = gtk.UIManager()
self.ui.insert_action_group(self.action_group, 0)
self.ui.add_ui_from_string(ui_string)
self.browser_win.add_accel_group(self.ui.get_accel_group())
self.menubar = self.ui.get_widget('/Menubar')
self.toolbar = self.ui.get_widget('/Toolbar')
def on_realize(self, widget):
# position the divider between the top and bottom pane
vpane_rect = self.browser_vpane.get_allocation()
self.browser_vpane.set_position(vpane_rect.height/4)
def get_geometry(self):
width, height = self.browser_win.get_size()
xoffset, yoffset = self.browser_win.get_position()
geometry = '%dx%d+%d+%d' % (width, height, xoffset, yoffset)
if debug:
log_gui.debug("get_geometry() %s", geometry)
return geometry
def set_geometry(self, geometry):
screen_width = gtk.gdk.screen_width()
screen_height = gtk.gdk.screen_height()
match = re.search("(\d+)x(\d+)", geometry)
if match:
width = int(match.group(1))
height = int(match.group(2))
#print "width=%d height=%d" % (width, height)
self.browser_win.resize(width, height)
match = re.search("([+-]\d+)([+-]\d+)", geometry)
if match:
xoffset = int(match.group(1))
yoffset = int(match.group(2))
#print "xoffset=%d yoffset=%d" % (xoffset, yoffset)
if xoffset >= 0:
x = xoffset
else:
x = screen_width() + xoffset
if yoffset >= 0:
y = yoffset
else:
y = screen_height()+ yoffset
x = max(x, 0)
y = max(y, 0)
# force window to be visible
if x >= screen_width - 20:
x = 0
if y >= screen_height - 20:
y = 0
self.browser_win.move(x, y)
def window_state_event_cb(self, window, event):
if debug:
log_gui.debug("window state event: %s", window_state_to_string(event.new_window_state))
self.window_state = event.new_window_state
def on_style_set(self, widget, previous_style):
if debug:
log_gui.debug("on_style_set:")
self.update_alert_view()
def configure_event_cb(self, window, event):
if debug:
log_gui.debug("configure event: x=%d y=%d width=%d height=%d",
event.x, event.y, event.width, event.height)
def init_widgets(self):
self.browser_win = gtk.Window()
self.browser_win.set_position(gtk.WIN_POS_CENTER)
self.browser_win.set_title(_("setroubleshoot browser"))
self.browser_win.set_default_size(800, 700)
icon_name = get_config('general','icon_name')
self.browser_win.set_icon_name(icon_name)
self.browser_win.connect('delete-event', self.on_close)
self.browser_win.connect_after("realize", self.on_realize)
self.browser_win.connect('window-state-event', self.window_state_event_cb)
self.browser_win.connect('configure-event', self.configure_event_cb)
self.browser_win.connect('style-set', self.on_style_set)
self.create_ui()
self.browser_vbox = gtk.VBox()
self.browser_win.add(self.browser_vbox)
self.browser_vbox.show()
self.browser_vbox.pack_start(self.menubar, expand=False)
self.browser_vbox.pack_start(self.toolbar, expand=False)
if self.toolbar_visible:
self.toolbar.show()
else:
self.toolbar.hide()
self.action_group.get_action('ToggleToolbar').set_active(self.toolbar_visible)
self.browser_vpane = gtk.VPaned()
self.browser_vpane.show()
self.browser_vbox.pack_start(self.browser_vpane)
self.statusbar = BrowserStatusBar()
# self.statusbar.show_all()
self.browser_vbox.pack_start(self.statusbar, expand=False)
# alert detail
self.alert_detail_scrolled_window = gtk.ScrolledWindow()
self.alert_detail_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.browser_vpane.add2(self.alert_detail_scrolled_window)
view, doc = self.create_htmlview(self.alert_detail_scrolled_window)
doc.connect("link-clicked", self.link_clicked)
self.detail_doc = doc
self.detail_view = view
self.alert_detail_scrolled_window.show_all()
self.popup_menu = self.ui.get_widget('/PopupMenu')
self.button_press_event = SignalConnection('button_press_event')
self.button_press_event.connect(self.detail_view, self.on_button_press_event)
self.visit_radio_action.set_current_value(self.VISIT_AUDIT)
def on_report_bug(self, action):
bug_report_url = get_config('help', 'bug_report_url')
# FIXME - Should be specific to the alert
launch_web_browser_on_url(bug_report_url)
def link_clicked(self, doc, link):
launch_web_browser_on_url(link)
# --- Utilities ---
def mark_delete(self, database, siginfo, value=True):
user_data = siginfo.get_user_data(self.username)
user_data.delete_flag = value
database.set_user_data(siginfo.sig, self.username, 'delete_flag', value)
# --- View Management ---
def show(self):
self.browser_win.present()
def hide(self):
self.browser_win.hide()
def update_window_state(self, state):
if debug:
log_gui.debug("set_window_state() %s:%s",
state, window_state_to_string(state))
if state & gtk.gdk.WINDOW_STATE_ICONIFIED:
self.browser_win.iconify()
else:
self.browser_win.deiconify()
if state & gtk.gdk.WINDOW_STATE_MAXIMIZED:
self.browser_win.maximize()
else:
self.browser_win.unmaximize()
if state & gtk.gdk.WINDOW_STATE_STICKY:
self.browser_win.stick()
if state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
self.browser_win.fullscreen()
else:
self.browser_win.unfullscreen()
if state & gtk.gdk.WINDOW_STATE_ABOVE:
self.browser_win.set_keep_above(True)
else:
self.browser_win.set_keep_above(False)
if state & gtk.gdk.WINDOW_STATE_BELOW:
self.browser_win.set_keep_below(True)
else:
self.browser_win.set_keep_below(False)
if state & gtk.gdk.WINDOW_STATE_WITHDRAWN:
self.browser_win.hide()
else:
self.browser_win.show()
def get_window_state(self):
return window_state_to_string(self.window_state)
def get_foreground_background_colors(self):
style = self.browser_win.get_style()
c = style.fg[gtk.STATE_NORMAL]
foreground_color = "#%.2X%.2X%.2X" % (c.red / 256, c.green / 256, c.blue / 256)
c = style.bg[gtk.STATE_NORMAL]
background_color = "#%.2X%.2X%.2X" % (c.red / 256, c.green / 256, c.blue / 256)
return foreground_color, background_color
def create_htmlview(self, container):
view = gtkhtml2.View()
doc = gtkhtml2.Document()
container.set_hadjustment(view.get_hadjustment())
container.set_vadjustment(view.get_vadjustment())
view.set_document(doc)
container.add(view)
return (view, doc)
def mark_seen(self, database, siginfo):
if self.displayed_siginfo == siginfo:
user_data = siginfo.get_user_data(self.username)
user_data.seen_flag = True
database.set_user_data(siginfo.sig, self.username, 'seen_flag', True)
self.mark_seen_timeout_event_id = None
return False # False ==> do not call again, True ==> call again
def idle_update_alert_view(self):
if debug:
log_gui.debug("idle_update_alert_view")
self.update_alert_view()
self.update_alert_view_idle_event_id = None
def update_alert_view(self):
if self.list_view is None: return
n_selected_paths = self.list_view.selection.count_selected_rows()
if n_selected_paths != 1:
if debug:
log_gui.debug("update_alert_view: %d selected paths, not single selection, clearing alert view",
n_selected_paths)
self.clear_alert_view()
return
siginfo = self.list_view.get_selected_siginfos()[0]
if debug:
log_gui.debug("update_alert_view: siginfo=%s", siginfo.local_id)
self.detail_doc.clear()
self.detail_doc.open_stream("text/html")
foreground_color, background_color = self.get_foreground_background_colors()
html_body = siginfo.format_html(foreground_color, background_color)
html_doc = html_document(html_body)
if debug:
#log_gui.debug(html_doc)
pass
self.detail_doc.write_stream(html_doc)
self.detail_doc.close_stream()
if self.displayed_siginfo != siginfo:
# Displaying different alert than previously.
# If we had a timeout pending from the previous alert then cancel it.
if self.mark_seen_timeout_event_id is not None:
gobject.source_remove(self.mark_seen_timeout_event_id)
self.mark_seen_timeout_event_id = None
# If this alert has not been seen start a timer to mark it seen
user_data = siginfo.get_user_data(self.username)
if not user_data.seen_flag:
self.mark_seen_timeout_event_id = \
gobject.timeout_add(1000*self.seen_after_displayed_seconds,
self.mark_seen, self.list_view.alert_data.database, siginfo)
self.displayed_siginfo = siginfo
# --- Data/View Management ---
def set_visit_data(self, name, view_data):
self.view_data_collection[name] = view_data
def get_visit_data(self, name):
return self.view_data_collection.get(name)
def clear_alert_view(self):
self.detail_doc.clear()
self.alert_detail_scrolled_window.queue_draw()
def do_visit(self, name):
if debug:
log_gui.debug("do_visit: %s", name)
visit_data = self.get_visit_data(name)
self.set_view_data(visit_data)
def set_view_data(self, view_data):
if debug:
log_gui.debug("set_view_data: %s", view_data.name)
self.selection_changed_signal.disconnect()
previous = self.browser_vpane.get_child1()
if previous is not None:
self.browser_vpane.remove(previous)
self.list_view = view_data.list_view
self.browser_vpane.add1(self.list_view.scrolled_window)
self.properties_changed_signal.connect(self.list_view, self.on_view_data_properties_changed)
self.load_data_signal.connect (self.list_view, self.on_load_data)
self.alert_list_changed_signal.connect(self.list_view, self.on_alert_list_changed)
self.action_group.get_action('HideDeleted').set_active(self.list_view.hide_deleted)
self.action_group.get_action('HideQuiet').set_active(self.list_view.hide_quiet)
# Column Visibility
for column in range(1, NUM_COLUMNS):
column_name = tv_column_info[column]['name']
tv_column = self.list_view.tv_columns[column]
self.action_group.get_action('Toggle%sColumn' % column_name).set_active(tv_column.get_visible())
self.selection_changed_signal.connect(self.list_view.selection, self.on_selection_changed)
self.update_alert_view()
self.update_alert_count()
self.update_visit_message(view_data.alert_data.database_properties.friendly_name)
def update_visit_message(self, name):
if name is None:
self.statusbar.set_visit_message(_('None'))
else:
self.statusbar.set_visit_message(name)
def update_alert_count(self):
if self.list_view is None or self.list_view.view_model is None:
text = '--/--'
else:
text = '%d/%d' % (len(self.list_view.base_model), len(self.list_view.view_model))
self.statusbar.alert_count.set_text(text)
# --- Selections ---
def idle_restore_selection(self):
if self.list_view is None: return
self.list_view.restore_selection()
self.restore_selection_idle_event_id = None
def on_selection_changed(self, selection):
if self.list_view is None: return
model, selected_paths = selection.get_selected_rows()
if debug:
log_gui.debug("on_selection_changed(): paths=%s", ','.join([str(row) for row in selected_paths]))
if len(selected_paths) > 0:
self.list_view.last_selected_row = selected_paths[0]
else:
self.list_view.last_selected_row = 0
if self.update_alert_view_idle_event_id is None:
self.update_alert_view_idle_event_id = gobject.idle_add(self.idle_update_alert_view)
# --- Data Handling ---
def on_alert_list_changed(self, alert_list, model, type, iter):
if debug:
log_gui_data.debug("on_alert_list_changed: %s", type)
if type == 'add':
self.update_alert_count()
elif type == 'delete':
self.update_alert_count()
elif type == 'modify':
# If the row which changed is what is currently being displayed
# in the alert detail window then update the alert view.
siginfo = model.get_value(iter, PYOBJECT_COLUMN)
if self.displayed_siginfo is None or self.displayed_siginfo.local_id == siginfo.local_id:
self.update_alert_view()
else:
raise ProgramError(ERR_UNKNOWN_VALUE, "on_alert_list_changed: type = %s not recognized" % type)
def on_view_data_properties_changed(self, list_view, alert_data, properties):
if debug:
log_gui.debug("on_view_data_properties_changed: %s %s", alert_data.name, properties)
self.update_visit_message(properties.friendly_name)
def progress_pulse(self):
if self.load_in_progress:
self.statusbar.progress.pulse()
return True # call again
else:
return False # do not call again
def on_load_data(self, list_view, alert_data, state, errno, strerror):
if debug:
log_gui.debug('on_load_data: alert_data=%s state=%s errno=%d strerror=%s',
alert_data.name, state, errno, strerror)
if state == 'start':
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('loading data ...'), message_name='load_data')
self.load_in_progress = True
self.statusbar.progress.pulse()
gobject.timeout_add(100, self.progress_pulse)
self.statusbar.progress.set_text(_("Load %s" % alert_data.name))
elif state == 'end':
self.load_in_progress = False
if self.list_view is not None: self.list_view.restore_selection()
self.statusbar.progress.set_text("")
self.statusbar.progress.set_fraction(0)
if errno == 0:
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('loading data done'), 15, message_name='load_data')
else:
self.statusbar.status.remove_message_by_name('load_data')
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.MEDIUM_PRIORITY, _("load data failed: %s") % (strerror), 15, message_name='load_data')
self.update_alert_count()
def save_siginfos(self, file, siginfos):
text = ''
try:
fd=open(file, "w")
except IOError, e:
raise ProgramError(ERR_FILE_OPEN, detail="%s, %s" % (filepath, e.strerror))
i = 0
for siginfo in siginfos:
text += siginfo.format_text()
i += 1
if i < len(siginfos):
text += "\f\n" # break between multiple siginfo's
fd.write(text)
fd.close()
# --- Event Handlers ---
def on_button_press_event(self, treeview, event):
if debug:
log_gui.debug("on_button_press_event(): button=%s", event.button)
if event.button == 3:
self.popup_menu.popup( None, None, None, event.button, event.time)
return True
def on_copy(self, action):
text = gtkhtml2.html_selection_get_text(self.detail_view)
if debug:
log_gui.debug("on_copy(): text=%s", text)
if text is not None:
self.clipboard.set_text(text)
return True # return True ==> handled, False ==> propagate
def on_copy_alert(self, action):
if self.list_view is None: return
text = ''
siginfos = self.list_view.get_selected_siginfos()
i = 0
for siginfo in siginfos:
text += siginfo.format_text()
i += 1
if i < len(siginfos):
text += "\n" # break between multiple siginfo's
self.clipboard.set_text(text)
return True # return True ==> handled, False ==> propagate
def on_close(self, *args):
if self.window_delete_hides:
self.hide()
return True
else:
gtk.main_quit()
return True # return True ==> handled, False ==> propagate
def on_connection_pending_retry(self, retry, seconds_pending, alert_client):
if seconds_pending <= 0.005:
self.statusbar.set_connected_state(False)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('retrying connection'), message_name='pending_retry')
else:
minutes = int(seconds_pending) / 60
seconds = seconds_pending % 60
if minutes:
pending = _("%d minutes %.1f seconds") % (minutes, seconds)
else:
pending = _("%.0f seconds") % (seconds)
self.statusbar.set_connected_state(False)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('retrying connection in %s' % pending), message_name='pending_retry')
def on_user_help(self, action):
help_url = get_config('help','help_url')
launch_web_browser_on_url(help_url)
def on_about(self, action):
pkg_name = get_config('general', 'pkg_name')
pkg_version = get_config('general', 'pkg_version')
project_url = get_config('general', 'project_url')
def email_clicked(dialog, addr, user_data=None):
launch_web_browser_on_url('mailto:' + addr)
def link_clicked(dialog, url, user_data=None):
launch_web_browser_on_url(url)
gtk.about_dialog_set_email_hook(email_clicked)
gtk.about_dialog_set_url_hook(link_clicked)
dlg = gtk.AboutDialog()
dlg.set_name(pkg_name)
dlg.set_version(pkg_version)
dlg.set_copyright(_("Copyright 2006-2007 Red Hat, Inc."))
dlg.set_comments(_("A user friendly tool to diagnose and monitor SELinux AVC denials"))
dlg.set_license("GPL v2")
dlg.set_website(project_url)
dlg.set_authors(["John Dennis <jdennis@redhat.com>",
"Daniel Walsh <dwalsh@redhat.com>",
"Karl MacMillon <kmacmil@redhat.com>"])
icon_name = get_config('general','icon_name')
dlg.set_logo_icon_name(icon_name)
dlg.run()
dlg.destroy()
def on_save_as(self, action):
dialog = gtk.FileChooserDialog(_("Save.."),
None,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
if self.default_save_folder is not None:
dialog.set_current_folder(self.default_save_folder)
if self.default_save_filename is not None:
dialog.set_current_name(self.default_save_filename)
filter = gtk.FileFilter()
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.default_save_folder = dialog.get_current_folder()
self.default_save_filename = os.path.basename(dialog.get_filename())
try:
if self.list_view is not None:
self.save_siginfos(dialog.get_filename(),
self.list_view.get_selected_siginfos())
except ProgramError, e:
self.display_error(e.strerror)
dialog.destroy()
def on_connect_to(self, action):
connection = None
dlg = OpenConnectionDialog()
response = dlg.run()
if response == gtk.RESPONSE_OK:
connection = dlg.get_connection()
dlg.destroy()
if debug:
log_communication.debug("on_connect_to: %s", connection)
# FIXME: we need a more centralized way to handle retry's
self.statusbar.status.remove_message_by_name('pending_retry')
self.server.retry_connection_if_closed = False
if self.server.connection_state.flags & ConnectionState.RETRY:
self.server.connection_retry.stop()
self.server.connection_state.update(0, ConnectionState.RETRY)
self.server.close_connection()
if connection is None:
self.statusbar.error.remove_all_messages()
else:
self.server.retry_connection_if_closed = False
self.server.open(connection)
else:
dlg.destroy()
def on_open_logfile(self, action):
dlg = ScanLogfileDialog()
response = dlg.run()
if response == gtk.RESPONSE_OK:
database = dlg.get_database()
database_rpc = SETroubleshootDatabaseLocal(database)
alert_data = AlertData('logfile', database_rpc)
# FIXME: Is there a better way to populate the model?
for siginfo in database.sigs.signature_list:
alert_data.signatures_updated(database_rpc, 'add', siginfo.local_id)
self.logfile_view_data.bind_data(alert_data)
self.action_group.get_action('ViewLogfile').set_sensitive(True)
self.visit_radio_action.set_current_value(self.VISIT_LOGFILE)
if self.restore_selection_idle_event_id is None:
self.restore_selection_idle_event_id = gobject.idle_add(self.idle_restore_selection)
dlg.destroy()
def on_edit_email_alert_list(self, action):
def query_email_recipients_callback(recipients):
if debug:
log_gui.debug("query_email_recipients_callback: %s", recipients)
dlg = EmailDialog(recipients, parent=self.browser_win)
new_recipients = dlg.run()
if debug:
log_gui.debug("EmailDialog returned: %s", str(new_recipients))
if new_recipients is not None:
self.server.set_email_recipients(new_recipients)
async_rpc = self.server.query_email_recipients()
async_rpc.add_callback(query_email_recipients_callback)
def on_print(self, action):
def draw_page(operation, context, page_number, siginfos):
try:
siginfo = siginfos[page_number]
except IndexError:
return
text = siginfo.format_text()
cr = context.get_cairo_context()
layout = context.create_pango_layout()
layout.set_font_description(pango.FontDescription('monospace 10'))
layout.set_text(text)
cr.show_layout(layout)
if self.list_view is not None:
siginfos = self.list_view.get_selected_siginfos()
else:
siginfos = []
dialog = gtk.PrintOperation()
if self.print_settings != None:
dialog.set_print_settings(self.print_settings)
dialog.connect("draw_page", draw_page, siginfos)
dialog.set_n_pages(len(siginfos))
res = dialog.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG)
if res == gtk.PRINT_OPERATION_RESULT_ERROR:
error_dialog = gtk.MessageDialog(self.browser_win, gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
"Error printing file:\n")
error_dialog.connect("response", lambda w,id: w.destroy())
error_dialog.show()
elif res == gtk.PRINT_OPERATION_RESULT_APPLY:
self.print_settings = dialog.get_print_settings()
return True # return True ==> handled, False ==> propagate
def on_hide_deleted(self, action):
if self.list_view is None: return
self.list_view.hide_deleted = action.get_active()
if debug:
log_gui.debug("on_hide_deleted() hide_deleted=%s", self.list_view.hide_deleted)
if self.list_view.filter_model is not None:
self.list_view.filter_model.refilter()
def on_hide_quiet(self, action):
if self.list_view is None: return
self.list_view.hide_quiet = action.get_active()
if debug:
log_gui.debug("on_hide_quiet() hide_quiet=%s", self.list_view.hide_quiet)
if self.list_view.filter_model is not None:
self.list_view.filter_model.refilter()
def on_visit_change(self, action, current):
visit_name = self.map_visit_num_to_name[action.get_current_value()]
if debug:
log_gui.debug("on_visit_change: %s", visit_name)
self.do_visit(visit_name)
def on_delete(self, action):
if self.list_view is None: return
database = self.list_view.alert_data.database
for siginfo in self.list_view.get_selected_siginfos():
self.mark_delete(database, siginfo, True)
return True # return True ==> handled, False ==> propagate
def on_expunge(self, action):
if self.list_view is None: return
if self.list_view.alert_data is None: return
database = self.list_view.alert_data.database
for siginfo in self.list_view.alert_data.get_siginfos():
user_data = siginfo.get_user_data(self.username)
if user_data.delete_flag:
database.delete_signature(siginfo.sig)
def on_undelete(self, action):
if self.list_view is None: return
database = self.list_view.alert_data.database
for siginfo in self.list_view.get_selected_siginfos():
self.mark_delete(database, siginfo, False)
return True # return True ==> handled, False ==> propagate
def on_select_all(self, action):
if self.list_view is None: return
self.list_view.selection.select_all()
return True # return True ==> handled, False ==> propagate
def on_select_none(self, action):
if self.list_view is None: return
self.list_view.selection.unselect_all()
return True # return True ==> handled, False ==> propagate
def on_toggle_toolbar(self, action):
self.toolbar_visible = action.get_active()
if debug:
log_gui.debug("toggle_toolbar: %s", ('hide', 'show')[self.toolbar_visible])
if self.toolbar_visible:
self.toolbar.show()
else:
self.toolbar.hide()
def on_toggle_column_visibility(self, action, column):
if self.list_view is None: return
tv_column = self.list_view.tv_columns[column]
is_active = action.get_active()
is_visible = tv_column.get_visible()
if debug:
log_gui.debug("toggle_column_visibility(%s): should be active=%s, currently is visible=%s",
tv_column.get_title(), is_active, is_visible)
tv_column.set_visible(is_active)
# --- Interaction Dialogs ---
def display_error(self, message):
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.MEDIUM_PRIORITY, message, 15)
return display_error(message)
# --- Status Notification ---
def on_async_error(self, alert_data, method, errno, strerror):
message_name = None
time_to_live = 15
if errno in [ERR_USER_PROHIBITED, ERR_NOT_AUTHENTICATED]:
message_name ='not_authenticated'
time_to_live = None
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.MEDIUM_PRIORITY, strerror, time_to_live, message_name)
def update_connection_state(self, connection, connection_state):
if debug:
log_gui.debug("update_connection_state: state=%s", connection_state)
if connection_state.flags & ConnectionState.OPEN:
self.statusbar.set_connected_state(True)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connected'), 15, message_name='connection_state')
self.statusbar.error.remove_all_messages()
if not (connection_state.flags & ConnectionState.AUTHENTICATED):
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('open but not logged on'), 15, message_name='connection_state')
else:
self.statusbar.set_connected_state(False)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connection closed'), 15, message_name='connection_state')
if connection_state.flags & ConnectionState.ERROR:
self.statusbar.set_connected_state(False)
errno, strerror = connection_state.get_result()
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.HIGH_PRIORITY, _('connection failed at %s, %s') % (connection.socket_address.get_friendly_name(), strerror), message_name='connection_state')
if connection_state.flags & ConnectionState.HUP:
self.statusbar.set_connected_state(False)
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.HIGH_PRIORITY, _('connection lost to %s') % (connection.socket_address.get_friendly_name()), message_name='connection_state')
if connection_state.flags & ConnectionState.TIMEOUT:
self.statusbar.set_connected_state(False)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connection timed out'), 15, message_name='connection_state')
if not (connection_state.flags & ConnectionState.RETRY):
self.statusbar.status.remove_message_by_name('pending_retry')
def on_connection_state_change(self, connection, connection_state, flags, flags_added, flags_removed):
if debug:
log_program.debug("%s.on_connection_state_change: connection_state=%s flags_added=%s flags_removed=%s address=%s",
self.__class__.__name__, connection_state,
connection_state.flags_to_string(flags_added), connection_state.flags_to_string(flags_removed),
connection.socket_address)
if flags_removed & ConnectionState.OPEN:
self.audit_data.clear()
self.audit_data.set_properties(None)
self.statusbar.set_connected_state(False)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connection closed'), 15, message_name='connection_state')
if flags_added & ConnectionState.OPEN:
self.statusbar.set_connected_state(True)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connected'), 15, message_name='connection_state')
self.statusbar.error.remove_all_messages()
if flags_added & ConnectionState.AUTHENTICATED:
self.audit_data.clear()
self.audit_data.load_data()
self.statusbar.set_connected_state(True)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('authenticated'), 15, message_name='connection_state')
if flags_added & ConnectionState.TIMEOUT:
self.statusbar.set_connected_state(True)
self.statusbar.status.new_message(StatusMessage.INFO_TYPE, StatusMessage.MEDIUM_PRIORITY, _('connection timed out'), 15, message_name='connection_state')
if flags_added & ConnectionState.HUP:
self.statusbar.set_connected_state(False)
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.HIGH_PRIORITY, _('connection lost to %s)') % (connection.socket_address.get_friendly_name()), message_name='connection_state')
if flags_added & (ConnectionState.ERROR):
errno, strerror = connection_state.get_result()
self.statusbar.set_connected_state(False)
self.statusbar.error.new_message(StatusMessage.ERROR_TYPE, StatusMessage.HIGH_PRIORITY, _('connection failed at %s, %s') % (connection.socket_address.get_friendly_name(), strerror), message_name='connection_state')
if flags_removed & ConnectionState.RETRY:
self.statusbar.status.remove_message_by_name('pending_retry')
#------------------------------------------------------------------------------
class OpenConnectionDialog(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, title=_('Open Connection'),
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
self.connection_choice = None
table = gtk.Table(3, 2)
self.vbox.pack_start(table)
self.disconnect_radiobutton = gtk.RadioButton(None, _('No Connection'))
self.disconnect_radiobutton.connect('toggled', self.on_connection_choice_changed, 'disconnect')
table.attach(self.disconnect_radiobutton, left_attach=0, right_attach=1, top_attach=0, bottom_attach=1)
radiobutton_group = self.disconnect_radiobutton
self.local_radiobutton = gtk.RadioButton(radiobutton_group, _('Local Server'))
self.local_radiobutton.connect('toggled', self.on_connection_choice_changed, 'local')
table.attach(self.local_radiobutton, left_attach=0, right_attach=1, top_attach=1, bottom_attach=2)
self.remote_radiobutton = gtk.RadioButton(radiobutton_group, _('Remote Server'))
self.remote_radiobutton.connect('toggled', self.on_connection_choice_changed, 'remote')
table.attach(self.remote_radiobutton, left_attach=0, right_attach=1, top_attach=2, bottom_attach=3)
self.remote_addr_entry = gtk.Entry()
self.remote_addr_entry.connect('activate', self.on_remote_addr_activate)
self.remote_addr_entry.set_sensitive(False)
table.attach(self.remote_addr_entry, left_attach=1, right_attach=2, top_attach=2, bottom_attach=3)
self.on_connection_choice_changed(radiobutton_group, 'local')
self.vbox.show_all()
self.local_radiobutton.set_active(True)
def on_connection_choice_changed(self, radiobutton, choice):
if radiobutton.get_active():
self.connection_choice = choice
log_gui.debug("on_connection_choice_changed: %s", self.connection_choice)
if self.connection_choice == 'remote':
self.remote_addr_entry.set_sensitive(True)
else:
self.remote_addr_entry.set_sensitive(False)
def on_remote_addr_activate(self, widget):
self.emit('response', gtk.RESPONSE_OK)
def get_connection_choice(self):
group = self.local_radiobutton.get_group()
for radiobutton in group:
if radiobutton.get_active():
return radiobutton
return None
def get_connection(self):
server_address = None
cfg_section = 'client_connect_to'
if self.connection_choice == 'local':
server_address = get_local_server_socket_address()
elif self.connection_choice == 'remote':
remote_addr = self.remote_addr_entry.get_text().strip()
server_address = SocketAddress('inet', remote_addr)
elif self.connection_choice == 'disconnect':
return None
return server_address
#-----------------------------------------------------------------------------
# -- Main --
if __name__ == "__main__":
import getopt
def usage():
print '''
-h help
'''
try:
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
browser_applet = BrowserApplet()
browser_applet.show()
gtk.main()