From 3647a7e01f75e2277ea564b16ea8edd144eec83e Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Thu, 14 Sep 2023 17:24:11 +0800 Subject: [PATCH] Add trojan import/export support Signed-off-by: Loren Eteval --- Furious/Action/Export.py | 59 +++-- Furious/Action/Import.py | 73 +++++- Furious/Core/Configuration.py | 431 ++++++++++++++++++++++------------ Furious/Core/Intellisense.py | 28 ++- Furious/Utility/Constants.py | 2 + Furious/Utility/Utility.py | 10 +- 6 files changed, 420 insertions(+), 183 deletions(-) diff --git a/Furious/Action/Export.py b/Furious/Action/Export.py index bbceafa..3a35472 100644 --- a/Furious/Action/Export.py +++ b/Furious/Action/Export.py @@ -38,12 +38,33 @@ class ExportLinkResultBox(MessageBox): self.setWindowTitle(_('Export')) - if len(self.failureStr): - self.setIcon(MessageBox.Icon.Critical) - else: - self.setIcon(MessageBox.Icon.Information) - def getText(self): + text = self.getTextAll() + + if len(text) <= 1000: + return text + else: + # Limited + if len(self.successStr) == 0: + return ( + _('Export as QR code failed:') + if self.isQRCodeExport + else _('Export share link to clipboard failed:') + ) + f'\n\n...' + elif len(self.failureStr) == 0: + return ( + _('Export as QR code success:') + if self.isQRCodeExport + else _('Export share link to clipboard success:') + ) + f'\n\n...' + else: + return ( + _('Export as QR code partially success:') + if self.isQRCodeExport + else _('Export share link to clipboard partially success:') + ) + f'\n\n...' + + def getTextAll(self): if len(self.successStr) == 0: return ( ( @@ -151,6 +172,20 @@ def exportLink(selectedIndex): return serverLink, successStr, failureStr +def showExportLinkResult(resultBox, successStr, failureStr): + resultBox.successStr = successStr + resultBox.failureStr = failureStr + resultBox.setText(resultBox.getText()) + + if len(successStr) == 0: + resultBox.setIcon(MessageBox.Icon.Critical) + else: + resultBox.setIcon(MessageBox.Icon.Information) + + # Show the MessageBox and wait for user to close it + resultBox.exec() + + class ExportLinkAction(Action): def __init__(self, **kwargs): super().__init__(_('Export Share Link To Clipboard'), **kwargs) @@ -168,12 +203,7 @@ class ExportLinkAction(Action): QApplication.clipboard().setText('\n'.join(serverLink)) - self.exportLinkResult.successStr = successStr - self.exportLinkResult.failureStr = failureStr - self.exportLinkResult.setText(self.exportLinkResult.getText()) - - # Show the MessageBox and wait for user to close it - self.exportLinkResult.exec() + showExportLinkResult(self.exportLinkResult, successStr, failureStr) class ExportQRCodeAction(Action): @@ -201,12 +231,7 @@ class ExportQRCodeAction(Action): self.exportQRCode.initTabWithData(list(zip(successStr, serverLink))) self.exportQRCode.show() - self.exportLinkResult.successStr = successStr - self.exportLinkResult.failureStr = failureStr - self.exportLinkResult.setText(self.exportLinkResult.getText()) - - # Show the MessageBox and wait for user to close it - self.exportLinkResult.exec() + showExportLinkResult(self.exportLinkResult, successStr, failureStr) class ExportJSONAction(Action): diff --git a/Furious/Action/Import.py b/Furious/Action/Import.py index 69a026b..934ea70 100644 --- a/Furious/Action/Import.py +++ b/Furious/Action/Import.py @@ -20,6 +20,7 @@ from Furious.Core.Configuration import ( XrayCoreConfiguration, ProxyOutboundObject, ProxyOutboundObjectSS, + ProxyOutboundObjectTrojan, ) from Furious.Gui.Action import Action from Furious.Widget.Widget import Menu, MessageBox @@ -110,6 +111,15 @@ class ImportLinkResultBox(MessageBox): self.setText(self.getText()) def getText(self): + text = self.getTextAll() + + if len(text) <= 1000: + return text + else: + # Limited + return _('Import share link success: ') + f'\n\n...' + + def getTextAll(self): return ( _('Import share link success: ') + f'\n\n' @@ -142,7 +152,9 @@ class ImportLinkAction(Action): def showLinkErrorBox(self): self.linkErrorBox.setWindowTitle(_('Import')) + if len(self.clipboard) > 1000: + # Limited self.linkErrorBox.setText(_('Invalid share link.')) self.linkErrorBox.setInformativeText('') else: @@ -359,6 +371,56 @@ class ImportLinkAction(Action): return self.parseShareLinkSIP002(data) + @staticmethod + def parseShareLinkTrojan(data): + try: + parseResult = urllib.parse.urlparse(data) + + queryObject = { + key: value for key, value in urllib.parse.parse_qsl(parseResult.query) + } + + remark = urllib.parse.unquote(parseResult.fragment) + + password, address, port = re.split(r'[@:]', parseResult.netloc) + + type_ = queryObject.get('type', 'tcp') + security = queryObject.get('security', 'none') + + # Remove redundant items + queryObject.pop('type', '') + queryObject.pop('security', '') + + myJSON = XrayCoreConfiguration.build( + ProxyOutboundObjectTrojan( + password, + address, + int(port), + type_, + security, + # kwargs + **queryObject, + ) + ) + + APP().ServerWidget.importServer( + remark, + ujson.dumps( + myJSON, indent=2, ensure_ascii=False, escape_forward_slashes=False + ), + ) + + logger.info( + f'import share link from clipboard success. ' + f'Remark: {remark}. Protocol: {Protocol.Trojan}' + ) + + return remark, True + except Exception: + # Any non-exit exceptions + + return '', False + def parseShareLink(self, shareLink): try: myHead, myBody = shareLink.split('://') @@ -378,13 +440,17 @@ class ImportLinkAction(Action): else: return self.parseShareLinkVMess(myJSON, isV2rayN=True) - if myHead.lower() == 'vless': + elif myHead.lower() == 'vless': return self.parseShareLinkStandard('vless', shareLink) - if myHead.lower() == 'ss': + elif myHead.lower() == 'ss': return self.parseShareLinkSS(shareLink) - return '', False + elif myHead.lower() == 'trojan': + return self.parseShareLinkTrojan(shareLink) + + else: + return '', False def triggeredCallback(self, checked): self.clipboard = QApplication.clipboard().text().strip() @@ -446,6 +512,7 @@ class ImportJSONAction(Action): self.jsonErrorBox.setWindowTitle(_('Import')) if len(self.clipboard) > 1000: + # Limited self.jsonErrorBox.setText(_('Invalid JSON data.')) self.jsonErrorBox.setInformativeText('') else: diff --git a/Furious/Core/Configuration.py b/Furious/Core/Configuration.py index 17c9c7a..1d4251c 100644 --- a/Furious/Core/Configuration.py +++ b/Furious/Core/Configuration.py @@ -18,7 +18,7 @@ from Furious.Core.Core import XrayCore from Furious.Core.Intellisense import Intellisense from Furious.Widget.Widget import MessageBox -from Furious.Utility.Constants import APPLICATION_NAME +from Furious.Utility.Constants import APPLICATION_NAME, PROXY_OUTBOUND_USER_EMAIL from Furious.Utility.Utility import Base64Encoder, Protocol, bootstrapIcon from Furious.Utility.Translator import gettext as _ @@ -42,6 +42,7 @@ class Configuration: super().__init__(*args, **kwargs) @staticmethod + @functools.lru_cache(32) def toJSON(text): return ujson.loads(text) @@ -72,23 +73,20 @@ class Configuration: def export(remark, ob): if Intellisense.getCoreType(ob) == XrayCore.name(): - def hasKey(obj, key): - return obj.get(key) is not None - - def getStreamNetSettings(protocol, streamObject, netType): + def streamNetSettingsVMess(streamObject, netType): kwargs = {} - netobj = streamObject[ - ProxyOutboundObject.getStreamNetworkSettingsName(netType) - ] + netobj = streamObject.get(ProxyOutboundObject.streamNetworkKey(netType)) + + if netobj is None: + return kwargs + + def hasKey(key): + return netobj.get(key) is not None if netType == 'tcp': try: - if protocol == Protocol.VMess: - kwargs['type'] = netobj['header']['type'] - - if protocol == Protocol.VLESS: - kwargs['headerType'] = netobj['header']['type'] + kwargs['type'] = netobj['header']['type'] except Exception: # Any non-exit exceptions @@ -97,17 +95,10 @@ class Configuration: elif netType == 'kcp': try: # Get order matters here - if protocol == Protocol.VMess: - if hasKey(netobj, 'seed'): - kwargs['path'] = netobj['seed'] + if hasKey('seed'): + kwargs['path'] = netobj['seed'] - kwargs['type'] = netobj['header']['type'] - - if protocol == Protocol.VLESS: - if hasKey(netobj, 'seed'): - kwargs['seed'] = netobj['seed'] - - kwargs['headerType'] = netobj['header']['type'] + kwargs['type'] = netobj['header']['type'] except Exception: # Any non-exit exceptions @@ -116,7 +107,7 @@ class Configuration: elif netType == 'ws': try: # Get order matters here - if hasKey(netobj, 'path'): + if hasKey('path'): kwargs['path'] = quote(netobj['path']) if netobj['headers']['Host']: @@ -129,7 +120,7 @@ class Configuration: elif netType == 'h2' or netType == 'http': try: # Get order matters here - if hasKey(netobj, 'path'): + if hasKey('path'): kwargs['path'] = quote(netobj['path']) kwargs['host'] = quote(','.join(netobj['host'])) @@ -141,48 +132,108 @@ class Configuration: elif netType == 'quic': try: # Get order matters here - if protocol == Protocol.VMess: - if hasKey(netobj, 'security'): - kwargs['host'] = netobj['security'] + if hasKey('security'): + kwargs['host'] = netobj['security'] - if hasKey(netobj, 'key'): - kwargs['path'] = quote(netobj['key']) + if hasKey('key'): + kwargs['path'] = quote(netobj['key']) - kwargs['type'] = netobj['header']['type'] - - if protocol == Protocol.VLESS: - if hasKey(netobj, 'security'): - kwargs['quicSecurity'] = netobj['security'] - - if hasKey(netobj, 'key'): - kwargs['path'] = quote(netobj['key']) - - kwargs['headerType'] = netobj['header']['type'] + kwargs['type'] = netobj['header']['type'] except Exception: # Any non-exit exceptions pass elif netType == 'grpc': - if protocol == Protocol.VMess: - if hasKey(netobj, 'serviceName'): - kwargs['path'] = netobj['serviceName'] - - if protocol == Protocol.VLESS: - if hasKey(netobj, 'serviceName'): - kwargs['serviceName'] = netobj['serviceName'] + if hasKey('serviceName'): + kwargs['path'] = netobj['serviceName'] return kwargs - def getStreamTLSSettings(streamObject, tlsType): - if tlsType == 'none': - return {} - + def streamNetSettingsVLESS(streamObject, netType): kwargs = {} - tlsobj = streamObject[ - ProxyOutboundObject.getStreamTLSSettingsName(tlsType) - ] + netobj = streamObject.get(ProxyOutboundObject.streamNetworkKey(netType)) + + if netobj is None: + return kwargs + + def hasKey(key): + return netobj.get(key) is not None + + if netType == 'tcp': + try: + kwargs['headerType'] = netobj['header']['type'] + except Exception: + # Any non-exit exceptions + + pass + + elif netType == 'kcp': + try: + # Get order matters here + if hasKey('seed'): + kwargs['seed'] = netobj['seed'] + + kwargs['headerType'] = netobj['header']['type'] + except Exception: + # Any non-exit exceptions + + pass + + elif netType == 'ws': + try: + # Get order matters here + if hasKey('path'): + kwargs['path'] = quote(netobj['path']) + + if netobj['headers']['Host']: + kwargs['host'] = quote(netobj['headers']['Host']) + except Exception: + # Any non-exit exceptions + + pass + + elif netType == 'h2' or netType == 'http': + try: + # Get order matters here + if hasKey('path'): + kwargs['path'] = quote(netobj['path']) + + kwargs['host'] = quote(','.join(netobj['host'])) + except Exception: + # Any non-exit exceptions + + pass + + elif netType == 'quic': + try: + # Get order matters here + if hasKey('security'): + kwargs['quicSecurity'] = netobj['security'] + + if hasKey('key'): + kwargs['path'] = quote(netobj['key']) + + kwargs['headerType'] = netobj['header']['type'] + except Exception: + # Any non-exit exceptions + + pass + + elif netType == 'grpc': + if hasKey('serviceName'): + kwargs['serviceName'] = netobj['serviceName'] + + return kwargs + + def streamTLSSettings(streamObject, tlsType): + kwargs = {} + + if tlsType == 'none': + return kwargs + + tlsobj = streamObject[ProxyOutboundObject.streamTLSKey(tlsType)] if tlsType == 'reality' or tlsType == 'tls': if tlsobj.get('fingerprint'): @@ -207,109 +258,142 @@ class Configuration: return kwargs + def getProxyOutboundObject(): + for outboundObject in ob['outbounds']: + if outboundObject['tag'] == 'proxy': + return outboundObject + + raise Exception('No proxy outbound found') + # Begin export - coreProtocol = Intellisense.getCoreProtocol(ob) - if coreProtocol == Protocol.VMess or coreProtocol == Protocol.VLESS: - proxyOutbound = None + outbound = getProxyOutboundObject() - for outbound in ob['outbounds']: - if outbound['tag'] == 'proxy': - proxyOutbound = outbound - - break - - if proxyOutbound is None: - raise Exception('No proxy outbound found') - - proxyServer = proxyOutbound['settings']['vnext'][0] + def exportVMess(): + proxyServer = outbound['settings']['vnext'][0] proxyServerUser = proxyServer['users'][0] - proxyStream = proxyOutbound['streamSettings'] - proxyStreamNet = proxyStream['network'] - proxyStreamTLS = proxyStream['security'] + proxyStream, proxyStreamNet, proxyStreamTLS = ( + outbound['streamSettings'], + outbound['streamSettings']['network'], + outbound['streamSettings']['security'], + ) - if coreProtocol == Protocol.VMess: - return ( - 'vmess://' - + Base64Encoder.encode( - ujson.dumps( - { - 'v': '2', - 'ps': quote(remark), - 'add': proxyServer['address'], - 'port': proxyServer['port'], - 'id': proxyServerUser['id'], - 'aid': proxyServerUser['alterId'], - 'scy': proxyServerUser['security'], - 'net': proxyStreamNet, - 'tls': proxyStreamTLS, - # kwargs - **getStreamNetSettings( - coreProtocol, proxyStream, proxyStreamNet - ), - **getStreamTLSSettings(proxyStream, proxyStreamTLS), - }, - ensure_ascii=False, - escape_forward_slashes=False, - ).encode() - ).decode() - ) + return ( + 'vmess://' + + Base64Encoder.encode( + ujson.dumps( + { + 'v': '2', + 'ps': quote(remark), + 'add': proxyServer['address'], + 'port': proxyServer['port'], + 'id': proxyServerUser['id'], + 'aid': proxyServerUser['alterId'], + 'scy': proxyServerUser['security'], + 'net': proxyStreamNet, + 'tls': proxyStreamTLS, + # kwargs + **streamNetSettingsVMess(proxyStream, proxyStreamNet), + **streamTLSSettings(proxyStream, proxyStreamTLS), + }, + ensure_ascii=False, + escape_forward_slashes=False, + ).encode() + ).decode() + ) - if coreProtocol == Protocol.VLESS: - flowArg = {} + def exportVLESS(): + proxyServer = outbound['settings']['vnext'][0] + proxyServerUser = proxyServer['users'][0] - if proxyServerUser.get('flow'): - flowArg['flow'] = proxyServerUser['flow'] + proxyStream, proxyStreamNet, proxyStreamTLS = ( + outbound['streamSettings'], + outbound['streamSettings']['network'], + outbound['streamSettings']['security'], + ) - netloc = f'{proxyServerUser["id"]}@{proxyServer["address"]}:{proxyServer["port"]}' + flowArg = {} - query = '&'.join( - f'{key}={value}' - for key, value in { - 'encryption': proxyServerUser['encryption'], - 'type': proxyStreamNet, - 'security': proxyStreamTLS, - # kwargs - **flowArg, - **getStreamNetSettings( - coreProtocol, proxyStream, proxyStreamNet - ), - **getStreamTLSSettings(proxyStream, proxyStreamTLS), - }.items() - ) + if proxyServerUser.get('flow'): + flowArg['flow'] = proxyServerUser['flow'] - return urlunparse(['vless', netloc, '', '', query, quote(remark)]) + netloc = f'{proxyServerUser["id"]}@{proxyServer["address"]}:{proxyServer["port"]}' - if coreProtocol == Protocol.Shadowsocks: - proxyOutbound = None + query = '&'.join( + f'{key}={value}' + for key, value in { + 'encryption': proxyServerUser['encryption'], + 'type': proxyStreamNet, + 'security': proxyStreamTLS, + # kwargs + **flowArg, + **streamNetSettingsVLESS(proxyStream, proxyStreamNet), + **streamTLSSettings(proxyStream, proxyStreamTLS), + }.items() + ) - for outbound in ob['outbounds']: - if outbound['tag'] == 'proxy': - proxyOutbound = outbound + return urlunparse(['vless', netloc, '', '', query, quote(remark)]) - break + def exportSS(): + proxyServer = outbound['settings']['servers'][0] - if proxyOutbound is None: - raise Exception('No proxy outbound found') - - proxyServer = proxyOutbound['settings']['servers'][0] - - method = proxyServer['method'] - password = proxyServer['password'] - address = proxyServer['address'] - port = proxyServer['port'] + method, password, address, port = ( + proxyServer['method'], + proxyServer['password'], + proxyServer['address'], + proxyServer['port'], + ) netloc = f'{quote(method)}:{quote(password)}@{address}:{port}' return urlunparse(['ss', netloc, '', '', '', quote(remark)]) - else: - raise UnsupportedServerExport('Unsupported core protocol export') + + def exportTrojan(): + proxyServer = outbound['settings']['servers'][0] + + password, address, port = ( + proxyServer['password'], + proxyServer['address'], + proxyServer['port'], + ) + + proxyStream, proxyStreamNet, proxyStreamTLS = ( + outbound['streamSettings'], + outbound['streamSettings']['network'], + outbound['streamSettings']['security'], + ) + + netloc = f'{quote(password)}@{address}:{port}' + + query = '&'.join( + f'{key}={value}' + for key, value in { + 'type': proxyStreamNet, + 'security': proxyStreamTLS, + # kwargs + **streamNetSettingsVLESS(proxyStream, proxyStreamNet), + **streamTLSSettings(proxyStream, proxyStreamTLS), + }.items() + ) + + return urlunparse(['trojan', netloc, '', '', query, quote(remark)]) + + exportFuncMap = { + Protocol.VMess: exportVMess, + Protocol.VLESS: exportVLESS, + Protocol.Shadowsocks: exportSS, + Protocol.Trojan: exportTrojan, + } + + return exportFuncMap[Intellisense.getCoreProtocol(ob)]() + + raise UnsupportedServerExport('Unsupported core protocol export') class OutboundObject: - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self): + super().__init__() def build(self): raise NotImplementedError @@ -377,12 +461,13 @@ class ProxyOutboundObject(OutboundObject): return TLSObject @staticmethod - def getStreamTLSSettingsName(security): + def streamTLSKey(security): # tlsSettings, realitySettings return f'{security}Settings' @staticmethod - def getStreamNetworkSettingsName(type_): + @functools.lru_cache(None) + def streamNetworkKey(type_): if type_ == 'h2': return 'httpSettings' else: @@ -483,7 +568,7 @@ class ProxyOutboundObject(OutboundObject): 'id': self.uuid_, 'security': self.encryption, # Extension - 'email': 'user@Furious.GUI', + 'email': PROXY_OUTBOUND_USER_EMAIL, } # For VMess(V2rayN share standard) only. @@ -497,7 +582,7 @@ class ProxyOutboundObject(OutboundObject): 'id': self.uuid_, 'encryption': self.encryption, # Extension - 'email': 'user@Furious.GUI', + 'email': PROXY_OUTBOUND_USER_EMAIL, } if self.kwargs.get('flow'): @@ -508,14 +593,15 @@ class ProxyOutboundObject(OutboundObject): return {} - def build(self): - myJSON = { + def getDefaultJSON(self): + return { 'tag': 'proxy', 'protocol': self.protocol, 'settings': { 'vnext': [ { 'address': self.remote_host, + # self.remote_port is already an integer 'port': self.remote_port, 'users': [ self.getUserObject(), @@ -533,40 +619,41 @@ class ProxyOutboundObject(OutboundObject): }, } + def build(self): + myJSON = self.getDefaultJSON() + if self.security != 'none': # tlsSettings, realitySettings myJSON['streamSettings'][ - ProxyOutboundObject.getStreamTLSSettingsName(self.security) + ProxyOutboundObject.streamTLSKey(self.security) ] = self.getTLSSettings(self.security) # Stream network settings myJSON['streamSettings'][ - ProxyOutboundObject.getStreamNetworkSettingsName(self.type_) + ProxyOutboundObject.streamNetworkKey(self.type_) ] = self.getStreamNetworkSettings() return myJSON -class ProxyOutboundObjectSS(OutboundObject): - def __init__(self, method, password, address, port, **kwargs): - super().__init__(**kwargs) +class ProxyOutboundObjectSS(ProxyOutboundObject): + def __init__(self, method, password, address, port): + super().__init__('shadowsocks', address, port, password, '', 'tcp', '') self.method = method - self.password = password - self.address = address - self.port = int(port) - def build(self): - myJSON = { + def getDefaultJSON(self): + return { 'tag': 'proxy', - 'protocol': 'shadowsocks', + 'protocol': self.protocol, 'settings': { 'servers': [ { - 'address': self.address, - 'port': self.port, + 'address': self.remote_host, + 'port': int(self.remote_port), 'method': self.method, - 'password': self.password, + 'password': self.uuid_, + 'email': PROXY_OUTBOUND_USER_EMAIL, 'ota': False, }, ] @@ -580,7 +667,39 @@ class ProxyOutboundObjectSS(OutboundObject): }, } - return myJSON + def build(self): + return self.getDefaultJSON() + + +class ProxyOutboundObjectTrojan(ProxyOutboundObject): + def __init__(self, password, address, port, type_, security, **kwargs): + super().__init__( + 'trojan', address, port, password, '', type_, security, **kwargs + ) + + def getDefaultJSON(self): + return { + 'tag': 'proxy', + 'protocol': self.protocol, + 'settings': { + 'servers': [ + { + 'address': self.remote_host, + 'port': int(self.remote_port), + 'password': self.uuid_, + 'email': PROXY_OUTBOUND_USER_EMAIL, + }, + ] + }, + 'streamSettings': { + 'network': self.type_, + 'security': self.security, + }, + 'mux': { + 'enabled': False, + 'concurrency': -1, + }, + } class Outbounds: diff --git a/Furious/Core/Intellisense.py b/Furious/Core/Intellisense.py index 41097b5..205fa21 100644 --- a/Furious/Core/Intellisense.py +++ b/Furious/Core/Intellisense.py @@ -70,6 +70,8 @@ class Intellisense: if outbound['tag'] == 'proxy': return protocolRepr(outbound['protocol']) + return '' + if Intellisense.getCoreType(ob) == Hysteria1.name(): return Protocol.Hysteria1 @@ -86,10 +88,10 @@ class Intellisense: def getCoreAddr(ob): try: if Intellisense.getCoreType(ob) == XrayCore.name(): - protocol = Intellisense.getCoreProtocol(ob) - for outbound in ob['outbounds']: if outbound['tag'] == 'proxy': + protocol = protocolRepr(outbound['protocol']) + if protocol == Protocol.VMess or protocol == Protocol.VLESS: return ';'.join( list( @@ -98,7 +100,10 @@ class Intellisense: ) ) - if protocol == Protocol.Shadowsocks: + if ( + protocol == Protocol.Shadowsocks + or protocol == Protocol.Trojan + ): return ';'.join( list( f'{server["address"]}' @@ -106,6 +111,8 @@ class Intellisense: ) ) + return '' + if Intellisense.getCoreType(ob) == Hysteria1.name(): return ob['server'].split(':')[0] @@ -122,10 +129,10 @@ class Intellisense: def getCorePort(ob): try: if Intellisense.getCoreType(ob) == XrayCore.name(): - protocol = Intellisense.getCoreProtocol(ob) - for outbound in ob['outbounds']: if outbound['tag'] == 'proxy': + protocol = protocolRepr(outbound['protocol']) + if protocol == Protocol.VMess or protocol == Protocol.VLESS: return ';'.join( list( @@ -134,7 +141,10 @@ class Intellisense: ) ) - if protocol == Protocol.Shadowsocks: + if ( + protocol == Protocol.Shadowsocks + or protocol == Protocol.Trojan + ): return ';'.join( list( f'{server["port"]}' @@ -142,6 +152,8 @@ class Intellisense: ) ) + return '' + if Intellisense.getCoreType(ob) == Hysteria1.name(): return ob['server'].split(':')[1] @@ -162,6 +174,8 @@ class Intellisense: if outbound['tag'] == 'proxy': return outbound['streamSettings']['network'] + return '' + return '' except Exception: # Any non-exit exceptions @@ -176,6 +190,8 @@ class Intellisense: if outbound['tag'] == 'proxy': return outbound['streamSettings']['security'] + return '' + return '' except Exception: # Any non-exit exceptions diff --git a/Furious/Utility/Constants.py b/Furious/Utility/Constants.py index dfccb98..19d678e 100644 --- a/Furious/Utility/Constants.py +++ b/Furious/Utility/Constants.py @@ -37,6 +37,8 @@ APPLICATION_REPO_NAME = 'Furious' ORGANIZATION_NAME = 'Furious' ORGANIZATION_DOMAIN = 'Furious.GUI' +PROXY_OUTBOUND_USER_EMAIL = f'user@{ORGANIZATION_DOMAIN}' + PLATFORM = platform.system() LOCAL_SERVER_NAME = '891ad49d-8996-43cb-820c-d9baf42a04de' diff --git a/Furious/Utility/Utility.py b/Furious/Utility/Utility.py index d1f9926..4b73343 100644 --- a/Furious/Utility/Utility.py +++ b/Furious/Utility/Utility.py @@ -45,10 +45,12 @@ logger = logging.getLogger(__name__) class Base64Encoder: @staticmethod + @functools.lru_cache(32) def encode(text): return pybase64.b64encode(text) @staticmethod + @functools.lru_cache(32) def decode(text): return pybase64.b64decode(text, validate=False) @@ -56,7 +58,8 @@ class Base64Encoder: class Protocol: VMess = 'VMess' VLESS = 'VLESS' - Shadowsocks = 'shadowsocks' + Shadowsocks = 'Shadowsocks' + Trojan = 'Trojan' Hysteria1 = 'hysteria1' Hysteria2 = 'hysteria2' @@ -226,6 +229,7 @@ class AsyncSubprocessMessage: return self.msgQueue.get_nowait() except queue.Empty: # Queue is empty + return '' @@ -328,9 +332,13 @@ def protocolRepr(protocol): if protocol.lower() == 'shadowsocks': return Protocol.Shadowsocks + if protocol.lower() == 'trojan': + return Protocol.Trojan + return protocol +@functools.lru_cache(None) def isValidIPAddress(address): try: ipaddress.ip_address(address)