import wx import wx.adv as adv import logging logger = logging.getLogger(__name__) class ScanWizardResultsPage(adv.WizardPageSimple): def __init__(self, parent, scan_handler, main_frame): super().__init__(parent) self.scan_handler = scan_handler self.main_frame = main_frame self.discovered = [] self._build_ui() def _build_ui(self): sizer = wx.BoxSizer(wx.VERTICAL) self.summary = wx.StaticText(self, label="Waiting to start…") sizer.Add(self.summary, 0, wx.ALL, 5) self.gauge = wx.Gauge(self, range=100, style=wx.GA_SMOOTH) sizer.Add(self.gauge, 0, wx.EXPAND | wx.ALL, 5) self.results_list = wx.ListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SUNKEN) self.results_list.InsertColumn(0, "Address", width=140) self.results_list.InsertColumn(1, "Port", width=60) self.results_list.InsertColumn(2, "Details", width=260) self.results_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self._toggle_buttons) self.results_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._toggle_buttons) sizer.Add(self.results_list, 1, wx.EXPAND | wx.ALL, 5) btn_row = wx.BoxSizer(wx.HORIZONTAL) self.quick_connect_btn = wx.Button(self, label="Quick Connect") self.quick_connect_btn.Disable() self.quick_connect_btn.Bind(wx.EVT_BUTTON, self.on_quick_connect) btn_row.Add(self.quick_connect_btn, 0, wx.RIGHT, 5) self.rescan_btn = wx.Button(self, label="Rescan") self.rescan_btn.Bind(wx.EVT_BUTTON, self.on_rescan) btn_row.Add(self.rescan_btn, 0) sizer.Add(btn_row, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.SetSizer(sizer) def prepare_for_scan(self, params, start_callback): self.results_list.DeleteAllItems() self.discovered = [] self.summary.SetLabel(f"Scanning {params['cidr']} on ports {', '.join(map(str, params['ports']))}…") self.gauge.SetRange(max(params["host_count"], 1)) self.gauge.SetValue(0) self.quick_connect_btn.Disable() start_callback(params) def on_scan_progress(self, scanned, total): try: total = max(total, 1) self.gauge.SetRange(total) self.gauge.SetValue(min(scanned, total)) self.summary.SetLabel(f"Scanning… {scanned}/{total} hosts checked") except RuntimeError: # C++ SHIT logger.debug("Scan progress update after controls destroyed; ignoring") def on_scan_result(self, server_info): """Handle a single discovered server row.""" try: idx = self.results_list.InsertItem(self.results_list.GetItemCount(), server_info["address"]) self.results_list.SetItem(idx, 1, str(server_info["port"])) self.results_list.SetItem(idx, 2, server_info.get("banner", "IRC server detected")) self.discovered.append(server_info) self.summary.SetLabel( f"Found {len(self.discovered)} {'server' if len(self.discovered) == 1 else 'servers'}" ) except RuntimeError: logger.debug("Scan result update after controls destroyed; ignoring") def on_scan_complete(self, results): """Final scan completion callback.""" try: if results: self.summary.SetLabel( f"Scan complete : {len(results)} " f"{'server' if len(results) == 1 else 'servers'} ready." ) else: self.summary.SetLabel("Scan complete : no IRC servers discovered.") self._toggle_buttons() except RuntimeError: logger.debug("Scan completion update after controls destroyed; ignoring") def on_quick_connect(self, event): row = self.results_list.GetFirstSelected() if row == -1: return server = self.results_list.GetItemText(row, 0) port = int(self.results_list.GetItemText(row, 1)) if self.main_frame.quick_connect(server, port): self.GetParent().EndModal(wx.ID_OK) def on_rescan(self, event): wizard = self.GetParent() wizard.ShowPage(wizard.intro_page) def _toggle_buttons(self, event=None): has_selection = self.results_list.GetFirstSelected() != -1 if has_selection: self.quick_connect_btn.Enable() else: self.quick_connect_btn.Disable()