diff --git a/Furious/Externals/GenTranslation.py b/Furious/Externals/GenTranslation.py index 32460c3..56d3274 100644 --- a/Furious/Externals/GenTranslation.py +++ b/Furious/Externals/GenTranslation.py @@ -1091,6 +1091,14 @@ TRANSLATION = { "ZH": "开机启动", "isReviewed": "True" }, + "Force To 127.0.0.1 When Setting Local Proxy": { + "source": [ + "Furious.TrayActions.Settings" + ], + "RU": "Принудительно 127.0.0.1 для локального прокси", + "ZH": "设置本地代理时强制使用127.0.0.1", + "isReviewed": "True" + }, "Automatically Update Asset Files": { "source": [ "Furious.TrayActions.Settings" diff --git a/Furious/Frozenlib/SystemRuntime.py b/Furious/Frozenlib/SystemRuntime.py index ce184e9..43c47b5 100644 --- a/Furious/Frozenlib/SystemRuntime.py +++ b/Furious/Frozenlib/SystemRuntime.py @@ -82,6 +82,11 @@ class SystemRuntime: else: return '' + @staticmethod + @functools.lru_cache(None) + def isAssetsFolderWritable() -> bool: + return os.access(XRAY_ASSET_DIR, os.W_OK) + @staticmethod @functools.lru_cache(None) def isAdmin() -> bool: diff --git a/Furious/Frozenlib/Utility.py b/Furious/Frozenlib/Utility.py index 41a6694..5548686 100644 --- a/Furious/Frozenlib/Utility.py +++ b/Furious/Frozenlib/Utility.py @@ -18,6 +18,7 @@ from __future__ import annotations from Furious.Frozenlib.Constants import * +from Furious.Frozenlib.AppSettings import * from enum import Enum from typing import AnyStr, Tuple @@ -34,6 +35,7 @@ import urllib.parse __all__ = [ 'Protocol', 'callRateLimited', + 'forceToLocalhostIfPossible', 'callOnceOnly', 'classname', 'isValidIPAddress', @@ -82,6 +84,7 @@ def callRateLimited(maxCallPerSecond): called = time.monotonic() def decorator(func): + @functools.wraps(func) def wrapper(*args, **kwargs): # Previously called nonlocal called @@ -102,6 +105,34 @@ def callRateLimited(maxCallPerSecond): return decorator +def forceToLocalhostIfPossible(): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + + if ( + AppSettings.isStateOFF('ForceToLocalhostWhenSettingLocalProxy') + or not isinstance(result, str) + or result == '' + ): + return result + + # Force to localhost according to settings + try: + host, port = parseHostPort(result) + + return f'127.0.0.1:{port}' + except Exception: + # Any non-exit exceptions + + return result + + return wrapper + + return decorator + + def callOnceOnly(func): """ Decorator that ensures a function is only called once. diff --git a/Furious/Library/Storage.py b/Furious/Library/Storage.py index c77cea9..66739b0 100644 --- a/Furious/Library/Storage.py +++ b/Furious/Library/Storage.py @@ -63,6 +63,7 @@ class Storage: class Extras: @staticmethod + @forceToLocalhostIfPossible() def UserHttpProxy() -> Union[str, None]: try: if APP().isSystemTrayConnected(): diff --git a/Furious/TrayActions/Connect.py b/Furious/TrayActions/Connect.py index befee7a..ee53102 100644 --- a/Furious/TrayActions/Connect.py +++ b/Furious/TrayActions/Connect.py @@ -210,7 +210,13 @@ class ConnectAction(AppQAction): else: assert isinstance(config, ConfigFactory) - if not validateProxyServer(config.httpProxy()): + @forceToLocalhostIfPossible() + def getHttpProxy() -> str: + return config.httpProxy() + + httpProxy = getHttpProxy() + + if not validateProxyServer(httpProxy): # Proxy server is not valid. Do not connect AppSettings.turnOFF('Connect') @@ -247,7 +253,7 @@ class ConnectAction(AppQAction): if self.actionQueue.empty(): if success: - SystemProxy.set(config.httpProxy(), PROXY_SERVER_BYPASS) + SystemProxy.set(httpProxy, PROXY_SERVER_BYPASS) self.doConnected() @@ -291,11 +297,13 @@ class ConnectAction(AppQAction): hasNewVersionCallback=newVersionCallback, ) - if SystemRuntime.appImagePath() or SystemRuntime.flatpakID(): + if not SystemRuntime.isAssetsFolderWritable(): logger.info( - 'skipped auto assets update due to application folder not writable' + f'skipped auto assets update due to assets folder \'{XRAY_ASSET_DIR}\' not writable' ) else: + logger.info(f'assets folder \'{XRAY_ASSET_DIR}\' is writable. Continue') + if AppSettings.isStateON_('AutoUpdateAssetFiles'): # Automatically update assets self.assetDownloadManager.configureHttpProxy(connectedHttpProxy) diff --git a/Furious/TrayActions/Settings.py b/Furious/TrayActions/Settings.py index d7d95b2..6cbeb78 100644 --- a/Furious/TrayActions/Settings.py +++ b/Furious/TrayActions/Settings.py @@ -32,6 +32,11 @@ if PLATFORM == 'Darwin': registerAppSettings('StartupOnBoot', isBinary=True, default=AppBinarySettings.ON_) registerAppSettings('PowerSaveMode', isBinary=True, default=AppBinarySettings.ON_) +registerAppSettings( + 'ForceToLocalhostWhenSettingLocalProxy', + isBinary=True, + default=AppBinarySettings.OFF, +) registerAppSettings( 'AutoUpdateAssetFiles', isBinary=True, default=AppBinarySettings.ON_ ) @@ -135,6 +140,13 @@ class SettingsChildAction(AppQAction): else: AppSettings.turnOFF('PowerSaveMode') + showMBoxNewChangesNextTime() + elif self.textCompare('Force To 127.0.0.1 When Setting Local Proxy'): + if checked: + AppSettings.turnON_('ForceToLocalhostWhenSettingLocalProxy') + else: + AppSettings.turnOFF('ForceToLocalhostWhenSettingLocalProxy') + showMBoxNewChangesNextTime() elif self.textCompare('Automatically Update Asset Files'): if checked: @@ -187,7 +199,7 @@ class SettingsAction(AppQAction): else: hideDockIconAction = [] - if SystemRuntime.appImagePath() or SystemRuntime.flatpakID(): + if not SystemRuntime.isAssetsFolderWritable(): autoUpdateAssetFilesAction = [] else: autoUpdateAssetFilesAction = ( @@ -226,6 +238,13 @@ class SettingsAction(AppQAction): checked=AppSettings.isStateON_('PowerSaveMode'), ), AppQSeperator(), + SettingsChildAction( + _('Force To 127.0.0.1 When Setting Local Proxy'), + checkable=True, + checked=AppSettings.isStateON_( + 'ForceToLocalhostWhenSettingLocalProxy' + ), + ), *autoUpdateAssetFilesAction, SettingsChildAction( _('Show Progress Bar When Connecting'),