def delete_profile(self, widget=None, extra=None): if self.deletable: dialog = HIGDialog(buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>'+_("Deleting Profile")+'</b>') text = HIGEntryLabel(_('Your profile is going to be deleted! Click\ Ok to continue, or Cancel to go back to Profile Editor.')) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CANCEL: return True self.profile.remove_profile(self.profile_name) self.update_profile_entry() self.destroy()
class ProfileEditor(HIGWindow): def __init__(self, command=None, profile_name=None, deletable=True, overwrite=False): HIGWindow.__init__(self) self.connect("delete_event", self.exit) self.set_title(_('Profile Editor')) self.set_position(gtk.WIN_POS_CENTER) self.deletable = deletable self.profile_name = profile_name self.overwrite = overwrite # Used to block recursive updating of the command entry when the # command entry causes the OptionBuilder widgets to change. self.inhibit_command_update = False self.__create_widgets() self.__pack_widgets() self.profile = CommandProfile() self.ops = NmapOptions() if profile_name: log.debug("Showing profile %s" % profile_name) prof = self.profile.get_profile(profile_name) # Interface settings self.profile_name_entry.set_text(profile_name) self.profile_description_text.get_buffer().set_text( prof['description']) command_string = prof['command'] self.ops.parse_string(command_string) if command: self.ops.parse_string(command) self.option_builder = OptionBuilder( Path.profile_editor, self.ops, self.update_command, self.help_field.get_buffer()) log.debug("Option groups: %s" % str(self.option_builder.groups)) log.debug("Option section names: %s" % str( self.option_builder.section_names)) #log.debug("Option tabs: %s" % str(self.option_builder.tabs)) for tab in self.option_builder.groups: self.__create_tab( _(tab), _(self.option_builder.section_names[tab]), self.option_builder.tabs[tab]) self.update_command() def command_entry_changed_cb(self, widget): command_string = self.command_entry.get_text().decode("UTF-8") self.ops.parse_string(command_string) self.inhibit_command_update = True self.option_builder.update() self.inhibit_command_update = False def update_command(self): """Regenerate and display the command.""" if not self.inhibit_command_update: # Block recursive updating of the OptionBuilder widgets when they # cause a change in the command entry. self.command_entry.handler_block(self.command_entry_changed_cb_id) self.command_entry.set_text(self.ops.render_string()) self.command_entry.handler_unblock( self.command_entry_changed_cb_id) def update_help_name(self, widget, extra): self.help_field.get_buffer().set_text( "Profile name\n\nThis is how the profile will be identified " "in the drop-down combo box in the scan tab.") def update_help_desc(self, widget, extra): self.help_field.get_buffer().set_text( "Description\n\nThe description is a full description of what " "the scan does, which may be long.") def __create_widgets(self): ### # Vertical box to keep 3 boxes self.main_whole_box = HIGVBox() self.upper_box = HIGHBox() self.middle_box = HIGHBox() self.lower_box = HIGHBox() #self.main_vbox = HIGVBox() self.command_entry = gtk.Entry() self.command_entry_changed_cb_id = self.command_entry.connect( "changed", self.command_entry_changed_cb) self.scan_button = HIGButton(_("Scan")) self.scan_button.connect("clicked", self.run_scan) self.notebook = gtk.Notebook() # Profile info page self.profile_info_vbox = HIGVBox() self.profile_info_label = HIGSectionLabel(_('Profile Information')) self.profile_name_label = HIGEntryLabel(_('Profile name')) self.profile_name_entry = gtk.Entry() self.profile_name_entry.connect( 'enter-notify-event', self.update_help_name) self.profile_description_label = HIGEntryLabel(_('Description')) self.profile_description_scroll = HIGScrolledWindow() self.profile_description_scroll.set_border_width(0) self.profile_description_text = HIGTextView() self.profile_description_text.connect( 'motion-notify-event', self.update_help_desc) # Buttons self.buttons_hbox = HIGHBox() self.cancel_button = HIGButton(stock=gtk.STOCK_CANCEL) self.cancel_button.connect('clicked', self.exit) self.delete_button = HIGButton(stock=gtk.STOCK_DELETE) self.delete_button.connect('clicked', self.delete_profile) self.save_button = HIGButton(_("Save Changes"), stock=gtk.STOCK_SAVE) self.save_button.connect('clicked', self.save_profile) ### self.help_vbox = HIGVBox() self.help_label = HIGSectionLabel(_('Help')) self.help_scroll = HIGScrolledWindow() self.help_scroll.set_border_width(0) self.help_field = HIGTextView() self.help_field.set_cursor_visible(False) self.help_field.set_left_margin(5) self.help_field.set_editable(False) self.help_vbox.set_size_request(200, -1) ### def __pack_widgets(self): ### self.add(self.main_whole_box) # Packing command entry to upper box self.upper_box._pack_expand_fill(self.command_entry) self.upper_box._pack_noexpand_nofill(self.scan_button) # Packing notebook (left) and help box (right) to middle box self.middle_box._pack_expand_fill(self.notebook) self.middle_box._pack_expand_fill(self.help_vbox) # Packing buttons to lower box self.lower_box.pack_end(self.buttons_hbox) # Packing the three vertical boxes to the main box self.main_whole_box._pack_noexpand_nofill(self.upper_box) self.main_whole_box._pack_expand_fill(self.middle_box) self.main_whole_box._pack_noexpand_nofill(self.lower_box) ### # Packing profile information tab on notebook self.notebook.append_page( self.profile_info_vbox, gtk.Label(_('Profile'))) self.profile_info_vbox.set_border_width(5) table = HIGTable() self.profile_info_vbox._pack_noexpand_nofill(self.profile_info_label) self.profile_info_vbox._pack_expand_fill(HIGSpacer(table)) self.profile_description_scroll.add(self.profile_description_text) vbox_desc = HIGVBox() vbox_desc._pack_noexpand_nofill(self.profile_description_label) vbox_desc._pack_expand_fill(hig_box_space_holder()) vbox_ann = HIGVBox() vbox_ann._pack_expand_fill(hig_box_space_holder()) table.attach( self.profile_name_label, 0, 1, 0, 1, xoptions=0, yoptions=0) table.attach(self.profile_name_entry, 1, 2, 0, 1, yoptions=0) table.attach(vbox_desc, 0, 1, 1, 2, xoptions=0) table.attach(self.profile_description_scroll, 1, 2, 1, 2) # Packing buttons on button_hbox self.buttons_hbox._pack_expand_fill(hig_box_space_holder()) if self.deletable: self.buttons_hbox._pack_noexpand_nofill(self.delete_button) self.buttons_hbox._pack_noexpand_nofill(self.cancel_button) self.buttons_hbox._pack_noexpand_nofill(self.save_button) self.buttons_hbox.set_border_width(5) self.buttons_hbox.set_spacing(6) ### self.help_vbox._pack_noexpand_nofill(self.help_label) self.help_vbox._pack_expand_fill(self.help_scroll) self.help_scroll.add(self.help_field) self.help_vbox.set_border_width(1) self.help_vbox.set_spacing(1) ### def __create_tab(self, tab_name, section_name, tab): log.debug(">>> Tab name: %s" % tab_name) log.debug(">>>Creating profile editor section: %s" % section_name) vbox = HIGVBox() if tab.notscripttab: # if notscripttab is set table = HIGTable() table.set_row_spacings(2) section = HIGSectionLabel(section_name) vbox._pack_noexpand_nofill(section) vbox._pack_noexpand_nofill(HIGSpacer(table)) vbox.set_border_width(5) tab.fill_table(table, True) else: hbox = tab.get_hmain_box() vbox.pack_start(hbox, True, True, 0) self.notebook.append_page(vbox, gtk.Label(tab_name)) def save_profile(self, widget): if self.overwrite: self.profile.remove_profile(self.profile_name) profile_name = self.profile_name_entry.get_text() if profile_name == '': alert = HIGAlertDialog( message_format=_('Unnamed profile'), secondary_text=_( 'You must provide a name for this profile.')) alert.run() alert.destroy() self.profile_name_entry.grab_focus() return None command = self.ops.render_string() buf = self.profile_description_text.get_buffer() description = buf.get_text( buf.get_start_iter(), buf.get_end_iter()) try: self.profile.add_profile( profile_name, command=command, description=description) except ValueError: alert = HIGAlertDialog( message_format=_('Disallowed profile name'), secondary_text=_('Sorry, the name "%s" is not allowed due ' 'to technical limitations. (The underlying ' 'ConfigParser used to store profiles does not allow ' 'it.) Choose a different name.' % profile_name)) alert.run() alert.destroy() return self.scan_interface.toolbar.profile_entry.update() self.destroy() def clean_profile_info(self): self.profile_name_entry.set_text('') self.profile_description_text.get_buffer().set_text('') def set_scan_interface(self, interface): self.scan_interface = interface def exit(self, *args): self.destroy() def delete_profile(self, widget=None, extra=None): if self.deletable: dialog = HIGDialog(buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>' + _("Deleting Profile") + '</b>') text = HIGEntryLabel(_( 'Your profile is going to be deleted! ClickOk to continue, ' 'or Cancel to go back to Profile Editor.')) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock( gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CANCEL: return True self.profile.remove_profile(self.profile_name) self.update_profile_entry() self.destroy() def run_scan(self, widget=None): command_string = self.command_entry.get_text().decode("UTF-8") self.scan_interface.command_toolbar.command = command_string self.scan_interface.start_scan_cb() self.exit() def update_profile_entry(self, widget=None, extra=None): self.scan_interface.toolbar.profile_entry.update() list = self.scan_interface.toolbar.profile_entry.get_model() length = len(list) if length > 0: self.scan_interface.toolbar.profile_entry.set_active(0)
class ProfileEditor(HIGWindow): def __init__(self, command=None, profile_name=None, deletable=True, overwrite=False): HIGWindow.__init__(self) self.connect("delete_event", self.exit) self.set_title(_('Profile Editor')) self.set_position(gtk.WIN_POS_CENTER) self.deletable = deletable self.profile_name = profile_name self.overwrite = overwrite # Used to block recursive updating of the command entry when the command # entry causes the OptionBuilder widgets to change. self.inhibit_command_update = False self.__create_widgets() self.__pack_widgets() self.profile = CommandProfile() self.ops = NmapOptions() if profile_name: log.debug("Showing profile %s" % profile_name) prof = self.profile.get_profile(profile_name) # Interface settings self.profile_name_entry.set_text(profile_name) self.profile_description_text.get_buffer().set_text(prof['description']) command_string = prof['command'] self.ops.parse_string(command_string) if command: self.ops.parse_string(command) self.option_builder = OptionBuilder(Path.profile_editor, self.ops, self.update_command, self.help_field.get_buffer()) log.debug("Option groups: %s" % str(self.option_builder.groups)) log.debug("Option section names: %s" % str(self.option_builder.section_names)) #log.debug("Option tabs: %s" % str(self.option_builder.tabs)) for tab in self.option_builder.groups: self.__create_tab(_(tab), _(self.option_builder.section_names[tab]), self.option_builder.tabs[tab]) self.update_command() def command_entry_changed_cb(self, widget): command_string = self.command_entry.get_text().decode("UTF-8") self.ops.parse_string(command_string) self.inhibit_command_update = True self.option_builder.update() self.inhibit_command_update = False def update_command(self): """Regenerate and display the command.""" if not self.inhibit_command_update: # Block recursive updating of the OptionBuilder widgets when they # cause a change in the command entry. self.command_entry.handler_block(self.command_entry_changed_cb_id) self.command_entry.set_text(self.ops.render_string()) self.command_entry.handler_unblock(self.command_entry_changed_cb_id) def update_help_name(self, widget, extra): self.help_field.get_buffer().set_text("Profile name\n\nThis is how the" +" profile will be identified in the drop-down combo box in the" +" scan tab.") def update_help_desc(self, widget, extra): self.help_field.get_buffer().set_text("Description\n\nThe description is a" + " full description of what the scan does, which may be long.") def __create_widgets(self): ### # Vertical box to keep 3 boxes self.main_whole_box = HIGVBox() self.upper_box = HIGHBox() self.middle_box = HIGHBox() self.lower_box = HIGHBox() #self.main_vbox = HIGVBox() self.command_entry = gtk.Entry() self.command_entry_changed_cb_id = \ self.command_entry.connect("changed", self.command_entry_changed_cb) self.scan_button = HIGButton(_("Scan")) self.scan_button.connect("clicked", self.run_scan) self.notebook = gtk.Notebook() # Profile info page self.profile_info_vbox = HIGVBox() self.profile_info_label = HIGSectionLabel(_('Profile Information')) self.profile_name_label = HIGEntryLabel(_('Profile name')) self.profile_name_entry = gtk.Entry() self.profile_name_entry.connect('enter-notify-event', self.update_help_name) self.profile_description_label = HIGEntryLabel(_('Description')) self.profile_description_scroll = HIGScrolledWindow() self.profile_description_scroll.set_border_width(0) self.profile_description_text = HIGTextView() self.profile_description_text.connect('motion-notify-event', self.update_help_desc) # Buttons self.buttons_hbox = HIGHBox() self.cancel_button = HIGButton(stock=gtk.STOCK_CANCEL) self.cancel_button.connect('clicked', self.exit) self.delete_button = HIGButton(stock=gtk.STOCK_DELETE) self.delete_button.connect('clicked', self.delete_profile) self.save_button = HIGButton(_("Save Changes"), stock=gtk.STOCK_SAVE) self.save_button.connect('clicked', self.save_profile) ### self.help_vbox = HIGVBox() self.help_label = HIGSectionLabel(_('Help')) self.help_scroll = HIGScrolledWindow() self.help_scroll.set_border_width(0) self.help_field = HIGTextView() self.help_field.set_cursor_visible(False) self.help_field.set_left_margin(5) self.help_field.set_editable(False) self.help_vbox.set_size_request(200,-1) ### def __pack_widgets(self): ### self.add(self.main_whole_box) # Packing command entry to upper box self.upper_box._pack_expand_fill(self.command_entry) self.upper_box._pack_noexpand_nofill(self.scan_button) # Packing notebook (left) and help box (right) to middle box self.middle_box._pack_expand_fill(self.notebook) self.middle_box._pack_expand_fill(self.help_vbox) # Packing buttons to lower box self.lower_box.pack_end(self.buttons_hbox) # Packing the three vertical boxes to the main box self.main_whole_box._pack_noexpand_nofill(self.upper_box) self.main_whole_box._pack_expand_fill(self.middle_box) self.main_whole_box._pack_noexpand_nofill(self.lower_box) ### # Packing profile information tab on notebook self.notebook.append_page(self.profile_info_vbox, gtk.Label(_('Profile'))) self.profile_info_vbox.set_border_width(5) table = HIGTable() self.profile_info_vbox._pack_noexpand_nofill(self.profile_info_label) self.profile_info_vbox._pack_expand_fill(HIGSpacer(table)) self.profile_description_scroll.add(self.profile_description_text) vbox_desc = HIGVBox() vbox_desc._pack_noexpand_nofill(self.profile_description_label) vbox_desc._pack_expand_fill(hig_box_space_holder()) vbox_ann = HIGVBox() vbox_ann._pack_expand_fill(hig_box_space_holder()) table.attach(self.profile_name_label,0,1,0,1,xoptions=0,yoptions=0) table.attach(self.profile_name_entry,1,2,0,1,yoptions=0) table.attach(vbox_desc,0,1,1,2,xoptions=0) table.attach(self.profile_description_scroll,1,2,1,2) # Packing buttons on button_hbox self.buttons_hbox._pack_expand_fill(hig_box_space_holder()) if self.deletable: self.buttons_hbox._pack_noexpand_nofill(self.delete_button) self.buttons_hbox._pack_noexpand_nofill(self.cancel_button) self.buttons_hbox._pack_noexpand_nofill(self.save_button) self.buttons_hbox.set_border_width(5) self.buttons_hbox.set_spacing(6) ### self.help_vbox._pack_noexpand_nofill(self.help_label) self.help_vbox._pack_expand_fill(self.help_scroll) self.help_scroll.add(self.help_field) self.help_vbox.set_border_width(1) self.help_vbox.set_spacing(1) ### def __create_tab(self, tab_name, section_name, tab): log.debug(">>> Tab name: %s" % tab_name) log.debug(">>>Creating profile editor section: %s" % section_name) vbox = HIGVBox() if tab.notscripttab: # if notscripttab is set table = HIGTable() table.set_row_spacings(2) section = HIGSectionLabel(section_name) vbox._pack_noexpand_nofill(section) vbox._pack_noexpand_nofill(HIGSpacer(table)) vbox.set_border_width(5) tab.fill_table(table, True) else: hbox = tab.get_hmain_box() vbox.pack_start(hbox,True,True,0) self.notebook.append_page(vbox, gtk.Label(tab_name)) def save_profile(self, widget): if self.overwrite: self.profile.remove_profile(self.profile_name) profile_name = self.profile_name_entry.get_text() if profile_name == '': alert = HIGAlertDialog(message_format=_('Unnamed profile'),\ secondary_text=_('You must provide a name \ for this profile.')) alert.run() alert.destroy() self.profile_name_entry.grab_focus() return None command = self.ops.render_string() buf = self.profile_description_text.get_buffer() description = buf.get_text(buf.get_start_iter(),\ buf.get_end_iter()) try: self.profile.add_profile(profile_name,\ command=command,\ description=description) except ValueError: alert = HIGAlertDialog(message_format=_('Disallowed profile name'),\ secondary_text=_('Sorry, the name "%s" \ is not allowed due to technical limitations. (The underlying ConfigParser \ used to store profiles does not allow it.) Choose a different \ name.' % profile_name)) alert.run() alert.destroy() return self.scan_interface.toolbar.profile_entry.update() self.destroy() def clean_profile_info(self): self.profile_name_entry.set_text('') self.profile_description_text.get_buffer().set_text('') def set_scan_interface(self, interface): self.scan_interface = interface def exit(self, *args): self.destroy() def delete_profile(self, widget=None, extra=None): if self.deletable: dialog = HIGDialog(buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>'+_("Deleting Profile")+'</b>') text = HIGEntryLabel(_('Your profile is going to be deleted! Click\ Ok to continue, or Cancel to go back to Profile Editor.')) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CANCEL: return True self.profile.remove_profile(self.profile_name) self.update_profile_entry() self.destroy() def run_scan(self, widget=None): command_string = self.command_entry.get_text().decode("UTF-8") self.scan_interface.command_toolbar.command = command_string self.scan_interface.start_scan_cb() self.exit() def update_profile_entry(self, widget=None, extra=None): self.scan_interface.toolbar.profile_entry.update() list = self.scan_interface.toolbar.profile_entry.get_model() length = len(list) if length >0 : self.scan_interface.toolbar.profile_entry.set_active(0)
def _exit_cb(self, *args): """Closes the window, prompting for confirmation if necessary. If one of the tabs couldn't be closed, the function returns True and doesn't exit the application.""" if self.scan_interface.changed: log.debug("Found changes on closing window") dialog = HIGDialog( buttons=(_('Close anyway').encode('utf-8'), gtk.RESPONSE_CLOSE, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>%s</b>' % _("Unsaved changes")) text = HIGEntryLabel(_("The given scan has unsaved changes.\n" "What do you want to do?")) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock( gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CANCEL: return True search_config = SearchConfig() if search_config.store_results: self.store_result(self.scan_interface) elif self.scan_interface.num_scans_running() > 0: log.debug("Trying to close a window with a running scan") dialog = HIGDialog( buttons=(_('Close anyway').encode('utf-8'), gtk.RESPONSE_CLOSE, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>%s</b>' % _("Trying to close")) text = HIGEntryLabel(_( "The window you are trying to close has a scan running in " "the background.\nWhat do you want to do?")) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock( gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CLOSE: self.scan_interface.kill_all_scans() elif response == gtk.RESPONSE_CANCEL: return True window = WindowConfig() window.x, window.y = self.get_position() window.width, window.height = self.get_size() window.save_changes() if config_parser.failed: alert = HIGAlertDialog( message_format=_("Can't save Zenmap configuration"), # newline before path to help avoid weird line wrapping secondary_text=_( 'An error occurred when saving to\n%s' '\nThe error was: %s.' ) % (Path.user_config_file, config_parser.failed)) alert.run() alert.destroy() self.destroy() return False
class SearchGUI(gtk.VBox, object): """This class is a VBox that holds the search entry field and buttons on top, and the results list on the bottom. The "Cancel" and "Open" buttons are a part of the SearchWindow class, not SearchGUI.""" def __init__(self, search_window): gtk.VBox.__init__(self) self._create_widgets() self._pack_widgets() self._connect_events() # Search options self.options = {} self.options["file_extension"] = search_config.file_extension self.options["directory"] = search_config.directory self.options["search_db"] = search_config.search_db self.parsed_results = {} self._set_result_view() self.id = 0 self.search_window = search_window # The Search* objects are created once per Search Window invocation, so that # they get a list of scans only once, not whenever the search conditions change if self.options["search_db"]: self.search_db = SearchDB() # Search directories can be added via the "dir:" operator, so it needs to be a map self.search_dirs = {} self.init_search_dirs() # We create an empty search dictionary, since SearchParser will fill it # with keywords as it encounters different operators in the search string. self.search_dict = dict() self.search_parser = SearchParser(self) # This list holds the (operator, argument) tuples, parsed from the GUI criteria rows self.gui_criteria_list = [] # Do an initial "empty" search, so that the results window initially holds # all scans in the database self.search_parser.update("") self.start_search() def init_search_dirs(self, dirs=[]): # Start fresh self.search_dirs.clear() # If specified, add the search directory from the Zenmap config file to the map conf_dir = self.options["directory"] if conf_dir: self.search_dirs[conf_dir] = SearchDir( conf_dir, self.options["file_extension"]) # Process any other dirs (as added by the dir: operator) for dir in dirs: self.search_dirs[dir] = SearchDir(dir, self.options["file_extension"]) def _create_widgets(self): # Search box and buttons self.search_top_hbox = HIGHBox() self.search_label = HIGSectionLabel(_("Search:")) self.search_entry = gtk.Entry() self.expressions_btn = HIGToggleButton(_("Expressions "), gtk.STOCK_EDIT) # The quick reference tooltip button self.search_tooltip_btn = HIGButton(" ", gtk.STOCK_INFO) # The expression VBox. This is only visible once the user clicks on "Expressions" self.expr_vbox = gtk.VBox() # Results section self.result_list = gtk.ListStore(str, str, int) # title, date, id self.result_view = gtk.TreeView(self.result_list) self.result_scrolled = gtk.ScrolledWindow() self.result_title_column = gtk.TreeViewColumn(_("Scan")) self.result_date_column = gtk.TreeViewColumn(_("Date")) self.expr_window = None def _pack_widgets(self): # Packing label, search box and buttons self.search_top_hbox.set_spacing(4) self.search_top_hbox.pack_start(self.search_label, False) self.search_top_hbox.pack_start(self.search_entry, True) self.search_top_hbox.pack_start(self.expressions_btn, False) self.search_top_hbox.pack_start(self.search_tooltip_btn, False) # The expressions (if any) should be tightly packed so that they don't take # too much screen real-estate self.expr_vbox.set_spacing(0) # Packing the result section self.result_scrolled.add(self.result_view) self.result_scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # Packing it all together self.set_spacing(4) self.pack_start(self.search_top_hbox, False) self.pack_start(self.expr_vbox, False) self.pack_start(self.result_scrolled, True) def _connect_events(self): self.search_entry.connect("changed", self.update_search_entry) self.search_tooltip_btn.connect("clicked", self.show_quick_help) self.expressions_btn.connect("toggled", self.expressions_clicked) def show_quick_help(self, widget=None, extra=None): quick_help = """Entering the text into the search performs a <b>keyword search</b> - \ the search string is matched against the entire output of each scan. To refine the search, you can use <b>operators</b> to search only within a specific part of \ a scan. Operators can be added to the search interactively if you click on the \ <b>Expressions</b> button, or you can enter them manually into the search field. \ You can also use <b>operator aliases</b> if you're an experienced user who likes to \ type in his searches quickly. <b>profile: (pr:)</b> - Profile used. <b>target: (t:)</b> - User-supplied target, or a rDNS result. <b>option: (o:)</b> - Scan options. <b>date: (d:)</b> - The date when scan was performed. Fuzzy matching is possible using the \ "~" suffix. Each "~" broadens the search by one day on "each side" of the date. In addition, \ it is possible to use the \"date:-n\" notation which means "n days ago". <b>after: (a:)</b> - Matches scans made after the supplied date (<i>YYYY-MM-DD</i> or <i>-n</i>). <b>before (b:)</b> - Matches scans made before the supplied date(<i>YYYY-MM-DD</i> or <i>-n</i>). <b>os:</b> - All OS-related fields. <b>scanned: (sp:)</b> - Matches a port if it was among those scanned. <b>open: (op:)</b> - Open ports discovered in a scan. <b>closed: (cp:)</b> - Closed ports discovered in a scan. <b>filtered: (fp:)</b> - Filtered ports discovered in scan. <b>unfiltered: (ufp:)</b> - Unfiltered ports found in a scan (using, for example, an ACK scan). <b>open|filtered: (ofp:)</b> - Ports in the \"open|filtered\" state. <b>closed|filtered: (cfp:)</b> - Ports in the \"closed|filtered\" state. <b>service: (s:)</b> - All service-related fields. <b>inroute: (ir:)</b> - Matches a router in the scan's traceroute output. """ hint_window = HintWindow(quick_help) hint_window.show_all() def expressions_clicked(self, widget=None, extra=None): if len(self.expr_vbox.get_children() ) == 0 and self.search_entry.get_text() == "": # This is the first time the user has clicked on "Show Expressions" # and the search entry box is empty, so we add a single Criterion row self.expr_vbox.pack_start(Criterion(self)) if self.expressions_btn.get_active(): # The Expressions GUI is about to be displayed. It needs to reflect all the # conditions in the search entry field, so a comparison between the entry field # and the GUI needs to be performed. # Make the search entry field insensitive while expressions are visible self.search_entry.set_sensitive(False) # Get a map of operator => argument from the Expressions GUI so that # we can compare them with the ones in the search entry field gui_ops = {} for criterion in self.expr_vbox.get_children(): if criterion.operator in gui_ops: gui_ops[criterion.operator].append(criterion.argument) else: gui_ops[criterion.operator] = [criterion.argument] # We compare the search entry field to the Expressions GUI. Every # (operator, value) pair must be present in the GUI after this loop is done. for op, args in self.search_dict.iteritems(): for arg in args: if (op not in gui_ops) or (arg not in gui_ops[op]): # We need to add this pair to the GUI self.expr_vbox.pack_start(Criterion(self, op, arg), False) # Now we check if there are any leftover criterion rows that aren't present # in the search_dict (for example, if a user has deleted something from the # search entry field) for criterion in self.expr_vbox.get_children(): if criterion.operator not in self.search_dict or \ criterion.argument not in self.search_dict[criterion.operator]: criterion.destroy() # If we have deleted all rows, add an empty one if len(self.expr_vbox.get_children()) == 0: self.expr_vbox.pack_start(Criterion(self)) # Display all elements self.expr_vbox.show_all() else: # The Expressions GUI is about to be hidden. No updates to the search entry field # are necessary, since it gets updated on every change in one of the criterion rows. self.expr_vbox.hide_all() self.search_entry.set_sensitive(True) def close(self): if self.expr_window != None: self.expr_window.close() def add_criterion(self, caller): # We need to find where the caller (Criteria object) is located among # all the rows, so that we can insert the new row after it caller_index = self.expr_vbox.get_children().index(caller) # Make a new Criteria row and insert it after the calling row criteria = Criterion(self, "keyword") self.expr_vbox.pack_start(criteria, False) self.expr_vbox.reorder_child(criteria, caller_index + 1) criteria.show_all() def remove_criterion(self, c): if len(self.expr_vbox.get_children()) > 1: c.destroy() self.criterion_changed() def criterion_changed(self): # We go through all criteria rows and make a new search string search_string = "" for criterion in self.expr_vbox.get_children(): if criterion.operator != "keyword": search_string += criterion.operator + ":" search_string += criterion.argument.replace(" ", "") + " " self.search_entry.set_text(search_string.strip()) self.search_parser.update(self.search_entry.get_text()) self.start_search() def add_search_dir(self, dir): if dir not in self.search_dirs: self.search_dirs[dir] = SearchDir(dir, self.options["file_extension"]) def update_search_entry(self, widget, extra=None): """Called when the search entry field is modified.""" self.search_parser.update(widget.get_text()) self.start_search() def start_search(self): if not self.options["search_db"] and not self.options["directory"]: d = HIGAlertDialog( message_format=_("No search method selected!"), secondary_text=_("%s can search results on directories or \ inside it's own database. Please, select a method by choosing a directory or by checking \ the search data base option at the 'Search options' tab before start the search" % APP_DISPLAY_NAME)) d.run() d.destroy() return self.clear_result_list() matched = 0 total = 0 if self.options["search_db"]: total += len(self.search_db.get_scan_results()) for result in self.search_db.search(**self.search_dict): self.append_result(result) matched += 1 for search_dir in self.search_dirs.itervalues(): total += len(search_dir.get_scan_results()) for result in search_dir.search(**self.search_dict): self.append_result(result) matched += 1 #total += len(self.search_tabs.get_scan_results()) #for result in self.search_tabs.search(**self.search_dict): # self.append_result(result) # matched += 1 self.search_window.set_label_text("Matched <b>%s</b> out of <b>%s</b> scans." % \ (str(matched), str(total))) def clear_result_list(self): for i in range(len(self.result_list)): iter = self.result_list.get_iter_root() del (self.result_list[iter]) def append_result(self, parsed_result): title = "" if parsed_result.scan_name: title = parsed_result.scan_name elif parsed_result.filename: title = os.path.split(parsed_result.filename)[-1] elif parsed_result.profile_name and parsed_result.target: title = "%s on %s" % (parsed_result.profile_name, parsed_result.target) else: title = parsed_result.get_nmap_command() try: date = localtime(float(parsed_result.start)) #date_field = "%02d %s %04d" % (date[2], months[date[1]][:3], date[0]) date_field = "%04d-%02d-%02d %02d:%02d" % ( date[0], date[1], date[2], date[3], date[4]) except ValueError: date_field = _("Unknown") self.parsed_results[self.id] = [title, parsed_result] self.result_list.append([title, date_field, self.id]) self.id += 1 def get_selected_results(self): selection = self.result_view.get_selection() rows = selection.get_selected_rows() list_store = rows[0] results = {} for row in rows[1]: r = row[0] results[list_store[r][2]] = self.parsed_results[list_store[r][2]] return results def _set_result_view(self): self.result_view.set_enable_search(True) self.result_view.set_search_column(0) selection = self.result_view.get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) self.result_view.append_column(self.result_title_column) self.result_view.append_column(self.result_date_column) self.result_title_column.set_resizable(True) self.result_title_column.set_min_width(200) self.result_date_column.set_resizable(True) self.result_title_column.set_sort_column_id(0) self.result_date_column.set_sort_column_id(1) self.result_title_column.set_reorderable(True) self.result_date_column.set_reorderable(True) cell = gtk.CellRendererText() self.result_title_column.pack_start(cell, True) self.result_date_column.pack_start(cell, True) self.result_title_column.set_attributes(cell, text=0) self.result_date_column.set_attributes(cell, text=1) selected_results = property(get_selected_results)
class ProfileEditor(HIGWindow): def __init__(self, profile_name=None, deletable=True, overwrite=False): HIGWindow.__init__(self) self.connect("delete_event", self.exit) self.set_title(_('Profile Editor')) self.set_position(gtk.WIN_POS_CENTER) self.deletable = deletable self.profile_name = profile_name self.overwrite = overwrite self.__create_widgets() self.__pack_widgets() self.profile = CommandProfile() options_used = {} if profile_name: log.debug("Showing profile %s" % profile_name) prof = self.profile.get_profile(profile_name) options_used = prof['options'] # Interface settings self.profile_name_entry.set_text(profile_name) self.profile_description_text.get_buffer().set_text( prof['description']) self.constructor = CommandConstructor(options_used) ### self.profilehelp = ProfileHelp(options_used) ### self.options = OptionBuilder(Path.profile_editor, self.constructor, self.update_command, self.profilehelp) log.debug("Option groups: %s" % str(self.options.groups)) log.debug("Option section names: %s" % str(self.options.section_names)) #log.debug("Option tabs: %s" % str(self.options.tabs)) for tab in self.options.groups: self.__create_tab(tab, self.options.section_names[tab], self.options.tabs[tab]) self.update_command() def update_command(self): """Regenerate command with target '<target>' and set the value for the command entry""" self.command_entry.set_text(self.constructor.get_command('<target>')) ### whenever the command would be updated, thats when we update the help self.update_help() def update_help(self, text=None): helpText = self.help_field.get_buffer() if text: helpText.set_text(text) else: tempText = "" if self.profilehelp.get_currentstate() == "Default": helpText.set_text(" ") else: tempText += self.profilehelp.get_label() tempText += "\n\n" tempText += self.profilehelp.get_shortdesc() if self.profilehelp.get_example(): tempText += "\n\nExample input:\n" tempText += self.profilehelp.get_example() helpText.set_text(tempText) def update_help_name(self, widget, extra): self.update_help( text="Profile name\n\nThis is how the" + " profile will be identified in the drop-down combo box in the" + " scan tab.") def update_help_desc(self, widget, extra): self.update_help( text="Description\n\nThe description is a" + " full description of what the scan does, which may be long.") def help(self, widget): d = HIGAlertDialog( parent=self, message_format=_("Help not implemented"), secondary_text=_("Profile editor help is not implemented yet.")) d.run() d.destroy() def __create_widgets(self): ### # Vertical box to keep 3 boxes self.main_whole_box = HIGVBox() self.upper_box = HIGVBox() self.middle_box = HIGHBox() self.lower_box = HIGHBox() #self.main_vbox = HIGVBox() self.command_expander = HIGExpander('<b>' + _('Command') + '</b>') self.command_expander.set_expanded(True) self.command_entry = gtk.Entry() self.command_entry.set_editable(False) self.notebook = gtk.Notebook() # Profile info page self.profile_info_vbox = HIGVBox() self.profile_info_label = HIGSectionLabel(_('Profile Information')) self.profile_name_label = HIGEntryLabel(_('Profile name')) self.profile_name_entry = gtk.Entry() self.profile_name_entry.connect('enter-notify-event', self.update_help_name) self.profile_description_label = HIGEntryLabel(_('Description')) self.profile_description_scroll = HIGScrolledWindow() self.profile_description_scroll.set_border_width(0) self.profile_description_text = HIGTextView() self.profile_description_text.connect('motion-notify-event', self.update_help_desc) # Buttons self.buttons_hbox = HIGHBox() self.help_button = HIGButton(stock=gtk.STOCK_HELP) self.help_button.connect('clicked', self.help) self.cancel_button = HIGButton(stock=gtk.STOCK_CANCEL) self.cancel_button.connect('clicked', self.exit) self.delete_button = HIGButton(stock=gtk.STOCK_DELETE) self.delete_button.connect('clicked', self.delete_profile) self.ok_button = HIGButton(stock=gtk.STOCK_OK) self.ok_button.connect('clicked', self.save_profile) ### self.help_vbox = HIGVBox() self.help_label = HIGSectionLabel(_('Help')) self.help_scroll = HIGScrolledWindow() self.help_scroll.set_border_width(0) self.help_field = HIGTextView() self.help_field.set_cursor_visible(False) self.help_field.set_left_margin(5) self.help_field.set_editable(False) self.help_vbox.set_size_request(200, -1) ### def __pack_widgets(self): ### self.add(self.main_whole_box) # Packing command expander to upper box self.upper_box._pack_noexpand_nofill(self.command_expander) # Packing notebook (left) and help box (right) to middle box self.middle_box._pack_expand_fill(self.notebook) self.middle_box._pack_expand_fill(self.help_vbox) # Packing buttons to lower box self.lower_box.pack_end(self.buttons_hbox) # Packing the three vertical boxes to the main box self.main_whole_box._pack_noexpand_nofill(self.upper_box) self.main_whole_box._pack_noexpand_nofill(self.middle_box) self.main_whole_box._pack_noexpand_nofill(self.lower_box) ### # Packing command_entry on command_expander self.command_expander.hbox.pack_start(self.command_entry) # Packing profile information tab on notebook self.notebook.append_page(self.profile_info_vbox, gtk.Label(_('Profile'))) self.profile_info_vbox.set_border_width(5) table = HIGTable() self.profile_info_vbox._pack_noexpand_nofill(self.profile_info_label) self.profile_info_vbox._pack_expand_fill(HIGSpacer(table)) self.profile_description_scroll.add(self.profile_description_text) vbox_desc = HIGVBox() vbox_desc._pack_noexpand_nofill(self.profile_description_label) vbox_desc._pack_expand_fill(hig_box_space_holder()) vbox_ann = HIGVBox() vbox_ann._pack_expand_fill(hig_box_space_holder()) table.attach(self.profile_name_label, 0, 1, 0, 1, xoptions=0, yoptions=0) table.attach(self.profile_name_entry, 1, 2, 0, 1, yoptions=0) table.attach(vbox_desc, 0, 1, 1, 2, xoptions=0) table.attach(self.profile_description_scroll, 1, 2, 1, 2) # Packing buttons on button_hbox #self.buttons_hbox.pack_start(self.help_button) self.buttons_hbox._pack_expand_fill(hig_box_space_holder()) if self.deletable: self.buttons_hbox._pack_noexpand_nofill(self.delete_button) self.buttons_hbox._pack_noexpand_nofill(self.cancel_button) self.buttons_hbox._pack_noexpand_nofill(self.ok_button) self.buttons_hbox.set_border_width(5) self.buttons_hbox.set_spacing(6) ### self.help_vbox._pack_noexpand_nofill(self.help_label) self.help_vbox._pack_expand_fill(self.help_scroll) self.help_scroll.add(self.help_field) self.help_vbox.set_border_width(1) self.help_vbox.set_spacing(1) ### def __create_tab(self, tab_name, section_name, tab): log.debug(">>> Tab name: %s" % tab_name) log.debug(">>>Creating profile editor section: %s" % section_name) vbox = HIGVBox() table = HIGTable() table.set_row_spacings(2) section = HIGSectionLabel(section_name) vbox._pack_noexpand_nofill(section) vbox._pack_noexpand_nofill(HIGSpacer(table)) vbox.set_border_width(5) tab.fill_table(table, True) self.notebook.append_page(vbox, gtk.Label(tab_name)) def save_profile(self, widget): if self.overwrite: self.profile.remove_profile(self.profile_name) profile_name = self.profile_name_entry.get_text() if profile_name == '': alert = HIGAlertDialog(message_format=_('Unnamed profile'),\ secondary_text=_('You must provide a name \ for this profile.' )) alert.run() alert.destroy() self.profile_name_entry.grab_focus() return None command = self.constructor.get_command('%s') buf = self.profile_description_text.get_buffer() description = buf.get_text(buf.get_start_iter(),\ buf.get_end_iter()) self.profile.add_profile(profile_name,\ command=command,\ description=description,\ options=self.constructor.get_options()) self.scan_interface.toolbar.profile_entry.update() self.scan_interface.refresh_command(None) self.destroy() def clean_profile_info(self): self.profile_name_entry.set_text('') self.profile_description_text.get_buffer().set_text('') def set_scan_interface(self, interface): self.scan_interface = interface def exit(self, *args): self.destroy() def delete_profile(self, widget=None, extra=None): if self.deletable: dialog = HIGDialog(buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) alert = HIGEntryLabel('<b>' + _("Deleting Profile") + '</b>') text = HIGEntryLabel( _('Your profile is going to be deleted! Click\ Ok to continue, or Cancel to go back to Profile Editor.')) hbox = HIGHBox() hbox.set_border_width(5) hbox.set_spacing(12) vbox = HIGVBox() vbox.set_border_width(5) vbox.set_spacing(12) image = gtk.Image() image.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) vbox.pack_start(alert) vbox.pack_start(text) hbox.pack_start(image) hbox.pack_start(vbox) dialog.vbox.pack_start(hbox) dialog.vbox.show_all() response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_CANCEL: return True self.profile.remove_profile(self.profile_name) self.update_profile_entry() self.destroy() def update_profile_entry(self, widget=None, extra=None): self.scan_interface.toolbar.profile_entry.update() list = self.scan_interface.toolbar.profile_entry.get_model() length = len(list) if length > 0: self.scan_interface.toolbar.profile_entry.set_active(0)
class SearchGUI(gtk.VBox, object): """This class is a VBox that holds the search entry field and buttons on top, and the results list on the bottom. The "Cancel" and "Open" buttons are a part of the SearchWindow class, not SearchGUI.""" def __init__(self, search_window): gtk.VBox.__init__(self) self._create_widgets() self._pack_widgets() self._connect_events() # Search options self.options = {} self.options["file_extension"] = search_config.file_extension self.options["directory"] = search_config.directory self.options["search_db"] = search_config.search_db self.parsed_results = {} self._set_result_view() self.id = 0 self.search_window = search_window # The Search* objects are created once per Search Window invocation, so # that they get a list of scans only once, not whenever the search # conditions change if self.options["search_db"]: try: self.search_db = SearchDB() except ImportError as e: self.search_db = SearchDummy() self.no_db_warning.show() self.no_db_warning.set_text( 'Warning: The database of saved scans is not ' 'available. (%s.) Use "Include Directory" under ' '"Expressions" to search a directory.' % str(e)) # Search directories can be added via the "dir:" operator, so it needs # to be a map self.search_dirs = {} self.init_search_dirs() # We create an empty search dictionary, since SearchParser will fill it # with keywords as it encounters different operators in the search # string. self.search_dict = dict() # We need to define our own keyword search dictionary search_keywords = dict() search_keywords["keyword"] = "keyword" search_keywords["profile"] = "profile" search_keywords["pr"] = "profile" search_keywords["target"] = "target" search_keywords["t"] = "target" search_keywords["option"] = "option" search_keywords["o"] = "option" search_keywords["date"] = "date" search_keywords["d"] = "date" search_keywords["after"] = "after" search_keywords["a"] = "after" search_keywords["before"] = "before" search_keywords["b"] = "before" search_keywords["os"] = "os" search_keywords["scanned"] = "scanned" search_keywords["sp"] = "scanned" search_keywords["open"] = "open" search_keywords["op"] = "open" search_keywords["closed"] = "closed" search_keywords["cp"] = "closed" search_keywords["filtered"] = "filtered" search_keywords["fp"] = "filtered" search_keywords["unfiltered"] = "unfiltered" search_keywords["ufp"] = "unfiltered" search_keywords["open|filtered"] = "open_filtered" search_keywords["ofp"] = "open_filtered" search_keywords["closed|filtered"] = "closed_filtered" search_keywords["cfp"] = "closed_filtered" search_keywords["service"] = "service" search_keywords["s"] = "service" search_keywords["inroute"] = "in_route" search_keywords["ir"] = "in_route" self.search_parser = SearchParser(self, search_keywords) # This list holds the (operator, argument) tuples, parsed from the GUI # criteria rows self.gui_criteria_list = [] # Do an initial "empty" search, so that the results window initially # holds all scans in the database self.search_parser.update("") self.start_search() def init_search_dirs(self, dirs=[]): # Start fresh self.search_dirs.clear() # If specified, add the search directory from the Zenmap config file to # the map conf_dir = self.options["directory"] if conf_dir: self.search_dirs[conf_dir] = SearchDir( conf_dir, self.options["file_extension"]) # Process any other dirs (as added by the dir: operator) for dir in dirs: self.search_dirs[dir] = SearchDir(dir, self.options["file_extension"]) def _create_widgets(self): # Search box and buttons self.search_top_hbox = HIGHBox() self.search_label = HIGSectionLabel(_("Search:")) self.search_entry = gtk.Entry() self.expressions_btn = HIGToggleButton(_("Expressions "), gtk.STOCK_EDIT) # The quick reference tooltip button self.search_tooltip_btn = HIGButton(" ", gtk.STOCK_INFO) # The expression VBox. This is only visible once the user clicks on # "Expressions" self.expr_vbox = gtk.VBox() # Results section self.result_list = gtk.ListStore(str, str, int) # title, date, id self.result_view = gtk.TreeView(self.result_list) self.result_scrolled = gtk.ScrolledWindow() self.result_title_column = gtk.TreeViewColumn(_("Scan")) self.result_date_column = gtk.TreeViewColumn(_("Date")) self.no_db_warning = gtk.Label() self.no_db_warning.set_line_wrap(True) self.no_db_warning.set_no_show_all(True) self.expr_window = None def _pack_widgets(self): # Packing label, search box and buttons self.search_top_hbox.set_spacing(4) self.search_top_hbox.pack_start(self.search_label, False) self.search_top_hbox.pack_start(self.search_entry, True) self.search_top_hbox.pack_start(self.expressions_btn, False) self.search_top_hbox.pack_start(self.search_tooltip_btn, False) # The expressions (if any) should be tightly packed so that they don't # take too much screen real-estate self.expr_vbox.set_spacing(0) # Packing the result section self.result_scrolled.add(self.result_view) self.result_scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # Packing it all together self.set_spacing(4) self.pack_start(self.search_top_hbox, False) self.pack_start(self.expr_vbox, False) self.pack_start(self.result_scrolled, True) self.pack_start(self.no_db_warning, False) def _connect_events(self): self.search_entry.connect("changed", self.update_search_entry) self.search_tooltip_btn.connect("clicked", self.show_quick_help) self.expressions_btn.connect("toggled", self.expressions_clicked) def show_quick_help(self, widget=None, extra=None): hint_window = HintWindow(QUICK_HELP_TEXT) hint_window.show_all() def expressions_clicked(self, widget=None, extra=None): if (len(self.expr_vbox.get_children()) == 0 and self.search_entry.get_text() == ""): # This is the first time the user has clicked on "Show Expressions" # and the search entry box is empty, so we add a single Criterion # row self.expr_vbox.pack_start(Criterion(self)) if self.expressions_btn.get_active(): # The Expressions GUI is about to be displayed. It needs to reflect # all the conditions in the search entry field, so a comparison # between the entry field and the GUI needs to be performed. # Make the search entry field insensitive while expressions are # visible self.search_entry.set_sensitive(False) # Get a map of operator => argument from the Expressions GUI so # that we can compare them with the ones in the search entry field gui_ops = {} for criterion in self.expr_vbox.get_children(): if criterion.operator in gui_ops: gui_ops[criterion.operator].append(criterion.argument) else: gui_ops[criterion.operator] = [criterion.argument] # We compare the search entry field to the Expressions GUI. Every # (operator, value) pair must be present in the GUI after this loop # is done. for op, args in six.iteritems(self.search_dict): for arg in args: if (op not in gui_ops) or (arg not in gui_ops[op]): # We need to add this pair to the GUI self.expr_vbox.pack_start(Criterion(self, op, arg), False) # Now we check if there are any leftover criterion rows that aren't # present in the search_dict (for example, if a user has deleted # something from the search entry field) for criterion in self.expr_vbox.get_children(): if (criterion.operator not in self.search_dict or criterion.argument not in self.search_dict[criterion.operator]): criterion.destroy() # If we have deleted all rows, add an empty one if len(self.expr_vbox.get_children()) == 0: self.expr_vbox.pack_start(Criterion(self)) # Display all elements self.expr_vbox.show_all() else: # The Expressions GUI is about to be hidden. No updates to the # search entry field are necessary, since it gets updated on every # change in one of the criterion rows. self.expr_vbox.hide_all() self.search_entry.set_sensitive(True) def close(self): if self.expr_window is not None: self.expr_window.close() def add_criterion(self, caller): # We need to find where the caller (Criteria object) is located among # all the rows, so that we can insert the new row after it caller_index = self.expr_vbox.get_children().index(caller) # Make a new Criteria row and insert it after the calling row criteria = Criterion(self, "keyword") self.expr_vbox.pack_start(criteria, False) self.expr_vbox.reorder_child(criteria, caller_index + 1) criteria.show_all() def remove_criterion(self, c): if len(self.expr_vbox.get_children()) > 1: c.destroy() self.criterion_changed() def criterion_changed(self): # We go through all criteria rows and make a new search string search_string = "" for criterion in self.expr_vbox.get_children(): if criterion.operator != "keyword": search_string += criterion.operator + ":" search_string += criterion.argument.replace(" ", "") + " " self.search_entry.set_text(search_string.strip()) self.search_parser.update(self.search_entry.get_text()) self.start_search() def add_search_dir(self, dir): if dir not in self.search_dirs: self.search_dirs[dir] = SearchDir(dir, self.options["file_extension"]) def update_search_entry(self, widget, extra=None): """Called when the search entry field is modified.""" self.search_parser.update(widget.get_text()) self.start_search() def start_search(self): if not self.options["search_db"] and not self.options["directory"]: d = HIGAlertDialog( message_format=_("No search method selected!"), secondary_text=_( "%s can search results on directories or inside its " "own database. Please select a method by choosing a " "directory or by checking the search data base option " "in the 'Search options' tab before starting a search") % APP_DISPLAY_NAME) d.run() d.destroy() return self.clear_result_list() matched = 0 total = 0 if self.options["search_db"]: total += len(self.search_db.get_scan_results()) for result in self.search_db.search(**self.search_dict): self.append_result(result) matched += 1 for search_dir in six.itervalues(self.search_dirs): total += len(search_dir.get_scan_results()) for result in search_dir.search(**self.search_dict): self.append_result(result) matched += 1 #total += len(self.search_tabs.get_scan_results()) #for result in self.search_tabs.search(**self.search_dict): # self.append_result(result) # matched += 1 self.search_window.set_label_text( "Matched <b>%s</b> out of <b>%s</b> scans." % (str(matched), str(total))) def clear_result_list(self): for i in range(len(self.result_list)): iter = self.result_list.get_iter_root() del (self.result_list[iter]) def append_result(self, parsed_result): title = parsed_result.scan_name try: date = datetime.datetime.fromtimestamp(float(parsed_result.start)) date_field = date.strftime("%Y-%m-%d %H:%M") except ValueError: date_field = _("Unknown") self.parsed_results[self.id] = [title, parsed_result] self.result_list.append([title, date_field, self.id]) self.id += 1 def get_selected_results(self): selection = self.result_view.get_selection() rows = selection.get_selected_rows() list_store = rows[0] results = {} for row in rows[1]: r = row[0] results[list_store[r][2]] = self.parsed_results[list_store[r][2]] return results def _set_result_view(self): self.result_view.set_enable_search(True) self.result_view.set_search_column(0) selection = self.result_view.get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) self.result_view.append_column(self.result_title_column) self.result_view.append_column(self.result_date_column) self.result_title_column.set_resizable(True) self.result_title_column.set_min_width(200) self.result_date_column.set_resizable(True) self.result_title_column.set_sort_column_id(0) self.result_date_column.set_sort_column_id(1) self.result_title_column.set_reorderable(True) self.result_date_column.set_reorderable(True) cell = gtk.CellRendererText() self.result_title_column.pack_start(cell, True) self.result_date_column.pack_start(cell, True) self.result_title_column.set_attributes(cell, text=0) self.result_date_column.set_attributes(cell, text=1) selected_results = property(get_selected_results)