mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-10-05 05:17:59 +03:00
1507 lines
42 KiB
Python
1507 lines
42 KiB
Python
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
|
||
#
|
||
# This file is part of Furious.
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
from __future__ import annotations
|
||
|
||
from Furious.Interface import *
|
||
from Furious.Library import *
|
||
from Furious.QtFramework import *
|
||
from Furious.QtFramework import gettext as _
|
||
from Furious.Utility import *
|
||
from Furious.Widget.GuiVTLS import *
|
||
|
||
from PySide6.QtWidgets import *
|
||
|
||
from typing import Callable
|
||
|
||
__all__ = ['GuiVTransportQGroupBox']
|
||
|
||
STREAM_NETWORK = [
|
||
'tcp',
|
||
'kcp',
|
||
'ws',
|
||
'h2',
|
||
'quic',
|
||
'grpc',
|
||
'httpupgrade',
|
||
'splithttp',
|
||
'xhttp',
|
||
]
|
||
|
||
|
||
class GuiVTransportItemNetwork(GuiEditorItemTextComboBox):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
self.addItems(STREAM_NETWORK)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
try:
|
||
oldNetwork = streamSettings['network']
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldNetwork = ''
|
||
|
||
newNetwork = self.text()
|
||
|
||
def setNewNetwork():
|
||
streamSettings['network'] = newNetwork
|
||
|
||
for network in STREAM_NETWORK:
|
||
if network == newNetwork:
|
||
continue
|
||
|
||
if network == 'h2':
|
||
networkKey = 'httpSettings'
|
||
else:
|
||
networkKey = f'{network}Settings'
|
||
|
||
# Remove irrelevant settings
|
||
streamSettings.pop(networkKey, None)
|
||
|
||
if isinstance(oldNetwork, str):
|
||
if newNetwork != oldNetwork:
|
||
setNewNetwork()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewNetwork()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
self.setText(streamSettings.get('network', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemTypeXXX(GuiEditorItemTextComboBox):
|
||
def __init__(self, *args, **kwargs):
|
||
networkKey = kwargs.pop('networkKey', '')
|
||
|
||
super().__init__(*args, **kwargs)
|
||
|
||
self.networkKey = networkKey
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get(self.networkKey), dict):
|
||
streamSettings[self.networkKey] = {}
|
||
|
||
xxxObject = streamSettings[self.networkKey]
|
||
|
||
try:
|
||
oldType = xxxObject['header']['type']
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldType = ''
|
||
|
||
newType = self.text()
|
||
|
||
def setNewType():
|
||
if not isinstance(xxxObject.get('header'), dict):
|
||
xxxObject['header'] = {}
|
||
|
||
if newType == '':
|
||
xxxObject['header'].pop('type', None)
|
||
else:
|
||
xxxObject['header']['type'] = newType
|
||
|
||
if isinstance(oldType, str):
|
||
if newType != oldType:
|
||
setNewType()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewType()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
xxxObject = getXrayProxyOutboundStream(config)[self.networkKey]
|
||
|
||
self.setText(xxxObject['header']['type'])
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemTypeTcp(GuiVTransportItemTypeXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
networkKey = kwargs.pop('networkKey', 'tcpSettings')
|
||
|
||
super().__init__(*args, **kwargs, networkKey=networkKey)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'none',
|
||
'http',
|
||
]
|
||
)
|
||
|
||
|
||
class GuiVTransportItemHostTcp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('tcpSettings'), dict):
|
||
streamSettings['tcpSettings'] = {}
|
||
|
||
tcpObject = streamSettings['tcpSettings']
|
||
|
||
try:
|
||
oldHost = ','.join(tcpObject['header']['request']['headers']['Host'])
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if not isinstance(tcpObject.get('header'), dict):
|
||
tcpObject['header'] = {}
|
||
|
||
if not isinstance(tcpObject['header'].get('request'), dict):
|
||
tcpObject['header']['request'] = {}
|
||
|
||
if not isinstance(tcpObject['header']['request'].get('headers'), dict):
|
||
tcpObject['header']['request']['headers'] = {}
|
||
|
||
if newHost == '':
|
||
tcpObject['header']['request']['headers'].pop('Host', None)
|
||
else:
|
||
tcpObject['header']['request']['headers']['Host'] = newHost.split(',')
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
tcpObject = getXrayProxyOutboundStream(config)['tcpSettings']
|
||
|
||
self.setText(','.join(tcpObject['header']['request']['headers']['Host']))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathTcp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('tcpSettings'), dict):
|
||
streamSettings['tcpSettings'] = {}
|
||
|
||
tcpObject = streamSettings['tcpSettings']
|
||
|
||
try:
|
||
oldPath = ','.join(tcpObject['header']['request']['path'])
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if not isinstance(tcpObject.get('header'), dict):
|
||
tcpObject['header'] = {}
|
||
|
||
if not isinstance(tcpObject['header'].get('request'), dict):
|
||
tcpObject['header']['request'] = {}
|
||
|
||
if newPath == '':
|
||
tcpObject['header']['request'].pop('path', None)
|
||
else:
|
||
tcpObject['header']['request']['path'] = newPath.split(',')
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
tcpObject = getXrayProxyOutboundStream(config)['tcpSettings']
|
||
|
||
self.setText(','.join(tcpObject['header']['request']['path']))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemTypeKcp(GuiVTransportItemTypeXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
networkKey = kwargs.pop('networkKey', 'kcpSettings')
|
||
|
||
super().__init__(*args, **kwargs, networkKey=networkKey)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'none',
|
||
'srtp',
|
||
'utp',
|
||
'wechat-video',
|
||
'dtls',
|
||
'wireguard',
|
||
'dns',
|
||
]
|
||
)
|
||
|
||
|
||
class GuiVTransportItemSeedKcp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('kcpSettings'), dict):
|
||
streamSettings['kcpSettings'] = {}
|
||
|
||
kcpObject = streamSettings['kcpSettings']
|
||
|
||
try:
|
||
oldSeed = kcpObject.get('seed', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldSeed = ''
|
||
|
||
newSeed = self.text()
|
||
|
||
def setNewSeed():
|
||
if newSeed == '':
|
||
kcpObject.pop('seed', None)
|
||
else:
|
||
kcpObject['seed'] = newSeed
|
||
|
||
if isinstance(oldSeed, str):
|
||
if newSeed != oldSeed:
|
||
setNewSeed()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewSeed()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
kcpObject = getXrayProxyOutboundStream(config)['kcpSettings']
|
||
|
||
self.setText(kcpObject.get('seed', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemHostWs(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('wsSettings'), dict):
|
||
streamSettings['wsSettings'] = {}
|
||
|
||
wsObject = streamSettings['wsSettings']
|
||
|
||
try:
|
||
oldHost = wsObject['headers']['Host']
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if not isinstance(wsObject.get('headers'), dict):
|
||
wsObject['headers'] = {}
|
||
|
||
if newHost == '':
|
||
wsObject['headers'].pop('Host', None)
|
||
else:
|
||
wsObject['headers']['Host'] = newHost
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
wsObject = getXrayProxyOutboundStream(config)['wsSettings']
|
||
|
||
self.setText(wsObject['headers']['Host'])
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathWs(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('wsSettings'), dict):
|
||
streamSettings['wsSettings'] = {}
|
||
|
||
wsObject = streamSettings['wsSettings']
|
||
|
||
try:
|
||
oldPath = wsObject.get('path', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if newPath == '':
|
||
wsObject.pop('path', None)
|
||
else:
|
||
wsObject['path'] = newPath
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
wsObject = getXrayProxyOutboundStream(config)['wsSettings']
|
||
|
||
self.setText(wsObject.get('path', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemHostHttpUpgrade(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('httpupgradeSettings'), dict):
|
||
streamSettings['httpupgradeSettings'] = {}
|
||
|
||
httpUpgradeObject = streamSettings['httpupgradeSettings']
|
||
|
||
try:
|
||
oldHost = httpUpgradeObject.get('host', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if newHost == '':
|
||
httpUpgradeObject.pop('host', None)
|
||
else:
|
||
httpUpgradeObject['host'] = newHost
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
httpUpgradeObject = getXrayProxyOutboundStream(config)[
|
||
'httpupgradeSettings'
|
||
]
|
||
|
||
self.setText(httpUpgradeObject.get('host', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathHttpUpgrade(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('httpupgradeSettings'), dict):
|
||
streamSettings['httpupgradeSettings'] = {}
|
||
|
||
httpUpgradeObject = streamSettings['httpupgradeSettings']
|
||
|
||
try:
|
||
oldPath = httpUpgradeObject.get('path', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if newPath == '':
|
||
httpUpgradeObject.pop('path', None)
|
||
else:
|
||
httpUpgradeObject['path'] = newPath
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
httpUpgradeObject = getXrayProxyOutboundStream(config)[
|
||
'httpupgradeSettings'
|
||
]
|
||
|
||
self.setText(httpUpgradeObject.get('path', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemHostSplitHttp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('splithttpSettings'), dict):
|
||
streamSettings['splithttpSettings'] = {}
|
||
|
||
splitHttpObject = streamSettings['splithttpSettings']
|
||
|
||
try:
|
||
oldHost = splitHttpObject.get('host', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if newHost == '':
|
||
splitHttpObject.pop('host', None)
|
||
else:
|
||
splitHttpObject['host'] = newHost
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
splitHttpObject = getXrayProxyOutboundStream(config)['splithttpSettings']
|
||
|
||
self.setText(splitHttpObject.get('host', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathSplitHttp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('splithttpSettings'), dict):
|
||
streamSettings['splithttpSettings'] = {}
|
||
|
||
splitHttpObject = streamSettings['splithttpSettings']
|
||
|
||
try:
|
||
oldPath = splitHttpObject.get('path', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if newPath == '':
|
||
splitHttpObject.pop('path', None)
|
||
else:
|
||
splitHttpObject['path'] = newPath
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
splitHttpObject = getXrayProxyOutboundStream(config)['splithttpSettings']
|
||
|
||
self.setText(splitHttpObject.get('path', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemHostXHttp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||
streamSettings['xhttpSettings'] = {}
|
||
|
||
xhttpObject = streamSettings['xhttpSettings']
|
||
|
||
try:
|
||
oldHost = xhttpObject.get('host', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if newHost == '':
|
||
xhttpObject.pop('host', None)
|
||
else:
|
||
xhttpObject['host'] = newHost
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||
|
||
self.setText(xhttpObject.get('host', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathXHttp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||
streamSettings['xhttpSettings'] = {}
|
||
|
||
xhttpObject = streamSettings['xhttpSettings']
|
||
|
||
try:
|
||
oldPath = xhttpObject.get('path', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if newPath == '':
|
||
xhttpObject.pop('path', None)
|
||
else:
|
||
xhttpObject['path'] = newPath
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||
|
||
self.setText(xhttpObject.get('path', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemModeXHttp(GuiEditorItemTextComboBox):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'auto',
|
||
'packet-up',
|
||
'stream-up',
|
||
]
|
||
)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||
streamSettings['xhttpSettings'] = {}
|
||
|
||
xhttpObject = streamSettings['xhttpSettings']
|
||
|
||
try:
|
||
oldMode = xhttpObject.get('mode', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldMode = ''
|
||
|
||
newMode = self.text()
|
||
|
||
def setNewMode():
|
||
if newMode == '':
|
||
xhttpObject.pop('mode', None)
|
||
else:
|
||
xhttpObject['mode'] = newMode
|
||
|
||
if isinstance(oldMode, str):
|
||
if newMode != oldMode:
|
||
setNewMode()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewMode()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||
|
||
self.setText(xhttpObject.get('mode', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemExtraXHttp(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('xhttpSettings'), dict):
|
||
streamSettings['xhttpSettings'] = {}
|
||
|
||
xhttpObject = streamSettings['xhttpSettings']
|
||
|
||
try:
|
||
if xhttpObject.get('extra', ''):
|
||
oldExtra = UJSONEncoder.encode(xhttpObject.get('extra', ''))
|
||
else:
|
||
oldExtra = ''
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldExtra = ''
|
||
|
||
newExtra = self.text()
|
||
|
||
def setNewExtra():
|
||
if newExtra == '':
|
||
xhttpObject.pop('extra', None)
|
||
else:
|
||
try:
|
||
xhttpObject['extra'] = UJSONEncoder.decode(newExtra)
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
xhttpObject.pop('extra', None)
|
||
|
||
if isinstance(oldExtra, str):
|
||
if newExtra != oldExtra:
|
||
setNewExtra()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewExtra()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
xhttpObject = getXrayProxyOutboundStream(config)['xhttpSettings']
|
||
|
||
if xhttpObject.get('extra', ''):
|
||
self.setText(UJSONEncoder.encode(xhttpObject.get('extra', '')))
|
||
else:
|
||
self.setText('')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemHostH2(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('httpSettings'), dict):
|
||
streamSettings['httpSettings'] = {}
|
||
|
||
httpObject = streamSettings['httpSettings']
|
||
|
||
try:
|
||
oldHost = ','.join(httpObject.get('host', []))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldHost = ''
|
||
|
||
newHost = self.text()
|
||
|
||
def setNewHost():
|
||
if newHost == '':
|
||
httpObject.pop('host', None)
|
||
else:
|
||
httpObject['host'] = newHost.split(',')
|
||
|
||
if isinstance(oldHost, str):
|
||
if newHost != oldHost:
|
||
setNewHost()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewHost()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
httpObject = getXrayProxyOutboundStream(config)['httpSettings']
|
||
|
||
self.setText(','.join(httpObject.get('host', [])))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemPathH2(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('httpSettings'), dict):
|
||
streamSettings['httpSettings'] = {}
|
||
|
||
httpObject = streamSettings['httpSettings']
|
||
|
||
try:
|
||
oldPath = httpObject.get('path', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldPath = ''
|
||
|
||
newPath = self.text()
|
||
|
||
def setNewPath():
|
||
if newPath == '':
|
||
httpObject.pop('path', None)
|
||
else:
|
||
httpObject['path'] = newPath
|
||
|
||
if isinstance(oldPath, str):
|
||
if newPath != oldPath:
|
||
setNewPath()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewPath()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
httpObject = getXrayProxyOutboundStream(config)['httpSettings']
|
||
|
||
self.setText(httpObject.get('path', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemTypeQuic(GuiVTransportItemTypeXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
networkKey = kwargs.pop('networkKey', 'quicSettings')
|
||
|
||
super().__init__(*args, **kwargs, networkKey=networkKey)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'none',
|
||
'srtp',
|
||
'utp',
|
||
'wechat-video',
|
||
'dtls',
|
||
'wireguard',
|
||
]
|
||
)
|
||
|
||
|
||
class GuiVTransportItemSecurityQuic(GuiEditorItemTextComboBox):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'none',
|
||
'aes-128-gcm',
|
||
'chacha20-poly1305',
|
||
]
|
||
)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('quicSettings'), dict):
|
||
streamSettings['quicSettings'] = {}
|
||
|
||
quicObject = streamSettings['quicSettings']
|
||
|
||
try:
|
||
oldSecurity = quicObject.get('security', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldSecurity = ''
|
||
|
||
newSecurity = self.text()
|
||
|
||
def setNewSecurity():
|
||
quicObject['security'] = newSecurity
|
||
|
||
if isinstance(oldSecurity, str):
|
||
if newSecurity != oldSecurity:
|
||
setNewSecurity()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewSecurity()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
quicObject = getXrayProxyOutboundStream(config)['quicSettings']
|
||
|
||
self.setText(quicObject.get('security', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemKeyQuic(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('quicSettings'), dict):
|
||
streamSettings['quicSettings'] = {}
|
||
|
||
quicObject = streamSettings['quicSettings']
|
||
|
||
try:
|
||
oldKey = quicObject.get('key', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldKey = ''
|
||
|
||
newKey = self.text()
|
||
|
||
def setNewKey():
|
||
if newKey == '':
|
||
quicObject.pop('key', None)
|
||
else:
|
||
quicObject['key'] = newKey
|
||
|
||
if isinstance(oldKey, str):
|
||
if newKey != oldKey:
|
||
setNewKey()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewKey()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
quicObject = getXrayProxyOutboundStream(config)['quicSettings']
|
||
|
||
self.setText(quicObject.get('key', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemModeGRPC(GuiEditorItemTextComboBox):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
self.addItems(
|
||
[
|
||
'',
|
||
'gun',
|
||
'multi',
|
||
]
|
||
)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||
streamSettings['grpcSettings'] = {}
|
||
|
||
grpcObject = streamSettings['grpcSettings']
|
||
|
||
try:
|
||
oldMode = grpcObject.get('multiMode', False)
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldMode = False
|
||
|
||
if oldMode is True:
|
||
oldMode = 'multi'
|
||
else:
|
||
oldMode = 'gun'
|
||
|
||
newMode = self.text()
|
||
|
||
def setNewMode():
|
||
if newMode == 'multi':
|
||
grpcObject['multiMode'] = True
|
||
else:
|
||
grpcObject['multiMode'] = False
|
||
|
||
if isinstance(oldMode, str):
|
||
if newMode != oldMode:
|
||
setNewMode()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewMode()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||
|
||
multi = grpcObject.get('multiMode', False)
|
||
|
||
if multi is True:
|
||
self.setText('multi')
|
||
elif multi is False:
|
||
self.setText('gun')
|
||
else:
|
||
self.setText('')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemAuthorityGRPC(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||
streamSettings['grpcSettings'] = {}
|
||
|
||
grpcObject = streamSettings['grpcSettings']
|
||
|
||
try:
|
||
oldAuthority = grpcObject.get('authority', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldAuthority = ''
|
||
|
||
newAuthority = self.text()
|
||
|
||
def setNewAuthority():
|
||
if newAuthority == '':
|
||
grpcObject.pop('authority', None)
|
||
else:
|
||
grpcObject['authority'] = newAuthority
|
||
|
||
if isinstance(oldAuthority, str):
|
||
if newAuthority != oldAuthority:
|
||
setNewAuthority()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewAuthority()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||
|
||
self.setText(grpcObject.get('authority', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportItemServiceNameGRPC(GuiEditorItemTextInput):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
|
||
if not isinstance(streamSettings.get('grpcSettings'), dict):
|
||
streamSettings['grpcSettings'] = {}
|
||
|
||
grpcObject = streamSettings['grpcSettings']
|
||
|
||
try:
|
||
oldServiceName = grpcObject.get('serviceName', '')
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
oldServiceName = ''
|
||
|
||
newServiceName = self.text()
|
||
|
||
def setNewServiceName():
|
||
if newServiceName == '':
|
||
grpcObject.pop('serviceName', None)
|
||
else:
|
||
grpcObject['serviceName'] = newServiceName
|
||
|
||
if isinstance(oldServiceName, str):
|
||
if newServiceName != oldServiceName:
|
||
setNewServiceName()
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
setNewServiceName()
|
||
|
||
return True
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
try:
|
||
grpcObject = getXrayProxyOutboundStream(config)['grpcSettings']
|
||
|
||
self.setText(grpcObject.get('serviceName', ''))
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
self.setText('')
|
||
|
||
|
||
class GuiVTransportPageXXX(GuiEditorWidgetQWidget):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def setNetworkText(self, text: str):
|
||
network = self._containers[0]
|
||
|
||
if isinstance(network, GuiEditorItemTextComboBox):
|
||
network.setText(text)
|
||
|
||
def connectActivated(self, func: Callable):
|
||
network = self._containers[0]
|
||
|
||
if isinstance(network, GuiEditorItemTextComboBox):
|
||
network.connectActivated(func)
|
||
|
||
|
||
class GuiVTransportPageTcp(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemTypeTcp(title='Type', translatable=False),
|
||
GuiVTransportItemHostTcp(title='Host', translatable=False),
|
||
GuiVTransportItemPathTcp(title='Path', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageKcp(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemTypeKcp(title='Type', translatable=False),
|
||
GuiVTransportItemSeedKcp(title='KCP seed', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageWs(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemHostWs(title='Host', translatable=False),
|
||
GuiVTransportItemPathWs(title='Path', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageHttpUpgrade(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemHostHttpUpgrade(title='Host', translatable=False),
|
||
GuiVTransportItemPathHttpUpgrade(title='Path', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageSplitHttp(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemHostSplitHttp(title='Host', translatable=False),
|
||
GuiVTransportItemPathSplitHttp(title='Path', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageXHttp(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemHostXHttp(title='Host', translatable=False),
|
||
GuiVTransportItemPathXHttp(title='Path', translatable=False),
|
||
GuiVTransportItemModeXHttp(title='Mode', translatable=False),
|
||
GuiVTransportItemExtraXHttp(title='Extra', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageH2(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemHostH2(title='Host', translatable=False),
|
||
GuiVTransportItemPathH2(title='Path', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageQuic(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemTypeQuic(title='Type', translatable=False),
|
||
GuiVTransportItemSecurityQuic(title='QUIC Security', translatable=False),
|
||
GuiVTransportItemKeyQuic(title='QUIC Key', translatable=False),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageGRPC(GuiVTransportPageXXX):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def containerSequence(self):
|
||
return [
|
||
GuiVTransportItemNetwork(title='Network', translatable=False),
|
||
GuiVTransportItemModeGRPC(title='gRPC mode', translatable=False),
|
||
GuiVTransportItemAuthorityGRPC(title='gRPC authority', translatable=False),
|
||
GuiVTransportItemServiceNameGRPC(
|
||
title='gRPC serviceName', translatable=False
|
||
),
|
||
]
|
||
|
||
|
||
class GuiVTransportPageStackedWidget(QStackedWidget):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
|
||
# Corresponds to stream network
|
||
self._pages = [
|
||
GuiVTransportPageTcp(),
|
||
GuiVTransportPageKcp(),
|
||
GuiVTransportPageWs(),
|
||
GuiVTransportPageH2(),
|
||
GuiVTransportPageQuic(),
|
||
GuiVTransportPageGRPC(),
|
||
GuiVTransportPageHttpUpgrade(),
|
||
GuiVTransportPageSplitHttp(),
|
||
GuiVTransportPageXHttp(),
|
||
]
|
||
|
||
for page in self._pages:
|
||
self.addWidget(page)
|
||
|
||
def page(self, index: int) -> GuiVTransportPageXXX:
|
||
return self._pages[index]
|
||
|
||
def connectActivated(self, func: Callable):
|
||
for page in self._pages:
|
||
page.connectActivated(func)
|
||
|
||
|
||
class GuiVTransportQGroupBox(GuiEditorItemFactory, AppQGroupBox):
|
||
def __init__(self, **kwargs):
|
||
super().__init__(_('Transport'), **kwargs)
|
||
|
||
self._config = ConfigurationFactory()
|
||
|
||
self._widget = GuiVTransportPageStackedWidget()
|
||
self._widget.connectActivated(self.handleActivated)
|
||
|
||
layout = QFormLayout()
|
||
layout.addRow(self._widget)
|
||
|
||
self.setLayout(layout)
|
||
|
||
def currentIndex(self) -> int:
|
||
return self._widget.currentIndex()
|
||
|
||
def setCurrentIndex(self, index: int):
|
||
self._widget.setCurrentIndex(index)
|
||
|
||
def page(self, index: int) -> GuiVTransportPageXXX:
|
||
return self._widget.page(index)
|
||
|
||
def handleActivated(self, index: int):
|
||
page = self.page(index)
|
||
page.factoryToInput(self._config)
|
||
page.setNetworkText(STREAM_NETWORK[index])
|
||
|
||
self.setCurrentIndex(index)
|
||
|
||
def inputToFactory(self, config: ConfigurationFactory) -> bool:
|
||
return self.page(self.currentIndex()).inputToFactory(config)
|
||
|
||
def factoryToInput(self, config: ConfigurationFactory):
|
||
# Shallow copy
|
||
self._config = config
|
||
|
||
streamSettings = getXrayProxyOutboundStream(config)
|
||
network = streamSettings.get('network', '')
|
||
|
||
if not isinstance(network, str):
|
||
return
|
||
|
||
def alterNetwork(sourceNetwork, targetNetwork):
|
||
nonlocal network
|
||
|
||
if network == sourceNetwork:
|
||
network = targetNetwork
|
||
|
||
streamSettings['network'] = network
|
||
|
||
# Adjust alternative values
|
||
# https://xtls.github.io/config/transport.html#streamsettingsobject
|
||
alterNetwork('http', 'h2')
|
||
alterNetwork('gun', 'grpc')
|
||
alterNetwork('mkcp', 'kcp')
|
||
|
||
try:
|
||
index = STREAM_NETWORK.index(network)
|
||
except Exception:
|
||
# Any non-exit exceptions
|
||
|
||
pass
|
||
else:
|
||
self.page(index).factoryToInput(config)
|
||
self.setCurrentIndex(index)
|