from guppyproxy.util import list_remove, display_error_box, set_default_dialog_dir, default_dialog_dir, save_dialog, open_dialog from guppyproxy.proxy import MessageError from guppyproxy.config import ProxyConfig from PyQt5.QtWidgets import QWidget, QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, QFormLayout, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QSizePolicy, QToolButton, QCheckBox, QLabel from PyQt5.QtCore import pyqtSlot, pyqtSignal import os import copy class ListenerList(QTableWidget): listenersUpdated = pyqtSignal(list) # list part of the listener tab def __init__(self, *args, **kwargs): QTableWidget.__init__(self, *args, **kwargs) self.listeners = [] # Set up table self.setColumnCount(1) self.horizontalHeader().hide() self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.verticalHeader().hide() self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) self.setEditTriggers(QAbstractItemView.NoEditTriggers) def _add_listener(self, interface, port): self.listeners.append((interface, port)) def add_listener(self, interface, port): self.listeners.append((interface, port)) self.redraw_table() self.listenersUpdated.emit(self.listeners[:]) def set_listeners(self, listeners): self.listeners = [] for interface, port in listeners: self._add_listener(interface, port) self.redraw_table() self.listenersUpdated.emit(copy.deepcopy(self.listeners)) def _append_row(self, interface, port): row = self.rowCount() self.insertRow(row) self.setItem(row, 0, QTableWidgetItem("%s:%s" % (interface, port))) def redraw_table(self): self.setRowCount(0) for interface, port in self.listeners: self._append_row(interface, port) @pyqtSlot() def delete_selected(self): rows = self.selectionModel().selectedRows() if len(rows) == 0: return rownums = [idx.row() for idx in rows] self.listeners = list_remove(self.listeners, rownums) self.redraw_table() self.listenersUpdated.emit(self.listeners[:]) def clear(self): self.listeners = [] self.redraw_table() self.listenersUpdated.emit(self.listeners[:]) def get_listeners(self): return self.listeners[:] class ListenerWidget(QWidget): listenersUpdated = pyqtSignal(list) def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.listenerlist = ListenerList() self.listenerlist.listenersUpdated.connect(self.listenersUpdated) self.layout().addWidget(self.listenerlist) self.hostinput = QLineEdit() self.hostinput.setText("127.0.0.1") self.hostinput.returnPressed.connect(self.add_listener) self.portinput = QLineEdit() self.portinput.setMaxLength(5) self.portinput.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self.portinput.returnPressed.connect(self.add_listener) self.addbutton = QToolButton() self.addbutton.setText("+") self.removebutton = QToolButton() self.removebutton.setText("-") editbar = QHBoxLayout() editbar.addWidget(self.addbutton) editbar.addWidget(self.removebutton) editbar.addWidget(QLabel("Interface:")) editbar.addWidget(self.hostinput) editbar.addWidget(QLabel("Port:")) editbar.addWidget(self.portinput) self.removebutton.clicked.connect(self.listenerlist.delete_selected) self.addbutton.clicked.connect(self.add_listener) self.layout().addLayout(editbar) @pyqtSlot() def add_listener(self): host = self.hostinput.text() port = self.portinput.text() if host == "": return if port == "": return try: port = int(port) except Exception: return self.listenerlist.add_listener(host, port) self.hostinput.setText("127.0.0.1") self.portinput.setText("") def set_listeners(self, listeners): self.listenerlist.set_listeners(listeners) class DatafileWidget(QWidget): datafileLoaded = pyqtSignal(str) def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.setLayout(QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.datapath = QLineEdit() newbutton = QPushButton("New") newbutton.clicked.connect(self.new_datafile) browsebutton = QPushButton("Open") browsebutton.clicked.connect(self.open_datafile) confirmbutton = QPushButton("Go!") confirmbutton.clicked.connect(self._load_datafile) self.layout().addWidget(self.datapath) self.layout().addWidget(newbutton) self.layout().addWidget(browsebutton) self.layout().addWidget(confirmbutton) @pyqtSlot() def _load_datafile(self): path = self.datapath.text() self.datafileLoaded.emit(path) @pyqtSlot() def new_datafile(self): fname = save_dialog(self, filter_string="Database File (*.gpy)") if not fname: return if len(fname) < 4 and fname[:-4] != ".gpy": fname += ".gpy" set_default_dialog_dir(fname) self.datapath.setText(fname) self._load_datafile() @pyqtSlot() def open_datafile(self): fname = open_dialog(self) if not fname: return self.datapath.setText(fname) set_default_dialog_dir(fname) self._load_datafile() class ProxyInfoWidget(QWidget): proxyInfoUpdated = pyqtSignal(dict) def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.setLayout(QFormLayout()) self.enablebox = QCheckBox() self.enablebox.stateChanged.connect(self._enable_cb_statechange) self.hostinput = QLineEdit() self.portinput = QLineEdit() self.credsbox = QCheckBox() self.credsbox.stateChanged.connect(self._login_cb_statechange) self.credsbox.setCheckState(0) self.usernameinput = QLineEdit() self.passwordinput = QLineEdit() self.passwordinput.setEchoMode(QLineEdit.Password) self.socksbox = QCheckBox() self.confirmbutton = QPushButton("Confirm") self.confirmbutton.clicked.connect(self._confirm_entry) self.layout().addRow(QLabel("Use Proxy"), self.enablebox) self.layout().addRow(QLabel("Host"), self.hostinput) self.layout().addRow(QLabel("Port"), self.portinput) self.layout().addRow(QLabel("Use Login"), self.credsbox) self.layout().addRow(QLabel("Username"), self.usernameinput) self.layout().addRow(QLabel("Password"), self.passwordinput) self.layout().addRow(QLabel("Use SOCKS"), self.socksbox) self.layout().addRow(QLabel(""), self.confirmbutton) self._set_enabled(False) self._set_login_enabled(False) @pyqtSlot(int) def _login_cb_statechange(self, state): if state == 0: self._set_login_enabled(False) else: self._set_login_enabled(True) @pyqtSlot(int) def _enable_cb_statechange(self, state): if state == 0: self._set_enabled(False) else: self._set_enabled(True) def _set_enabled(self, enabled): self.all_enabled = enabled self.hostinput.setEnabled(enabled) self.portinput.setEnabled(enabled) self.credsbox.setEnabled(enabled) self.socksbox.setEnabled(enabled) if enabled: self._set_login_enabled(self.loginenabled) else: self._set_login_enabled(False) def _set_login_enabled(self, enabled): self.loginenabled = enabled self.usernameinput.setEnabled(enabled) self.passwordinput.setEnabled(enabled) def _fill_form(self, enabled, host, port, need_creds, username, password, use_socks): if enabled: self.enablebox.setCheckState(2) else: self.enablebox.setCheckState(0) self.hostinput.setText(host) if port == 0: self.portinput.setText("") else: self.portinput.setText(str(port)) if need_creds: self.credsbox.setCheckState(2) else: self.credsbox.setCheckState(0) self.usernameinput.setText(username) self.passwordinput.setText(password) if use_socks: self.socksbox.setCheckState(2) else: self.socksbox.setCheckState(0) def _confirm_entry(self): use_proxy = not (self.enablebox.checkState() == 0) if use_proxy: host = self.hostinput.text() port = self.portinput.text() try: port = int(port) except Exception: return is_socks = not (self.socksbox.checkState() == 0) if self.credsbox.checkState() == 0: username = "" password = "" else: username = self.usernameinput.text() password = self.passwordinput.text() entry = {"use_proxy": use_proxy, "host": host, "port": port, "is_socks": is_socks, "username": username, "password": password} else: entry = {"use_proxy": False, "host": "", "port": 0, "is_socks": False, "username": "", "password": ""} self.proxyInfoUpdated.emit(entry) class SettingsWidget(QWidget): datafileLoaded = pyqtSignal() def __init__(self, client, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.client = client self.setLayout(QFormLayout()) # Datafile self.datafilewidg = DatafileWidget() self.datafilewidg.datafileLoaded.connect(self._load_datafile) self.layout().addRow(QLabel("Datafile"), self.datafilewidg) # Listeners self.listenerwidg = ListenerWidget() self.listenerwidg.listenersUpdated.connect(self._listeners_updated) self.layout().addRow(QLabel("Listeners"), self.listenerwidg) # Proxy settings self.proxywidg = ProxyInfoWidget() self.proxywidg.proxyInfoUpdated.connect(self._set_proxy_settings) self.layout().addRow(QLabel("Proxy Settings"), self.proxywidg) self.load_config() def load_config(self): # Load config self.config = ProxyConfig() try: configs = self.client.get_plugin_value(ProxyConfig.PLUGIN_KEY) except MessageError: configs = self.config.dumps() self.client.set_plugin_value(ProxyConfig.PLUGIN_KEY, configs) self.config.loads(configs) new_listeners = [(vals[0], vals[1]) for vals in self.config.listeners] self.listenerwidg.set_listeners(new_listeners) # fill proxy self.proxywidg._fill_form(self.config.use_proxy, self.config.proxy_host, self.config.proxy_port, not (self.config.proxy_username == "" and self.config.proxy_password == ""), self.config.proxy_username, self.config.proxy_password, self.config.is_socks_proxy) self.reload_listeners() @pyqtSlot(str) def _load_datafile(self, path): old_storage = self.client.proxy_storage try: storage = self.client.add_sqlite_storage(path, "tmpprefix") except MessageError as e: display_error_box("Could not load datafile:\n%s" % e) return self.client.close_storage(old_storage) self.client.set_storage_prefix(storage.storage_id, "") self.client.set_proxy_storage(storage.storage_id) self.client.disk_storage = storage self.load_config() self.datafileLoaded.emit() @pyqtSlot(list) def _listeners_updated(self, new_listeners): old_listensers = self.client.get_listeners() parsedold = {} for lid, addr in old_listensers: iface, port = addr.rsplit(':', 1) port = int(port) parsedold[(iface, port)] = lid oldset = set(parsedold.keys()) newset = set(new_listeners) hosts_to_remove = oldset.difference(new_listeners) ids_to_remove = [parsedold[i] for i in hosts_to_remove] hosts_to_add = newset.difference(oldset) failed_listeners = [] for i in ids_to_remove: self.client.remove_listener(i) for iface, port in hosts_to_add: try: self.client.add_listener(iface, port) except MessageError as e: err = "%s:%s: %s" % (iface, port, e) failed_listeners.append(err) if failed_listeners: errmsg = "Failed to create listener(s):\n\n%s" % ('\n'.join(failed_listeners)) display_error_box(errmsg) self.config.set_listeners([(host, port, None) for host, port in new_listeners]) # ignore transparent self.save_config() @pyqtSlot(dict) def _set_proxy_settings(self, proxy_data): self.config.proxy = proxy_data use_creds = (self.config.proxy_username != "" or self.config.proxy_password != "") self.client.set_proxy(self.config.use_proxy, self.config.proxy_host, self.config.proxy_port, use_creds, self.config.proxy_username, self.config.proxy_password, self.config.is_socks_proxy) self.save_config() def reload_listeners(self): hosts = self.client.get_listeners() pairs = [] for lid, iface in hosts: host, port = iface.rsplit(":", 1) pairs.append((host, port)) self.listenerwidg.blockSignals(True) self.listenerwidg.set_listeners(pairs) self.listenerwidg.blockSignals(False) def save_config(self): self.client.set_plugin_value(ProxyConfig.PLUGIN_KEY, self.config.dumps())