class Window(QWidget, Ui_FormFrameworkTools): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.setupUi(self) # 初始化server self._server = QTcpServer(self) self._server.newConnection.connect(self.onNewConnection) self._server.listen(QHostAddress.LocalHost, 49496) self._getPsPath() self._initCodeEdit() # 设置默认代码和参数 self.argsEdit.setPlainText('testColor("{}")'.format( os.path.abspath('Resources/GM6C6860.jpg').replace('\\', '/'))) self.codeEdit.setText(self._formatArgs(Template)) def _formatArgs(self, code): code = code.replace( '#1#', os.path.abspath('Resources/ProgressBar.jsx').replace('\\', '/')) code = code.replace( '#2#', os.path.abspath('Resources/Core.jsx').replace('\\', '/')) code = code.replace( '#3#', os.path.abspath('Resources/test.jsx').replace('\\', '/')) return code def _initCodeEdit(self): # 初始化编辑器的工作 self.codeEdit.setUtf8(True) self.codeEdit.linesChanged.connect(self.onLinesChanged) # 行改变 # 代码高亮 self.codeEdit.setLexer(QsciLexerJavaScript(self)) # 自动折叠 self.codeEdit.setMarginType(3, QsciScintilla.SymbolMargin) self.codeEdit.setMarginLineNumbers(3, False) self.codeEdit.setMarginWidth(3, 15) self.codeEdit.setMarginSensitivity(3, True) # 显示行号 #self.codeEdit.setMarginType(0, QsciScintilla.NumberMargin) self.codeEdit.setMarginLineNumbers(0, True) self.onLinesChanged() # 代码提示 sciApi = QsciAPIs(self.codeEdit.lexer()) sciApi.prepare() self.codeEdit.setAutoCompletionSource(QsciScintilla.AcsAll) # 设置源 self.codeEdit.setAutoCompletionCaseSensitivity(True) # 设置自动补全大小写敏感 self.codeEdit.setAutoCompletionThreshold(1) # 设置每输入一个字符就会出现自动补全的提示 # 设置字体 self.codeEdit.setFont(QFont('Consolas', 16)) self.codeEdit.setMarginsFont(self.codeEdit.font()) # 设置编码 self.codeEdit.SendScintilla(QsciScintilla.SCI_SETCODEPAGE, QsciScintilla.SC_CP_UTF8) self.codeEdit.setBraceMatching(QsciScintilla.StrictBraceMatch) # 设置当前行高亮 self.codeEdit.setCaretLineVisible(True) self.codeEdit.setCaretLineBackgroundColor(Qt.lightGray) self.codeEdit.setCaretForegroundColor(Qt.white) # tab # table relative self.codeEdit.setIndentationsUseTabs(True) self.codeEdit.setIndentationWidth(4) self.codeEdit.setTabIndents(True) self.codeEdit.setAutoIndent(True) self.codeEdit.setBackspaceUnindents(True) self.codeEdit.setTabWidth(4) # indentation guides self.codeEdit.setIndentationGuides(True) # folding margin self.codeEdit.setFolding(QsciScintilla.PlainFoldStyle) self.codeEdit.setMarginWidth(2, 12) # 自动换行 self.codeEdit.setWrapMode(QsciScintilla.WrapWord) def onLinesChanged(self): # 动态设置左边的边距 self.codeEdit.setMarginWidth( 0, self.codeEdit.fontMetrics().width(str(self.codeEdit.lines())) + 5) def _getPsPath(self): # 获取ps的路径 settings = QSettings('FrameworkTools', 'Settings') psPath = settings.value('path', '') if not psPath: # 如果没有找到自己保存的路径则去找系统安装的路径 settings = QSettings( 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Photoshop.exe', QSettings.NativeFormat) psPath = settings.value('.', '') self.pathEdit.setText(psPath) def onNewConnection(self): # 当有新的连接来时 while self._server.hasPendingConnections(): socket = self._server.nextPendingConnection() socket.readyRead.connect(self.onReadyRead) if socket.bytesAvailable() > 0: # 如果已有数据发送过来 self.doRecv(socket) def onReadyRead(self): # 准备接收数据 socket = self.sender() if socket.bytesAvailable() > 0: self.doRecv(socket) def doRecv(self, socket): # 接收数据 try: data = socket.readAll().data().decode() # 对数据解密 data = xxtea.decryptFromBase64(data, '0123456789abcdef') method, args = data.split('|') self.resultEdit.append('被调用函数: {}, 参数: {}'.format(method, args)) try: args = eval(args) except Exception as e: args = [] # 动态执行函数 if hasattr(self, method): getattr(self, method)(socket, *args) except Exception as e: self.resultEdit.append(str(e)) def getCode(self, socket): # 传递参数的函数 args = self.argsEdit.toPlainText().strip() args = xxtea.encryptToBase64(args, '0123456789abcdef') + '\n' print('发送加密数据: ', args) socket.write(args.encode()) socket.flush() def showError(self, _, message): # 显示错误消息 if message: QMessageBox.critical(self, '错误', message) @pyqtSlot() def on_selectButton_clicked(self): # 手动选择路径 path, _ = QFileDialog.getOpenFileName(self, '选择Ps路径', '', 'Photoshop.exe') if path: self.pathEdit.setText(path) settings = QSettings('FrameworkTools', 'Settings') settings.setValue('path', path) settings.sync() @pyqtSlot() def on_runButton_clicked(self): # 运行按钮 code = self.codeEdit.text().strip() if not code: return if not code.startswith('#target'): code = '#target photoshop\n' + code path = tempfile.mktemp('.jsx') open(path, 'wb').write(code.encode('utf-8')) subprocess.call([self.pathEdit.text().strip(), path]) def closeEvent(self, event): if self._server.isListening(): self._server.close() self._server.deleteLater() super(Window, self).closeEvent(event)
class CodeExecutor: """ This class is responsible for executing code (when starting Tribler in debug mode). The protocol to execute code is as follows. First, a client that wants to execute some code opens a connection with the TCP server and sends the string: <code in base64 format> <task_id>\n This code will be executed and the result will be sent to the client in the following format: result <result> <task_id>\n. If Tribler crashes, the server sends the following result: crash <stack trace in base64 format> Note that the socket uses the newline as separator. """ def __init__(self, port, shell_variables=None): self.logger = logging.getLogger(self.__class__.__name__) self.tcp_server = QTcpServer() self.sockets = [] self.stack_trace = None if not self.tcp_server.listen(port=port): self.logger.error("Unable to start code execution socket! Error: %s", self.tcp_server.errorString()) else: connect(self.tcp_server.newConnection, self._on_new_connection) self.shell = Console(locals=shell_variables or {}, logger=self.logger) def _on_new_connection(self): self.logger.info("CodeExecutor has new connection") while self.tcp_server.hasPendingConnections(): socket = self.tcp_server.nextPendingConnection() connect(socket.readyRead, self._on_socket_read_ready) connect(socket.disconnected, self._on_socket_disconnect(socket)) self.sockets.append(socket) # If Tribler has crashed, notify the other side immediately if self.stack_trace: self.on_crash(self.stack_trace) def run_code(self, code, task_id): self.logger.info(f"Run code for task {task_id}") self.logger.debug(f"Code for execution:\n{code}") try: self.shell.runcode(code) except SystemExit: pass if self.shell.last_traceback: self.on_crash(self.shell.last_traceback) return self.logger.info("Code execution with task %s finished:", task_id) return_value = b64encode(self.shell.locals.get('return_value', '').encode('utf-8')) for socket in self.sockets: socket.write(b"result %s %s\n" % (return_value, task_id)) def on_crash(self, exception_text): self.logger.error(f"Crash in CodeExecutor:\n{exception_text}") self.stack_trace = exception_text for socket in self.sockets: socket.write(b"crash %s\n" % b64encode(exception_text.encode('utf-8'))) def _on_socket_read_ready(self): data = bytes(self.sockets[0].readAll()) parts = data.split(b" ") if len(parts) != 2: return try: code = b64decode(parts[0]).decode('utf8') task_id = parts[1].replace(b'\n', b'') self.run_code(code, task_id) except binascii.Error: self.logger.error("Invalid base64 code string received!") def _on_socket_disconnect(self, socket): def on_socket_disconnect_handler(): self.sockets.remove(socket) return on_socket_disconnect_handler
class CodeExecutor(object): """ This class is responsible for executing code (when starting Tribler in debug mode). The protocol to execute code is as follows. First, a client that wants to execute some code opens a connection with the TCP server and sends the string: <code in base64 format> <task_id>\n This code will be executed and the result will be sent to the client in the following format: result <result> <task_id>\n. If Tribler crashes, the server sends the following result: crash <stack trace in base64 format> Note that the socket uses the newline as separator. """ def __init__(self, port, shell_variables={}): self.logger = logging.getLogger(self.__class__.__name__) self.tcp_server = QTcpServer() self.sockets = [] self.stack_trace = None if not self.tcp_server.listen(port=port): self.logger.error( "Unable to start code execution socket! Error: %s", self.tcp_server.errorString()) else: self.tcp_server.newConnection.connect(self._on_new_connection) self.shell = Console(locals=shell_variables) def _on_new_connection(self): while self.tcp_server.hasPendingConnections(): socket = self.tcp_server.nextPendingConnection() socket.readyRead.connect(self._on_socket_read_ready) socket.disconnected.connect( lambda dc_socket=socket: self._on_socket_disconnect(dc_socket)) self.sockets.append(socket) # If Tribler has crashed, notify the other side immediately if self.stack_trace: self.on_crash(self.stack_trace) def run_code(self, code, task_id): self.shell.runcode(code) stdout = self.shell.stdout.read() stderr = self.shell.stderr.read() self.logger.info("Code execution with task %s finished:", task_id) self.logger.info("Stdout of task %s: %s", task_id, stdout) if 'Traceback' in stderr and 'SystemExit' not in stderr: self.logger.error("Executed code with failure: %s", b64encode(code)) # Determine the return value if 'return_value' not in self.shell.console.locals: return_value = ''.encode('base64') else: return_value = str( self.shell.console.locals['return_value']).encode('base64') for socket in self.sockets: socket.write("result %s %s\n" % (return_value, task_id)) def on_crash(self, exception_text): self.stack_trace = exception_text for socket in self.sockets: socket.write("crash %s\n" % exception_text.encode('base64')) def _on_socket_read_ready(self): data = str(self.sockets[0].readAll()) parts = data.split(" ") if len(parts) != 2: return try: code = parts[0].decode('base64') task_id = parts[1].replace('\n', '') self.run_code(code, task_id) except binascii.Error: self.logger.error("Invalid base64 code string received!") def _on_socket_disconnect(self, socket): self.sockets.remove(socket)
class Server(QObject): debug = True log = sys.stderr username = '******' password = '******' stopServing = pyqtSlot() def __init__(self, parent=None): super(Server, self).__init__(parent) self.proxy_server = QTcpServer(self) self.proxy_server.listen(QHostAddress.Any, 8000) self.proxy_server.newConnection.connect(self.manage_request) if self.debug: self.log.write('Server running on localhost port %s\n\n' % self.port()) def port(self): return self.proxy_server.serverPort() def addr(self): return self.proxy_server.serverAddress().toString() def stopServing(self): if self.debug: self.log.write('Service is stopping...\n\n') # does not "close" the server, just stop listening... self.proxy_server.close() if self.proxy_server.hasPendingConnections(): socket = self.proxy_server.nextPendingConnection() while socket: socket.abort() socket = self.proxy_server.nextPendingConnection() def manage_request(self): proxy_server = self.sender() # Qt docs says that the caller of nextPendingConnection() # is the parent of the socket socket = proxy_server.nextPendingConnection() socket.readyRead.connect(self.process_request) socket.disconnected.connect(socket.deleteLater) def authorize_request(self, request_data): return True header = QHttpRequestHeader(QString(request_data)) if self.debug: self.log.write(header.toString()) auth = header.value('Proxy-Authorization') if not auth: return False challenge = base64.b64encode(self.username + ':' + self.password) return challenge == str(auth).split()[1] def process_request(self): socket = self.sender() request_data = socket.readAll() if not self.authorize_request(request_data): socket.write('HTTP/1.1 407 Proxy Authentication Required\r\n') if self.debug: self.log.write('407 Proxy Authentication Required\n\n') socket.write('Proxy-Authenticate: Basic realm="test"\r\n') socket.write('\r\n') socket.disconnectFromHost() return else: # remove Proxy-Authorization header start = request_data.indexOf('Proxy-Authorization:') end = request_data.lastIndexOf('\r\n') request_data.remove(start, end) request_data.append('\r\n') pos = request_data.indexOf('\r\n') request_line = request_data.left(pos) request_data.remove(0, pos + 2) entries = request_line.split(' ') method = entries[0] address = entries[1] version = entries[2] port = '80' if address.count(':') > 1: protocol, host, port = address.split(':') else: protocol, host = address.split(':') print('address' + str(address)) #url = QUrl( protocol + host ) url = QUrl.fromEncoded(address) #url.setHost( host ) #url.setPort( int(port) ) if not url.isValid(): if self.debug: self.log.write('Invalid URL: %s\n\n', url) socket.disconnectFromHost() return host = url.host() port = 80 if (url.port() < 0) else url.port() req = url.encodedPath() if url.hasQuery(): req.append('?').append(url.encodedQuery()) request_line = method + ' ' + req + ' ' + version + '\r\n' request_data.prepend(request_line) if self.debug: self.log.write(method + ' ' + address + ' ' + version + '\n\n') key = host + ':' + QString.number(port) proxy_socket = socket.findChild(QTcpSocket, key) if proxy_socket: proxy_socket.setObjectName(key) proxy_socket.setProperty('url', url) proxy_socket.setProperty('request_data', request_data) proxy_socket.write(request_data) else: proxy_socket = QTcpSocket(socket) proxy_socket.setObjectName(key) proxy_socket.setProperty('url', url) proxy_socket.setProperty('request_data', request_data) proxy_socket.connected.connect(self.send_request) proxy_socket.readyRead.connect(self.transfer_data) proxy_socket.disconnected.connect(self.close_connection) proxy_socket.error.connect(self.close_connection) proxy_socket.connectToHost(host, port) def send_request(self): proxy_socket = self.sender() request_data = proxy_socket.property('request_data').toByteArray() proxy_socket.write(request_data) def transfer_data(self): proxy_socket = self.sender() socket = proxy_socket.parent() socket.write(proxy_socket.readAll()) def close_connection(self): proxy_socket = self.sender() if proxy_socket: socket = proxy_socket.parent() if isinstance(socket, QTcpSocket) and socket: socket.disconnectFromHost() if proxy_socket.error() != QTcpSocket.RemoteHostClosedError: url = proxy_socket.property('url').toUrl() error_string = proxy_socket.errorString() if self.debug: self.log.write('Error for %s %s\n\n' % (url, error_string)) proxy_socket.deleteLater()
class CodeExecutor(object): """ This class is responsible for executing code (when starting Tribler in debug mode). The protocol to execute code is as follows. First, a client that wants to execute some code opens a connection with the TCP server and sends the string: <code in base64 format> <task_id>\n This code will be executed and the result will be sent to the client in the following format: result <result> <task_id>\n. If Tribler crashes, the server sends the following result: crash <stack trace in base64 format> Note that the socket uses the newline as separator. """ def __init__(self, port, shell_variables={}): self.logger = logging.getLogger(self.__class__.__name__) self.tcp_server = QTcpServer() self.sockets = [] self.stack_trace = None if not self.tcp_server.listen(port=port): self.logger.error("Unable to start code execution socket! Error: %s", self.tcp_server.errorString()) else: self.tcp_server.newConnection.connect(self._on_new_connection) self.shell = Console(locals=shell_variables) def _on_new_connection(self): while self.tcp_server.hasPendingConnections(): socket = self.tcp_server.nextPendingConnection() socket.readyRead.connect(self._on_socket_read_ready) socket.disconnected.connect(lambda dc_socket=socket: self._on_socket_disconnect(dc_socket)) self.sockets.append(socket) # If Tribler has crashed, notify the other side immediately if self.stack_trace: self.on_crash(self.stack_trace) def run_code(self, code, task_id): self.shell.runcode(code) stdout = self.shell.stdout.read() stderr = self.shell.stderr.read() self.logger.info("Code execution with task %s finished:", task_id) self.logger.info("Stdout of task %s: %s", task_id, stdout) if 'Traceback' in stderr and 'SystemExit' not in stderr: self.logger.error("Executed code with failure: %s", b64encode(code)) # Determine the return value if 'return_value' not in self.shell.console.locals: return_value = ''.encode('base64') else: return_value = str(self.shell.console.locals['return_value']).encode('base64') for socket in self.sockets: socket.write("result %s %s\n" % (return_value, task_id)) def on_crash(self, exception_text): self.stack_trace = exception_text for socket in self.sockets: socket.write("crash %s\n" % exception_text.encode('base64')) def _on_socket_read_ready(self): data = str(self.sockets[0].readAll()) parts = data.split(" ") if len(parts) != 2: return try: code = parts[0].decode('base64') task_id = parts[1].replace('\n', '') self.run_code(code, task_id) except binascii.Error: self.logger.error("Invalid base64 code string received!") def _on_socket_disconnect(self, socket): self.sockets.remove(socket)