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 __create_widgets(self): self.property_name_label = HIGEntryLabel("") self.example_label = HIGEntryLabel("") self.bold_tg_button = HIGToggleButton("", gtk.STOCK_BOLD) self.italic_tg_button = HIGToggleButton("", gtk.STOCK_ITALIC) self.underline_tg_button = HIGToggleButton("", gtk.STOCK_UNDERLINE) self.text_color_button = HIGButton(_("Text"), stock=gtk.STOCK_SELECT_COLOR) self.highlight_color_button = HIGButton(_("Highlight"), stock=gtk.STOCK_SELECT_COLOR)
class HighlightProperty(object): def __init__(self, property_name, property): self.__create_widgets() self.property_name = property_name self.property_label = property[0].capitalize() self.example = property[1] self.bold = property[2] self.italic = property[3] self.underline = property[4] self.text_color = property[5] self.highlight_color = property[6] self.__connect_buttons() def __create_widgets(self): self.property_name_label = HIGEntryLabel("") self.example_label = HIGEntryLabel("") self.bold_tg_button = HIGToggleButton("", gtk.STOCK_BOLD) self.italic_tg_button = HIGToggleButton("", gtk.STOCK_ITALIC) self.underline_tg_button = HIGToggleButton("", gtk.STOCK_UNDERLINE) self.text_color_button = HIGButton( _("Text"), stock=gtk.STOCK_SELECT_COLOR) self.highlight_color_button = HIGButton( _("Highlight"), stock=gtk.STOCK_SELECT_COLOR) def __connect_buttons(self): self.bold_tg_button.connect("toggled", self.update_example) self.italic_tg_button.connect("toggled", self.update_example) self.underline_tg_button.connect("toggled", self.update_example) self.text_color_button.connect("clicked", self.text_color_dialog) self.highlight_color_button.connect( "clicked", self.highlight_color_dialog) #################################### # Text color dialog def text_color_dialog(self, widget): color_dialog = gtk.ColorSelectionDialog( "%s %s" % (self.label, _("text color"))) color_dialog.colorsel.set_current_color(self.text_color) color_dialog.ok_button.connect( "clicked", self.text_color_dialog_ok, color_dialog) color_dialog.cancel_button.connect( "clicked", self.text_color_dialog_cancel, color_dialog) color_dialog.connect( "delete-event", self.text_color_dialog_close, color_dialog) color_dialog.run() def text_color_dialog_ok(self, widget, color_dialog): self.text_color = color_dialog.colorsel.get_current_color() color_dialog.destroy() self.update_example() def text_color_dialog_cancel(self, widget, color_dialog): color_dialog.destroy() def text_color_dialog_close(self, widget, extra, color_dialog): color_dialog.destroy() ######################################### # Highlight color dialog def highlight_color_dialog(self, widget): color_dialog = gtk.ColorSelectionDialog( "%s %s" % (self.property_name, _("highlight color"))) color_dialog.colorsel.set_current_color(self.highlight_color) color_dialog.ok_button.connect( "clicked", self.highlight_color_dialog_ok, color_dialog) color_dialog.cancel_button.connect( "clicked", self.highlight_color_dialog_cancel, color_dialog) color_dialog.connect( "delete-event", self.highlight_color_dialog_close, color_dialog) color_dialog.run() def highlight_color_dialog_ok(self, widget, color_dialog): self.highlight_color = color_dialog.colorsel.get_current_color() color_dialog.destroy() self.update_example() def highlight_color_dialog_cancel(self, widget, color_dialog): color_dialog.destroy() def highlight_color_dialog_close(self, widget, extra, color_dialog): color_dialog.destroy() def update_example(self, widget=None): start = 0 end = len(self.example) attributes = pango.AttrList() attributes.insert( pango.AttrForeground(self.text_color.red, self.text_color.green, self.text_color.blue, start, end)) attributes.insert(pango.AttrBackground(self.highlight_color.red, self.highlight_color.green, self.highlight_color.blue, start, end)) # Bold verification if self.bold_tg_button.get_active(): attributes.insert(pango.AttrWeight(pango.WEIGHT_HEAVY, start, end)) else: attributes.insert( pango.AttrWeight(pango.WEIGHT_NORMAL, start, end)) # Italic verification if self.italic_tg_button.get_active(): attributes.insert(pango.AttrStyle(pango.STYLE_ITALIC, start, end)) else: attributes.insert(pango.AttrStyle(pango.STYLE_NORMAL, start, end)) # Underline verification if self.underline_tg_button.get_active(): attributes.insert( pango.AttrUnderline(pango.UNDERLINE_SINGLE, start, end)) else: attributes.insert( pango.AttrUnderline(pango.UNDERLINE_NONE, start, end)) self.example_label.set_attributes(attributes) def show_bold(self, widget): self.example_label.set_markup("<>") def get_example(self): return self.example_label.get_text() def set_example(self, example): self.example_label.set_text(example) def get_bold(self): if self.bold_tg_button.get_active(): return 1 return 0 def set_bold(self, bold): self.bold_tg_button.set_active(bold) def get_italic(self): if self.italic_tg_button.get_active(): return 1 return 0 def set_italic(self, italic): self.italic_tg_button.set_active(italic) def get_underline(self): if self.underline_tg_button.get_active(): return 1 return 0 def set_underline(self, underline): self.underline_tg_button.set_active(underline) def get_label(self): return self.property_name_label.get_text() def set_label(self, label): self.property_name_label.set_text(label) label = property(get_label, set_label) example = property(get_example, set_example) bold = property(get_bold, set_bold) italic = property(get_italic, set_italic) underline = property(get_underline, set_underline)
class HighlightProperty(object): def __init__(self, property_name, property): self.__create_widgets() self.property_name = property_name self.property_label = property[0].capitalize() self.example = property[1] self.bold = property[2] self.italic = property[3] self.underline = property[4] self.text_color = property[5] self.highlight_color = property[6] self.__connect_buttons() def __create_widgets(self): self.property_name_label = HIGEntryLabel("") self.example_label = HIGEntryLabel("") self.bold_tg_button = HIGToggleButton("", gtk.STOCK_BOLD) self.italic_tg_button = HIGToggleButton("", gtk.STOCK_ITALIC) self.underline_tg_button = HIGToggleButton("", gtk.STOCK_UNDERLINE) self.text_color_button = HIGButton(_("Text"), stock=gtk.STOCK_SELECT_COLOR) self.highlight_color_button = HIGButton(_("Highlight"), stock=gtk.STOCK_SELECT_COLOR) def __connect_buttons(self): self.bold_tg_button.connect("toggled", self.update_example) self.italic_tg_button.connect("toggled", self.update_example) self.underline_tg_button.connect("toggled", self.update_example) self.text_color_button.connect("clicked", self.text_color_dialog) self.highlight_color_button.connect("clicked", self.highlight_color_dialog) #################################### # Text color dialog def text_color_dialog(self, widget): color_dialog = gtk.ColorSelectionDialog("%s %s" % (self.label, _("text color"))) color_dialog.colorsel.set_current_color(self.text_color) color_dialog.ok_button.connect("clicked", self.text_color_dialog_ok, color_dialog) color_dialog.cancel_button.connect("clicked", self.text_color_dialog_cancel, color_dialog) color_dialog.connect("delete-event", self.text_color_dialog_close, color_dialog) color_dialog.run() def text_color_dialog_ok(self, widget, color_dialog): self.text_color = color_dialog.colorsel.get_current_color() color_dialog.destroy() self.update_example() def text_color_dialog_cancel(self, widget, color_dialog): color_dialog.destroy() def text_color_dialog_close(self, widget, extra, color_dialog): color_dialog.destroy() ######################################### # Highlight color dialog def highlight_color_dialog(self, widget): color_dialog = gtk.ColorSelectionDialog( "%s %s" % (self.property_name, _("highlight color"))) color_dialog.colorsel.set_current_color(self.highlight_color) color_dialog.ok_button.connect("clicked", self.highlight_color_dialog_ok, color_dialog) color_dialog.cancel_button.connect("clicked", self.highlight_color_dialog_cancel, color_dialog) color_dialog.connect("delete-event", self.highlight_color_dialog_close, color_dialog) color_dialog.run() def highlight_color_dialog_ok(self, widget, color_dialog): self.highlight_color = color_dialog.colorsel.get_current_color() color_dialog.destroy() self.update_example() def highlight_color_dialog_cancel(self, widget, color_dialog): color_dialog.destroy() def highlight_color_dialog_close(self, widget, extra, color_dialog): color_dialog.destroy() def update_example(self, widget=None): start = 0 end = len(self.example) attributes = pango.AttrList() attributes.insert( pango.AttrForeground(self.text_color.red, self.text_color.green, self.text_color.blue, start, end)) attributes.insert( pango.AttrBackground(self.highlight_color.red, self.highlight_color.green, self.highlight_color.blue, start, end)) # Bold verification if self.bold_tg_button.get_active(): attributes.insert(pango.AttrWeight(pango.WEIGHT_HEAVY, start, end)) else: attributes.insert(pango.AttrWeight(pango.WEIGHT_NORMAL, start, end)) # Italic verification if self.italic_tg_button.get_active(): attributes.insert(pango.AttrStyle(pango.STYLE_ITALIC, start, end)) else: attributes.insert(pango.AttrStyle(pango.STYLE_NORMAL, start, end)) # Underline verification if self.underline_tg_button.get_active(): attributes.insert( pango.AttrUnderline(pango.UNDERLINE_SINGLE, start, end)) else: attributes.insert( pango.AttrUnderline(pango.UNDERLINE_NONE, start, end)) self.example_label.set_attributes(attributes) def show_bold(self, widget): self.example_label.set_markup("<>") def get_example(self): return self.example_label.get_text() def set_example(self, example): self.example_label.set_text(example) def get_bold(self): if self.bold_tg_button.get_active(): return 1 return 0 def set_bold(self, bold): self.bold_tg_button.set_active(bold) def get_italic(self): if self.italic_tg_button.get_active(): return 1 return 0 def set_italic(self, italic): self.italic_tg_button.set_active(italic) def get_underline(self): if self.underline_tg_button.get_active(): return 1 return 0 def set_underline(self, underline): self.underline_tg_button.set_active(underline) def get_label(self): return self.property_name_label.get_text() def set_label(self, label): self.property_name_label.set_text(label) label = property(get_label, set_label) example = property(get_example, set_example) bold = property(get_bold, set_bold) italic = property(get_italic, set_italic) underline = property(get_underline, set_underline)
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 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)