fix: update code style

This commit is contained in:
Qv2ray-dev 2020-02-28 22:49:53 +08:00
parent 194f07c027
commit 09de96efe5
106 changed files with 4787 additions and 4162 deletions

View File

@ -1,306 +0,0 @@
# ~/.astylerc
#
# Courtesy of HN's super_mario: http://news.ycombinator.com/item?id=5348401
#
# Use K&R formatting style
style=kr
# Indent class and struct blocks so that the blocks 'public', 'private' and
# 'protected' are indented. This option is effective in C++ files only
indent-classes
# Indent 'switch' blocks so that the 'case X:' statements are indented with
# the switch block. The entire case block is indented.
#
# For example:
# switch (foo)
# {
# case 1:
# a += 1;
# break;
#
# case 2:
# {
# a += 2;
# break;
# }
# }
#
# becomes
#
# switch (foo)
# {
# case 1:
# a += 1;
# break;
#
# case 2:
# {
# a += 2;
# break;
# }
# }
indent-switches
# Indent C++ namespaces (this option has no effect on other file types)
# Add extra indentation to namespace blocks.
# For example:
# namespace foospace
# {
# class Foo
# {
# public:
# Foo();
# virtual ~Foo();
# };
# }
#
# becomes
#
# namespace foospace
# {
# class Foo
# {
# public:
# Foo();
# virtual ~Foo();
# };
# }
indent-namespaces
# Indent multi line preprocessor definitions ending with a backslash
# For example:
#
# #define Is_Bar(arg,a,b) \
# (Is_Foo((arg), (a)) \
# || Is_Foo((arg), (b)))
#
# becomes:
#
# #define Is_Bar(arg,a,b) \
# (Is_Foo((arg), (a)) \
# || Is_Foo((arg), (b)))
#
indent-preprocessor
# Indent C++ comments beginning in column one.
# For example
#
# void Foo()\n"
# {
# // comment
# if (isFoo)
# bar();
# }
#
# becomes:
#
# void Foo()\n"
# {
# // comment
# if (isFoo)
# bar();
# }
#
indent-col1-comments
# Pad empty lines around header blocks (e.g. 'if', 'for', 'while'...).
#
# isFoo = true;
# if (isFoo) {
# bar();
# } else {
# anotherBar();
# }
# isBar = false;
#
# becomes:
#
# isFoo = true;
#
# if (isFoo) {
# bar();
# } else {
# anotherBar();
# }
#
# isBar = false;
#
break-blocks
# Insert space padding around operators. Any end of line comments will remain
# in the original column, if possible. Note that there is no option to unpad.
# Once padded, they stay padded.
#
# if (foo==2)
# a=bar((b-c)*a,d--);
#
# becomes:
#
# if (foo == 2)
# a = bar((b - c) * a, d--);
#
pad-oper
# Insert space padding after paren headers only (e.g. 'if', 'for', 'while'...).
# Any end of line comments will remain in the original column, if possible.
# This can be used with unpad-paren to remove unwanted spaces.
#
# if(isFoo(a, b))
# bar(a, b);
#
# becomes:
#
# if (isFoo(a, b))
# bar(a, b);
#
pad-header
# Remove extra space padding around parenthesis on the inside and outside. Any
# end of line comments will remain in the original column, if possible. This
# option can be used in combination with the paren padding options padparen,
# padparenout, padparenin, and padheader above. Only padding that has not
# been requested by other options will be removed.
#
# For example, if a source has parens padded on both the inside and outside,
# and you want inside only. You need to use unpad-paren to remove the outside
# padding, and padparenin to retain the inside padding. Using only
# padparenin would not remove the outside padding.
#
# if ( isFoo( a, b ) )
# bar ( a, b );
#
# becomes (with no padding option requested):
#
# if(isFoo(a, b))
# bar(a, b);
#
unpad-paren
# Delete empty lines within a function or method. Empty lines outside of
# functions or methods are NOT deleted. If used with break-blocks or
# break-blocks=all it will delete all lines EXCEPT the lines added by the
# break-blocks options.
#
# void Foo()
# {
#
# foo1 = 1;
#
# foo2 = 2;
#
# }
#
# becomes:
#
# void Foo()
# {
# foo1 = 1;
# foo2 = 2;
# }
#
delete-empty-lines
# Attach a pointer or reference operator (* or &) to either the variable type
# (left) or variable name (right), or place it between the type and name
# (middle). The spacing between the type and name will be preserved, if
# possible. To format references separately use the following align-reference
# option.
#
# char *foo1;
# char &foo2;
#
# becomes (with align-pointer=type):
#
# char* foo1;
# char& foo2;
#
# char* foo1;
# char& foo2;
#
# becomes (with align-pointer=middle):
#
# char * foo1;
# char & foo2;
#
# char* foo1;
# char& foo2;
#
# becomes (with align-pointer=name):
#
# char *foo1;
# char &foo2;
#
align-pointer=name
# Set the minimal indent that is added when a header is built of multiple
# lines. This indent helps to easily separate the header from the command
# statements that follow. The value for # indicates a number of indents and is
# a minimum value. The indent may be greater to align with the data on the
# previous line.
# The valid values are:
# 0 - no minimal indent. The lines will be aligned with the paren on the
# preceding line.
# 1 - indent at least one additional indent.
# 2 - indent at least two additional indents.
# 3 - indent at least one-half an additional indent. This is intended for large
# indents (e.g. 8).
#
# The default value is 2, two additional indents.
#
# // default setting makes this non-bracketed code clear
# if (a < b
# || c > d)
# foo++;
#
# // but creates an exaggerated indent in this bracketed code
# if (a < b
# || c > d)
# {
# foo++;
# }
#
# becomes (when setting --min-conditional-indent=0):
#
# // setting makes this non-bracketed code less clear
# if (a < b
# || c > d)
# foo++;
#
# // but makes this bracketed code clearer
# if (a < b
# || c > d)
# {
# foo++;
# }
#
min-conditional-indent=0
# Set the maximum of # spaces to indent a continuation line. The # indicates
# a number of columns and must not be greater than 120. If no # is set, the
# default value of 40 will be used. A maximum of less than two indent lengths
# will be ignored. This option will prevent continuation lines from extending
# too far to the right. Setting a larger value will allow the code to be
# extended further to the right.
#
# fooArray[] = { red,
# green,
# blue };
#
# fooFunction(barArg1,
# barArg2,
# barArg3);
#
# becomes (with larger value):
#
# fooArray[] = { red,
# green,
# blue };
#
# fooFunction(barArg1,
# barArg2,
# barArg3);
#
#max-instatement-indent=9

50
_clang-format Normal file
View File

@ -0,0 +1,50 @@
---
BasedOnStyle: Microsoft
AccessModifierOffset: '-2'
AlignAfterOpenBracket: Align
AllowAllArgumentsOnNextLine: 'false'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: 'true'
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'false'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'true'
BinPackParameters: 'true'
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: 'false'
BreakInheritanceList: BeforeComma
BreakStringLiterals: 'false'
ColumnLimit: '145'
CompactNamespaces: 'false'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
Cpp11BracedListStyle: 'false'
ExperimentalAutoDetectBinPacking: 'false'
FixNamespaceComments: 'true'
IncludeBlocks: Regroup
IndentCaseLabels: 'true'
IndentPPDirectives: BeforeHash
IndentWidth: '4'
Language: Cpp
MaxEmptyLinesToKeep: '1'
NamespaceIndentation: All
ReflowComments: 'true'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpaceAfterCStyleCast: 'true'
SpaceAfterTemplateKeyword: 'false'
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: 'true'
SpacesInParentheses: 'false'
Standard: Cpp11
StatementMacros: [ Q_UNUSED LOG DEBUG ]
TabWidth: '4'
UseTab: Never
...

View File

@ -1 +1 @@
4014 4031

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "base/models/QvConfigModel.hpp"
#include "base/models/QvRuntimeConfig.hpp" #include "base/models/QvRuntimeConfig.hpp"
#include "base/models/QvStartupConfig.hpp" #include "base/models/QvStartupConfig.hpp"
#include "base/models/QvConfigModel.hpp"
#include <QTranslator> #include <QTranslator>
@ -18,4 +18,4 @@ namespace Qv2ray
inline base::QvStartupOptions StartupOption = base::QvStartupOptions(); inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
// //
inline std::unique_ptr<QTranslator> Qv2rayTranslator; inline std::unique_ptr<QTranslator> Qv2rayTranslator;
} } // namespace Qv2ray

View File

@ -1,42 +1,42 @@
#pragma once #pragma once
#define STRINGIZE(arg) STRINGIZE1(arg) #define STRINGIZE(arg) STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg) #define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg #define STRINGIZE2(arg) #arg
#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2) arg1##arg2 #define CONCATENATE2(arg1, arg2) arg1##arg2
#define EXPAND(x) x #define EXPAND(x) x
#define FOR_EACH_1(what, x, ...) what(x) #define FOR_EACH_1(what, x, ...) what(x)
#define FOR_EACH_2(what, x, ...)\ #define FOR_EACH_2(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_1(what, __VA_ARGS__)) EXPAND(FOR_EACH_1(what, __VA_ARGS__))
#define FOR_EACH_3(what, x, ...)\ #define FOR_EACH_3(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_2(what, __VA_ARGS__)) EXPAND(FOR_EACH_2(what, __VA_ARGS__))
#define FOR_EACH_4(what, x, ...)\ #define FOR_EACH_4(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_3(what, __VA_ARGS__)) EXPAND(FOR_EACH_3(what, __VA_ARGS__))
#define FOR_EACH_5(what, x, ...)\ #define FOR_EACH_5(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_4(what, __VA_ARGS__)) EXPAND(FOR_EACH_4(what, __VA_ARGS__))
#define FOR_EACH_6(what, x, ...)\ #define FOR_EACH_6(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_5(what, __VA_ARGS__)) EXPAND(FOR_EACH_5(what, __VA_ARGS__))
#define FOR_EACH_7(what, x, ...)\ #define FOR_EACH_7(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_6(what, __VA_ARGS__)) EXPAND(FOR_EACH_6(what, __VA_ARGS__))
#define FOR_EACH_8(what, x, ...)\ #define FOR_EACH_8(what, x, ...) \
what(x);\ what(x); \
EXPAND(FOR_EACH_7(what, __VA_ARGS__)) EXPAND(FOR_EACH_7(what, __VA_ARGS__))
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__)) #define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 #define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y #define CONCATENATE(x, y) x##y
#define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__)) #define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__))
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__) #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
#define JADDEx_(jsonObj, field) jsonObj.insert(#field, field); #define JADDEx_(jsonObj, field) jsonObj.insert(#field, field);

View File

@ -1,30 +1,30 @@
#pragma once #pragma once
// //
#ifndef XTOSTRUCT_QT #ifndef XTOSTRUCT_QT
# define XTOSTRUCT_QT #define XTOSTRUCT_QT
#endif #endif
// //
#include <QtCore>
#include <QtGui>
#include <QApplication> #include <QApplication>
#include <QMap> #include <QMap>
#include <vector> #include <QtCore>
#include <QtGui>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <ctime> #include <ctime>
#include <iostream>
#include <optional> #include <optional>
#include <vector>
// Base support. // Base support.
#include "base/Qv2rayLog.hpp"
#include "base/Qv2rayFeatures.hpp"
#include "base/JsonHelpers.hpp"
#include "base/GlobalInstances.hpp" #include "base/GlobalInstances.hpp"
#include "base/JsonHelpers.hpp"
#include "base/Qv2rayFeatures.hpp"
#include "base/Qv2rayLog.hpp"
// Code Models // Code Models
#include "base/models/QvSafeType.hpp"
#include "base/models/CoreObjectModels.hpp" #include "base/models/CoreObjectModels.hpp"
#include "base/models/QvConfigModel.hpp"
#include "base/models/QvConfigIdentifier.hpp" #include "base/models/QvConfigIdentifier.hpp"
#include "base/models/QvStartupConfig.hpp" #include "base/models/QvConfigModel.hpp"
#include "base/models/QvRuntimeConfig.hpp" #include "base/models/QvRuntimeConfig.hpp"
#include "base/models/QvSafeType.hpp"
#include "base/models/QvStartupConfig.hpp"
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -38,9 +38,9 @@ using namespace Qv2ray::base::objects::transfer;
// Linux users and DEs should handle the darkMode UI themselves. // Linux users and DEs should handle the darkMode UI themselves.
#ifndef QV2RAY_USE_BUILTIN_DARKTHEME #ifndef QV2RAY_USE_BUILTIN_DARKTHEME
# ifndef Q_OS_LINUX #ifndef Q_OS_LINUX
# define QV2RAY_USE_BUILTIN_DARKTHEME #define QV2RAY_USE_BUILTIN_DARKTHEME
# endif #endif
#endif #endif
#define QV2RAY_BUILD_INFO QString(_QV2RAY_BUILD_INFO_STR_) #define QV2RAY_BUILD_INFO QString(_QV2RAY_BUILD_INFO_STR_)
@ -48,9 +48,9 @@ using namespace Qv2ray::base::objects::transfer;
// Base folder suffix. // Base folder suffix.
#ifdef QT_DEBUG #ifdef QT_DEBUG
# define QV2RAY_CONFIG_DIR_SUFFIX "_debug/" #define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
#else #else
# define QV2RAY_CONFIG_DIR_SUFFIX "/" #define QV2RAY_CONFIG_DIR_SUFFIX "/"
#endif #endif
// Get Configured Config Dir Path // Get Configured Config Dir Path
@ -68,17 +68,17 @@ using namespace Qv2ray::base::objects::transfer;
#define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/") #define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/")
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json") #define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json")
#if ! defined (QV2RAY_DEFAULT_VCORE_PATH) && ! defined (QV2RAY_DEFAULT_VASSETS_PATH) #if !defined(QV2RAY_DEFAULT_VCORE_PATH) && !defined(QV2RAY_DEFAULT_VASSETS_PATH)
# define QV2RAY_DEFAULT_VASSETS_PATH (QV2RAY_CONFIG_DIR + "vcore/") #define QV2RAY_DEFAULT_VASSETS_PATH (QV2RAY_CONFIG_DIR + "vcore/")
# ifdef Q_OS_WIN #ifdef Q_OS_WIN
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe") #define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
# else #else
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray") #define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
# endif #endif
#elif defined (QV2RAY_DEFAULT_VCORE_PATH) && defined (QV2RAY_DEFAULT_VASSETS_PATH) #elif defined(QV2RAY_DEFAULT_VCORE_PATH) && defined(QV2RAY_DEFAULT_VASSETS_PATH)
// ---- Using user-specified VCore and VAssets path // ---- Using user-specified VCore and VAssets path
#else #else
# error Both QV2RAY_DEFAULT_VCORE_PATH and QV2RAY_DEFAULT_VASSETS_PATH need to be presented when using manually specify the paths. #error Both QV2RAY_DEFAULT_VCORE_PATH and QV2RAY_DEFAULT_VASSETS_PATH need to be presented when using manually specify the paths.
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -86,8 +86,8 @@ using namespace Qv2ray::base::objects::transfer;
//# define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe") //# define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
//# define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl.exe") //# define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl.exe")
#else #else
# define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray") #define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
# define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl") #define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl")
#endif #endif
#define QV2RAY_VCORE_LOG_DIRNAME "logs/" #define QV2RAY_VCORE_LOG_DIRNAME "logs/"
@ -95,15 +95,15 @@ using namespace Qv2ray::base::objects::transfer;
#define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log" #define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log"
// GUI TOOLS // GUI TOOLS
#define RED(obj) \ #define RED(obj) \
auto _temp = obj->palette(); \ auto _temp = obj->palette(); \
_temp.setColor(QPalette::Text, Qt::red); \ _temp.setColor(QPalette::Text, Qt::red); \
obj->setPalette(_temp); obj->setPalette(_temp);
#define BLACK(obj) \ #define BLACK(obj) obj->setPalette(QWidget::palette());
obj->setPalette(QWidget::palette());
#define QV2RAY_UI_RESOURCES_ROOT ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/")) #define QV2RAY_UI_RESOURCES_ROOT \
((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
#define QICON_R(file) QIcon(QV2RAY_UI_RESOURCES_ROOT + file) #define QICON_R(file) QIcon(QV2RAY_UI_RESOURCES_ROOT + file)
#define QSTRN(num) QString::number(num) #define QSTRN(num) QString::number(num)
@ -117,7 +117,8 @@ using namespace Qv2ray::base::objects::transfer;
#define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_" #define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_"
#define JSON_ROOT_TRY_REMOVE(obj) if (root.contains(obj)) { root.remove(obj); } #define JSON_ROOT_TRY_REMOVE(obj) \
if (root.contains(obj)) { root.remove(obj); }
namespace Qv2ray namespace Qv2ray
{ {
@ -129,4 +130,4 @@ namespace Qv2ray
isExiting = true; isExiting = true;
QApplication::quit(); QApplication::quit();
} }
} } // namespace Qv2ray

View File

@ -3,7 +3,7 @@
// //
// Always use libgRPC++ on windows platform. // Always use libgRPC++ on windows platform.
#ifndef WITH_LIB_GRPCPP #ifndef WITH_LIB_GRPCPP
# ifdef _WIN32 #ifdef _WIN32
# define WITH_LIB_GRPCPP #define WITH_LIB_GRPCPP
# endif #endif
#endif #endif

View File

@ -1,5 +1,7 @@
#include "Qv2rayLog.hpp" #include "Qv2rayLog.hpp"
#include "base/GlobalInstances.hpp" #include "base/GlobalInstances.hpp"
#include <iostream> #include <iostream>
namespace Qv2ray::base namespace Qv2ray::base
@ -15,18 +17,22 @@ namespace Qv2ray::base
auto logString = "[" + module + "]: " + log; auto logString = "[" + module + "]: " + log;
auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " "); auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " ");
if (isDebugBuild) { if (isDebugBuild)
// Debug build version, we only print info for DEBUG logs and print ALL info when debugLog presents, {
if (type == QV2RAY_LOG_DEBUG || StartupOption.debugLog) { // Debug build version, we only print info for DEBUG logs and print
logString = logString.prepend(funcPrepend); // ALL info when debugLog presents,
} if (type == QV2RAY_LOG_DEBUG || StartupOption.debugLog) { logString = logString.prepend(funcPrepend); }
} else { }
else
{
// We only process DEBUG log in Release mode // We only process DEBUG log in Release mode
if (type == QV2RAY_LOG_DEBUG) { if (type == QV2RAY_LOG_DEBUG)
if (StartupOption.debugLog) { {
logString = logString.prepend(funcPrepend); if (StartupOption.debugLog) { logString = logString.prepend(funcPrepend); }
} else { else
// Discard debug log in non-debug Qv2ray version with no-debugLog mode. {
// Discard debug log in non-debug Qv2ray version with
// no-debugLog mode.
return; return;
} }
} }
@ -50,4 +56,4 @@ namespace Qv2ray::base
__purgerBuffer->clear(); __purgerBuffer->clear();
return result; return result;
} }
} } // namespace Qv2ray::base

View File

@ -17,17 +17,17 @@ namespace Qv2ray::base
{ {
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log); void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log);
const QString readLastLog(); const QString readLastLog();
} } // namespace Qv2ray::base
#define NEWLINE "\r\n" #define NEWLINE "\r\n"
#define QV2RAY_LOG_NORMAL 0 #define QV2RAY_LOG_NORMAL 0
#define QV2RAY_LOG_DEBUG 1 #define QV2RAY_LOG_DEBUG 1
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
#define Qv2ray_PRETTY_FUNCTION __PRETTY_FUNCTION__ #define Qv2ray_PRETTY_FUNCTION __PRETTY_FUNCTION__
#else #else
#define Qv2ray_PRETTY_FUNCTION __FUNCSIG__ #define Qv2ray_PRETTY_FUNCTION __FUNCSIG__
#endif #endif
#define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, Qv2ray_PRETTY_FUNCTION, __LINE__, MODULE, MSG); #define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, Qv2ray_PRETTY_FUNCTION, __LINE__, MODULE, MSG);
@ -36,23 +36,23 @@ namespace Qv2ray::base
#define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG)); #define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG));
// Log modules used by Qv2ray // Log modules used by Qv2ray
const inline QString MODULE_INIT = "INIT" ; const inline QString MODULE_INIT = "INIT";
const inline QString MODULE_MESSAGING = "BASE-MESSAGING" ; const inline QString MODULE_MESSAGING = "BASE-MESSAGING";
const inline QString MODULE_UI = "CORE-UI" ; const inline QString MODULE_UI = "CORE-UI";
const inline QString MODULE_GRAPH = "CORE-UI-GRAPH" ; const inline QString MODULE_GRAPH = "CORE-UI-GRAPH";
const inline QString MODULE_SETTINGS = "CORE-SETTINGS" ; const inline QString MODULE_SETTINGS = "CORE-SETTINGS";
const inline QString MODULE_VCORE = "CORE-VCORE" ; const inline QString MODULE_VCORE = "CORE-VCORE";
// //
const inline QString MODULE_CONNECTION = "CORE-CONNECTION" ; const inline QString MODULE_CONNECTION = "CORE-CONNECTION";
const inline QString MODULE_SUBSCRIPTION = "CORE-SUBSCRIPTION" ; const inline QString MODULE_SUBSCRIPTION = "CORE-SUBSCRIPTION";
const inline QString MODULE_IMPORT = "CORE-IMPORT" ; const inline QString MODULE_IMPORT = "CORE-IMPORT";
const inline QString MODULE_EXPORT = "CORE-EXPORT" ; const inline QString MODULE_EXPORT = "CORE-EXPORT";
// //
const inline QString MODULE_NETWORK = "COMMON-NETWORK" ; const inline QString MODULE_NETWORK = "COMMON-NETWORK";
const inline QString MODULE_FILEIO = "COMMON-FILEIO" ; const inline QString MODULE_FILEIO = "COMMON-FILEIO";
// //
const inline QString MODULE_PROXY = "COMPONENT-PROXY" ; const inline QString MODULE_PROXY = "COMPONENT-PROXY";
const inline QString MODULE_UPDATE = "COMPONENT-UPDATE" ; const inline QString MODULE_UPDATE = "COMPONENT-UPDATE";
const inline QString MODULE_PLUGIN = "COMPONENT-PLUGIN" ; const inline QString MODULE_PLUGIN = "COMPONENT-PLUGIN";
// ================================================================ // ================================================================
const inline QString MODULE_CORE_HANDLER = "QV2RAY-CORE"; const inline QString MODULE_CORE_HANDLER = "QV2RAY-CORE";

View File

@ -1,37 +1,46 @@
#pragma once #pragma once
#include <QString> #include "3rdparty/x2struct/x2struct.hpp"
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include "3rdparty/x2struct/x2struct.hpp" #include <QString>
namespace Qv2ray::base::objects namespace Qv2ray::base::objects
{ {
// //
// Used in config generation // Used in config generation
struct AccountObject { struct AccountObject
{
QString user; QString user;
QString pass; QString pass;
XTOSTRUCT(O(user, pass)) XTOSTRUCT(O(user, pass))
}; };
// //
// //
struct ApiObject { struct ApiObject
{
QString tag; QString tag;
QList<QString> services; QList<QString> services;
ApiObject() : tag("api"), services() {} ApiObject() : tag("api"), services()
{
}
XTOSTRUCT(O(tag, services)) XTOSTRUCT(O(tag, services))
}; };
// //
// //
struct SystemPolicyObject { struct SystemPolicyObject
{
bool statsInboundUplink; bool statsInboundUplink;
bool statsInboundDownlink; bool statsInboundDownlink;
SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() {} SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink()
{
}
XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink)) XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink))
}; };
// //
// //
struct LevelPolicyObject { struct LevelPolicyObject
{
int handshake; int handshake;
int connIdle; int connIdle;
int uplinkOnly; int uplinkOnly;
@ -39,20 +48,26 @@ namespace Qv2ray::base::objects
bool statsUserUplink; bool statsUserUplink;
bool statsUserDownlink; bool statsUserDownlink;
int bufferSize; int bufferSize;
LevelPolicyObject(): handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() {} LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize()
{
}
XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)) XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
}; };
// //
// //
struct PolicyObject { struct PolicyObject
{
QMap<QString, LevelPolicyObject> level; QMap<QString, LevelPolicyObject> level;
QList<SystemPolicyObject> system; QList<SystemPolicyObject> system;
PolicyObject(): level(), system() {} PolicyObject() : level(), system()
{
}
XTOSTRUCT(O(level, system)) XTOSTRUCT(O(level, system))
}; };
// //
// //
struct RuleObject { struct RuleObject
{
// Added due to the request of @aliyuchang33 // Added due to the request of @aliyuchang33
bool QV2RAY_RULE_ENABLED; bool QV2RAY_RULE_ENABLED;
bool QV2RAY_RULE_USE_BALANCER; bool QV2RAY_RULE_USE_BALANCER;
@ -70,65 +85,89 @@ namespace Qv2ray::base::objects
QString attrs; QString attrs;
QString outboundTag; QString outboundTag;
QString balancerTag; QString balancerTag;
RuleObject() : QV2RAY_RULE_ENABLED(true), QV2RAY_RULE_USE_BALANCER(false), QV2RAY_RULE_TAG("new rule"), type("field"), domain(), ip(), port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("") {} RuleObject()
XTOSTRUCT(O(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag, protocol, attrs, outboundTag, balancerTag)) : QV2RAY_RULE_ENABLED(true), QV2RAY_RULE_USE_BALANCER(false), QV2RAY_RULE_TAG("new rule"), type("field"), domain(), ip(),
port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("")
{
}
XTOSTRUCT(O(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag,
protocol, attrs, outboundTag, balancerTag))
}; };
// //
// //
struct BalancerObject { struct BalancerObject
QString tag ; {
QString tag;
QList<QString> selector; QList<QString> selector;
BalancerObject() : tag(), selector() {} BalancerObject() : tag(), selector()
{
}
XTOSTRUCT(O(tag, selector)) XTOSTRUCT(O(tag, selector))
}; };
// //
// //
namespace transfer namespace transfer
{ {
struct HTTPRequestObject { struct HTTPRequestObject
{
QString version; QString version;
QString method; QString method;
QList<QString> path; QList<QString> path;
QMap<QString, QList<QString>> headers; QMap<QString, QList<QString>> headers;
HTTPRequestObject(): version("1.1"), method("GET"), path(), headers() {} HTTPRequestObject() : version("1.1"), method("GET"), path(), headers()
{
}
XTOSTRUCT(O(version, method, path, headers)) XTOSTRUCT(O(version, method, path, headers))
}; };
// //
// //
struct HTTPResponseObject { struct HTTPResponseObject
{
QString version; QString version;
QString status; QString status;
QString reason; QString reason;
QMap<QString, QList<QString>> headers; QMap<QString, QList<QString>> headers;
HTTPResponseObject(): version("1.1"), status("200"), reason("OK"), headers() {} HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers()
{
}
XTOSTRUCT(O(version, status, reason, headers)) XTOSTRUCT(O(version, status, reason, headers))
}; };
// //
// //
struct TCPHeader_M_Object { struct TCPHeader_M_Object
{
QString type; QString type;
HTTPRequestObject request; HTTPRequestObject request;
HTTPResponseObject response; HTTPResponseObject response;
TCPHeader_M_Object(): type("none"), request(), response() {} TCPHeader_M_Object() : type("none"), request(), response()
{
}
XTOSTRUCT(O(type, request, response)) XTOSTRUCT(O(type, request, response))
}; };
// //
// //
struct HeaderObject { struct HeaderObject
{
QString type; QString type;
HeaderObject(): type("none") {} HeaderObject() : type("none")
{
}
XTOSTRUCT(O(type)) XTOSTRUCT(O(type))
}; };
// //
// //
struct TCPObject { struct TCPObject
{
TCPHeader_M_Object header; TCPHeader_M_Object header;
TCPObject(): header() {} TCPObject() : header()
{
}
XTOSTRUCT(O(header)) XTOSTRUCT(O(header))
}; };
// //
// //
struct KCPObject { struct KCPObject
{
int mtu = 1350; int mtu = 1350;
int tti = 20; int tti = 20;
int uplinkCapacity = 5; int uplinkCapacity = 5;
@ -137,84 +176,111 @@ namespace Qv2ray::base::objects
int readBufferSize = 1; int readBufferSize = 1;
int writeBufferSize = 1; int writeBufferSize = 1;
HeaderObject header; HeaderObject header;
KCPObject(): header() {} KCPObject() : header()
{
}
XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)) XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header))
}; };
// //
// //
struct WebSocketObject { struct WebSocketObject
{
QString path; QString path;
QMap<QString, QString> headers; QMap<QString, QString> headers;
WebSocketObject(): path("/"), headers() {} WebSocketObject() : path("/"), headers()
{
}
XTOSTRUCT(O(path, headers)) XTOSTRUCT(O(path, headers))
}; };
// //
// //
struct HttpObject { struct HttpObject
{
QList<QString> host; QList<QString> host;
QString path; QString path;
HttpObject() : host(), path("/") {} HttpObject() : host(), path("/")
{
}
XTOSTRUCT(O(host, path)) XTOSTRUCT(O(host, path))
}; };
// //
// //
struct DomainSocketObject { struct DomainSocketObject
{
QString path; QString path;
DomainSocketObject(): path("/") {} DomainSocketObject() : path("/")
{
}
XTOSTRUCT(O(path)) XTOSTRUCT(O(path))
}; };
// //
// //
struct QuicObject { struct QuicObject
{
QString security; QString security;
QString key; QString key;
HeaderObject header; HeaderObject header;
QuicObject(): security(""), key(""), header() {} QuicObject() : security(""), key(""), header()
{
}
XTOSTRUCT(O(security, key, header)) XTOSTRUCT(O(security, key, header))
}; };
// //
// //
struct SockoptObject { struct SockoptObject
{
int mark; int mark;
bool tcpFastOpen; bool tcpFastOpen;
QString tproxy; QString tproxy;
SockoptObject(): mark(0), tcpFastOpen(false), tproxy("off") {} SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off")
{
}
XTOSTRUCT(O(mark, tcpFastOpen, tproxy)) XTOSTRUCT(O(mark, tcpFastOpen, tproxy))
}; };
// //
// //
struct CertificateObject { struct CertificateObject
{
QString usage; QString usage;
QString certificateFile; QString certificateFile;
QString keyFile; QString keyFile;
QList<QString> certificate; QList<QString> certificate;
QList<QString> key; QList<QString> key;
CertificateObject(): usage(), certificateFile(), keyFile(), certificate(), key() {} CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key()
{
}
XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key)) XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key))
}; };
// //
// //
struct TLSObject { struct TLSObject
{
QString serverName; QString serverName;
bool allowInsecure; bool allowInsecure;
QList<QString> alpn; QList<QString> alpn;
QList<CertificateObject> certificates; QList<CertificateObject> certificates;
bool disableSystemRoot; bool disableSystemRoot;
TLSObject(): serverName(), allowInsecure(), certificates(), disableSystemRoot() {} TLSObject() : serverName(), allowInsecure(), certificates(), disableSystemRoot()
{
}
XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot)) XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot))
}; };
} } // namespace transfer
// //
// //
struct SniffingObject { struct SniffingObject
{
bool enabled = false; bool enabled = false;
QList<QString> destOverride; QList<QString> destOverride;
SniffingObject(): enabled(), destOverride() {} SniffingObject() : enabled(), destOverride()
{
}
XTOSTRUCT(O(enabled, destOverride)) XTOSTRUCT(O(enabled, destOverride))
}; };
// //
// //
struct StreamSettingsObject { struct StreamSettingsObject
{
QString network; QString network;
QString security; QString security;
transfer::SockoptObject sockopt; transfer::SockoptObject sockopt;
@ -225,15 +291,22 @@ namespace Qv2ray::base::objects
transfer::HttpObject httpSettings; transfer::HttpObject httpSettings;
transfer::DomainSocketObject dsSettings; transfer::DomainSocketObject dsSettings;
transfer::QuicObject quicSettings; transfer::QuicObject quicSettings;
StreamSettingsObject(): network("tcp"), security("none"), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {} StreamSettingsObject()
: network("tcp"), security("none"), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(),
dsSettings(), quicSettings()
{
}
XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings)) XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings))
}; };
// //
// //
struct MuxObject { struct MuxObject
{
bool enabled; bool enabled;
int concurrency; int concurrency;
MuxObject(): enabled(), concurrency() {} MuxObject() : enabled(), concurrency()
{
}
XTOSTRUCT(O(enabled, concurrency)) XTOSTRUCT(O(enabled, concurrency))
}; };
// //
@ -241,21 +314,28 @@ namespace Qv2ray::base::objects
namespace protocol namespace protocol
{ {
// DNS, OutBound // DNS, OutBound
struct DNSOut { struct DNSOut
{
QString network; QString network;
QString address; QString address;
int port; int port;
DNSOut(): network(""), address("0.0.0.0"), port(0) {} DNSOut() : network(""), address("0.0.0.0"), port(0)
{
}
XTOSTRUCT(O(network, address, port)) XTOSTRUCT(O(network, address, port))
}; };
// //
// MTProto, InBound || OutBound // MTProto, InBound || OutBound
struct MTProtoIn { struct MTProtoIn
struct UserObject { {
struct UserObject
{
QString email; QString email;
int level; int level;
QString secret; QString secret;
UserObject() : email("user@domain.com"), level(0), secret("") {} UserObject() : email("user@domain.com"), level(0), secret("")
{
}
XTOSTRUCT(O(email, level, secret)) XTOSTRUCT(O(email, level, secret))
}; };
QList<UserObject> users; QList<UserObject> users;
@ -263,42 +343,55 @@ namespace Qv2ray::base::objects
}; };
// //
// Socks, OutBound // Socks, OutBound
struct SocksServerObject { struct SocksServerObject
struct UserObject { {
struct UserObject
{
QString user; QString user;
QString pass; QString pass;
int level; int level;
UserObject(): user("username"), pass("password"), level(0) {} UserObject() : user("username"), pass("password"), level(0)
{
}
XTOSTRUCT(O(user, pass, level)) XTOSTRUCT(O(user, pass, level))
}; };
QString address; QString address;
int port; int port;
QList<UserObject> users; QList<UserObject> users;
SocksServerObject(): address("0.0.0.0"), port(0), users() {} SocksServerObject() : address("0.0.0.0"), port(0), users()
{
}
XTOSTRUCT(O(address, port, users)) XTOSTRUCT(O(address, port, users))
}; };
// //
// VMess Server // VMess Server
struct VMessServerObject { struct VMessServerObject
struct UserObject { {
struct UserObject
{
QString id; QString id;
int alterId; int alterId;
QString security; QString security;
int level; int level;
UserObject() : id(""), alterId(64), security("auto"), level(0) {} UserObject() : id(""), alterId(64), security("auto"), level(0)
{
}
XTOSTRUCT(O(id, alterId, security, level)) XTOSTRUCT(O(id, alterId, security, level))
}; };
QString address; QString address;
int port; int port;
QList<UserObject> users; QList<UserObject> users;
VMessServerObject(): address(""), port(0), users() {} VMessServerObject() : address(""), port(0), users()
{
}
XTOSTRUCT(O(address, port, users)) XTOSTRUCT(O(address, port, users))
}; };
// //
// ShadowSocks Server // ShadowSocks Server
struct ShadowSocksServerObject { struct ShadowSocksServerObject
{
QString email; QString email;
QString address; QString address;
QString method; QString method;
@ -306,8 +399,11 @@ namespace Qv2ray::base::objects
bool ota; bool ota;
int level; int level;
int port; int port;
ShadowSocksServerObject(): email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) {} ShadowSocksServerObject()
: email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0)
{
}
XTOSTRUCT(O(email, address, port, method, password, ota, level)) XTOSTRUCT(O(email, address, port, method, password, ota, level))
}; };
} } // namespace protocol
} } // namespace Qv2ray::base::objects

View File

@ -1,38 +1,50 @@
#pragma once #pragma once
#include "3rdparty/x2struct/x2struct.hpp"
#include <QString> #include <QString>
#include <QtCore> #include <QtCore>
#include "3rdparty/x2struct/x2struct.hpp"
namespace Qv2ray::base namespace Qv2ray::base
{ {
using namespace std::chrono; using namespace std::chrono;
// Common struct for Groups and Subscriptions // Common struct for Groups and Subscriptions
struct GroupObject_Config { struct GroupObject_Config
{
QString displayName; QString displayName;
QList<QString> connections; QList<QString> connections;
int64_t importDate; int64_t importDate;
GroupObject_Config(): displayName(), connections(), importDate() { } GroupObject_Config() : displayName(), connections(), importDate()
{
}
XTOSTRUCT(O(displayName, connections, importDate)) XTOSTRUCT(O(displayName, connections, importDate))
}; };
struct SubscriptionObject_Config : GroupObject_Config { struct SubscriptionObject_Config : GroupObject_Config
{
// //
QString address; QString address;
int64_t lastUpdated; int64_t lastUpdated;
float updateInterval; float updateInterval;
SubscriptionObject_Config(): address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10) { } SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10)
{
}
XTOSTRUCT(O(lastUpdated, updateInterval, address, connections, displayName, importDate)) XTOSTRUCT(O(lastUpdated, updateInterval, address, connections, displayName, importDate))
}; };
struct ConnectionObject_Config { struct ConnectionObject_Config
{
QString displayName; QString displayName;
int64_t importDate; int64_t importDate;
int64_t lastConnected; int64_t lastConnected;
int64_t latency; int64_t latency;
int64_t upLinkData; int64_t upLinkData;
int64_t downLinkData; int64_t downLinkData;
ConnectionObject_Config(): displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(0), upLinkData(0), downLinkData(0) { } ConnectionObject_Config()
: displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(0), upLinkData(0),
downLinkData(0)
{
}
XTOSTRUCT(O(displayName, importDate, lastConnected, latency, upLinkData, downLinkData)) XTOSTRUCT(O(displayName, importDate, lastConnected, latency, upLinkData, downLinkData))
}; };
} } // namespace Qv2ray::base
using namespace Qv2ray::base; using namespace Qv2ray::base;

View File

@ -2,46 +2,59 @@
#include "3rdparty/x2struct/x2struct.hpp" #include "3rdparty/x2struct/x2struct.hpp"
#include "base/models/CoreObjectModels.hpp" #include "base/models/CoreObjectModels.hpp"
#include "base/models/QvConfigIdentifier.hpp" #include "base/models/QvConfigIdentifier.hpp"
#include <chrono> #include <chrono>
const int QV2RAY_CONFIG_VERSION = 9; const int QV2RAY_CONFIG_VERSION = 9;
namespace Qv2ray::base::config namespace Qv2ray::base::config
{ {
struct QvBarLine { struct QvBarLine
{
QString Family; QString Family;
bool Bold, Italic; bool Bold, Italic;
int ColorA, ColorR, ColorG, ColorB; int ColorA, ColorR, ColorG, ColorB;
int ContentType; int ContentType;
double Size; double Size;
QString Message; QString Message;
QvBarLine(): Family("Consolas"), Bold(true), Italic(false), ColorA(255), ColorR(255), ColorG(255), ColorB(255), QvBarLine()
ContentType(0), Size(9), Message("") { } : Family("Consolas"), Bold(true), Italic(false), ColorA(255), ColorR(255), ColorG(255), ColorB(255), ContentType(0), Size(9),
Message("")
{
}
XTOSTRUCT(O(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType)) XTOSTRUCT(O(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType))
}; };
struct QvBarPage { struct QvBarPage
{
int OffsetYpx; int OffsetYpx;
QList<QvBarLine> Lines; QList<QvBarLine> Lines;
QvBarPage(): OffsetYpx(5) { } QvBarPage() : OffsetYpx(5)
{
}
XTOSTRUCT(O(OffsetYpx, Lines)) XTOSTRUCT(O(OffsetYpx, Lines))
}; };
struct Qv2rayToolBarConfig { struct Qv2rayToolBarConfig
{
QList<QvBarPage> Pages; QList<QvBarPage> Pages;
XTOSTRUCT(O(Pages)) XTOSTRUCT(O(Pages))
}; };
struct Qv2rayPACConfig { struct Qv2rayPACConfig
{
bool enablePAC; bool enablePAC;
int port; int port;
QString localIP; QString localIP;
bool useSocksProxy; bool useSocksProxy;
Qv2rayPACConfig(): enablePAC(false), port(8989), useSocksProxy(false) { } Qv2rayPACConfig() : enablePAC(false), port(8989), useSocksProxy(false)
{
}
XTOSTRUCT(O(enablePAC, port, localIP, useSocksProxy)) XTOSTRUCT(O(enablePAC, port, localIP, useSocksProxy))
}; };
struct Qv2rayForwardProxyConfig { struct Qv2rayForwardProxyConfig
{
bool enableForwardProxy; bool enableForwardProxy;
QString type; QString type;
QString serverAddress; QString serverAddress;
@ -49,12 +62,15 @@ namespace Qv2ray::base::config
bool useAuth; bool useAuth;
QString username; QString username;
QString password; QString password;
Qv2rayForwardProxyConfig(): enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), Qv2rayForwardProxyConfig()
useAuth(false), username(), password() { } : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password()
{
}
XTOSTRUCT(O(enableForwardProxy, type, serverAddress, port, useAuth, username, password)) XTOSTRUCT(O(enableForwardProxy, type, serverAddress, port, useAuth, username, password))
}; };
struct Qv2rayInboundsConfig { struct Qv2rayInboundsConfig
{
QString listenip; QString listenip;
bool setSystemProxy; bool setSystemProxy;
Qv2rayPACConfig pacConfig; Qv2rayPACConfig pacConfig;
@ -72,42 +88,56 @@ namespace Qv2ray::base::config
bool http_useAuth; bool http_useAuth;
objects::AccountObject httpAccount; objects::AccountObject httpAccount;
Qv2rayInboundsConfig(): Qv2rayInboundsConfig()
listenip("127.0.0.1"), setSystemProxy(false), pacConfig(), : listenip("127.0.0.1"), setSystemProxy(false), pacConfig(), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true),
useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true), socksLocalIP("127.0.0.1"), socksAccount(), socksLocalIP("127.0.0.1"), socksAccount(), useHTTP(true), http_port(8888), http_useAuth(false), httpAccount()
useHTTP(true), http_port(8888), http_useAuth(false), httpAccount() {} {
}
XTOSTRUCT(O(setSystemProxy, pacConfig, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port, http_useAuth, httpAccount)) XTOSTRUCT(O(setSystemProxy, pacConfig, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP,
http_port, http_useAuth, httpAccount))
}; };
struct Qv2rayUIConfig { struct Qv2rayUIConfig
{
QString theme; QString theme;
QString language; QString language;
bool useDarkTheme; bool useDarkTheme;
bool useDarkTrayIcon; bool useDarkTrayIcon;
int maximumLogLines; int maximumLogLines;
Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { } Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500)
{
}
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines)) XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
}; };
struct Qv2rayConnectionConfig { struct Qv2rayConnectionConfig
{
bool bypassCN; bool bypassCN;
bool enableProxy; bool enableProxy;
bool withLocalDNS; bool withLocalDNS;
QList<QString> dnsList; QList<QString> dnsList;
Qv2rayForwardProxyConfig forwardProxyConfig; Qv2rayForwardProxyConfig forwardProxyConfig;
Qv2rayConnectionConfig() : bypassCN(true), enableProxy(true), withLocalDNS(false), dnsList(QStringList() << "8.8.4.4" << "1.1.1.1") { } Qv2rayConnectionConfig()
: bypassCN(true), enableProxy(true), withLocalDNS(false), dnsList(QStringList() << "8.8.4.4"
<< "1.1.1.1")
{
}
XTOSTRUCT(O(bypassCN, enableProxy, withLocalDNS, dnsList, forwardProxyConfig)) XTOSTRUCT(O(bypassCN, enableProxy, withLocalDNS, dnsList, forwardProxyConfig))
}; };
struct Qv2rayAPIConfig { struct Qv2rayAPIConfig
{
bool enableAPI; bool enableAPI;
int statsPort; int statsPort;
Qv2rayAPIConfig(): enableAPI(true), statsPort(15490) { } Qv2rayAPIConfig() : enableAPI(true), statsPort(15490)
{
}
XTOSTRUCT(O(enableAPI, statsPort)) XTOSTRUCT(O(enableAPI, statsPort))
}; };
struct Qv2rayConfig { struct Qv2rayConfig
{
int config_version; int config_version;
bool tProxySupport; bool tProxySupport;
int logLevel; int logLevel;
@ -129,23 +159,13 @@ namespace Qv2ray::base::config
Qv2rayInboundsConfig inboundConfig; Qv2rayInboundsConfig inboundConfig;
Qv2rayConnectionConfig connectionConfig; Qv2rayConnectionConfig connectionConfig;
Qv2rayConfig(): Qv2rayConfig()
config_version(QV2RAY_CONFIG_VERSION), : config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), v2CorePath(), v2AssetsPath(), ignoredVersion(), groups(),
tProxySupport(false), subscriptions(), connections(), uiConfig(), apiConfig(), toolBarConfig(), inboundConfig(), connectionConfig()
logLevel(), {
v2CorePath(), }
v2AssetsPath(),
ignoredVersion(),
groups(),
subscriptions(),
connections(),
uiConfig(), apiConfig(), toolBarConfig(), inboundConfig(), connectionConfig() { }
XTOSTRUCT(O(config_version, ignoredVersion, XTOSTRUCT(O(config_version, ignoredVersion, tProxySupport, logLevel, uiConfig, v2CorePath, v2AssetsPath, groups, connections,
tProxySupport, subscriptions, autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
logLevel, uiConfig,
v2CorePath, v2AssetsPath,
groups, connections, subscriptions,
autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
}; };
} } // namespace Qv2ray::base::config

View File

@ -5,8 +5,9 @@
namespace Qv2ray::base namespace Qv2ray::base
{ {
struct Qv2rayRuntimeConfig { struct Qv2rayRuntimeConfig
{
// //
bool screenShotHideQv2ray = false; bool screenShotHideQv2ray = false;
}; };
} } // namespace Qv2ray::base

View File

@ -1,15 +1,22 @@
#pragma once #pragma once
#include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject>
#define SAFE_TYPEDEF_EXTRA(Base, name, extra) \ #define SAFE_TYPEDEF_EXTRA(Base, name, extra) \
class name : public Base { \ class name : public Base \
public: \ { \
template <class... Args> \ public: \
explicit name (Args... args) : Base(args...) {} \ template<class... Args> \
const Base& raw() const { return *this; } \ explicit name(Args... args) : Base(args...) \
extra }; { \
} \
const Base &raw() const \
{ \
return *this; \
} \
extra \
};
#define nothing #define nothing
#define SAFE_TYPEDEF(Base, name) SAFE_TYPEDEF_EXTRA(Base, name, nothing) #define SAFE_TYPEDEF(Base, name) SAFE_TYPEDEF_EXTRA(Base, name, nothing)
@ -31,5 +38,4 @@ namespace Qv2ray::base::safetype
SAFE_TYPEDEF(QJsonObject, ROUTERULE) SAFE_TYPEDEF(QJsonObject, ROUTERULE)
SAFE_TYPEDEF(INOUTLIST, OUTBOUNDS) SAFE_TYPEDEF(INOUTLIST, OUTBOUNDS)
SAFE_TYPEDEF(INOUTLIST, INBOUNDS) SAFE_TYPEDEF(INOUTLIST, INBOUNDS)
} } // namespace Qv2ray::base::safetype

View File

@ -4,7 +4,8 @@ namespace Qv2ray
{ {
namespace base namespace base
{ {
struct QvStartupOptions { struct QvStartupOptions
{
/// No API subsystem /// No API subsystem
bool noAPI; bool noAPI;
/// Explicitly run as root user. /// Explicitly run as root user.
@ -14,5 +15,5 @@ namespace Qv2ray
/// Enable Network toolbar plugin. /// Enable Network toolbar plugin.
bool enableToolbarPlguin; bool enableToolbarPlguin;
}; };
} } // namespace base
} } // namespace Qv2ray

View File

@ -1,15 +1,16 @@
#include "CommandArgs.hpp" #include "CommandArgs.hpp"
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
namespace Qv2ray::common namespace Qv2ray::common
{ {
QvCommandArgParser::QvCommandArgParser() : QObject(), QvCommandArgParser::QvCommandArgParser()
noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystems.")), : QObject(), noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystems.")),
runAsRootOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root.")), runAsRootOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root.")),
debugOption("debug", QObject::tr("Enable Debug Output")), debugOption("debug", QObject::tr("Enable Debug Output")),
withToolbarOption("withToolbarPlugin", QObject::tr("Enable Qv2ray network toolbar plugin")), withToolbarOption("withToolbarPlugin", QObject::tr("Enable Qv2ray network toolbar plugin")),
// //
helpOption("FAKE"), versionOption("FAKE") helpOption("FAKE"), versionOption("FAKE")
{ {
parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray.")); parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
@ -24,33 +25,36 @@ namespace Qv2ray::common
CommandLineParseResult QvCommandArgParser::ParseCommandLine(QString *errorMessage) CommandLineParseResult QvCommandArgParser::ParseCommandLine(QString *errorMessage)
{ {
if (!parser.parse(QCoreApplication::arguments())) { if (!parser.parse(QCoreApplication::arguments()))
{
*errorMessage = parser.errorText(); *errorMessage = parser.errorText();
return CommandLineError; return CommandLineError;
} }
if (parser.isSet(versionOption)) if (parser.isSet(versionOption)) return CommandLineVersionRequested;
return CommandLineVersionRequested;
if (parser.isSet(helpOption)) if (parser.isSet(helpOption)) return CommandLineHelpRequested;
return CommandLineHelpRequested;
if (parser.isSet(noAPIOption)) { if (parser.isSet(noAPIOption))
{
DEBUG(MODULE_INIT, "noAPIOption is set.") DEBUG(MODULE_INIT, "noAPIOption is set.")
StartupOption.noAPI = true; StartupOption.noAPI = true;
} }
if (parser.isSet(runAsRootOption)) { if (parser.isSet(runAsRootOption))
{
DEBUG(MODULE_INIT, "runAsRootOption is set.") DEBUG(MODULE_INIT, "runAsRootOption is set.")
StartupOption.forceRunAsRootUser = true; StartupOption.forceRunAsRootUser = true;
} }
if (parser.isSet(debugOption)) { if (parser.isSet(debugOption))
{
DEBUG(MODULE_INIT, "debugOption is set.") DEBUG(MODULE_INIT, "debugOption is set.")
StartupOption.debugLog = true; StartupOption.debugLog = true;
} }
if (parser.isSet(withToolbarOption)) { if (parser.isSet(withToolbarOption))
{
DEBUG(MODULE_INIT, "withToolbarOption is set.") DEBUG(MODULE_INIT, "withToolbarOption is set.")
StartupOption.enableToolbarPlguin = true; StartupOption.enableToolbarPlguin = true;
} }
@ -58,4 +62,4 @@ namespace Qv2ray::common
return CommandLineOk; return CommandLineOk;
} }
} } // namespace Qv2ray::common

View File

@ -4,7 +4,8 @@
namespace Qv2ray::common namespace Qv2ray::common
{ {
enum CommandLineParseResult { enum CommandLineParseResult
{
CommandLineOk, CommandLineOk,
CommandLineError, CommandLineError,
CommandLineVersionRequested, CommandLineVersionRequested,
@ -12,24 +13,24 @@ namespace Qv2ray::common
}; };
class QvCommandArgParser : public QObject class QvCommandArgParser : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
QvCommandArgParser(); QvCommandArgParser();
CommandLineParseResult ParseCommandLine(QString *errorMessage); CommandLineParseResult ParseCommandLine(QString *errorMessage);
const QCommandLineParser *Parser() const QCommandLineParser *Parser()
{ {
return &parser; return &parser;
} }
private: private:
QCommandLineParser parser; QCommandLineParser parser;
QCommandLineOption noAPIOption; QCommandLineOption noAPIOption;
QCommandLineOption runAsRootOption; QCommandLineOption runAsRootOption;
QCommandLineOption debugOption; QCommandLineOption debugOption;
QCommandLineOption withToolbarOption; QCommandLineOption withToolbarOption;
QCommandLineOption helpOption; QCommandLineOption helpOption;
QCommandLineOption versionOption; QCommandLineOption versionOption;
}; };
} } // namespace Qv2ray::common
using namespace Qv2ray::common; using namespace Qv2ray::common;

View File

@ -1,7 +1,9 @@
#include "HTTPRequestHelper.hpp" #include "HTTPRequestHelper.hpp"
#include "base/Qv2rayBase.hpp"
#include <QByteArray> #include <QByteArray>
#include <QNetworkProxy> #include <QNetworkProxy>
#include "base/Qv2rayBase.hpp"
namespace Qv2ray::common namespace Qv2ray::common
{ {
@ -18,7 +20,8 @@ namespace Qv2ray::common
{ {
QUrl qUrl = QUrl(url); QUrl qUrl = QUrl(url);
if (!qUrl.isValid()) { if (!qUrl.isValid())
{
LOG(MODULE_NETWORK, "Provided URL is invalid: " + url) LOG(MODULE_NETWORK, "Provided URL is invalid: " + url)
return false; return false;
} }
@ -37,11 +40,14 @@ namespace Qv2ray::common
{ {
this->setUrl(url); this->setUrl(url);
if (useProxy) { if (useProxy)
{
auto proxy = QNetworkProxyFactory::systemProxyForQuery(); auto proxy = QNetworkProxyFactory::systemProxyForQuery();
accessManager.setProxy(proxy.first()); accessManager.setProxy(proxy.first());
LOG(MODULE_NETWORK, "Sync get is using system proxy settings") LOG(MODULE_NETWORK, "Sync get is using system proxy settings")
} else { }
else
{
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
} }
@ -62,28 +68,34 @@ namespace Qv2ray::common
void QvHttpRequestHelper::get(const QString &url) void QvHttpRequestHelper::get(const QString &url)
{ {
this->setUrl(url); this->setUrl(url);
// request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); // request.setRawHeader("Content-Type",
// "application/x-www-form-urlencoded");
reply = accessManager.get(request); reply = accessManager.get(request);
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p); connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead);
} }
//void QvHttpRequestHelper::post(const QString &url, const QByteArray &data) // void QvHttpRequestHelper::post(const QString &url, const QByteArray
// &data)
//{ //{
// this->setUrl(url); // this->setUrl(url);
// request.setRawHeader("Content-Type", "application/json"); // request.setRawHeader("Content-Type", "application/json");
// reply = accessManager.post(request, data); // reply = accessManager.post(request, data);
// connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished); // connect(reply, &QNetworkReply::finished, this,
// connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); // &QvHttpRequestHelper::onRequestFinished); connect(reply,
// &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead);
//} //}
// void QvHttpRequestHelper::put(const QString &url, const QByteArray &data) // void QvHttpRequestHelper::put(const QString &url, const QByteArray
// &data)
// { // {
// this->setUrl(url); // this->setUrl(url);
// request.setRawHeader("Content-Type", "application/json"); // request.setRawHeader("Content-Type", "application/json");
// reply = accessManager.put(request, data); // reply = accessManager.put(request, data);
// connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished); // connect(reply, &QNetworkReply::finished, this,
// connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); // &QvHttpRequestHelper::onRequestFinished); connect(reply,
// &QNetworkReply::readyRead, this,
// &QvHttpRequestHelper::onReadyRead);
// } // }
// void QvHttpRequestHelper::del(const QString &url) // void QvHttpRequestHelper::del(const QString &url)
@ -91,26 +103,31 @@ namespace Qv2ray::common
// this->setUrl(url); // this->setUrl(url);
// request.setRawHeader("Content-Type", "application/json"); // request.setRawHeader("Content-Type", "application/json");
// reply = accessManager.deleteResource(request); // reply = accessManager.deleteResource(request);
// connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished); // connect(reply, &QNetworkReply::finished, this,
// connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); // &QvHttpRequestHelper::onRequestFinished); connect(reply,
// &QNetworkReply::readyRead, this,
// &QvHttpRequestHelper::onReadyRead);
// } // }
// void QvHttpRequestHelper::login(const QString &url, const QByteArray &data) // void QvHttpRequestHelper::login(const QString &url, const QByteArray
// &data)
// { // {
// this->setUrl(url); // this->setUrl(url);
// request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); // request.setRawHeader("Content-Type",
// reply = accessManager.post(request, data); // "application/x-www-form-urlencoded"); reply =
// connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished); // accessManager.post(request, data); connect(reply,
// connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); // &QNetworkReply::finished, this,
// &QvHttpRequestHelper::onRequestFinished); connect(reply,
// &QNetworkReply::readyRead, this,
// &QvHttpRequestHelper::onReadyRead);
// } // }
void QvHttpRequestHelper::onRequestFinished_p() void QvHttpRequestHelper::onRequestFinished_p()
{ {
if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()) { if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()) { DEBUG(MODULE_NETWORK, "HTTP/2 was used.") }
DEBUG(MODULE_NETWORK, "HTTP/2 was used.")
}
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError)
{
QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error()); QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error());
LOG(MODULE_NETWORK, "Network request error string: " + error) LOG(MODULE_NETWORK, "Network request error string: " + error)
QByteArray empty; QByteArray empty;
@ -125,4 +142,4 @@ namespace Qv2ray::common
DEBUG(MODULE_NETWORK, "A request is now ready read") DEBUG(MODULE_NETWORK, "A request is now ready read")
this->data += reply->readAll(); this->data += reply->readAll();
} }
} } // namespace Qv2ray::common

View File

@ -18,45 +18,46 @@
#pragma once #pragma once
#include <QObject>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
namespace Qv2ray::common namespace Qv2ray::common
{ {
class QvHttpRequestHelper : public QObject class QvHttpRequestHelper : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvHttpRequestHelper(); explicit QvHttpRequestHelper();
~QvHttpRequestHelper(); ~QvHttpRequestHelper();
bool setUrl(const QString &url); bool setUrl(const QString &url);
void setHeader(const QByteArray &key, const QByteArray &value); void setHeader(const QByteArray &key, const QByteArray &value);
// get // get
QByteArray syncget(const QString &url, bool useProxy); QByteArray syncget(const QString &url, bool useProxy);
void get(const QString &url); void get(const QString &url);
//// insert //// insert
//void post(const QString &url, const QByteArray &data); // void post(const QString &url, const QByteArray &data);
//// update //// update
//void put(const QString &url, const QByteArray &data); // void put(const QString &url, const QByteArray &data);
//// delete //// delete
//void del(const QString &url); // void del(const QString &url);
//void login(const QString &url, const QByteArray &data); // void login(const QString &url, const QByteArray &data);
signals: signals:
void httpRequestFinished(QByteArray &data); void httpRequestFinished(QByteArray &data);
public slots: public slots:
void onRequestFinished_p(); void onRequestFinished_p();
private slots: private slots:
void onReadyRead(); void onReadyRead();
private:
QByteArray data; private:
QUrl url; QByteArray data;
QNetworkReply *reply; QUrl url;
QNetworkRequest request; QNetworkReply *reply;
QNetworkAccessManager accessManager; QNetworkRequest request;
QNetworkAccessManager accessManager;
}; };
} } // namespace Qv2ray::common
using namespace Qv2ray::common; using namespace Qv2ray::common;

View File

@ -1,30 +1,32 @@
#include "LogHighlighter.hpp" #include "LogHighlighter.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$" #define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
namespace Qv2ray::common namespace Qv2ray::common
{ {
SyntaxHighlighter::SyntaxHighlighter(bool darkMode, QTextDocument *parent) SyntaxHighlighter::SyntaxHighlighter(bool darkMode, QTextDocument *parent) : QSyntaxHighlighter(parent)
: QSyntaxHighlighter(parent)
{ {
HighlightingRule rule; HighlightingRule rule;
keywordFormat.setForeground(darkMode ? Qt::darkMagenta : Qt::magenta); keywordFormat.setForeground(darkMode ? Qt::darkMagenta : Qt::magenta);
keywordFormat.setFontWeight(QFont::Bold); keywordFormat.setFontWeight(QFont::Bold);
const QString keywordPatterns[] = { const QString keywordPatterns[] = { "tcp", "udp" };
"tcp", "udp"
};
for (const QString &pattern : keywordPatterns) { for (const QString &pattern : keywordPatterns)
{
rule.pattern = QRegularExpression(pattern); rule.pattern = QRegularExpression(pattern);
rule.format = keywordFormat; rule.format = keywordFormat;
highlightingRules.append(rule); highlightingRules.append(rule);
} }
if (darkMode) { if (darkMode)
{
ipHostFormat.setForeground(Qt::yellow); ipHostFormat.setForeground(Qt::yellow);
warningFormat.setForeground(QColor(230, 180, 0)); warningFormat.setForeground(QColor(230, 180, 0));
} else { }
else
{
ipHostFormat.setForeground(Qt::black); ipHostFormat.setForeground(Qt::black);
ipHostFormat.setFontWeight(QFont::Bold); ipHostFormat.setFontWeight(QFont::Bold);
warningFormat.setForeground(Qt::white); warningFormat.setForeground(Qt::white);
@ -49,7 +51,7 @@ namespace Qv2ray::common
rule.format = debugFormat; rule.format = debugFormat;
highlightingRules.append(rule); highlightingRules.append(rule);
// //
infoFormat.setForeground(darkMode ? Qt::lightGray : Qt::darkCyan); infoFormat.setForeground(darkMode ? Qt::lightGray : Qt::darkCyan);
rule.pattern = QRegularExpression("\\[[Ii]nfo\\]" TO_EOL); rule.pattern = QRegularExpression("\\[[Ii]nfo\\]" TO_EOL);
rule.format = infoFormat; rule.format = infoFormat;
highlightingRules.append(rule); highlightingRules.append(rule);
@ -119,10 +121,12 @@ namespace Qv2ray::common
void SyntaxHighlighter::highlightBlock(const QString &text) void SyntaxHighlighter::highlightBlock(const QString &text)
{ {
for (const HighlightingRule &rule : qAsConst(highlightingRules)) { for (const HighlightingRule &rule : qAsConst(highlightingRules))
{
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while (matchIterator.hasNext()) { while (matchIterator.hasNext())
{
QRegularExpressionMatch match = matchIterator.next(); QRegularExpressionMatch match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), rule.format); setFormat(match.capturedStart(), match.capturedLength(), rule.format);
} }
@ -130,4 +134,4 @@ namespace Qv2ray::common
setCurrentBlockState(0); setCurrentBlockState(0);
} }
} } // namespace Qv2ray::common

View File

@ -49,45 +49,46 @@
****************************************************************************/ ****************************************************************************/
#pragma once #pragma once
#include <QRegularExpression>
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include <QTextCharFormat> #include <QTextCharFormat>
#include <QRegularExpression>
#include <QTextDocument> #include <QTextDocument>
namespace Qv2ray::common namespace Qv2ray::common
{ {
class SyntaxHighlighter : public QSyntaxHighlighter class SyntaxHighlighter : public QSyntaxHighlighter
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SyntaxHighlighter(bool darkMode, QTextDocument *parent = nullptr); explicit SyntaxHighlighter(bool darkMode, QTextDocument *parent = nullptr);
protected: protected:
void highlightBlock(const QString &text) override; void highlightBlock(const QString &text) override;
private: private:
struct HighlightingRule { struct HighlightingRule
QRegularExpression pattern; {
QTextCharFormat format; QRegularExpression pattern;
}; QTextCharFormat format;
QVector<HighlightingRule> highlightingRules; };
QVector<HighlightingRule> highlightingRules;
QTextCharFormat keywordFormat; QTextCharFormat keywordFormat;
QTextCharFormat dateFormat; QTextCharFormat dateFormat;
QTextCharFormat acceptedFormat; QTextCharFormat acceptedFormat;
QTextCharFormat rejectedFormat; QTextCharFormat rejectedFormat;
QTextCharFormat failedFormat; QTextCharFormat failedFormat;
QTextCharFormat warningFormat; QTextCharFormat warningFormat;
QTextCharFormat infoFormat; QTextCharFormat infoFormat;
QTextCharFormat debugFormat; QTextCharFormat debugFormat;
QTextCharFormat timeFormat; QTextCharFormat timeFormat;
QTextCharFormat ipHostFormat; QTextCharFormat ipHostFormat;
QTextCharFormat v2rayComponentFormat; QTextCharFormat v2rayComponentFormat;
// //
QTextCharFormat qvAppLogFormat; QTextCharFormat qvAppLogFormat;
QTextCharFormat qvAppDebugLogFormat; QTextCharFormat qvAppDebugLogFormat;
}; };
} } // namespace Qv2ray::common
using namespace Qv2ray::common; using namespace Qv2ray::common;

View File

@ -10,8 +10,8 @@
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in
* copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -23,10 +23,10 @@
*/ */
#include "QJsonModel.hpp" #include "QJsonModel.hpp"
#include <QFile>
#include <QDebug>
#include <QFont>
#include <QDebug>
#include <QFile>
#include <QFont>
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent)
{ {
@ -60,8 +60,7 @@ int QJsonTreeItem::childCount() const
int QJsonTreeItem::row() const int QJsonTreeItem::row() const
{ {
if (mParent) if (mParent) return mParent->mChilds.indexOf(const_cast<QJsonTreeItem *>(this));
return mParent->mChilds.indexOf(const_cast<QJsonTreeItem *>(this));
return 0; return 0;
} }
@ -101,27 +100,34 @@ QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value, QJsonTreeItem *paren
QJsonTreeItem *rootItem = new QJsonTreeItem(parent); QJsonTreeItem *rootItem = new QJsonTreeItem(parent);
rootItem->setKey("root"); rootItem->setKey("root");
if (value.isObject()) { if (value.isObject())
//Get all QJsonValue childs {
for (QString key : value.toObject().keys()) { // Get all QJsonValue childs
for (QString key : value.toObject().keys())
{
QJsonValue v = value.toObject().value(key); QJsonValue v = value.toObject().value(key);
QJsonTreeItem *child = load(v, rootItem); QJsonTreeItem *child = load(v, rootItem);
child->setKey(key); child->setKey(key);
child->setType(v.type()); child->setType(v.type());
rootItem->appendChild(child); rootItem->appendChild(child);
} }
} else if (value.isArray()) { }
//Get all QJsonValue childs else if (value.isArray())
{
// Get all QJsonValue childs
int index = 0; int index = 0;
for (QJsonValue v : value.toArray()) { for (QJsonValue v : value.toArray())
{
QJsonTreeItem *child = load(v, rootItem); QJsonTreeItem *child = load(v, rootItem);
child->setKey(QString::number(index)); child->setKey(QString::number(index));
child->setType(v.type()); child->setType(v.type());
rootItem->appendChild(child); rootItem->appendChild(child);
++index; ++index;
} }
} else { }
else
{
rootItem->setValue(value.toVariant().toString()); rootItem->setValue(value.toVariant().toString());
rootItem->setType(value.type()); rootItem->setType(value.type());
} }
@ -131,35 +137,27 @@ QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value, QJsonTreeItem *paren
//========================================================================= //=========================================================================
QJsonModel::QJsonModel(QObject *parent) QJsonModel::QJsonModel(QObject *parent) : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
: QAbstractItemModel(parent)
, mRootItem{new QJsonTreeItem}
{ {
mHeaders.append("key"); mHeaders.append("key");
mHeaders.append("value"); mHeaders.append("value");
} }
QJsonModel::QJsonModel(const QString &fileName, QObject *parent) QJsonModel::QJsonModel(const QString &fileName, QObject *parent) : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
: QAbstractItemModel(parent)
, mRootItem{new QJsonTreeItem}
{ {
mHeaders.append("key"); mHeaders.append("key");
mHeaders.append("value"); mHeaders.append("value");
load(fileName); load(fileName);
} }
QJsonModel::QJsonModel(QIODevice *device, QObject *parent) QJsonModel::QJsonModel(QIODevice *device, QObject *parent) : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
: QAbstractItemModel(parent)
, mRootItem{new QJsonTreeItem}
{ {
mHeaders.append("key"); mHeaders.append("key");
mHeaders.append("value"); mHeaders.append("value");
load(device); load(device);
} }
QJsonModel::QJsonModel(const QByteArray &json, QObject *parent) QJsonModel::QJsonModel(const QByteArray &json, QObject *parent) : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
: QAbstractItemModel(parent)
, mRootItem{new QJsonTreeItem}
{ {
mHeaders.append("key"); mHeaders.append("key");
mHeaders.append("value"); mHeaders.append("value");
@ -176,10 +174,13 @@ bool QJsonModel::load(const QString &fileName)
QFile file(fileName); QFile file(fileName);
bool success = false; bool success = false;
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly))
{
success = load(&file); success = load(&file);
file.close(); file.close();
} else success = false; }
else
success = false;
return success; return success;
} }
@ -193,14 +194,18 @@ bool QJsonModel::loadJson(const QByteArray &json)
{ {
auto const &jdoc = QJsonDocument::fromJson(json); auto const &jdoc = QJsonDocument::fromJson(json);
if (!jdoc.isNull()) { if (!jdoc.isNull())
{
beginResetModel(); beginResetModel();
delete mRootItem; delete mRootItem;
if (jdoc.isArray()) { if (jdoc.isArray())
{
mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array())); mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array()));
mRootItem->setType(QJsonValue::Array); mRootItem->setType(QJsonValue::Array);
} else { }
else
{
mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object())); mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object()));
mRootItem->setType(QJsonValue::Object); mRootItem->setType(QJsonValue::Object);
} }
@ -213,24 +218,21 @@ bool QJsonModel::loadJson(const QByteArray &json)
return false; return false;
} }
QVariant QJsonModel::data(const QModelIndex &index, int role) const QVariant QJsonModel::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid()) return QVariant();
return QVariant();
QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer()); QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer());
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole)
if (index.column() == 0) {
return QString("%1").arg(item->key()); if (index.column() == 0) return QString("%1").arg(item->key());
if (index.column() == 1) if (index.column() == 1) return QString("%1").arg(item->value());
return QString("%1").arg(item->value()); }
} else if (Qt::EditRole == role) { else if (Qt::EditRole == role)
if (index.column() == 1) { {
return QString("%1").arg(item->value()); if (index.column() == 1) { return QString("%1").arg(item->value()); }
}
} }
return QVariant(); return QVariant();
@ -240,11 +242,13 @@ bool QJsonModel::setData(const QModelIndex &index, const QVariant &value, int ro
{ {
int col = index.column(); int col = index.column();
if (Qt::EditRole == role) { if (Qt::EditRole == role)
if (col == 1) { {
if (col == 1)
{
QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer()); QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer());
item->setValue(value.toString()); item->setValue(value.toString());
emit dataChanged(index, index, {Qt::EditRole}); emit dataChanged(index, index, { Qt::EditRole });
return true; return true;
} }
} }
@ -252,49 +256,40 @@ bool QJsonModel::setData(const QModelIndex &index, const QVariant &value, int ro
return false; return false;
} }
QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole) return QVariant();
return QVariant();
if (orientation == Qt::Horizontal) { if (orientation == Qt::Horizontal) { return mHeaders.value(section); }
return mHeaders.value(section); else
} else
return QVariant(); return QVariant();
} }
QModelIndex QJsonModel::index(int row, int column, const QModelIndex &parent) const QModelIndex QJsonModel::index(int row, int column, const QModelIndex &parent) const
{ {
if (!hasIndex(row, column, parent)) if (!hasIndex(row, column, parent)) return QModelIndex();
return QModelIndex();
QJsonTreeItem *parentItem; QJsonTreeItem *parentItem;
if (!parent.isValid()) if (!parent.isValid()) parentItem = mRootItem;
parentItem = mRootItem;
else else
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer()); parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
QJsonTreeItem *childItem = parentItem->child(row); QJsonTreeItem *childItem = parentItem->child(row);
if (childItem) if (childItem) return createIndex(row, column, childItem);
return createIndex(row, column, childItem);
else else
return QModelIndex(); return QModelIndex();
} }
QModelIndex QJsonModel::parent(const QModelIndex &index) const QModelIndex QJsonModel::parent(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid()) return QModelIndex();
return QModelIndex();
QJsonTreeItem *childItem = static_cast<QJsonTreeItem *>(index.internalPointer()); QJsonTreeItem *childItem = static_cast<QJsonTreeItem *>(index.internalPointer());
QJsonTreeItem *parentItem = childItem->parent(); QJsonTreeItem *parentItem = childItem->parent();
if (parentItem == mRootItem) if (parentItem == mRootItem) return QModelIndex();
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem); return createIndex(parentItem->row(), 0, parentItem);
} }
@ -303,11 +298,9 @@ int QJsonModel::rowCount(const QModelIndex &parent) const
{ {
QJsonTreeItem *parentItem; QJsonTreeItem *parentItem;
if (parent.column() > 0) if (parent.column() > 0) return 0;
return 0;
if (!parent.isValid()) if (!parent.isValid()) parentItem = mRootItem;
parentItem = mRootItem;
else else
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer()); parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
@ -322,14 +315,14 @@ int QJsonModel::columnCount(const QModelIndex &parent) const
Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const
{ {
int col = index.column(); int col = index.column();
auto item = static_cast<QJsonTreeItem *>(index.internalPointer()); auto item = static_cast<QJsonTreeItem *>(index.internalPointer());
auto isArray = QJsonValue::Array == item->type(); auto isArray = QJsonValue::Array == item->type();
auto isObject = QJsonValue::Object == item->type(); auto isObject = QJsonValue::Object == item->type();
if ((col == 1) && !(isArray || isObject)) { if ((col == 1) && !(isArray || isObject)) { return Qt::ItemIsEditable | QAbstractItemModel::flags(index); }
return Qt::ItemIsEditable | QAbstractItemModel::flags(index); else
} else { {
return QAbstractItemModel::flags(index); return QAbstractItemModel::flags(index);
} }
} }
@ -339,40 +332,47 @@ QJsonDocument QJsonModel::json() const
auto v = genJson(mRootItem); auto v = genJson(mRootItem);
QJsonDocument doc; QJsonDocument doc;
if (v.isObject()) { if (v.isObject()) { doc = QJsonDocument(v.toObject()); }
doc = QJsonDocument(v.toObject()); else
} else { {
doc = QJsonDocument(v.toArray()); doc = QJsonDocument(v.toArray());
} }
return doc; return doc;
} }
QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const
{ {
auto type = item->type(); auto type = item->type();
int nchild = item->childCount(); int nchild = item->childCount();
if (QJsonValue::Object == type) { if (QJsonValue::Object == type)
{
QJsonObject jo; QJsonObject jo;
for (int i = 0; i < nchild; ++i) { for (int i = 0; i < nchild; ++i)
{
auto ch = item->child(i); auto ch = item->child(i);
auto key = ch->key(); auto key = ch->key();
jo.insert(key, genJson(ch)); jo.insert(key, genJson(ch));
} }
return jo; return jo;
} else if (QJsonValue::Array == type) { }
else if (QJsonValue::Array == type)
{
QJsonArray arr; QJsonArray arr;
for (int i = 0; i < nchild; ++i) { for (int i = 0; i < nchild; ++i)
{
auto ch = item->child(i); auto ch = item->child(i);
arr.append(genJson(ch)); arr.append(genJson(ch));
} }
return arr; return arr;
} else { }
else
{
QJsonValue va(item->value()); QJsonValue va(item->value());
return va; return va;
} }

View File

@ -1,101 +1,94 @@
/* /*
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2011 SCHUTZ Sacha * Copyright (c) 2011 SCHUTZ Sacha
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#pragma once #pragma once
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon> #include <QIcon>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
class QJsonModel; class QJsonModel;
class QJsonItem; class QJsonItem;
class QJsonTreeItem class QJsonTreeItem
{ {
public: public:
QJsonTreeItem(QJsonTreeItem *parent = nullptr); QJsonTreeItem(QJsonTreeItem *parent = nullptr);
~QJsonTreeItem(); ~QJsonTreeItem();
void appendChild(QJsonTreeItem *item); void appendChild(QJsonTreeItem *item);
QJsonTreeItem *child(int row); QJsonTreeItem *child(int row);
QJsonTreeItem *parent(); QJsonTreeItem *parent();
int childCount() const; int childCount() const;
int row() const; int row() const;
void setKey(const QString &key); void setKey(const QString &key);
void setValue(const QString &value); void setValue(const QString &value);
void setType(const QJsonValue::Type &type); void setType(const QJsonValue::Type &type);
QString key() const; QString key() const;
QString value() const; QString value() const;
QJsonValue::Type type() const; QJsonValue::Type type() const;
static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0);
protected:
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<QJsonTreeItem *> mChilds;
QJsonTreeItem *mParent;
static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0);
protected:
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<QJsonTreeItem *> mChilds;
QJsonTreeItem *mParent;
}; };
//--------------------------------------------------- //---------------------------------------------------
class QJsonModel : public QAbstractItemModel class QJsonModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QJsonModel(QObject *parent = nullptr); explicit QJsonModel(QObject *parent = nullptr);
QJsonModel(const QString &fileName, QObject *parent = nullptr); QJsonModel(const QString &fileName, QObject *parent = nullptr);
QJsonModel(QIODevice *device, QObject *parent = nullptr); QJsonModel(QIODevice *device, QObject *parent = nullptr);
QJsonModel(const QByteArray &json, QObject *parent = nullptr); QJsonModel(const QByteArray &json, QObject *parent = nullptr);
~QJsonModel(); ~QJsonModel();
bool load(const QString &fileName); bool load(const QString &fileName);
bool load(QIODevice *device); bool load(QIODevice *device);
bool loadJson(const QByteArray &json); bool loadJson(const QByteArray &json);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QJsonDocument json() const; QJsonDocument json() const;
private:
QJsonValue genJson(QJsonTreeItem *) const;
QJsonTreeItem *mRootItem;
QStringList mHeaders;
private:
QJsonValue genJson(QJsonTreeItem *) const;
QJsonTreeItem *mRootItem;
QStringList mHeaders;
}; };

View File

@ -1,4 +1,5 @@
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include <QQueue> #include <QQueue>
namespace Qv2ray::common namespace Qv2ray::common
@ -8,7 +9,8 @@ namespace Qv2ray::common
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString; QString randomString;
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i)
{
uint rand = QRandomGenerator::system()->generate(); uint rand = QRandomGenerator::system()->generate();
uint max = static_cast<uint>(possibleCharacters.length()); uint max = static_cast<uint>(possibleCharacters.length());
QChar nextChar = possibleCharacters[rand % max]; QChar nextChar = possibleCharacters[rand % max];
@ -73,9 +75,9 @@ namespace Qv2ray::common
QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error); QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error);
Q_UNUSED(doc) Q_UNUSED(doc)
if (error.error == QJsonParseError::NoError) { if (error.error == QJsonParseError::NoError) { return ""; }
return ""; else
} else { {
LOG(MODULE_UI, "WARNING: Json parse returns: " + error.errorString()) LOG(MODULE_UI, "WARNING: Json parse returns: " + error.errorString())
return error.errorString(); return error.errorString();
} }
@ -108,16 +110,16 @@ namespace Qv2ray::common
{ {
list<string> list; list<string> list;
for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) { for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) { list.push_back(line.toStdString()); }
list.push_back(line.toStdString());
}
return list; return list;
} }
QStringList GetFileList(QDir dir) QStringList GetFileList(QDir dir)
{ {
return dir.entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files); return dir.entryList(QStringList() << "*"
<< "*.*",
QDir::Hidden | QDir::Files);
} }
bool FileExistsIn(QDir dir, QString fileName) bool FileExistsIn(QDir dir, QString fileName)
@ -147,8 +149,7 @@ namespace Qv2ray::common
int i; int i;
double dblByte = bytes; double dblByte = bytes;
for (i = 0; i < 5 && bytes >= 1024; i++, bytes /= 1024) for (i = 0; i < 5 && bytes >= 1024; i++, bytes /= 1024) dblByte = bytes / 1024.0;
dblByte = bytes / 1024.0;
sprintf(str, "%.2f", dblByte); sprintf(str, "%.2f", dblByte);
return QString(str) + " " + QString(sizes[i]); return QString(str) + " " + QString(sizes[i]);
@ -163,9 +164,8 @@ namespace Qv2ray::common
QString RemoveInvalidFileName(const QString &fileName) QString RemoveInvalidFileName(const QString &fileName)
{ {
std::string _name = fileName.toStdString(); std::string _name = fileName.toStdString();
std::replace_if(_name.begin(), _name.end(), [](char c) { std::replace_if(
return std::string::npos != string(R"("/\?%&^*;:|><)").find(c); _name.begin(), _name.end(), [](char c) { return std::string::npos != string(R"("/\?%&^*;:|><)").find(c); }, '_');
}, '_');
return QString::fromStdString(_name); return QString::fromStdString(_name);
} }
@ -174,20 +174,25 @@ namespace Qv2ray::common
{ {
int i = 1; int i = 1;
if (!QDir(baseDir).exists()) { if (!QDir(baseDir).exists())
{
QDir(baseDir).mkpath(baseDir); QDir(baseDir).mkpath(baseDir);
LOG(MODULE_FILEIO, "Making path: " + baseDir) LOG(MODULE_FILEIO, "Making path: " + baseDir)
} }
while (true) { while (true)
if (!QFile(baseDir + "/" + fileName + "_" + QSTRN(i) + extension).exists()) { {
if (!QFile(baseDir + "/" + fileName + "_" + QSTRN(i) + extension).exists())
{
*fileName = *fileName + "_" + QSTRN(i); *fileName = *fileName + "_" + QSTRN(i);
return; return;
} else { }
else
{
DEBUG(MODULE_FILEIO, "File with name: " + *fileName + "_" + QSTRN(i) + extension + " already exists") DEBUG(MODULE_FILEIO, "File with name: " + *fileName + "_" + QSTRN(i) + extension + " already exists")
} }
i++; i++;
} }
} }
} } // namespace Qv2ray::common

View File

@ -1,10 +1,13 @@
#pragma once #pragma once
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include <QMessageBox> #include <QMessageBox>
#define REGEX_IPV6_ADDR R"(\[\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*\])" #define REGEX_IPV6_ADDR \
#define REGEX_IPV4_ADDR R"((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]))" R"(\[\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*\])"
#define REGEX_IPV4_ADDR \
R"((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]))"
#define REGEX_PORT_NUMBER R"(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])*)" #define REGEX_PORT_NUMBER R"(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])*)"
namespace Qv2ray::common namespace Qv2ray::common
@ -19,7 +22,8 @@ namespace Qv2ray::common
// //
void QvMessageBoxWarn(QWidget *parent, QString title, QString text); void QvMessageBoxWarn(QWidget *parent, QString title, QString text);
void QvMessageBoxInfo(QWidget *parent, QString title, QString text); void QvMessageBoxInfo(QWidget *parent, QString title, QString text);
QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton); QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, QString title, QString text,
QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
// //
QString StringFromFile(const QString &filePath); QString StringFromFile(const QString &filePath);
QString StringFromFile(QFile *source); QString StringFromFile(QFile *source);
@ -40,16 +44,16 @@ namespace Qv2ray::common
inline QString GenerateUuid() inline QString GenerateUuid()
{ {
return GenerateRandomString().toLower(); return GenerateRandomString().toLower();
//return QUuid::createUuid().toString(QUuid::WithoutBraces); // return QUuid::createUuid().toString(QUuid::WithoutBraces);
} }
// //
template <typename TYPE> template<typename TYPE>
QString StructToJsonString(const TYPE t) QString StructToJsonString(const TYPE t)
{ {
return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' ')); return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' '));
} }
// //
template <typename TYPE> template<typename TYPE>
TYPE StructFromJsonString(const QString &str) TYPE StructFromJsonString(const QString &str)
{ {
TYPE v; TYPE v;
@ -68,9 +72,7 @@ namespace Qv2ray::common
{ {
QString t = str; QString t = str;
t.truncate(limit); t.truncate(limit);
return (limit == -1 || str.length() < limit) return (limit == -1 || str.length() < limit) ? str : (t + suffix);
? str
: (t + suffix);
} }
namespace validation namespace validation
@ -92,7 +94,7 @@ namespace Qv2ray::common
{ {
return IsIPv4Address(addr) || IsIPv6Address(addr); return IsIPv4Address(addr) || IsIPv6Address(addr);
} }
} } // namespace validation
inline QString timeToString(const time_t &t) inline QString timeToString(const time_t &t)
{ {
@ -102,6 +104,6 @@ namespace Qv2ray::common
strftime(MY_TIME, sizeof(MY_TIME), "%x - %I:%M%p", _tm); strftime(MY_TIME, sizeof(MY_TIME), "%x - %I:%M%p", _tm);
return QString(MY_TIME); return QString(MY_TIME);
} }
} } // namespace Qv2ray::common
using namespace Qv2ray::common; using namespace Qv2ray::common;

View File

@ -7,15 +7,15 @@ namespace Qv2ray::common
{ {
class QvTranslator class QvTranslator
{ {
public: public:
QvTranslator(const QString &lang) QvTranslator(const QString &lang)
{ {
QTranslator *translator = new QTranslator(); QTranslator *translator = new QTranslator();
translator->load(lang + ".qm", ":/translations/"); translator->load(lang + ".qm", ":/translations/");
this->pTranslator.reset(translator); this->pTranslator.reset(translator);
} }
public: public:
std::unique_ptr<QTranslator> pTranslator; std::unique_ptr<QTranslator> pTranslator;
}; };
} // namespace Qv2ray::common } // namespace Qv2ray::common

View File

@ -1,13 +1,14 @@
#include "QvAutoLaunch.hpp" #include "QvAutoLaunch.hpp"
#include <QSettings>
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
#include <QSettings>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream> #include <QTextStream>
// macOS headers (possibly OBJ-c) // macOS headers (possibly OBJ-c)
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include <CoreServices/CoreServices.h> #include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h>
#endif #endif
namespace Qv2ray::components::autolaunch namespace Qv2ray::components::autolaunch
{ {
@ -34,8 +35,10 @@ namespace Qv2ray::components::autolaunch
} }
#elif defined Q_OS_MAC #elif defined Q_OS_MAC
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp // From
// this is quite some duplicate code with setLaunchOnStartup, at some point we should fix this FIXME. // https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
// this is quite some duplicate code with setLaunchOnStartup, at some
// point we should fix this FIXME.
bool returnValue = false; bool returnValue = false;
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
@ -49,16 +52,16 @@ namespace Qv2ray::components::autolaunch
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { for (int i = 0; i < CFArrayGetCount(itemsArray); i++)
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i); {
LSSharedFileListItemRef item = (LSSharedFileListItemRef) CFArrayGetValueAtIndex(itemsArray, i);
CFURLRef itemUrlRef = NULL; CFURLRef itemUrlRef = NULL;
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) { if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef)
{
CFStringRef itemUrlString = CFURLGetString(itemUrlRef); CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { returnValue = true; }
returnValue = true;
}
CFRelease(itemUrlRef); CFRelease(itemUrlRef);
} }
@ -86,16 +89,20 @@ namespace Qv2ray::components::autolaunch
QString appName = QApplication::applicationName(); QString appName = QApplication::applicationName();
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
if (enable) { if (enable)
{
QString strAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath()); QString strAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
reg.setValue(appName, strAppPath); reg.setValue(appName, strAppPath);
} else { }
else
{
reg.remove(appName); reg.remove(appName);
} }
} }
#elif defined Q_OS_MAC #elif defined Q_OS_MAC
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp // From
// https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
@ -103,30 +110,31 @@ namespace Qv2ray::components::autolaunch
if (loginItems && enable) if (loginItems && enable)
{ {
//Insert an item to the list. // Insert an item to the list.
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, 0, 0, urlRef, 0, 0);
kLSSharedFileListItemLast, 0, 0,
urlRef, 0, 0);
if (item) if (item) CFRelease(item);
CFRelease(item);
CFRelease(loginItems); CFRelease(loginItems);
} else if (loginItems && !enable) }
else if (loginItems && !enable)
{ {
// We need to iterate over the items and check which one is "ours". // We need to iterate over the items and check which one is "ours".
UInt32 seedValue; UInt32 seedValue;
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
CFStringRef appUrlRefString = CFURLGetString(urlRef); CFStringRef appUrlRefString = CFURLGetString(urlRef);
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { for (int i = 0; i < CFArrayGetCount(itemsArray); i++)
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i); {
LSSharedFileListItemRef item = (LSSharedFileListItemRef) CFArrayGetValueAtIndex(itemsArray, i);
CFURLRef itemUrlRef = NULL; CFURLRef itemUrlRef = NULL;
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) { if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef)
{
CFStringRef itemUrlString = CFURLGetString(itemUrlRef); CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo)
{
LSSharedFileListItemRemove(loginItems, item); // remove it! LSSharedFileListItemRemove(loginItems, item); // remove it!
} }
@ -143,22 +151,27 @@ namespace Qv2ray::components::autolaunch
} }
#elif defined Q_OS_LINUX #elif defined Q_OS_LINUX
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp // From
// https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp
QString appName = QApplication::applicationName(); QString appName = QApplication::applicationName();
QString userAutoStartPath = getUserAutostartDir_private(); QString userAutoStartPath = getUserAutostartDir_private();
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop"); QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
if (enable) if (enable)
{ {
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) { if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath))
// qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath; {
// qCWarning(lcUtility) << "Could not create autostart folder"
// << userAutoStartPath;
return; return;
} }
QFile iniFile(desktopFileLocation); QFile iniFile(desktopFileLocation);
if (!iniFile.open(QIODevice::WriteOnly)) { if (!iniFile.open(QIODevice::WriteOnly))
// qCWarning(lcUtility) << "Could not write auto start entry" << desktopFileLocation; {
// qCWarning(lcUtility) << "Could not write auto start entry" <<
// desktopFileLocation;
return; return;
} }
@ -174,13 +187,15 @@ namespace Qv2ray::components::autolaunch
<< QLatin1String("Type=") << "Application" << endl << QLatin1String("Type=") << "Application" << endl
<< QLatin1String("StartupNotify=") << "false" << endl << QLatin1String("StartupNotify=") << "false" << endl
<< QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl; << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl;
} else }
else
{ {
if (!QFile::remove(desktopFileLocation)) { if (!QFile::remove(desktopFileLocation))
// qCWarning(lcUtility) << "Could not remove autostart desktop file"; {
// qCWarning(lcUtility) << "Could not remove autostart desktop
// file";
} }
} }
} }
#endif #endif
} } // namespace Qv2ray::components::autolaunch

View File

@ -4,7 +4,7 @@ namespace Qv2ray::components::autolaunch
{ {
bool GetLaunchAtLoginStatus(); bool GetLaunchAtLoginStatus();
void SetLaunchAtLoginStatus(bool enable); void SetLaunchAtLoginStatus(bool enable);
} } // namespace Qv2ray::components::autolaunch
using namespace Qv2ray::components; using namespace Qv2ray::components;
using namespace Qv2ray::components::autolaunch; using namespace Qv2ray::components::autolaunch;

View File

@ -1,4 +1,5 @@
#include "QvGeositeReader.hpp" #include "QvGeositeReader.hpp"
#include "libs/gen/v2ray_geosite.pb.h" #include "libs/gen/v2ray_geosite.pb.h"
namespace Qv2ray::components::geosite namespace Qv2ray::components::geosite
@ -13,7 +14,8 @@ namespace Qv2ray::components::geosite
QFile f(filepath); QFile f(filepath);
bool opened = f.open(QFile::OpenModeFlag::ReadOnly); bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
if (!opened) { if (!opened)
{
LOG(MODULE_FILEIO, "File cannot be opened: " + filepath) LOG(MODULE_FILEIO, "File cannot be opened: " + filepath)
return list; return list;
} }
@ -24,7 +26,8 @@ namespace Qv2ray::components::geosite
GeoSiteList sites; GeoSiteList sites;
sites.ParseFromArray(content.data(), content.size()); sites.ParseFromArray(content.data(), content.size());
for (auto e : sites.entry()) { for (auto e : sites.entry())
{
// We want to use lower string. // We want to use lower string.
list << QString::fromStdString(e.country_code()).toLower(); list << QString::fromStdString(e.country_code()).toLower();
} }
@ -34,4 +37,4 @@ namespace Qv2ray::components::geosite
google::protobuf::ShutdownProtobufLibrary(); google::protobuf::ShutdownProtobufLibrary();
return list; return list;
} }
} } // namespace Qv2ray::components::geosite

View File

@ -1,41 +1,38 @@
#include "ICMPPinger.hpp" #include "ICMPPinger.hpp"
ICMPPinger::ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT) { ICMPPinger::ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT)
{
// create icmp handle // create icmp handle
if ((this->hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE) { if ((this->hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE) { throw "IcmpCreateFile failed"; }
throw "IcmpCreateFile failed";
}
// remember the timeout // remember the timeout
this->timeout = timeout; this->timeout = timeout;
} }
ICMPPinger::~ICMPPinger() { ICMPPinger::~ICMPPinger()
{
// release the handle on destruction // release the handle on destruction
IcmpCloseHandle(this->hIcmpFile); IcmpCloseHandle(this->hIcmpFile);
} }
std::pair<std::optional<UINT64>, std::optional<std::string>> ICMPPinger::ping(const std::string& ipAddr) { std::pair<std::optional<UINT64>, std::optional<std::string>> ICMPPinger::ping(const std::string &ipAddr)
{
// convert network address // convert network address
const auto addr = inet_addr(ipAddr.c_str()); const auto addr = inet_addr(ipAddr.c_str());
if (addr == INADDR_NONE) { if (addr == INADDR_NONE) { return std::pair(std::nullopt, "invalid ip address: " + ipAddr); }
return std::pair(std::nullopt, "invalid ip address: " + ipAddr);
}
// request buffer // request buffer
const static char bufRequest[] = "echo test"; const static char bufRequest[] = "echo test";
// response buffer // response buffer
const auto responseSize = sizeof(ICMP_ECHO_REPLY) + sizeof(bufRequest); const auto responseSize = sizeof(ICMP_ECHO_REPLY) + sizeof(bufRequest);
const std::unique_ptr<char> bufRecv(new(char[responseSize])); const std::unique_ptr<char> bufRecv(new (char[responseSize]));
// send echo // send echo
auto ret = IcmpSendEcho(this->hIcmpFile, addr, (LPVOID)bufRequest, sizeof(bufRequest), NULL, bufRecv.get(), responseSize, this->timeout); auto ret = IcmpSendEcho(this->hIcmpFile, addr, (LPVOID) bufRequest, sizeof(bufRequest), NULL, bufRecv.get(), responseSize, this->timeout);
// ret == 0: failed // ret == 0: failed
if (ret == 0) { if (ret == 0) { return std::pair(std::nullopt, "IcmpSendEcho returned error"); }
return std::pair(std::nullopt, "IcmpSendEcho returned error");
}
// read round-trip time // read round-trip time
PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY) bufRecv.get(); PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY) bufRecv.get();

View File

@ -6,27 +6,26 @@
* License: WTFPL * License: WTFPL
*/ */
#include<winsock2.h> #include <icmpapi.h>
#include<iphlpapi.h> #include <iphlpapi.h>
#include<icmpapi.h> #include <memory>
#include <optional>
#include <utility>
#include <winsock2.h>
#include<utility> class ICMPPinger
#include<optional> {
#include<memory> public:
ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
~ICMPPinger();
public:
static const UINT64 DEFAULT_TIMEOUT = 10000U;
class ICMPPinger { public:
public: std::pair<std::optional<UINT64>, std::optional<std::string>> ping(const std::string &ipAddr);
ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
~ICMPPinger();
public: private:
static const UINT64 DEFAULT_TIMEOUT = 10000U; HANDLE hIcmpFile;
UINT64 timeout = DEFAULT_TIMEOUT;
public:
std::pair<std::optional<UINT64>, std::optional<std::string>> ping(const std::string& ipAddr);
private:
HANDLE hIcmpFile;
UINT64 timeout = DEFAULT_TIMEOUT;
}; };

View File

@ -21,29 +21,23 @@ namespace Qv2ray::components::pac
bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines
bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines
if (originLine[endPosition] == '\n') { if (originLine[endPosition] == '\n') { endPosition -= 1; }
endPosition -= 1;
}
if (originLine.find("http://") != string::npos) { if (originLine.find("http://") != string::npos) { startPosition += 8; }
startPosition += 8; else if (originLine.find("https://") != string::npos)
} else if (originLine.find("https://") != string::npos) { {
startPosition += 9; startPosition += 9;
} }
// Skip unrelated lines // Skip unrelated lines
if (skipRule1 || skipRule2 || skipRule3 || skipRule4) { if (skipRule1 || skipRule2 || skipRule3 || skipRule4) { return ""; }
return ""; else if (passRule2)
} else if (passRule2) { {
if (passRule1) { if (passRule1) { startPosition += originLine.find_last_of("|") + 1; }
startPosition += originLine.find_last_of("|") + 1;
}
if (originLine[startPosition] == '\n') startPosition += 1; if (originLine[startPosition] == '\n') startPosition += 1;
for (size_t i = startPosition; i < endPosition; ++i) { for (size_t i = startPosition; i < endPosition; ++i) { returnBuffer += originLine[i]; }
returnBuffer += originLine[i];
}
} }
return returnBuffer; return returnBuffer;
@ -52,19 +46,19 @@ namespace Qv2ray::components::pac
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString) QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString)
{ {
auto rawFileContent = Base64Decode(rawContent).toStdString(); auto rawFileContent = Base64Decode(rawContent).toStdString();
string readBuffer = ""; //cleanup string readBuffer = ""; // cleanup
string writeBuffer; string writeBuffer;
string domainListCache = ""; string domainListCache = "";
for (size_t i = 0; i < rawFileContent.size(); ++i) { for (size_t i = 0; i < rawFileContent.size(); ++i)
{
readBuffer += rawFileContent[i]; readBuffer += rawFileContent[i];
if (rawFileContent[i + 1] == '\n') { if (rawFileContent[i + 1] == '\n')
{
writeBuffer = getRawDomain(readBuffer); writeBuffer = getRawDomain(readBuffer);
if (writeBuffer != "") { if (writeBuffer != "") { domainListCache += writeBuffer + "\n"; }
domainListCache += writeBuffer + "\n";
}
readBuffer = ""; readBuffer = "";
i += 1; i += 1;
@ -72,19 +66,22 @@ namespace Qv2ray::components::pac
} }
size_t rotatorTwo = 0; size_t rotatorTwo = 0;
string readDomainBuffer = ""; string readDomainBuffer = "";
bool isFirstLine = true; bool isFirstLine = true;
string outputContent = ""; string outputContent = "";
//Header // Header
outputContent += "var domains = {\n"; outputContent += "var domains = {\n";
//Read and process output content line by line // Read and process output content line by line
while (rotatorTwo < domainListCache.size()) { while (rotatorTwo < domainListCache.size())
while (true) { {
//Get Domain while (true)
{
// Get Domain
readDomainBuffer += domainListCache[rotatorTwo]; readDomainBuffer += domainListCache[rotatorTwo];
if (domainListCache[rotatorTwo + 1] == '\n') { if (domainListCache[rotatorTwo + 1] == '\n')
{
rotatorTwo += 2; rotatorTwo += 2;
break; break;
} }
@ -92,9 +89,10 @@ namespace Qv2ray::components::pac
rotatorTwo++; rotatorTwo++;
} }
//Format // Format
if (!isFirstLine) outputContent += ",\n"; if (!isFirstLine) outputContent += ",\n";
else isFirstLine = false; else
isFirstLine = false;
outputContent += "\t\""; outputContent += "\t\"";
outputContent += readDomainBuffer; outputContent += readDomainBuffer;
@ -102,32 +100,18 @@ namespace Qv2ray::components::pac
readDomainBuffer = ""; readDomainBuffer = "";
} }
//End Message // End Message
outputContent += outputContent += NEWLINE "};" NEWLINE "" NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
NEWLINE "};" NEWLINE " var direct = 'DIRECT;';" NEWLINE " function FindProxyForURL(url, host) {" NEWLINE
NEWLINE "" " var suffix;" NEWLINE " var pos = host.lastIndexOf('.');" NEWLINE
NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" + " pos = host.lastIndexOf('.', pos - 1);" NEWLINE " //" NEWLINE " while (1) {" NEWLINE
NEWLINE " var direct = 'DIRECT;';" " if (domains[host] != undefined) {" NEWLINE " return proxy;" NEWLINE
NEWLINE " function FindProxyForURL(url, host) {" " }" NEWLINE " else if (pos <= 0) {" NEWLINE
NEWLINE " var suffix;" " return domains['.' + host] != undefined ? proxy : direct;" NEWLINE " }" NEWLINE
NEWLINE " var pos = host.lastIndexOf('.');" " suffix = host.substring(pos);" NEWLINE " if (domains[suffix] != undefined) {" NEWLINE
NEWLINE " pos = host.lastIndexOf('.', pos - 1);" " return proxy;" NEWLINE " }" NEWLINE
NEWLINE " //" " pos = host.lastIndexOf('.', pos - 1);" NEWLINE " }" NEWLINE " }";
NEWLINE " while (1) {"
NEWLINE " if (domains[host] != undefined) {"
NEWLINE " return proxy;"
NEWLINE " }"
NEWLINE " else if (pos <= 0) {"
NEWLINE " return domains['.' + host] != undefined ? proxy : direct;"
NEWLINE " }"
NEWLINE " suffix = host.substring(pos);"
NEWLINE " if (domains[suffix] != undefined) {"
NEWLINE " return proxy;"
NEWLINE " }"
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
NEWLINE " }"
NEWLINE " }";
// //
return QString::fromStdString(outputContent); return QString::fromStdString(outputContent);
} }
} } // namespace Qv2ray::components::pac

View File

@ -1,8 +1,9 @@
#include "QvPACHandler.hpp" #include "QvPACHandler.hpp"
#include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
#include "qhttprequest.h" #include "qhttprequest.h"
#include "qhttpresponse.h" #include "qhttpresponse.h"
#include "core/CoreUtils.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::components::pac namespace Qv2ray::components::pac
{ {
@ -13,9 +14,7 @@ namespace Qv2ray::components::pac
} }
PACServer::~PACServer() PACServer::~PACServer()
{ {
if (isStarted) { if (isStarted) { pacServer.close(); }
pacServer.close();
}
} }
void PACServer::SetProxyString(const QString &proxyString) void PACServer::SetProxyString(const QString &proxyString)
{ {
@ -36,10 +35,13 @@ namespace Qv2ray::components::pac
// //
auto result = pacServer.listen(QHostAddress(address), static_cast<ushort>(port)); auto result = pacServer.listen(QHostAddress(address), static_cast<ushort>(port));
if (result) { if (result)
{
isStarted = true; isStarted = true;
DEBUG(MODULE_PROXY, "Started PAC handler") DEBUG(MODULE_PROXY, "Started PAC handler")
} else { }
else
{
LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.") LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.")
QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions")); QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
} }
@ -47,7 +49,8 @@ namespace Qv2ray::components::pac
void PACServer::StopServer() void PACServer::StopServer()
{ {
if (isStarted) { if (isStarted)
{
pacServer.close(); pacServer.close();
DEBUG(MODULE_PROXY, "PAC Handler stopped.") DEBUG(MODULE_PROXY, "PAC Handler stopped.")
isStarted = false; isStarted = false;
@ -58,22 +61,28 @@ namespace Qv2ray::components::pac
{ {
rsp->setHeader("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler"); rsp->setHeader("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler");
if (req->method() == QHttpRequest::HTTP_GET) { if (req->method() == QHttpRequest::HTTP_GET)
{
// //
if (req->path() == "/pac") { if (req->path() == "/pac")
{
DEBUG(MODULE_PROXY, "Serving PAC file request.") DEBUG(MODULE_PROXY, "Serving PAC file request.")
// //
rsp->setHeader("Content-Type", "application/javascript; charset=utf-8"); rsp->setHeader("Content-Type", "application/javascript; charset=utf-8");
rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK); rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK);
rsp->end(pacContent.toUtf8()); rsp->end(pacContent.toUtf8());
DEBUG(MODULE_PROXY, "Serving a pac file...") DEBUG(MODULE_PROXY, "Serving a pac file...")
} else { }
else
{
rsp->writeHead(QHttpResponse::StatusCode::STATUS_NOT_FOUND); rsp->writeHead(QHttpResponse::StatusCode::STATUS_NOT_FOUND);
rsp->end("NOT FOUND"); rsp->end("NOT FOUND");
} }
} else { }
else
{
rsp->writeHead(QHttpResponse::StatusCode::STATUS_METHOD_NOT_ALLOWED); rsp->writeHead(QHttpResponse::StatusCode::STATUS_METHOD_NOT_ALLOWED);
rsp->end("PAC ONLY SUPPORT GET"); rsp->end("PAC ONLY SUPPORT GET");
} }
} }
} } // namespace Qv2ray::components::pac

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "qhttpserver.h" #include "qhttpserver.h"
#include <QObject> #include <QObject>
#include <memory> #include <memory>
@ -8,26 +9,26 @@ namespace Qv2ray::components::pac
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString); QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString);
class PACServer : public QObject class PACServer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PACServer(); explicit PACServer();
~PACServer(); ~PACServer();
void SetProxyString(const QString &proxyString); void SetProxyString(const QString &proxyString);
void StartListen(); void StartListen();
void StopServer(); void StopServer();
QString gfwFilePath; QString gfwFilePath;
public slots: public slots:
void onNewRequest(QHttpRequest *request, QHttpResponse *response); void onNewRequest(QHttpRequest *request, QHttpResponse *response);
private: private:
bool isStarted; bool isStarted;
QHttpServer pacServer; QHttpServer pacServer;
QString pacContent; QString pacContent;
QString proxyString; QString proxyString;
}; };
} } // namespace Qv2ray::components::pac
using namespace Qv2ray::components; using namespace Qv2ray::components;
using namespace Qv2ray::components::pac; using namespace Qv2ray::components::pac;

View File

@ -1,7 +1,8 @@
#include <QThread> #include <QThread>
#include "ui/w_MainWindow.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp" #include "components/plugins/toolbar/QvToolbar.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "ui/w_MainWindow.hpp"
namespace Qv2ray::components::plugins namespace Qv2ray::components::plugins
{ {
@ -31,118 +32,137 @@ namespace Qv2ray::components::plugins
QString GetAnswerToRequest(const QString &pchRequest) QString GetAnswerToRequest(const QString &pchRequest)
{ {
auto instance = MainWindow::mwInstance; auto instance = MainWindow::mwInstance;
//if (instance == nullptr || instance->vinstance == nullptr) { // if (instance == nullptr || instance->vinstance == nullptr) {
// LOG(PLUGIN, "MainWindow != nullptr Assertion failed!") // LOG(PLUGIN, "MainWindow != nullptr Assertion failed!")
// return "{}"; // return "{}";
//} //}
// //
//auto vinstance = instance->vinstance; // auto vinstance = instance->vinstance;
// //
auto req = pchRequest.trimmed(); auto req = pchRequest.trimmed();
QString reply = "{}"; QString reply = "{}";
if (req == "START") { if (req == "START") { emit instance->Connect(); }
emit instance->Connect(); else if (req == "STOP")
} else if (req == "STOP") { {
emit instance->DisConnect(); emit instance->DisConnect();
} else if (req == "RESTART") { }
else if (req == "RESTART")
{
emit instance->ReConnect(); emit instance->ReConnect();
} }
auto BarConfig = GlobalConfig.toolBarConfig; auto BarConfig = GlobalConfig.toolBarConfig;
for (auto i = 0; i < BarConfig.Pages.size(); i++) { for (auto i = 0; i < BarConfig.Pages.size(); i++)
for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++) { {
for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++)
{
#define CL BarConfig.Pages[i].Lines[j] #define CL BarConfig.Pages[i].Lines[j]
switch (CL.ContentType) { switch (CL.ContentType)
case 0: { {
case 0:
{
// Custom Text // Custom Text
// We do nothing... // We do nothing...
break; break;
} }
case 101: { case 101:
{
// Current Time // Current Time
CL.Message = QTime().currentTime().toString("hh:mm:ss"); CL.Message = QTime().currentTime().toString("hh:mm:ss");
break; break;
} }
case 102: { case 102:
{
// Current Date // Current Date
CL.Message = QDate().currentDate().toString("yyyy-MM-dd"); CL.Message = QDate().currentDate().toString("yyyy-MM-dd");
break; break;
} }
case 103: { case 103:
{
// Current Qv2ray Version // Current Qv2ray Version
CL.Message = QV2RAY_VERSION_STRING; CL.Message = QV2RAY_VERSION_STRING;
break; break;
} }
//case 104: { // case 104: {
// // Current Connection Name // // Current Connection Name
// CL.Message = instance->GetCurrentConnectedConfigName(); // CL.Message =
// break; // instance->GetCurrentConnectedConfigName();
//} // break;
// //}
//case 105: { //
// // Current Connection Status // case 105: {
// CL.Message = instance->vinstance->KernelStarted // // Current Connection Status
// ? QObject::tr("Connected") // CL.Message =
// : QObject::tr("Disconnected"); // instance->vinstance->KernelStarted
// break; // ? QObject::tr("Connected")
//} // : QObject::tr("Disconnected");
// // break;
//case 201: { //}
// // Total upload speed; //
// CL.Message = FormatBytes(vinstance->getAllSpeedUp()) + "/s"; // case 201: {
// break; // // Total upload speed;
//} // CL.Message =
// // FormatBytes(vinstance->getAllSpeedUp()) +
//case 202: { // "/s"; break;
// // Total download speed; //}
// CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s"; //
// break; // case 202: {
//} // // Total download speed;
// // CL.Message =
//case 203: { // FormatBytes(vinstance->getAllSpeedDown()) +
// // Upload speed for tag // "/s"; break;
// CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s"; //}
// break; //
//} // case 203: {
// // // Upload speed for tag
//case 204: { // CL.Message =
// // Download speed for tag // FormatBytes(vinstance->getTagSpeedUp(CL.Message))
// CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s"; // + "/s"; break;
// break; //}
//} //
// // case 204: {
//case 301: { // // Download speed for tag
// // Total Upload // CL.Message =
// CL.Message = FormatBytes(vinstance->getAllDataUp()); // FormatBytes(vinstance->getTagSpeedDown(CL.Message))
// break; // + "/s"; break;
//} //}
// //
//case 302: { // case 301: {
// // Total download // // Total Upload
// CL.Message = FormatBytes(vinstance->getAllDataDown()); // CL.Message =
// break; // FormatBytes(vinstance->getAllDataUp()); break;
//} //}
// //
//case 303: { // case 302: {
// // Upload for tag // // Total download
// CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message)); // CL.Message =
// break; // FormatBytes(vinstance->getAllDataDown());
//} // break;
// //}
//case 304: { //
// // Download for tag // case 303: {
// CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message)); // // Upload for tag
// break; // CL.Message =
//} // FormatBytes(vinstance->getTagDataUp(CL.Message));
// break;
//}
//
// case 304: {
// // Download for tag
// CL.Message =
// FormatBytes(vinstance->getTagDataDown(CL.Message));
// break;
//}
default: { default:
{
CL.Message = "Not Supported?"; CL.Message = "Not Supported?";
break; break;
} }
@ -153,5 +173,5 @@ namespace Qv2ray::components::plugins
reply = StructToJsonString(BarConfig); reply = StructToJsonString(BarConfig);
return reply; return reply;
} }
} } // namespace Toolbar
} } // namespace Qv2ray::components::plugins

View File

@ -9,25 +9,23 @@ namespace Qv2ray::components::plugins
namespace Toolbar namespace Toolbar
{ {
/// NO NOT CHANGE THE ORDER /// NO NOT CHANGE THE ORDER
static const QMap<int, QString> NetSpeedPluginMessages { static const QMap<int, QString> NetSpeedPluginMessages{ { 0, QObject::tr("Custom Text") },
{ 0, QObject::tr("Custom Text")}, // Current Status
// Current Status { 101, QObject::tr("Current Time") },
{ 101, QObject::tr("Current Time") }, { 102, QObject::tr("Current Date") },
{ 102, QObject::tr("Current Date") }, { 103, QObject::tr("Current Qv2ray Version") },
{ 103, QObject::tr("Current Qv2ray Version") }, { 104, QObject::tr("Current Connection Name") },
{ 104, QObject::tr("Current Connection Name") }, { 105, QObject::tr("Current Connection Status") },
{ 105, QObject::tr("Current Connection Status") }, // Speeds
// Speeds { 201, QObject::tr("Total Upload Speed") },
{ 201, QObject::tr("Total Upload Speed") }, { 202, QObject::tr("Total Download Speed") },
{ 202, QObject::tr("Total Download Speed") }, { 203, QObject::tr("Upload Speed for Specific Tag") },
{ 203, QObject::tr("Upload Speed for Specific Tag") }, { 204, QObject::tr("Download Speed for Specific Tag") },
{ 204, QObject::tr("Download Speed for Specific Tag") }, // Datas
// Datas { 301, QObject::tr("Total Uploaded Data") },
{ 301, QObject::tr("Total Uploaded Data") }, { 302, QObject::tr("Total Downloaded Data") },
{ 302, QObject::tr("Total Downloaded Data") }, { 303, QObject::tr("Uploaded Data for Specific Tag") },
{ 303, QObject::tr("Uploaded Data for Specific Tag") }, { 304, QObject::tr("Downloaded Data for Specific Tag") } };
{ 304, QObject::tr("Downloaded Data for Specific Tag") }
};
void StartProcessingPlugins(); void StartProcessingPlugins();
void StopProcessingPlugins(); void StopProcessingPlugins();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -35,7 +33,7 @@ namespace Qv2ray::components::plugins
{ {
void StartNamedPipeThread(); void StartNamedPipeThread();
void KillNamedPipeThread(); void KillNamedPipeThread();
} } // namespace _win
#endif #endif
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
namespace _linux namespace _linux
@ -45,13 +43,12 @@ namespace Qv2ray::components::plugins
void StartMessageQThread(); void StartMessageQThread();
void StopMessageQThread(); void StopMessageQThread();
} } // namespace _linux
#endif #endif
QString GetAnswerToRequest(const QString &pchRequest); QString GetAnswerToRequest(const QString &pchRequest);
} } // namespace Toolbar
} } // namespace Qv2ray::components::plugins
using namespace Qv2ray::components; using namespace Qv2ray::components;
using namespace Qv2ray::components::plugins::Toolbar; using namespace Qv2ray::components::plugins::Toolbar;

View File

@ -1,9 +1,10 @@
#include <QtCore> #include <QtCore>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include "components/plugins/toolbar/QvToolbar.hpp" #include "common/QvHelpers.hpp"
#include "common/QvHelpers.hpp" #include "components/plugins/toolbar/QvToolbar.hpp"
#include <QLocalSocket>
#include <QLocalServer> #include <QLocalServer>
#include <QLocalSocket>
namespace Qv2ray::components::plugins::Toolbar namespace Qv2ray::components::plugins::Toolbar
{ {
namespace _linux namespace _linux
@ -16,35 +17,43 @@ namespace Qv2ray::components::plugins::Toolbar
{ {
QLocalSocket *socket = server->nextPendingConnection(); QLocalSocket *socket = server->nextPendingConnection();
if (!socket->waitForConnected() || !socket->waitForReadyRead()) if (!socket->waitForConnected() || !socket->waitForReadyRead()) return;
return;
try { try
while (!isExiting && socket->isOpen() && socket->isValid() && socket->waitForReadyRead()) { {
while (!isExiting && socket->isOpen() && socket->isValid() && socket->waitForReadyRead())
{
// CANNOT PROPERLY READ... // CANNOT PROPERLY READ...
// Temp-ly fixed (but why and how?) // Temp-ly fixed (but why and how?)
auto in = QString(socket->readAll()); auto in = QString(socket->readAll());
if (!isExiting && !in.isEmpty()) { if (!isExiting && !in.isEmpty())
{
auto out = GetAnswerToRequest(in); auto out = GetAnswerToRequest(in);
// //
socket->write(out.toUtf8()); socket->write(out.toUtf8());
socket->flush(); socket->flush();
} else { }
else
{
QThread::msleep(200); QThread::msleep(200);
} }
} }
} catch (...) { }
catch (...)
{
LOG(MODULE_PLUGIN, "Closing a broken socket.") LOG(MODULE_PLUGIN, "Closing a broken socket.")
} }
} }
void DataMessageQThread() void DataMessageQThread()
{ {
server = new QLocalServer(); server = new QLocalServer();
// BUG Sometimes failed to listen due to improper close of last session. // BUG Sometimes failed to listen due to improper close of last
// session.
bool listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX); bool listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
while (!isExiting && !listening) { while (!isExiting && !listening)
{
QThread::msleep(500); QThread::msleep(500);
listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX); listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
} }
@ -53,7 +62,8 @@ namespace Qv2ray::components::plugins::Toolbar
server->setSocketOptions(QLocalServer::WorldAccessOption); server->setSocketOptions(QLocalServer::WorldAccessOption);
QObject::connect(server, &QLocalServer::newConnection, &qobject_proxy); QObject::connect(server, &QLocalServer::newConnection, &qobject_proxy);
while (!isExiting) { while (!isExiting)
{
bool result = server->waitForNewConnection(5000, &timeOut); bool result = server->waitForNewConnection(5000, &timeOut);
DEBUG(MODULE_PLUGIN, "Plugin thread listening failed: " + server->errorString()) DEBUG(MODULE_PLUGIN, "Plugin thread listening failed: " + server->errorString())
DEBUG(MODULE_PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) DEBUG(MODULE_PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
@ -72,13 +82,14 @@ namespace Qv2ray::components::plugins::Toolbar
{ {
isExiting = true; isExiting = true;
if (linuxWorkerThread->isRunning()) { if (linuxWorkerThread->isRunning())
{
LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.") LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.")
linuxWorkerThread->wait(); linuxWorkerThread->wait();
} }
delete _linux::linuxWorkerThread; delete _linux::linuxWorkerThread;
} }
} } // namespace _linux
} } // namespace Qv2ray::components::plugins::Toolbar
#endif #endif

View File

@ -1,8 +1,9 @@
#include <QtCore> #include <QtCore>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "components/plugins/toolbar/QvToolbar.hpp" #include "common/QvHelpers.hpp"
#include "common/QvHelpers.hpp" #include "components/plugins/toolbar/QvToolbar.hpp"
#include <windows.h>
#include <windows.h>
namespace Qv2ray::components::plugins::Toolbar namespace Qv2ray::components::plugins::Toolbar
{ {
namespace _win namespace _win
@ -25,40 +26,53 @@ namespace Qv2ray::components::plugins::Toolbar
{ {
auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr); auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr);
if (hThread == nullptr) { if (hThread == nullptr)
{
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError())) LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
return; return;
} else CloseHandle(hThread); }
else
CloseHandle(hThread);
} }
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam) DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam)
{ {
Q_UNUSED(lpvParam) Q_UNUSED(lpvParam)
BOOL fConnected = FALSE; BOOL fConnected = FALSE;
DWORD dwThreadId = 0; DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE; HANDLE hPipe = INVALID_HANDLE_VALUE;
auto lpszPipename = QString(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN).toStdWString(); auto lpszPipename = QString(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN).toStdWString();
while (!isExiting) { while (!isExiting)
//printf("Pipe Server: Main thread awaiting client connection on %s\n", lpszPipename.c_str()); {
hPipe = CreateNamedPipe(lpszPipename.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, nullptr); // printf("Pipe Server: Main thread awaiting client connection
// on %s\n", lpszPipename.c_str());
hPipe = CreateNamedPipe(lpszPipename.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, nullptr);
if (hPipe == INVALID_HANDLE_VALUE) { if (hPipe == INVALID_HANDLE_VALUE)
{
LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError())) LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError()))
return static_cast<DWORD>(-1); return static_cast<DWORD>(-1);
} }
fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED); fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected) { if (fConnected)
{
LOG(MODULE_PLUGIN, "Client connected, creating a processing thread") LOG(MODULE_PLUGIN, "Client connected, creating a processing thread")
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId); ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
if (ThreadHandle == nullptr) { if (ThreadHandle == nullptr)
{
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError())) LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
return static_cast<DWORD>(-1); return static_cast<DWORD>(-1);
} else CloseHandle(ThreadHandle); }
} else CloseHandle(hPipe); else
CloseHandle(ThreadHandle);
}
else
CloseHandle(hPipe);
} }
return 0; return 0;
@ -71,13 +85,16 @@ namespace Qv2ray::components::plugins::Toolbar
HANDLE hPipe = static_cast<HANDLE>(lpvParam); HANDLE hPipe = static_cast<HANDLE>(lpvParam);
TCHAR pchRequest[BUFSIZE] = { 0 }; TCHAR pchRequest[BUFSIZE] = { 0 };
while (!isExiting) { while (!isExiting)
{
fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr); fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr);
if (!fSuccess || cbBytesRead == 0) { if (!fSuccess || cbBytesRead == 0)
if (GetLastError() == ERROR_BROKEN_PIPE) { {
LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) if (GetLastError() == ERROR_BROKEN_PIPE)
} else { { LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) }
else
{
LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError())) LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
} }
@ -87,17 +104,20 @@ namespace Qv2ray::components::plugins::Toolbar
auto req = QString::fromStdWString(pchRequest); auto req = QString::fromStdWString(pchRequest);
QString replyQString = "{}"; QString replyQString = "{}";
if (!isExiting) { if (!isExiting)
{
replyQString = GetAnswerToRequest(req); replyQString = GetAnswerToRequest(req);
// //
// REPLY as std::string // REPLY as std::string
std::string pchReply = replyQString.toUtf8().constData(); std::string pchReply = replyQString.toUtf8().constData();
cbReplyBytes = static_cast<DWORD>(pchReply.length() + 1) * sizeof(CHAR); cbReplyBytes = static_cast<DWORD>(pchReply.length() + 1) * sizeof(CHAR);
//cbReplyBytes = static_cast<DWORD>(replyQString.length() + 1) * sizeof(TCHAR); // cbReplyBytes = static_cast<DWORD>(replyQString.length() +
// 1) * sizeof(TCHAR);
// //
fSuccess = WriteFile(hPipe, pchReply.c_str(), cbReplyBytes, &cbWritten, nullptr); fSuccess = WriteFile(hPipe, pchReply.c_str(), cbReplyBytes, &cbWritten, nullptr);
if (!fSuccess || cbReplyBytes != cbWritten) { if (!fSuccess || cbReplyBytes != cbWritten)
{
LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError())) LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
break; break;
} }
@ -109,6 +129,6 @@ namespace Qv2ray::components::plugins::Toolbar
CloseHandle(hPipe); CloseHandle(hPipe);
return 1; return 1;
} }
} } // namespace _win
} } // namespace Qv2ray::components::plugins::Toolbar
#endif #endif

View File

@ -1,8 +1,10 @@
#include "QvProxyConfigurator.hpp" #include "QvProxyConfigurator.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "wininet.h" #include "wininet.h"
#include <windows.h>
#include <windows.h>
#endif #endif
namespace Qv2ray::components::proxy namespace Qv2ray::components::proxy
@ -23,11 +25,10 @@ namespace Qv2ray::components::proxy
QStringList result; QStringList result;
// Start from 1 since first line is unneeded. // Start from 1 since first line is unneeded.
for (auto i = 1; i < lines.count(); i++) { for (auto i = 1; i < lines.count(); i++)
{
// * means disabled. // * means disabled.
if (!lines[i].contains("*")) { if (!lines[i].contains("*")) { result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]); }
result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]);
}
} }
LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";")) LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
@ -35,8 +36,9 @@ namespace Qv2ray::components::proxy
} }
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#define NO_CONST(expr) const_cast<wchar_t *>(expr) #define NO_CONST(expr) const_cast<wchar_t *>(expr)
//static auto DEFAULT_CONNECTION_NAME = NO_CONST(L"DefaultConnectionSettings"); // static auto DEFAULT_CONNECTION_NAME =
// NO_CONST(L"DefaultConnectionSettings");
/// ///
/// INTERNAL FUNCTION /// INTERNAL FUNCTION
bool __QueryProxyOptions() bool __QueryProxyOptions()
@ -52,81 +54,64 @@ namespace Qv2ray::components::proxy
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER; Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
// //
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST); List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
List.pszConnection = nullptr;// NO_CONST(DEFAULT_CONNECTION_NAME); List.pszConnection = nullptr; // NO_CONST(DEFAULT_CONNECTION_NAME);
List.dwOptionCount = 5; List.dwOptionCount = 5;
List.dwOptionError = 0; List.dwOptionError = 0;
List.pOptions = Option; List.pOptions = Option;
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) { if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize))
LOG(MODULE_PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError())) { LOG(MODULE_PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError())) }
}
LOG(MODULE_PROXY, "System default proxy info:") LOG(MODULE_PROXY, "System default proxy info:")
if (Option[0].Value.pszValue != nullptr) { if (Option[0].Value.pszValue != nullptr) { LOG(MODULE_PROXY, QString::fromWCharArray(Option[0].Value.pszValue)) }
LOG(MODULE_PROXY, QString::fromWCharArray(Option[0].Value.pszValue))
}
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) { if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL)
LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_PROXY_URL") { LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_PROXY_URL") }
}
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) { if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) { LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_DETECT") }
LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_DETECT")
}
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) { if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) { LOG(MODULE_PROXY, "PROXY_TYPE_DIRECT") }
LOG(MODULE_PROXY, "PROXY_TYPE_DIRECT")
}
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) { if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) { LOG(MODULE_PROXY, "PROXY_TYPE_PROXY") }
LOG(MODULE_PROXY, "PROXY_TYPE_PROXY")
}
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) { if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize))
LOG(MODULE_PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError())) { LOG(MODULE_PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError())) }
}
if (Option[4].Value.pszValue != nullptr) { if (Option[4].Value.pszValue != nullptr) { LOG(MODULE_PROXY, QString::fromStdWString(Option[4].Value.pszValue)) }
LOG(MODULE_PROXY, QString::fromStdWString(Option[4].Value.pszValue))
}
INTERNET_VERSION_INFO Version; INTERNET_VERSION_INFO Version;
nSize = sizeof(INTERNET_VERSION_INFO); nSize = sizeof(INTERNET_VERSION_INFO);
InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize); InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize);
if (Option[0].Value.pszValue != nullptr) { if (Option[0].Value.pszValue != nullptr) { GlobalFree(Option[0].Value.pszValue); }
GlobalFree(Option[0].Value.pszValue);
}
if (Option[3].Value.pszValue != nullptr) { if (Option[3].Value.pszValue != nullptr) { GlobalFree(Option[3].Value.pszValue); }
GlobalFree(Option[3].Value.pszValue);
}
if (Option[4].Value.pszValue != nullptr) { if (Option[4].Value.pszValue != nullptr) { GlobalFree(Option[4].Value.pszValue); }
GlobalFree(Option[4].Value.pszValue);
}
return false; return false;
} }
bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC) bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC)
{ {
INTERNET_PER_CONN_OPTION_LIST list; INTERNET_PER_CONN_OPTION_LIST list;
BOOL bReturn; BOOL bReturn;
DWORD dwBufSize = sizeof(list); DWORD dwBufSize = sizeof(list);
// Fill the list structure. // Fill the list structure.
list.dwSize = sizeof(list); list.dwSize = sizeof(list);
// NULL == LAN, otherwise connectoid name. // NULL == LAN, otherwise connectoid name.
list.pszConnection = nullptr; list.pszConnection = nullptr;
if (isPAC) { if (isPAC)
{
LOG(MODULE_PROXY, "Setting system proxy for PAC") LOG(MODULE_PROXY, "Setting system proxy for PAC")
// //
list.dwOptionCount = 2; list.dwOptionCount = 2;
list.pOptions = new INTERNET_PER_CONN_OPTION[2]; list.pOptions = new INTERNET_PER_CONN_OPTION[2];
// Ensure that the memory was allocated. // Ensure that the memory was allocated.
if (nullptr == list.pOptions) { if (nullptr == list.pOptions)
{
// Return FALSE if the memory wasn't allocated. // Return FALSE if the memory wasn't allocated.
return FALSE; return FALSE;
} }
@ -137,15 +122,15 @@ namespace Qv2ray::components::proxy
// Set proxy name. // Set proxy name.
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL; list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
list.pOptions[1].Value.pszValue = proxy_full_addr; list.pOptions[1].Value.pszValue = proxy_full_addr;
} else { }
else
{
LOG(MODULE_PROXY, "Setting system proxy for Global Proxy") LOG(MODULE_PROXY, "Setting system proxy for Global Proxy")
// //
list.dwOptionCount = 3; list.dwOptionCount = 3;
list.pOptions = new INTERNET_PER_CONN_OPTION[3]; list.pOptions = new INTERNET_PER_CONN_OPTION[3];
if (nullptr == list.pOptions) { if (nullptr == list.pOptions) { return false; }
return false;
}
// Set flags. // Set flags.
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS; list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
@ -161,7 +146,7 @@ namespace Qv2ray::components::proxy
// Set the options on the connection. // Set the options on the connection.
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize); bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
delete [] list.pOptions; delete[] list.pOptions;
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0); InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0); InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
return bReturn; return bReturn;
@ -173,29 +158,26 @@ namespace Qv2ray::components::proxy
bool hasHTTP = (httpPort != 0); bool hasHTTP = (httpPort != 0);
bool hasSOCKS = (socksPort != 0); bool hasSOCKS = (socksPort != 0);
if (!(hasHTTP || hasSOCKS || usePAC)) { if (!(hasHTTP || hasSOCKS || usePAC))
{
LOG(MODULE_PROXY, "Nothing?") LOG(MODULE_PROXY, "Nothing?")
return; return;
} }
if (usePAC) { if (usePAC) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file") }
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file") else
} else { {
if (hasHTTP) { if (hasHTTP) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP") }
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
}
if (hasSOCKS) { if (hasSOCKS) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS") }
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
}
} }
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QString __a; QString __a;
if (usePAC) { if (usePAC) { __a = address; }
__a = address; else
} else { {
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort); __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
} }
@ -205,9 +187,7 @@ namespace Qv2ray::components::proxy
// //
__QueryProxyOptions(); __QueryProxyOptions();
if (!__SetProxyOptions(proxyStrW, usePAC)) { if (!__SetProxyOptions(proxyStrW, usePAC)) { LOG(MODULE_PROXY, "Failed to set proxy.") }
LOG(MODULE_PROXY, "Failed to set proxy.")
}
__QueryProxyOptions(); __QueryProxyOptions();
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
@ -215,18 +195,21 @@ namespace Qv2ray::components::proxy
auto proxyMode = usePAC ? "auto" : "manual"; auto proxyMode = usePAC ? "auto" : "manual";
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode); actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
if (usePAC) { if (usePAC) { actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); }
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); else
} else { {
if (hasHTTP) { if (hasHTTP)
{
actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address); actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address);
actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort); actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort);
// //
actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address); actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address);
actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);; actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);
;
} }
if (hasSOCKS) { if (hasSOCKS)
{
actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address); actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address);
actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort); actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort);
} }
@ -234,12 +217,13 @@ namespace Qv2ray::components::proxy
// note: do not use std::all_of / any_of / none_of, // note: do not use std::all_of / any_of / none_of,
// because those are short-circuit and cannot guarantee atomicity. // because those are short-circuit and cannot guarantee atomicity.
auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString & action) { auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString &action) {
DEBUG(MODULE_PROXY, action) DEBUG(MODULE_PROXY, action)
return QProcess::execute(action) == QProcess::NormalExit; return QProcess::execute(action) == QProcess::NormalExit;
}) == actions.size(); }) == actions.size();
if (!result) { if (!result)
{
LOG(MODULE_PROXY, "Something wrong happens when setting system proxy -> Gnome ONLY.") LOG(MODULE_PROXY, "Something wrong happens when setting system proxy -> Gnome ONLY.")
LOG(MODULE_PROXY, "If you are using KDE Plasma and receiving this message, just simply ignore this.") LOG(MODULE_PROXY, "If you are using KDE Plasma and receiving this message, just simply ignore this.")
} }
@ -247,21 +231,27 @@ namespace Qv2ray::components::proxy
Q_UNUSED(result); Q_UNUSED(result);
#else #else
for (auto service : macOSgetNetworkServices()) { for (auto service : macOSgetNetworkServices())
{
LOG(MODULE_PROXY, "Setting proxy for interface: " + service) LOG(MODULE_PROXY, "Setting proxy for interface: " + service)
if (usePAC) { if (usePAC)
{
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on"); QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on");
QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address); QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address);
} else { }
if (hasHTTP) { else
{
if (hasHTTP)
{
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on"); QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on"); QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on");
QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort)); QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort));
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort)); QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
} }
if (hasSOCKS) { if (hasSOCKS)
{
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on"); QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort)); QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
} }
@ -287,7 +277,8 @@ namespace Qv2ray::components::proxy
list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount]; list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount];
// Make sure the memory was allocated. // Make sure the memory was allocated.
if (nullptr == list.pOptions) { if (nullptr == list.pOptions)
{
// Return FALSE if the memory wasn't allocated. // Return FALSE if the memory wasn't allocated.
LOG(MODULE_PROXY, "Failed to allocat memory in DisableConnectionProxy()") LOG(MODULE_PROXY, "Failed to allocat memory in DisableConnectionProxy()")
} }
@ -298,14 +289,15 @@ namespace Qv2ray::components::proxy
// //
// Set the options on the connection. // Set the options on the connection.
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize); bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
delete [] list.pOptions; delete[] list.pOptions;
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0); InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0); InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'"); QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'");
#else #else
for (auto service : macOSgetNetworkServices()) { for (auto service : macOSgetNetworkServices())
{
LOG(MODULE_PROXY, "Clearing proxy for interface: " + service) LOG(MODULE_PROXY, "Clearing proxy for interface: " + service)
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off"); QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off");
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off"); QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off");
@ -315,4 +307,4 @@ namespace Qv2ray::components::proxy
#endif #endif
} }
} } // namespace Qv2ray::components::proxy

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include <QString>
#include <QObject> #include <QObject>
#include <QString>
// //
namespace Qv2ray::components::proxy namespace Qv2ray::components::proxy
{ {
void ClearSystemProxy(); void ClearSystemProxy();
void SetSystemProxy(const QString &address, int http_port, int socks_port, bool usePAC); void SetSystemProxy(const QString &address, int http_port, int socks_port, bool usePAC);
} } // namespace Qv2ray::components::proxy
using namespace Qv2ray::components; using namespace Qv2ray::components;
using namespace Qv2ray::components::proxy; using namespace Qv2ray::components::proxy;

View File

@ -14,39 +14,40 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
* *
* In addition, as a special exception, the copyright holders give permission to * In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with * link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library), * modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public * and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you * License in all respects for all of the code used other than "OpenSSL". If
* modify file(s), you may extend this exception to your version of the file(s), * you modify file(s), you may extend this exception to your version of the
* but you are not obligated to do so. If you do not wish to do so, delete this * file(s), but you are not obligated to do so. If you do not wish to do so,
* exception statement from your version. * delete this exception statement from your version.
*/ */
#include "speedplotview.hpp" #include "speedplotview.hpp"
#include <QCoreApplication>
#include <QLocale> #include <QLocale>
#include <QPainter> #include <QPainter>
#include <QPen> #include <QPen>
#include <list> #include <list>
#include <QCoreApplication>
#define VIEWABLE 120 #define VIEWABLE 120
// use binary prefix standards from IEC 60027-2 // use binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte // see http://en.wikipedia.org/wiki/Kilobyte
enum class SizeUnit { enum class SizeUnit
Byte, // 1024^0, {
KibiByte, // 1024^1, Byte, // 1024^0,
MebiByte, // 1024^2, KibiByte, // 1024^1,
GibiByte, // 1024^3, MebiByte, // 1024^2,
TebiByte, // 1024^4, GibiByte, // 1024^3,
PebiByte, // 1024^5, TebiByte, // 1024^4,
ExbiByte // 1024^6, PebiByte, // 1024^5,
ExbiByte // 1024^6,
// int64 is used for sizes and thus the next units can not be handled // int64 is used for sizes and thus the next units can not be handled
// ZebiByte, // 1024^7, // ZebiByte, // 1024^7,
// YobiByte, // 1024^8 // YobiByte, // 1024^8
@ -54,27 +55,25 @@ enum class SizeUnit {
namespace namespace
{ {
const struct { const struct
{
const char *source; const char *source;
const char *comment; const char *comment;
} units[] = { } units[] = { QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
QT_TRANSLATE_NOOP3("misc", "B", "bytes"), QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"), QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"), QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"), QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"), QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"), QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)") };
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)") } // namespace
};
}
QString unitString(const SizeUnit unit, const bool isSpeed) QString unitString(const SizeUnit unit, const bool isSpeed)
{ {
const auto &unitString = units[static_cast<int>(unit)]; const auto &unitString = units[static_cast<int>(unit)];
QString ret = QCoreApplication::translate("misc", unitString.source, unitString.comment); QString ret = QCoreApplication::translate("misc", unitString.source, unitString.comment);
if (isSpeed) if (isSpeed) ret += QCoreApplication::translate("misc", "/s", "per second");
ret += QCoreApplication::translate("misc", "/s", "per second");
return ret; return ret;
} }
@ -82,36 +81,34 @@ QString unitString(const SizeUnit unit, const bool isSpeed)
int friendlyUnitPrecision(const SizeUnit unit) int friendlyUnitPrecision(const SizeUnit unit)
{ {
// friendlyUnit's number of digits after the decimal point // friendlyUnit's number of digits after the decimal point
switch (unit) { switch (unit)
case SizeUnit::Byte: {
return 0; case SizeUnit::Byte: return 0;
case SizeUnit::KibiByte: case SizeUnit::KibiByte:
case SizeUnit::MebiByte: case SizeUnit::MebiByte: return 1;
return 1;
case SizeUnit::GibiByte: case SizeUnit::GibiByte: return 2;
return 2;
default: default: return 3;
return 3;
} }
} }
qlonglong sizeInBytes(qreal size, const SizeUnit unit) qlonglong sizeInBytes(qreal size, const SizeUnit unit)
{ {
for (int i = 0; i < static_cast<int>(unit); ++i) for (int i = 0; i < static_cast<int>(unit); ++i) size *= 1024;
size *= 1024;
return size; return size;
} }
namespace namespace
{ {
// table of supposed nice steps for grid marks to get nice looking quarters of scale // table of supposed nice steps for grid marks to get nice looking quarters
const double roundingTable[] = {1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8}; // of scale
const double roundingTable[] = { 1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8 };
struct SplittedValue { struct SplittedValue
{
double arg; double arg;
SizeUnit unit; SizeUnit unit;
qint64 sizeInBytes() const qint64 sizeInBytes() const
@ -122,56 +119,53 @@ namespace
SplittedValue getRoundedYScale(double value) SplittedValue getRoundedYScale(double value)
{ {
if (value == 0.0) return {0, SizeUnit::Byte}; if (value == 0.0) return { 0, SizeUnit::Byte };
if (value <= 12.0) return {12, SizeUnit::Byte}; if (value <= 12.0) return { 12, SizeUnit::Byte };
SizeUnit calculatedUnit = SizeUnit::Byte; SizeUnit calculatedUnit = SizeUnit::Byte;
while (value > 1024) { while (value > 1024)
{
value /= 1024; value /= 1024;
calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1); calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1);
} }
if (value > 100.0) { if (value > 100.0)
{
int roundedValue = static_cast<int>(value / 40) * 40; int roundedValue = static_cast<int>(value / 40) * 40;
while (roundedValue < value) while (roundedValue < value) roundedValue += 40;
roundedValue += 40;
return {static_cast<double>(roundedValue), calculatedUnit}; return { static_cast<double>(roundedValue), calculatedUnit };
} }
if (value > 10.0) { if (value > 10.0)
{
int roundedValue = static_cast<int>(value / 4) * 4; int roundedValue = static_cast<int>(value / 4) * 4;
while (roundedValue < value) while (roundedValue < value) roundedValue += 4;
roundedValue += 4;
return {static_cast<double>(roundedValue), calculatedUnit}; return { static_cast<double>(roundedValue), calculatedUnit };
} }
for (const auto &roundedValue : roundingTable) { for (const auto &roundedValue : roundingTable)
if (value <= roundedValue) {
return {roundedValue, calculatedUnit}; if (value <= roundedValue) return { roundedValue, calculatedUnit };
} }
return {10.0, calculatedUnit}; return { 10.0, calculatedUnit };
} }
QString formatLabel(const double argValue, const SizeUnit unit) QString formatLabel(const double argValue, const SizeUnit unit)
{ {
// check is there need for digits after decimal separator // check is there need for digits after decimal separator
const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0; const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0;
return QLocale::system().toString(argValue, 'f', precision) return QLocale::system().toString(argValue, 'f', precision) + QString::fromUtf8(" ") + unitString(unit, true);
+ QString::fromUtf8(" ")
+ unitString(unit, true);
} }
} } // namespace
SpeedPlotView::SpeedPlotView(QWidget *parent) SpeedPlotView::SpeedPlotView(QWidget *parent) : QGraphicsView(parent), m_currentData(&m_datahalfMin)
: QGraphicsView(parent)
, m_currentData(&m_datahalfMin)
{ {
QPen greenPen; QPen greenPen;
greenPen.setWidthF(1.5); greenPen.setWidthF(1.5);
@ -193,9 +187,7 @@ void SpeedPlotView::pushPoint(const SpeedPlotView::PointData &point)
{ {
m_datahalfMin.push_back(point); m_datahalfMin.push_back(point);
while (m_datahalfMin.length() > VIEWABLE) { while (m_datahalfMin.length() > VIEWABLE) { m_datahalfMin.removeFirst(); }
m_datahalfMin.removeFirst();
}
} }
void SpeedPlotView::replot() void SpeedPlotView::replot()
@ -213,11 +205,12 @@ quint64 SpeedPlotView::maxYValue()
auto &queue = getCurrentData(); auto &queue = getCurrentData();
quint64 maxYValue = 0; quint64 maxYValue = 0;
for (int id = UP; id < NB_GRAPHS; ++id) { for (int id = UP; id < NB_GRAPHS; ++id)
{
// 30 is half min // 30 is half min
for (int i = queue.size() - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j) { for (int i = queue.size() - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
if (queue[i].y[id] > maxYValue) {
maxYValue = queue[i].y[id]; if (queue[i].y[id] > maxYValue) maxYValue = queue[i].y[id];
} }
} }
@ -244,12 +237,12 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
int yAxisWidth = 0; int yAxisWidth = 0;
for (const QString &label : speedLabels) for (const QString &label : speedLabels)
if (fontMetrics.horizontalAdvance(label) > yAxisWidth) if (fontMetrics.horizontalAdvance(label) > yAxisWidth) yAxisWidth = fontMetrics.horizontalAdvance(label);
yAxisWidth = fontMetrics.horizontalAdvance(label);
int i = 0; int i = 0;
for (const QString &label : speedLabels) { for (const QString &label : speedLabels)
{
QRectF labelRect(rect.topLeft() + QPointF(-yAxisWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()), QRectF labelRect(rect.topLeft() + QPointF(-yAxisWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
QSizeF(2 * yAxisWidth, fontMetrics.height())); QSizeF(2 * yAxisWidth, fontMetrics.height()));
painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop); painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop);
@ -269,7 +262,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
painter.drawLine(fullRect.left(), rect.bottom(), rect.right(), rect.bottom()); painter.drawLine(fullRect.left(), rect.bottom(), rect.right(), rect.bottom());
const int TIME_AXIS_DIVISIONS = 6; const int TIME_AXIS_DIVISIONS = 6;
for (int i = 0; i < TIME_AXIS_DIVISIONS; ++i) { for (int i = 0; i < TIME_AXIS_DIVISIONS; ++i)
{
const int x = rect.left() + (i * rect.width()) / TIME_AXIS_DIVISIONS; const int x = rect.left() + (i * rect.width()) / TIME_AXIS_DIVISIONS;
painter.drawLine(x, fullRect.top(), x, fullRect.bottom()); painter.drawLine(x, fullRect.top(), x, fullRect.bottom());
} }
@ -282,10 +276,12 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
const double xTickSize = static_cast<double>(rect.width()) / VIEWABLE; const double xTickSize = static_cast<double>(rect.width()) / VIEWABLE;
auto &queue = getCurrentData(); auto &queue = getCurrentData();
for (int id = UP; id < NB_GRAPHS; ++id) { for (int id = UP; id < NB_GRAPHS; ++id)
{
QVector<QPoint> points; QVector<QPoint> points;
for (int i = static_cast<int>(queue.size()) - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j) { for (int i = static_cast<int>(queue.size()) - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
{
int newX = rect.right() - j * xTickSize; int newX = rect.right() - j * xTickSize;
int newY = rect.bottom() - queue[i].y[id] * yMultiplier; int newY = rect.bottom() - queue[i].y[id] * yMultiplier;
points.push_back(QPoint(newX, newY)); points.push_back(QPoint(newX, newY));
@ -300,27 +296,28 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
double legendHeight = 0; double legendHeight = 0;
int legendWidth = 0; int legendWidth = 0;
for (const auto &property : m_properties) { for (const auto &property : m_properties)
if (fontMetrics.horizontalAdvance(property.name) > legendWidth) {
legendWidth = fontMetrics.horizontalAdvance(property.name); if (fontMetrics.horizontalAdvance(property.name) > legendWidth) legendWidth = fontMetrics.horizontalAdvance(property.name);
legendHeight += 1.5 * fontMetrics.height(); legendHeight += 1.5 * fontMetrics.height();
} }
QRectF legendBackgroundRect(QPoint(legendTopLeft.x() - 4, legendTopLeft.y() - 4), QSizeF(legendWidth + 8, legendHeight + 8)); QRectF legendBackgroundRect(QPoint(legendTopLeft.x() - 4, legendTopLeft.y() - 4), QSizeF(legendWidth + 8, legendHeight + 8));
QColor legendBackgroundColor = QWidget::palette().color(QWidget::backgroundRole()); QColor legendBackgroundColor = QWidget::palette().color(QWidget::backgroundRole());
legendBackgroundColor.setAlpha(128); // 50% transparent legendBackgroundColor.setAlpha(128); // 50% transparent
painter.fillRect(legendBackgroundRect, legendBackgroundColor); painter.fillRect(legendBackgroundRect, legendBackgroundColor);
i = 0; i = 0;
for (const auto &property : m_properties) { for (const auto &property : m_properties)
{
int nameSize = fontMetrics.horizontalAdvance(property.name); int nameSize = fontMetrics.horizontalAdvance(property.name);
double indent = 1.5 * (i++) * fontMetrics.height(); double indent = 1.5 * (i++) * fontMetrics.height();
painter.setPen(property.pen); painter.setPen(property.pen);
painter.drawLine(legendTopLeft + QPointF(0, indent + fontMetrics.height()), painter.drawLine(legendTopLeft + QPointF(0, indent + fontMetrics.height()),
legendTopLeft + QPointF(nameSize, indent + fontMetrics.height())); legendTopLeft + QPointF(nameSize, indent + fontMetrics.height()));
painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())), painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())), property.name,
property.name, QTextOption(Qt::AlignVCenter)); QTextOption(Qt::AlignVCenter));
} }
} }
@ -328,8 +325,6 @@ SpeedPlotView::GraphProperties::GraphProperties()
{ {
} }
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen) SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen) : name(name), pen(pen)
: name(name)
, pen(pen)
{ {
} }

View File

@ -36,43 +36,46 @@ class QPen;
class SpeedPlotView : public QGraphicsView class SpeedPlotView : public QGraphicsView
{ {
Q_OBJECT Q_OBJECT
public: public:
enum GraphID { enum GraphID
UP = 0, {
DOWN, UP = 0,
NB_GRAPHS DOWN,
}; NB_GRAPHS
};
struct PointData { struct PointData
qint64 x; {
quint64 y[NB_GRAPHS]; qint64 x;
}; quint64 y[NB_GRAPHS];
};
explicit SpeedPlotView(QWidget *parent = nullptr); explicit SpeedPlotView(QWidget *parent = nullptr);
void pushPoint(const PointData &point); void pushPoint(const PointData &point);
void Clear(); void Clear();
void replot(); void replot();
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
private: private:
struct GraphProperties { struct GraphProperties
GraphProperties(); {
GraphProperties(const QString &name, const QPen &pen); GraphProperties();
GraphProperties(const QString &name, const QPen &pen);
QString name; QString name;
QPen pen; QPen pen;
}; };
quint64 maxYValue(); quint64 maxYValue();
QList<PointData> &getCurrentData(); QList<PointData> &getCurrentData();
QList<PointData> m_datahalfMin; QList<PointData> m_datahalfMin;
QList<PointData> *m_currentData; QList<PointData> *m_currentData;
QMap<GraphID, GraphProperties> m_properties; QMap<GraphID, GraphProperties> m_properties;
}; };
#endif // SPEEDPLOTVIEW_H #endif // SPEEDPLOTVIEW_H

View File

@ -14,20 +14,23 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
* *
* In addition, as a special exception, the copyright holders give permission to * In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with * link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library), * modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public * and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you * License in all respects for all of the code used other than "OpenSSL". If
* modify file(s), you may extend this exception to your version of the file(s), * you modify file(s), you may extend this exception to your version of the
* but you are not obligated to do so. If you do not wish to do so, delete this * file(s), but you are not obligated to do so. If you do not wish to do so,
* exception statement from your version. * delete this exception statement from your version.
*/ */
#include "speedwidget.hpp" #include "speedwidget.hpp"
#include "speedplotview.hpp"
#include <QDateTime> #include <QDateTime>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@ -35,10 +38,7 @@
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "speedplotview.hpp" SpeedWidget::SpeedWidget(QWidget *parent) : QWidget(parent)
SpeedWidget::SpeedWidget(QWidget *parent)
: QWidget(parent)
{ {
m_layout = new QVBoxLayout(this); m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setContentsMargins(0, 0, 0, 0);
@ -52,7 +52,9 @@ SpeedWidget::SpeedWidget(QWidget *parent)
m_plot->show(); m_plot->show();
} }
SpeedWidget::~SpeedWidget() {} SpeedWidget::~SpeedWidget()
{
}
void SpeedWidget::AddPointData(long up, long down) void SpeedWidget::AddPointData(long up, long down)
{ {

View File

@ -41,17 +41,18 @@ class SpeedPlotView;
class SpeedWidget : public QWidget class SpeedWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SpeedWidget(QWidget *parent); explicit SpeedWidget(QWidget *parent);
~SpeedWidget(); ~SpeedWidget();
void AddPointData(long up, long down); void AddPointData(long up, long down);
void Clear(); void Clear();
private:
QVBoxLayout *m_layout; private:
QHBoxLayout *m_hlayout; QVBoxLayout *m_layout;
SpeedPlotView *m_plot; QHBoxLayout *m_hlayout;
SpeedPlotView *m_plot;
}; };
#endif // SPEEDWIDGET_H #endif // SPEEDWIDGET_H

View File

@ -1,38 +1,43 @@
#pragma once #pragma once
#include <QString>
#include <QHash>
#include <QHashFunctions>
#include "base/models/QvConfigIdentifier.hpp" #include "base/models/QvConfigIdentifier.hpp"
#include <QHash>
#include <QHashFunctions>
#include <QString>
namespace Qv2ray::core namespace Qv2ray::core
{ {
template <typename T> template<typename T>
class IDType class IDType
{ {
public: public:
explicit IDType(const QString &id): m_id(id) {} explicit IDType() : m_id("null")
friend bool operator==(const IDType<T> &lhs, const IDType<T> &rhs) {
{ }
return lhs.m_id == rhs.m_id; explicit IDType(const QString &id) : m_id(id)
} {
friend bool operator!=(const IDType<T> &lhs, const IDType<T> &rhs) }
{ friend bool operator==(const IDType<T> &lhs, const IDType<T> &rhs)
return lhs.toString() != rhs.toString(); {
} return lhs.m_id == rhs.m_id;
const QString &toString() const }
{ friend bool operator!=(const IDType<T> &lhs, const IDType<T> &rhs)
return m_id; {
} return lhs.toString() != rhs.toString();
uint qHash(uint seed) const }
{ const QString &toString() const
return ::qHash(m_id, seed); {
} return m_id;
private: }
QString m_id; uint qHash(uint seed) const
}; {
return ::qHash(m_id, seed);
}
private:
QString m_id;
};
// Define several safetypes to prevent misuse of QString. // Define several safetypes to prevent misuse of QString.
class __QvGroup; class __QvGroup;
@ -48,9 +53,7 @@ namespace Qv2ray::core
{ {
QList<IDType> list; QList<IDType> list;
for (auto str : strings) { for (auto str : strings) { list << IDType(str); }
list << IDType(str);
}
return list; return list;
} }
@ -60,21 +63,23 @@ namespace Qv2ray::core
{ {
QList<QString> list; QList<QString> list;
for (auto id : ids) { for (auto id : ids) { list << id.toString(); }
list << id.toString();
}
return list; return list;
} }
template <typename T> uint qHash(const IDType<T> &key, uint seed = 0) template<typename T>
uint qHash(const IDType<T> &key, uint seed = 0)
{ {
return key.qHash(seed); return key.qHash(seed);
} }
// //
/// Metadata object representing a connection. /// Metadata object representing a connection.
struct ConnectionMetaObject : ConnectionObject_Config { struct ConnectionMetaObject : ConnectionObject_Config
{
GroupId groupId = NullGroupId; GroupId groupId = NullGroupId;
ConnectionMetaObject(): ConnectionObject_Config() { } ConnectionMetaObject() : ConnectionObject_Config()
{
}
// Suger for down casting. // Suger for down casting.
ConnectionMetaObject(const ConnectionObject_Config &base) : ConnectionMetaObject() ConnectionMetaObject(const ConnectionObject_Config &base) : ConnectionMetaObject()
{ {
@ -88,13 +93,16 @@ namespace Qv2ray::core
}; };
/// Metadata object representing a group. /// Metadata object representing a group.
struct GroupMetaObject: SubscriptionObject_Config { struct GroupMetaObject : SubscriptionObject_Config
{
// Implicit base of two types, since group object is actually the group base object. // Implicit base of two types, since group object is actually the group base object.
bool isSubscription; bool isSubscription;
QList<ConnectionId> connections; QList<ConnectionId> connections;
// Suger for down casting. // Suger for down casting.
GroupMetaObject(): connections() {} GroupMetaObject() : connections()
GroupMetaObject(const GroupObject_Config &base): GroupMetaObject() {
}
GroupMetaObject(const GroupObject_Config &base) : GroupMetaObject()
{ {
this->isSubscription = false; this->isSubscription = false;
this->displayName = base.displayName; this->displayName = base.displayName;
@ -102,7 +110,7 @@ namespace Qv2ray::core
this->connections = StringsToIdList<ConnectionId>(base.connections); this->connections = StringsToIdList<ConnectionId>(base.connections);
} }
// Suger for down casting. // Suger for down casting.
GroupMetaObject(const SubscriptionObject_Config &base): GroupMetaObject((GroupObject_Config)base) GroupMetaObject(const SubscriptionObject_Config &base) : GroupMetaObject((GroupObject_Config) base)
{ {
this->address = base.address; this->address = base.address;
this->lastUpdated = base.lastUpdated; this->lastUpdated = base.lastUpdated;
@ -110,6 +118,6 @@ namespace Qv2ray::core
this->isSubscription = true; this->isSubscription = true;
} }
}; };
} } // namespace Qv2ray::core
using namespace Qv2ray::core; using namespace Qv2ray::core;

View File

@ -24,26 +24,33 @@ namespace Qv2ray::core
*port = 0; *port = 0;
*protocol = out["protocol"].toString(QObject::tr("N/A")).toLower(); *protocol = out["protocol"].toString(QObject::tr("N/A")).toLower();
if (*protocol == "vmess") { if (*protocol == "vmess")
auto Server = StructFromJsonString<VMessServerObject>(JsonToString(out["settings"].toObject()["vnext"].toArray().first().toObject())); {
auto Server =
StructFromJsonString<VMessServerObject>(JsonToString(out["settings"].toObject()["vnext"].toArray().first().toObject()));
*host = Server.address; *host = Server.address;
*port = Server.port; *port = Server.port;
return true; return true;
} else if (*protocol == "shadowsocks") { }
else if (*protocol == "shadowsocks")
{
auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject()); auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject());
auto Server = StructFromJsonString<ShadowSocksServerObject>(x); auto Server = StructFromJsonString<ShadowSocksServerObject>(x);
*host = Server.address; *host = Server.address;
*port = Server.port; *port = Server.port;
return true; return true;
} else if (*protocol == "socks") { }
else if (*protocol == "socks")
{
auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject()); auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject());
auto Server = StructFromJsonString<SocksServerObject>(x); auto Server = StructFromJsonString<SocksServerObject>(x);
*host = Server.address; *host = Server.address;
*port = Server.port; *port = Server.port;
return true; return true;
} else { }
else
{
return false; return false;
} }
} }
} } // namespace Qv2ray::core

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <base/models/QvSafeType.hpp>
#include <base/models/CoreObjectModels.hpp> #include <base/models/CoreObjectModels.hpp>
#include <base/models/QvSafeType.hpp>
namespace Qv2ray::core namespace Qv2ray::core
{ {
@ -26,6 +26,6 @@ namespace Qv2ray::core
bool GetOutboundData(const OUTBOUND &out, QString *host, int *port, QString *protocol); bool GetOutboundData(const OUTBOUND &out, QString *host, int *port, QString *protocol);
bool IsComplexConfig(const CONFIGROOT &root); bool IsComplexConfig(const CONFIGROOT &root);
} } // namespace Qv2ray::core
using namespace Qv2ray::core; using namespace Qv2ray::core;

View File

@ -1,4 +1,5 @@
#include "ConfigBackend.hpp" #include "ConfigBackend.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
namespace Qv2ray::core::config namespace Qv2ray::core::config
@ -14,10 +15,8 @@ namespace Qv2ray::core::config
{ {
Qv2rayConfigPath = path; Qv2rayConfigPath = path;
if (!path.endsWith("/")) { if (!path.endsWith("/")) { Qv2rayConfigPath += "/"; }
Qv2rayConfigPath += "/";
}
} }
} } // namespace Qv2ray::core::config
using namespace Qv2ray::core::config; using namespace Qv2ray::core::config;

View File

@ -4,7 +4,7 @@ namespace Qv2ray::core::config
{ {
void SaveGlobalConfig(const Qv2rayConfig &conf); void SaveGlobalConfig(const Qv2rayConfig &conf);
void SetConfigDirPath(const QString &path); void SetConfigDirPath(const QString &path);
} } // namespace Qv2ray::core::config
using namespace Qv2ray::core; using namespace Qv2ray::core;
using namespace Qv2ray::core::config; using namespace Qv2ray::core::config;

View File

@ -13,11 +13,14 @@ namespace Qv2ray
// Private member // Private member
QJsonObject UpgradeConfig_Inc(int fromVersion, QJsonObject root) QJsonObject UpgradeConfig_Inc(int fromVersion, QJsonObject root)
{ {
switch (fromVersion) { switch (fromVersion)
{
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Below is for Qv2ray version 2 // Below is for Qv2ray version 2
case 4: { case 4:
// We changed the "proxyCN" to "bypassCN" as it's easier to understand.... {
// We changed the "proxyCN" to "bypassCN" as it's easier to
// understand....
auto v2_oldProxyCN = root["proxyCN"].toBool(); auto v2_oldProxyCN = root["proxyCN"].toBool();
// //
// From 3 to 4, we changed 'runAsRoot' to 'tProxySupport' // From 3 to 4, we changed 'runAsRoot' to 'tProxySupport'
@ -38,7 +41,7 @@ namespace Qv2ray
root.remove("inBoundSettings"); root.remove("inBoundSettings");
UPGRADELOG("Renamed inBoundSettings to inboundConfig.") UPGRADELOG("Renamed inBoundSettings to inboundConfig.")
// //
//connectionConfig // connectionConfig
QJsonObject o; QJsonObject o;
o["dnsList"] = root["dnsList"]; o["dnsList"] = root["dnsList"];
o["withLocalDNS"] = root["withLocalDNS"]; o["withLocalDNS"] = root["withLocalDNS"];
@ -66,13 +69,15 @@ namespace Qv2ray
} }
// Qv2ray version 2, RC 2 // Qv2ray version 2, RC 2
case 5: { case 5:
{
// Added subscription auto update // Added subscription auto update
auto subs = root["subscribes"].toObject(); auto subs = root["subscribes"].toObject();
root.remove("subscribes"); root.remove("subscribes");
QJsonObject newSubscriptions; QJsonObject newSubscriptions;
for (auto item = subs.begin(); item != subs.end(); item++) { for (auto item = subs.begin(); item != subs.end(); item++)
{
auto key = item.key(); auto key = item.key();
SubscriptionObject_Config _conf; SubscriptionObject_Config _conf;
_conf.address = item.value().toString(); _conf.address = item.value().toString();
@ -88,7 +93,8 @@ namespace Qv2ray
} }
// Qv2ray version 2, RC 4 // Qv2ray version 2, RC 4
case 6: { case 6:
{
// Moved API Stats port from connectionConfig to apiConfig // Moved API Stats port from connectionConfig to apiConfig
QJsonObject apiConfig; QJsonObject apiConfig;
apiConfig["enableAPI"] = true; apiConfig["enableAPI"] = true;
@ -97,7 +103,8 @@ namespace Qv2ray
break; break;
} }
case 7: { case 7:
{
auto lang = root["uiConfig"].toObject()["language"].toString().replace("-", "_"); auto lang = root["uiConfig"].toObject()["language"].toString().replace("-", "_");
auto uiConfig = root["uiConfig"].toObject(); auto uiConfig = root["uiConfig"].toObject();
uiConfig["language"] = lang; uiConfig["language"] = lang;
@ -106,23 +113,24 @@ namespace Qv2ray
break; break;
} }
// From version 8 to 9, we introduced a lot of new connection metainfo(s) // From version 8 to 9, we introduced a lot of new connection
case 8: { // metainfo(s)
case 8:
{
// Generate a default group // Generate a default group
QJsonObject defaultGroup; QJsonObject defaultGroup;
QStringList defaultGroupConnectionId; QStringList defaultGroupConnectionId;
defaultGroup["displayName"] = QObject::tr("Default Group"); defaultGroup["displayName"] = QObject::tr("Default Group");
QString defaultGroupId = "000000000000"; QString defaultGroupId = "000000000000";
if (!QDir(QV2RAY_CONNECTIONS_DIR + defaultGroupId).exists()) { if (!QDir(QV2RAY_CONNECTIONS_DIR + defaultGroupId).exists()) { QDir().mkpath(QV2RAY_CONNECTIONS_DIR + defaultGroupId); }
QDir().mkpath(QV2RAY_CONNECTIONS_DIR + defaultGroupId);
}
QString autoStartId; QString autoStartId;
UPGRADELOG("Upgrading connections...") UPGRADELOG("Upgrading connections...")
QJsonObject rootConnections; QJsonObject rootConnections;
for (auto config : root["configs"].toArray()) { for (auto config : root["configs"].toArray())
{
UPGRADELOG("Migrating: " + config.toString()) UPGRADELOG("Migrating: " + config.toString())
// //
// MOVE FILES. // MOVE FILES.
@ -133,17 +141,19 @@ namespace Qv2ray
DEBUG(MODULE_SETTINGS, "Generated new UUID: " + newUuid); DEBUG(MODULE_SETTINGS, "Generated new UUID: " + newUuid);
// Check Autostart Id // Check Autostart Id
if (root["autoStartConfig"].toObject()["subscriptionName"].toString().isEmpty()) { if (root["autoStartConfig"].toObject()["subscriptionName"].toString().isEmpty())
if (root["autoStartConfig"].toObject()["connectionName"].toString() == config.toString()) { {
autoStartId = newUuid; if (root["autoStartConfig"].toObject()["connectionName"].toString() == config.toString()) { autoStartId = newUuid; }
}
} }
if (configFile.exists()) { if (configFile.exists())
{
auto newPath = QV2RAY_CONNECTIONS_DIR + defaultGroupId + "/" + newUuid + QV2RAY_CONFIG_FILE_EXTENSION; auto newPath = QV2RAY_CONNECTIONS_DIR + defaultGroupId + "/" + newUuid + QV2RAY_CONFIG_FILE_EXTENSION;
configFile.rename(newPath); configFile.rename(newPath);
UPGRADELOG("Moved: " + filePath + " to " + newPath); UPGRADELOG("Moved: " + filePath + " to " + newPath);
} else { }
else
{
UPGRADELOG("WARNING! This file is not found, possible loss of data!") UPGRADELOG("WARNING! This file is not found, possible loss of data!")
continue; continue;
} }
@ -159,7 +169,8 @@ namespace Qv2ray
QJsonObject rootSubscriptions = root.take("subscriptions").toObject(); QJsonObject rootSubscriptions = root.take("subscriptions").toObject();
QJsonObject newSubscriptions; QJsonObject newSubscriptions;
for (auto i = 0; i < rootSubscriptions.count(); i++) { for (auto i = 0; i < rootSubscriptions.count(); i++)
{
auto key = rootSubscriptions.keys()[i]; auto key = rootSubscriptions.keys()[i];
auto value = rootSubscriptions.value(key); auto value = rootSubscriptions.value(key);
// //
@ -176,15 +187,14 @@ namespace Qv2ray
auto newDirPath = QV2RAY_SUBSCRIPTION_DIR + subsUuid; auto newDirPath = QV2RAY_SUBSCRIPTION_DIR + subsUuid;
QDir newDir(newDirPath); QDir newDir(newDirPath);
if (!newDir.exists()) { if (!newDir.exists()) { newDir.mkpath(newDirPath); }
newDir.mkpath(newDirPath);
}
// With extensions // With extensions
auto fileList = GetFileList(baseDirPath); auto fileList = GetFileList(baseDirPath);
// Copy every file within a subscription. // Copy every file within a subscription.
for (auto fileName : fileList) { for (auto fileName : fileList)
{
auto subsConnectionId = GenerateUuid(); auto subsConnectionId = GenerateUuid();
auto baseFilePath = baseDirPath + "/" + fileName; auto baseFilePath = baseDirPath + "/" + fileName;
auto newFilePath = newDirPath + "/" + subsConnectionId + QV2RAY_CONFIG_FILE_EXTENSION; auto newFilePath = newDirPath + "/" + subsConnectionId + QV2RAY_CONFIG_FILE_EXTENSION;
@ -199,10 +209,10 @@ namespace Qv2ray
// //
// Check Autostart Id // Check Autostart Id
if (root["autoStartConfig"].toObject()["subscriptionName"].toString() == key) { if (root["autoStartConfig"].toObject()["subscriptionName"].toString() == key)
if (root["autoStartConfig"].toObject()["connectionName"].toString() == subsConnection["displayName"].toString()) { {
autoStartId = subsConnectionId; if (root["autoStartConfig"].toObject()["connectionName"].toString() == subsConnection["displayName"].toString())
} { autoStartId = subsConnectionId; }
} }
} }
@ -222,12 +232,15 @@ namespace Qv2ray
break; break;
} }
default: { default:
// Due to technical issue, we cannot maintain all of those upgrade processes anymore. {
// Check https://github.com/Qv2ray/Qv2ray/issues/353#issuecomment-586117507 for more information // Due to technical issue, we cannot maintain all of those
// upgrade processes anymore. Check
// https://github.com/Qv2ray/Qv2ray/issues/353#issuecomment-586117507
// for more information
QvMessageBoxWarn(nullptr, QObject::tr("Configuration Upgrade Failed"), QvMessageBoxWarn(nullptr, QObject::tr("Configuration Upgrade Failed"),
QObject::tr("Unsupported config version number: ") + QSTRN(fromVersion) + NEWLINE + NEWLINE + QObject::tr("Unsupported config version number: ") + QSTRN(fromVersion) + NEWLINE + NEWLINE +
QObject::tr("Please upgrade firstly up to Qv2ray v2.0/v2.1 and try again.")); QObject::tr("Please upgrade firstly up to Qv2ray v2.0/v2.1 and try again."));
throw new runtime_error("The configuration version of your old Qv2ray installation is out-of-date and that" throw new runtime_error("The configuration version of your old Qv2ray installation is out-of-date and that"
" version is not supported anymore, please try to update to an intermediate version of Qv2ray first."); " version is not supported anymore, please try to update to an intermediate version of Qv2ray first.");
} }
@ -242,10 +255,8 @@ namespace Qv2ray
{ {
LOG(MODULE_SETTINGS, "Migrating config from version " + QSTRN(fromVersion) + " to " + QSTRN(toVersion)) LOG(MODULE_SETTINGS, "Migrating config from version " + QSTRN(fromVersion) + " to " + QSTRN(toVersion))
for (int i = fromVersion; i < toVersion; i++) { for (int i = fromVersion; i < toVersion; i++) { root = UpgradeConfig_Inc(i, root); }
root = UpgradeConfig_Inc(i, root);
}
return root; return root;
} }
} } // namespace Qv2ray

View File

@ -1,4 +1,5 @@
#include "ConnectionIO.hpp" #include "ConnectionIO.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
namespace Qv2ray::core::connection namespace Qv2ray::core::connection
@ -8,16 +9,19 @@ namespace Qv2ray::core::connection
//// ////
//// Save Connection to a place, with checking if there's existing file. //// Save Connection to a place, with checking if there's existing file.
//// If so, append "_N" to the name. //// If so, append "_N" to the name.
//bool SaveConnectionConfig(CONFIGROOT obj, QString *alias, bool canOverrideExisting) // bool SaveConnectionConfig(CONFIGROOT obj, QString *alias, bool
// canOverrideExisting)
//{ //{
// auto str = JsonToString(obj); // auto str = JsonToString(obj);
// auto fullPath = QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION; // auto fullPath = QV2RAY_CONFIG_DIR + *alias +
// QV2RAY_CONFIG_FILE_EXTENSION;
// //
// // If there's already a file AND we CANNOT override existing file. // // If there's already a file AND we CANNOT override existing file.
// if (QFile::exists(fullPath) && !canOverrideExisting) { // if (QFile::exists(fullPath) && !canOverrideExisting) {
// // Alias is a pointer to a QString. // // Alias is a pointer to a QString.
// DeducePossibleFileName(QV2RAY_CONFIG_DIR, alias, QV2RAY_CONFIG_FILE_EXTENSION); // DeducePossibleFileName(QV2RAY_CONFIG_DIR, alias,
// fullPath = QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION; // QV2RAY_CONFIG_FILE_EXTENSION); fullPath = QV2RAY_CONFIG_DIR +
// *alias + QV2RAY_CONFIG_FILE_EXTENSION;
// } // }
// //
// LOG(MODULE_SETTINGS, "Saving a config named: " + *alias) // LOG(MODULE_SETTINGS, "Saving a config named: " + *alias)
@ -25,7 +29,8 @@ namespace Qv2ray::core::connection
// return StringToFile(&str, &config); // return StringToFile(&str, &config);
//} //}
// //
//bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, QString *name) // bool SaveSubscriptionConfig(CONFIGROOT obj, const QString
// &subscription, QString *name)
//{ //{
// auto str = JsonToString(obj); // auto str = JsonToString(obj);
// auto fName = *name; // auto fName = *name;
@ -34,27 +39,31 @@ namespace Qv2ray::core::connection
// fName = RemoveInvalidFileName(fName); // fName = RemoveInvalidFileName(fName);
// } // }
// //
// QFile config(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + fName + QV2RAY_CONFIG_FILE_EXTENSION); // QFile config(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + fName
// + QV2RAY_CONFIG_FILE_EXTENSION);
// //
// // If there's already a file. THIS IS EXTREMELY RARE // // If there's already a file. THIS IS EXTREMELY RARE
// if (config.exists()) { // if (config.exists()) {
// LOG(MODULE_FILEIO, "Trying to overrwrite an existing subscription config file. THIS IS RARE") // LOG(MODULE_FILEIO, "Trying to overrwrite an existing
// subscription config file. THIS IS RARE")
// } // }
// //
// LOG(MODULE_SETTINGS, "Saving a subscription named: " + fName) // LOG(MODULE_SETTINGS, "Saving a subscription named: " + fName)
// bool result = StringToFile(&str, &config); // bool result = StringToFile(&str, &config);
// //
// if (!result) { // if (!result) {
// LOG(MODULE_FILEIO, "Failed to save a connection config from subscription: " + subscription + ", name: " + fName) // LOG(MODULE_FILEIO, "Failed to save a connection config from
// subscription: " + subscription + ", name: " + fName)
// } // }
// //
// *name = fName; // *name = fName;
// return result; // return result;
//} //}
// //
//bool RemoveConnection(const QString &alias) // bool RemoveConnection(const QString &alias)
//{ //{
// QFile config(QV2RAY_CONFIG_DIR + alias + QV2RAY_CONFIG_FILE_EXTENSION); // QFile config(QV2RAY_CONFIG_DIR + alias +
// QV2RAY_CONFIG_FILE_EXTENSION);
// //
// if (!config.exists()) { // if (!config.exists()) {
// LOG(MODULE_FILEIO, "Trying to remove a non-existing file?") // LOG(MODULE_FILEIO, "Trying to remove a non-existing file?")
@ -64,9 +73,11 @@ namespace Qv2ray::core::connection
// } // }
//} //}
// //
//bool RemoveSubscriptionConnection(const QString &subsName, const QString &name) // bool RemoveSubscriptionConnection(const QString &subsName, const
// QString &name)
//{ //{
// QFile config(QV2RAY_SUBSCRIPTION_DIR + subsName + "/" + name + QV2RAY_CONFIG_FILE_EXTENSION); // QFile config(QV2RAY_SUBSCRIPTION_DIR + subsName + "/" + name +
// QV2RAY_CONFIG_FILE_EXTENSION);
// //
// if (!config.exists()) { // if (!config.exists()) {
// LOG(MODULE_FILEIO, "Trying to remove a non-existing file?") // LOG(MODULE_FILEIO, "Trying to remove a non-existing file?")
@ -76,25 +87,32 @@ namespace Qv2ray::core::connection
// } // }
//} //}
// //
//bool RenameConnection(const QString &originalName, const QString &newName) // bool RenameConnection(const QString &originalName, const QString
// &newName)
//{ //{
// LOG(MODULE_CONNECTION, "[RENAME] --> ORIGINAL: " + originalName + ", NEW: " + newName) // LOG(MODULE_CONNECTION, "[RENAME] --> ORIGINAL: " + originalName +
// return QFile::rename(QV2RAY_CONFIG_DIR + originalName + QV2RAY_CONFIG_FILE_EXTENSION, QV2RAY_CONFIG_DIR + newName + QV2RAY_CONFIG_FILE_EXTENSION); // ", NEW: " + newName) return QFile::rename(QV2RAY_CONFIG_DIR +
// originalName + QV2RAY_CONFIG_FILE_EXTENSION, QV2RAY_CONFIG_DIR +
// newName + QV2RAY_CONFIG_FILE_EXTENSION);
//} //}
// //
//bool RenameSubscription(const QString &originalName, const QString &newName) // bool RenameSubscription(const QString &originalName, const QString
// &newName)
//{ //{
// LOG(MODULE_SUBSCRIPTION, "[RENAME] --> ORIGINAL: " + originalName + ", NEW: " + newName) // LOG(MODULE_SUBSCRIPTION, "[RENAME] --> ORIGINAL: " + originalName
// return QDir().rename(QV2RAY_SUBSCRIPTION_DIR + originalName, QV2RAY_SUBSCRIPTION_DIR + newName); // + ", NEW: " + newName) return
// QDir().rename(QV2RAY_SUBSCRIPTION_DIR + originalName,
// QV2RAY_SUBSCRIPTION_DIR + newName);
//} //}
// //
//CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool importComplex) // CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool
// importComplex)
//{ //{
// QFile source(sourceFilePath); // QFile source(sourceFilePath);
// //
// if (!source.exists()) { // if (!source.exists()) {
// LOG(MODULE_FILEIO, "Trying to import from an non-existing file.") // LOG(MODULE_FILEIO, "Trying to import from an non-existing
// return CONFIGROOT(); // file.") return CONFIGROOT();
// } // }
// //
// auto root = CONFIGROOT(JsonFromString(StringFromFile(&source))); // auto root = CONFIGROOT(JsonFromString(StringFromFile(&source)));
@ -111,4 +129,4 @@ namespace Qv2ray::core::connection
// return root; // return root;
//} //}
} }
} } // namespace Qv2ray::core::connection

View File

@ -7,8 +7,8 @@ namespace Qv2ray::core::connection
{ {
// File Protocol // File Protocol
CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool importComplex); CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool importComplex);
} } // namespace ConnectionIO
} } // namespace Qv2ray::core::connection
using namespace Qv2ray::core::connection; using namespace Qv2ray::core::connection;
using namespace Qv2ray::core::connection::ConnectionIO; using namespace Qv2ray::core::connection::ConnectionIO;

View File

@ -1,12 +1,14 @@
#include "Generation.hpp" #include "Generation.hpp"
#include "core/CoreUtils.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
namespace Qv2ray::core::connection namespace Qv2ray::core::connection
{ {
namespace Generation namespace Generation
{ {
// -------------------------- BEGIN CONFIG GENERATIONS ---------------------------------------------------------------------------- // -------------------------- BEGIN CONFIG GENERATIONS
// ----------------------------------------------------------------------------
ROUTING GenerateRoutes(bool enableProxy, bool proxyCN) ROUTING GenerateRoutes(bool enableProxy, bool proxyCN)
{ {
ROUTING root; ROUTING root;
@ -15,8 +17,10 @@ namespace Qv2ray::core::connection
// For Rules list // For Rules list
ROUTERULELIST rulesList; ROUTERULELIST rulesList;
if (!enableProxy) { if (!enableProxy)
// This is added to disable all proxies, as a alternative influence of #64 {
// This is added to disable all proxies, as a alternative
// influence of #64
rulesList.append(GenerateSingleRouteRule(QStringList() << "regexp:.*", true, OUTBOUND_TAG_DIRECT)); rulesList.append(GenerateSingleRouteRule(QStringList() << "regexp:.*", true, OUTBOUND_TAG_DIRECT));
} }
@ -28,7 +32,8 @@ namespace Qv2ray::core::connection
rulesList.append(GenerateSingleRouteRule(QStringList() << "geosite:cn", true, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY)); rulesList.append(GenerateSingleRouteRule(QStringList() << "geosite:cn", true, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
// //
// As a bug fix of #64, this default rule has been disabled. // As a bug fix of #64, this default rule has been disabled.
//rulesList.append(GenerateSingleRouteRule(QStringList({"regexp:.*"}), true, globalProxy ? OUTBOUND_TAG_PROXY : OUTBOUND_TAG_DIRECT)); // rulesList.append(GenerateSingleRouteRule(QStringList({"regexp:.*"}),
// true, globalProxy ? OUTBOUND_TAG_PROXY : OUTBOUND_TAG_DIRECT));
root.insert("rules", rulesList); root.insert("rules", rulesList);
RROOT RROOT
} }
@ -56,20 +61,24 @@ namespace Qv2ray::core::connection
RROOT RROOT
} }
OUTBOUNDSETTING GenerateShadowSocksOUT(QList<ShadowSocksServerObject> servers) OUTBOUNDSETTING
GenerateShadowSocksOUT(QList<ShadowSocksServerObject> servers)
{ {
OUTBOUNDSETTING root; OUTBOUNDSETTING root;
QJsonArray x; QJsonArray x;
foreach (auto server, servers) { foreach (auto server, servers)
x.append(GenerateShadowSocksServerOUT(server.email, server.address, server.port, server.method, server.password, server.ota, server.level)); {
x.append(GenerateShadowSocksServerOUT(server.email, server.address, server.port, server.method, server.password, server.ota,
server.level));
} }
root.insert("servers", x); root.insert("servers", x);
RROOT RROOT
} }
OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota, int level) OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota,
int level)
{ {
OUTBOUNDSETTING root; OUTBOUNDSETTING root;
JADD(email, address, port, method, password, level, ota) JADD(email, address, port, method, password, level, ota)
@ -81,7 +90,8 @@ namespace Qv2ray::core::connection
QJsonObject root; QJsonObject root;
QJsonArray servers(QJsonArray::fromStringList(dnsServers)); QJsonArray servers(QJsonArray::fromStringList(dnsServers));
if (withLocalhost) { if (withLocalhost)
{
// https://github.com/lhy0403/Qv2ray/issues/64 // https://github.com/lhy0403/Qv2ray/issues/64
// The fix patch didn't touch this line below. // The fix patch didn't touch this line below.
// //
@ -93,7 +103,7 @@ namespace Qv2ray::core::connection
RROOT RROOT
} }
INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel) INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel)
{ {
INBOUNDSETTING root; INBOUNDSETTING root;
JADD(address, port, network, timeout, followRedirect, userLevel) JADD(address, port, network, timeout, followRedirect, userLevel)
@ -105,17 +115,14 @@ namespace Qv2ray::core::connection
INBOUNDSETTING root; INBOUNDSETTING root;
QJsonArray accounts; QJsonArray accounts;
foreach (auto account, _accounts) { foreach (auto account, _accounts)
if (account.user.isEmpty() && account.pass.isEmpty()) { {
continue; if (account.user.isEmpty() && account.pass.isEmpty()) { continue; }
}
accounts.append(GetRootObject(account)); accounts.append(GetRootObject(account));
} }
if (!accounts.isEmpty()) { if (!accounts.isEmpty()) { JADD(accounts) }
JADD(accounts)
}
JADD(timeout, allowTransparent, userLevel) JADD(timeout, allowTransparent, userLevel)
RROOT RROOT
@ -130,7 +137,8 @@ namespace Qv2ray::core::connection
oneServer["address"] = address; oneServer["address"] = address;
oneServer["port"] = port; oneServer["port"] = port;
if (useAuth) { if (useAuth)
{
QJsonArray users; QJsonArray users;
QJsonObject oneUser; QJsonObject oneUser;
oneUser["user"] = username; oneUser["user"] = username;
@ -150,35 +158,34 @@ namespace Qv2ray::core::connection
INBOUNDSETTING root; INBOUNDSETTING root;
QJsonArray accounts; QJsonArray accounts;
foreach (auto acc, _accounts) { foreach (auto acc, _accounts)
if (acc.user.isEmpty() && acc.pass.isEmpty()) { {
continue; if (acc.user.isEmpty() && acc.pass.isEmpty()) { continue; }
}
accounts.append(GetRootObject(acc)); accounts.append(GetRootObject(acc));
} }
if (!accounts.isEmpty()) { if (!accounts.isEmpty()) { JADD(accounts) }
JADD(accounts)
}
if (udp) { if (udp) { JADD(auth, udp, ip, userLevel) }
JADD(auth, udp, ip, userLevel) else
} else { {
JADD(auth, userLevel) JADD(auth, userLevel)
} }
RROOT RROOT
} }
OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux, QString sendThrough, QString tag) OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux,
QString sendThrough, QString tag)
{ {
OUTBOUND root; OUTBOUND root;
JADD(sendThrough, protocol, settings, tag, streamSettings, mux) JADD(sendThrough, protocol, settings, tag, streamSettings, mux)
RROOT RROOT
} }
INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag, QJsonObject sniffing, QJsonObject allocate) INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag, QJsonObject sniffing,
QJsonObject allocate)
{ {
INBOUND root; INBOUND root;
LOG(MODULE_CONNECTION, "allocation is not used here.") LOG(MODULE_CONNECTION, "allocation is not used here.")
@ -192,20 +199,18 @@ namespace Qv2ray::core::connection
QJsonObject root; QJsonObject root;
QJsonArray services; QJsonArray services;
if (withHandler) if (withHandler) services << "HandlerService";
services << "HandlerService";
if (withLogger) if (withLogger) services << "LoggerService";
services << "LoggerService";
if (withStats) if (withStats) services << "StatsService";
services << "StatsService";
JADD(services, tag) JADD(services, tag)
RROOT RROOT
} }
// -------------------------- END CONFIG GENERATIONS ------------------------------------------------------------------------------ // -------------------------- END CONFIG GENERATIONS
// ------------------------------------------------------------------------------
// BEGIN RUNTIME CONFIG GENERATION // BEGIN RUNTIME CONFIG GENERATION
CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root) CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root)
@ -213,8 +218,10 @@ namespace Qv2ray::core::connection
bool isComplex = IsComplexConfig(root); bool isComplex = IsComplexConfig(root);
QJsonObject logObject; QJsonObject logObject;
// //
//logObject.insert("access", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ACCESS_LOG_FILENAME); // logObject.insert("access", QV2RAY_CONFIG_PATH +
//logObject.insert("error", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ERROR_LOG_FILENAME); // QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ACCESS_LOG_FILENAME);
// logObject.insert("error", QV2RAY_CONFIG_PATH +
// QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ERROR_LOG_FILENAME);
// //
logObject.insert("loglevel", vLogLevels[GlobalConfig.logLevel]); logObject.insert("loglevel", vLogLevels[GlobalConfig.logLevel]);
root.insert("log", logObject); root.insert("log", logObject);
@ -225,20 +232,24 @@ namespace Qv2ray::core::connection
// //
// //
// If inbounds list is empty we append our global configured inbounds to the config. // If inbounds list is empty we append our global configured
if (!root.contains("inbounds") || root["inbounds"].toArray().empty()) { // inbounds to the config.
if (!root.contains("inbounds") || root["inbounds"].toArray().empty())
{
INBOUNDS inboundsList; INBOUNDS inboundsList;
// HTTP Inbound // HTTP Inbound
if (GlobalConfig.inboundConfig.useHTTP) { if (GlobalConfig.inboundConfig.useHTTP)
{
INBOUND httpInBoundObject; INBOUND httpInBoundObject;
httpInBoundObject.insert("listen", GlobalConfig.inboundConfig.listenip); httpInBoundObject.insert("listen", GlobalConfig.inboundConfig.listenip);
httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port); httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port);
httpInBoundObject.insert("protocol", "http"); httpInBoundObject.insert("protocol", "http");
httpInBoundObject.insert("tag", "http_IN"); httpInBoundObject.insert("tag", "http_IN");
if (GlobalConfig.inboundConfig.http_useAuth) { if (GlobalConfig.inboundConfig.http_useAuth)
auto httpInSettings = GenerateHTTPIN(QList<AccountObject>() << GlobalConfig.inboundConfig.httpAccount); {
auto httpInSettings = GenerateHTTPIN(QList<AccountObject>() << GlobalConfig.inboundConfig.httpAccount);
httpInBoundObject.insert("settings", httpInSettings); httpInBoundObject.insert("settings", httpInSettings);
} }
@ -246,7 +257,8 @@ namespace Qv2ray::core::connection
} }
// SOCKS Inbound // SOCKS Inbound
if (GlobalConfig.inboundConfig.useSocks) { if (GlobalConfig.inboundConfig.useSocks)
{
INBOUND socksInBoundObject; INBOUND socksInBoundObject;
socksInBoundObject.insert("listen", GlobalConfig.inboundConfig.listenip); socksInBoundObject.insert("listen", GlobalConfig.inboundConfig.listenip);
socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port); socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port);
@ -254,8 +266,7 @@ namespace Qv2ray::core::connection
socksInBoundObject.insert("tag", "socks_IN"); socksInBoundObject.insert("tag", "socks_IN");
auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth", auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth",
QList<AccountObject>() << GlobalConfig.inboundConfig.socksAccount, QList<AccountObject>() << GlobalConfig.inboundConfig.socksAccount,
GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP);
GlobalConfig.inboundConfig.socksLocalIP);
socksInBoundObject.insert("settings", socksInSettings); socksInBoundObject.insert("settings", socksInSettings);
inboundsList.append(socksInBoundObject); inboundsList.append(socksInBoundObject);
} }
@ -264,14 +275,16 @@ namespace Qv2ray::core::connection
DEBUG(MODULE_CONNECTION, "Added global config inbounds to the config") DEBUG(MODULE_CONNECTION, "Added global config inbounds to the config")
} }
// Process every inbounds to make sure a tag is configured, fixed API 0 speed // Process every inbounds to make sure a tag is configured, fixed
// issue when no tag is configured. // API 0 speed issue when no tag is configured.
INBOUNDS newTaggedInbounds = INBOUNDS(root["inbounds"].toArray()); INBOUNDS newTaggedInbounds = INBOUNDS(root["inbounds"].toArray());
for (auto i = 0; i < newTaggedInbounds.count(); i++) { for (auto i = 0; i < newTaggedInbounds.count(); i++)
{
auto _inboundItem = newTaggedInbounds[i].toObject(); auto _inboundItem = newTaggedInbounds[i].toObject();
if (!_inboundItem.contains("tag") || _inboundItem["tag"].toString().isEmpty()) { if (!_inboundItem.contains("tag") || _inboundItem["tag"].toString().isEmpty())
{
LOG(MODULE_SETTINGS, "Adding a tag to an inbound.") LOG(MODULE_SETTINGS, "Adding a tag to an inbound.")
_inboundItem["tag"] = GenerateRandomString(8); _inboundItem["tag"] = GenerateRandomString(8);
newTaggedInbounds[i] = _inboundItem; newTaggedInbounds[i] = _inboundItem;
@ -281,52 +294,66 @@ namespace Qv2ray::core::connection
root["inbounds"] = newTaggedInbounds; root["inbounds"] = newTaggedInbounds;
// //
// //
// Note: The part below always makes the whole functionality in trouble...... // Note: The part below always makes the whole functionality in
// BE EXTREME CAREFUL when changing these code below... // trouble...... BE EXTREME CAREFUL when changing these code
// See: https://github.com/lhy0403/Qv2ray/issues/129 // below... See: https://github.com/lhy0403/Qv2ray/issues/129
// routeCountLabel in Mainwindow makes here failed to ENOUGH-ly check the routing tables // routeCountLabel in Mainwindow makes here failed to ENOUGH-ly
// check the routing tables
if (isComplex) { if (isComplex)
{
// For some config files that has routing entries already. // For some config files that has routing entries already.
// We DO NOT add extra routings. // We DO NOT add extra routings.
// //
// HOWEVER, we need to verify the QV2RAY_RULE_ENABLED entry. // HOWEVER, we need to verify the QV2RAY_RULE_ENABLED entry.
// And what's more, process (by removing unused items) from a rule object. // And what's more, process (by removing unused items) from a
// rule object.
ROUTING routing = ROUTING(root["routing"].toObject()); ROUTING routing = ROUTING(root["routing"].toObject());
ROUTERULELIST rules; ROUTERULELIST rules;
LOG(MODULE_CONNECTION, "Processing an existing routing table.") LOG(MODULE_CONNECTION, "Processing an existing routing table.")
for (auto _rule : routing["rules"].toArray()) { for (auto _rule : routing["rules"].toArray())
{
auto _b = _rule.toObject(); auto _b = _rule.toObject();
if (_b.contains("QV2RAY_RULE_USE_BALANCER")) { if (_b.contains("QV2RAY_RULE_USE_BALANCER"))
if (_b["QV2RAY_RULE_USE_BALANCER"].toBool()) { {
if (_b["QV2RAY_RULE_USE_BALANCER"].toBool())
{
// We use balancer // We use balancer
_b.remove("outboundTag"); _b.remove("outboundTag");
} else { }
else
{
// We only use the normal outbound // We only use the normal outbound
_b.remove("balancerTag"); _b.remove("balancerTag");
} }
} else { }
else
{
LOG(MODULE_SETTINGS, "We found a rule without QV2RAY_RULE_USE_BALANCER, so don't process it.") LOG(MODULE_SETTINGS, "We found a rule without QV2RAY_RULE_USE_BALANCER, so don't process it.")
} }
// If this entry has been disabled. // If this entry has been disabled.
if (_b.contains("QV2RAY_RULE_ENABLED") && _b["QV2RAY_RULE_ENABLED"].toBool() == true) { if (_b.contains("QV2RAY_RULE_ENABLED") && _b["QV2RAY_RULE_ENABLED"].toBool() == true) { rules.append(_b); }
rules.append(_b); else
} else { {
LOG(MODULE_SETTINGS, "Discarded a rule as it's been set DISABLED") LOG(MODULE_SETTINGS, "Discarded a rule as it's been set DISABLED")
} }
} }
routing["rules"] = rules; routing["rules"] = rules;
root["routing"] = routing; root["routing"] = routing;
} else { }
else
{
// //
LOG(MODULE_CONNECTION, "Inserting default values to simple config") LOG(MODULE_CONNECTION, "Inserting default values to simple config")
if (root["outbounds"].toArray().count() != 1) { if (root["outbounds"].toArray().count() != 1)
// There are no ROUTING but 2 or more outbounds.... This is rare, but possible. {
// There are no ROUTING but 2 or more outbounds.... This is
// rare, but possible.
LOG(MODULE_CONNECTION, "WARN: This message usually indicates the config file has logic errors:") LOG(MODULE_CONNECTION, "WARN: This message usually indicates the config file has logic errors:")
LOG(MODULE_CONNECTION, "WARN: --> The config file has NO routing section, however more than 1 outbounds are detected.") LOG(MODULE_CONNECTION, "WARN: --> The config file has NO routing section, however more than 1 outbounds are detected.")
} }
@ -337,11 +364,13 @@ namespace Qv2ray::core::connection
// Process forward proxy // Process forward proxy
#define fpConf GlobalConfig.connectionConfig.forwardProxyConfig #define fpConf GlobalConfig.connectionConfig.forwardProxyConfig
if (fpConf.enableForwardProxy) { if (fpConf.enableForwardProxy)
{
auto outboundArray = root["outbounds"].toArray(); auto outboundArray = root["outbounds"].toArray();
auto firstOutbound = outboundArray.first().toObject(); auto firstOutbound = outboundArray.first().toObject();
if (firstOutbound[QV2RAY_USE_FPROXY_KEY].toBool(false)) { if (firstOutbound[QV2RAY_USE_FPROXY_KEY].toBool(false))
{
LOG(MODULE_CONNECTION, "Applying forward proxy to current connection.") LOG(MODULE_CONNECTION, "Applying forward proxy to current connection.")
auto proxy = PROXYSETTING(); auto proxy = PROXYSETTING();
proxy["tag"] = OUTBOUND_TAG_FORWARD_PROXY; proxy["tag"] = OUTBOUND_TAG_FORWARD_PROXY;
@ -349,13 +378,20 @@ namespace Qv2ray::core::connection
// FP Outbound. // FP Outbound.
OUTBOUNDSETTING fpOutbound; OUTBOUNDSETTING fpOutbound;
if (fpConf.type.toLower() == "http" || fpConf.type.toLower() == "socks") { if (fpConf.type.toLower() == "http" || fpConf.type.toLower() == "socks")
fpOutbound = GenerateHTTPSOCKSOut(fpConf.serverAddress, fpConf.port, fpConf.useAuth, fpConf.username, fpConf.password); {
outboundArray.push_back(GenerateOutboundEntry(fpConf.type.toLower(), fpOutbound, QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_FORWARD_PROXY)); fpOutbound =
} else { GenerateHTTPSOCKSOut(fpConf.serverAddress, fpConf.port, fpConf.useAuth, fpConf.username, fpConf.password);
outboundArray.push_back(GenerateOutboundEntry(fpConf.type.toLower(), fpOutbound, QJsonObject(), QJsonObject(),
"0.0.0.0", OUTBOUND_TAG_FORWARD_PROXY));
}
else
{
LOG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + fpConf.type) LOG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + fpConf.type)
} }
} else { }
else
{
// Remove proxySettings from firstOutbound // Remove proxySettings from firstOutbound
firstOutbound.remove("proxySettings"); firstOutbound.remove("proxySettings");
} }
@ -366,7 +402,8 @@ namespace Qv2ray::core::connection
#undef fpConf #undef fpConf
OUTBOUNDS outbounds = OUTBOUNDS(root["outbounds"].toArray()); OUTBOUNDS outbounds = OUTBOUNDS(root["outbounds"].toArray());
outbounds.append(GenerateOutboundEntry("freedom", GenerateFreedomOUT("AsIs", ":0", 0), QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_DIRECT)); outbounds.append(GenerateOutboundEntry("freedom", GenerateFreedomOUT("AsIs", ":0", 0), QJsonObject(), QJsonObject(), "0.0.0.0",
OUTBOUND_TAG_DIRECT));
root["outbounds"] = outbounds; root["outbounds"] = outbounds;
} }
@ -407,7 +444,8 @@ namespace Qv2ray::core::connection
INBOUNDS inbounds = INBOUNDS(root["inbounds"].toArray()); INBOUNDS inbounds = INBOUNDS(root["inbounds"].toArray());
INBOUNDSETTING fakeDocodemoDoor; INBOUNDSETTING fakeDocodemoDoor;
fakeDocodemoDoor["address"] = "127.0.0.1"; fakeDocodemoDoor["address"] = "127.0.0.1";
QJsonObject apiInboundsRoot = GenerateInboundEntry("127.0.0.1", GlobalConfig.apiConfig.statsPort, "dokodemo-door", fakeDocodemoDoor, API_TAG_INBOUND); QJsonObject apiInboundsRoot =
GenerateInboundEntry("127.0.0.1", GlobalConfig.apiConfig.statsPort, "dokodemo-door", fakeDocodemoDoor, API_TAG_INBOUND);
inbounds.push_front(apiInboundsRoot); inbounds.push_front(apiInboundsRoot);
root["inbounds"] = inbounds; root["inbounds"] = inbounds;
// //
@ -417,5 +455,5 @@ namespace Qv2ray::core::connection
} }
return root; return root;
} }
} } // namespace Generation
} } // namespace Qv2ray::core::connection

View File

@ -5,7 +5,7 @@ namespace Qv2ray::core::connection
namespace Generation namespace Generation
{ {
// Important config generation algorithms. // Important config generation algorithms.
const QStringList vLogLevels = {"none", "debug", "info", "warning", "error"}; const QStringList vLogLevels = { "none", "debug", "info", "warning", "error" };
ROUTING GenerateRoutes(bool enableProxy, bool cnProxy); ROUTING GenerateRoutes(bool enableProxy, bool cnProxy);
ROUTERULE GenerateSingleRouteRule(QStringList list, bool isDomain, QString outboundTag, QString type = "field"); ROUTERULE GenerateSingleRouteRule(QStringList list, bool isDomain, QString outboundTag, QString type = "field");
QJsonObject GenerateDNS(bool withLocalhost, QStringList dnsServers); QJsonObject GenerateDNS(bool withLocalhost, QStringList dnsServers);
@ -15,20 +15,24 @@ namespace Qv2ray::core::connection
OUTBOUNDSETTING GenerateFreedomOUT(QString domainStrategy, QString redirect, int userLevel); OUTBOUNDSETTING GenerateFreedomOUT(QString domainStrategy, QString redirect, int userLevel);
OUTBOUNDSETTING GenerateBlackHoleOUT(bool useHTTP); OUTBOUNDSETTING GenerateBlackHoleOUT(bool useHTTP);
OUTBOUNDSETTING GenerateShadowSocksOUT(QList<ShadowSocksServerObject> servers); OUTBOUNDSETTING GenerateShadowSocksOUT(QList<ShadowSocksServerObject> servers);
OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota, int level); OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota,
int level);
OUTBOUNDSETTING GenerateHTTPSOCKSOut(QString address, int port, bool useAuth, QString username, QString password); OUTBOUNDSETTING GenerateHTTPSOCKSOut(QString address, int port, bool useAuth, QString username, QString password);
// //
// Inbounds Protocols // Inbounds Protocols
INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel); INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel);
INBOUNDSETTING GenerateHTTPIN(QList<AccountObject> accounts, int timeout = 300, bool allowTransparent = true, int userLevel = 0); INBOUNDSETTING GenerateHTTPIN(QList<AccountObject> accounts, int timeout = 300, bool allowTransparent = true, int userLevel = 0);
INBOUNDSETTING GenerateSocksIN(QString auth, QList<AccountObject> _accounts, bool udp = false, QString ip = "127.0.0.1", int userLevel = 0); INBOUNDSETTING GenerateSocksIN(QString auth, QList<AccountObject> _accounts, bool udp = false, QString ip = "127.0.0.1",
int userLevel = 0);
// //
// Generate FINAL Configs // Generate FINAL Configs
CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root); CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root);
OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux = QJsonObject(), QString sendThrough = "0.0.0.0", QString tag = OUTBOUND_TAG_PROXY); OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux = QJsonObject(),
INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag, QJsonObject sniffing = QJsonObject(), QJsonObject allocate = QJsonObject()); QString sendThrough = "0.0.0.0", QString tag = OUTBOUND_TAG_PROXY);
} INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag,
} QJsonObject sniffing = QJsonObject(), QJsonObject allocate = QJsonObject());
} // namespace Generation
} // namespace Qv2ray::core::connection
using namespace Qv2ray::core; using namespace Qv2ray::core;
using namespace Qv2ray::core::connection; using namespace Qv2ray::core::connection;

View File

@ -33,57 +33,68 @@ namespace Qv2ray::core::handlers
* - log list * - log list
* in case of error, no objects will be returned. * in case of error, no objects will be returned.
*/ */
std::pair <std::optional<std::pair<QString, QList<std::pair<QString, ShadowSocksServerObject>>>>, QStringList> std::pair<std::optional<std::pair<QString, QList<std::pair<QString, ShadowSocksServerObject>>>>, QStringList> decodeSSD(
decodeSSD(const QString &uri, const QString &pattern = DEFAULT_NAME_PATTERN); const QString &uri, const QString &pattern = DEFAULT_NAME_PATTERN);
} } // namespace ssd
} } // namespace Qv2ray::core::handlers
#define MUST_EXIST(fieldName) \ #define MUST_EXIST(fieldName) \
if (obj[(fieldName)].isUndefined()) {\ if (obj[(fieldName)].isUndefined()) \
logList << QObject::tr("invalid ssd link: json: field %1 must exist").arg(fieldName);\ { \
return std::make_pair(std::nullopt, logList);\ logList << QObject::tr("invalid ssd link: json: field %1 must exist").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \
} }
#define MUST_PORT(fieldName) MUST_EXIST(fieldName);\ #define MUST_PORT(fieldName) \
if (int value = obj[(fieldName)].toInt(-1); value < 0 || value > 65535) { \ MUST_EXIST(fieldName); \
logList << QObject::tr("invalid ssd link: json: field %1 must be valid port number");\ if (int value = obj[(fieldName)].toInt(-1); value < 0 || value > 65535) \
return std::make_pair(std::nullopt, logList);\ { \
logList << QObject::tr("invalid ssd link: json: field %1 must be valid port number"); \
return std::make_pair(std::nullopt, logList); \
} }
#define MUST_STRING(fieldName) MUST_EXIST(fieldName);\ #define MUST_STRING(fieldName) \
if (!obj[(fieldName)].isString()) {\ MUST_EXIST(fieldName); \
logList << QObject::tr("invalid ssd link: json: field %1 must be of type 'string'").arg(fieldName);\ if (!obj[(fieldName)].isString()) \
return std::make_pair(std::nullopt, logList);\ { \
logList << QObject::tr("invalid ssd link: json: field %1 must be of type 'string'").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \
} }
#define MUST_ARRAY(fieldName) MUST_EXIST(fieldName);\ #define MUST_ARRAY(fieldName) \
if (!obj[(fieldName)].isArray()) {\ MUST_EXIST(fieldName); \
logList << QObject::tr("invalid ssd link: json: field %1 must be an array").arg(fieldName);\ if (!obj[(fieldName)].isArray()) \
return std::make_pair(std::nullopt, logList);\ { \
logList << QObject::tr("invalid ssd link: json: field %1 must be an array").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \
} }
#define SERVER_SHOULD_BE_OBJECT(server) \ #define SERVER_SHOULD_BE_OBJECT(server) \
if (!server.isObject()) {\ if (!server.isObject()) \
logList << QObject::tr("skipping invalid ssd server: server must be an object");\ { \
continue;\ logList << QObject::tr("skipping invalid ssd server: server must be an object"); \
continue; \
} }
#define SHOULD_EXIST(fieldName) \ #define SHOULD_EXIST(fieldName) \
if (serverObject[(fieldName)].isUndefined()) { \ if (serverObject[(fieldName)].isUndefined()) \
logList << QObject::tr("skipping invalid ssd server: missing required field %1").arg(fieldName);\ { \
continue;\ logList << QObject::tr("skipping invalid ssd server: missing required field %1").arg(fieldName); \
continue; \
} }
#define SHOULD_STRING(fieldName) SHOULD_EXIST(fieldName); \ #define SHOULD_STRING(fieldName) \
if (!serverObject[(fieldName)].isString()) { \ SHOULD_EXIST(fieldName); \
logList << QObject::tr("skipping invalid ssd server: field %1 should be of type 'string'").arg(fieldName);\ if (!serverObject[(fieldName)].isString()) \
continue; \ { \
logList << QObject::tr("skipping invalid ssd server: field %1 should be of type 'string'").arg(fieldName); \
continue; \
} }
std::pair<std::optional<std::pair<QString, QList<std::pair<QString, ShadowSocksServerObject>>>>, QStringList> Qv2ray::core::handlers::ssd::
std::pair <std::optional<std::pair<QString, QList<std::pair<QString, ShadowSocksServerObject>>>>, QStringList> decodeSSD(const QString &uri, const QString &pattern)
Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &pattern)
{ {
// The list for the parsing log. // The list for the parsing log.
QStringList logList; QStringList logList;
// ssd links should begin with "ssd://" // ssd links should begin with "ssd://"
if (!uri.startsWith("ssd://")) { if (!uri.startsWith("ssd://"))
{
logList << QObject::tr("invalid ssd link: should begin with ssd://"); logList << QObject::tr("invalid ssd link: should begin with ssd://");
return std::make_pair(std::nullopt, logList); return std::make_pair(std::nullopt, logList);
} }
@ -92,7 +103,8 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
const auto ssdURIBody = QStringRef(&uri, 5, uri.length() - 6); const auto ssdURIBody = QStringRef(&uri, 5, uri.length() - 6);
const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8()); const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8());
if (decodedJSON.length() == 0) { if (decodedJSON.length() == 0)
{
logList << QObject::tr("invalid ssd link: base64 parse failed"); logList << QObject::tr("invalid ssd link: base64 parse failed");
return std::make_pair(std::nullopt, logList); return std::make_pair(std::nullopt, logList);
} }
@ -101,13 +113,15 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
QJsonParseError err; QJsonParseError err;
QJsonDocument document = QJsonDocument::fromJson(decodedJSON, &err); QJsonDocument document = QJsonDocument::fromJson(decodedJSON, &err);
if (document.isNull()) { if (document.isNull())
{
logList << QObject::tr("invalid ssd link: json parse failed: ") % err.errorString(); logList << QObject::tr("invalid ssd link: json parse failed: ") % err.errorString();
return std::make_pair(std::nullopt, logList); return std::make_pair(std::nullopt, logList);
} }
// json should be an object // json should be an object
if (!document.isObject()) { if (!document.isObject())
{
logList << QObject::tr("invalid ssd link: found non-object json, aborting"); logList << QObject::tr("invalid ssd link: found non-object json, aborting");
return std::make_pair(std::nullopt, logList); return std::make_pair(std::nullopt, logList);
} }
@ -127,7 +141,8 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
// check: rc4-md5 is not supported by v2ray-core // check: rc4-md5 is not supported by v2ray-core
// TODO: more checks, including all algorithms // TODO: more checks, including all algorithms
if (encryption == "rc4-md5") { if (encryption == "rc4-md5")
{
logList << QObject::tr("invalid ssd link: rc4-md5 encryption is not supported by v2ray-core"); logList << QObject::tr("invalid ssd link: rc4-md5 encryption is not supported by v2ray-core");
return std::make_pair(std::nullopt, logList); return std::make_pair(std::nullopt, logList);
} }
@ -142,7 +157,8 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
// //
// iterate through the servers // iterate through the servers
for (QJsonValueRef server : obj["servers"].toArray()) { for (QJsonValueRef server : obj["servers"].toArray())
{
SERVER_SHOULD_BE_OBJECT(server); SERVER_SHOULD_BE_OBJECT(server);
QJsonObject serverObject = server.toObject(); QJsonObject serverObject = server.toObject();
ShadowSocksServerObject ssObject; ShadowSocksServerObject ssObject;
@ -158,11 +174,13 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
// port selection: // port selection:
// normal: use global settings // normal: use global settings
// overriding: use current config // overriding: use current config
if (serverObject["port"].isUndefined()) { if (serverObject["port"].isUndefined()) { ssObject.port = port; }
ssObject.port = port; else if (auto currPort = serverObject["port"].toInt(-1); port >= 0 && port <= 65535)
} else if (auto currPort = serverObject["port"].toInt(-1); port >= 0 && port <= 65535) { {
ssObject.port = currPort; ssObject.port = currPort;
} else { }
else
{
logList << QObject::tr("warning: invalid port encountered. using fallback value."); logList << QObject::tr("warning: invalid port encountered. using fallback value.");
ssObject.port = port; ssObject.port = port;
} }
@ -172,11 +190,13 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
// entitled: using given name // entitled: using given name
QString nodeName; QString nodeName;
if (serverObject["remarks"].isUndefined()) { if (serverObject["remarks"].isUndefined()) { nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port); }
nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port); else if (serverObject["remarks"].isString())
} else if (serverObject["remarks"].isString()) { {
nodeName = serverObject["remarks"].toString(); nodeName = serverObject["remarks"].toString();
} else { }
else
{
logList << QObject::tr("warning: invalid name encountered. using fallback value."); logList << QObject::tr("warning: invalid name encountered. using fallback value.");
nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port); nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port);
} }
@ -186,9 +206,9 @@ Qv2ray::core::handlers::ssd::decodeSSD(const QString &uri, const QString &patter
// specified: use given value // specified: use given value
double ratio = 1.0; double ratio = 1.0;
if (auto currRatio = serverObject["ratio"].toDouble(-1.0); currRatio != -1.0) { if (auto currRatio = serverObject["ratio"].toDouble(-1.0); currRatio != -1.0) { ratio = currRatio; }
ratio = currRatio; else if (!serverObject["ratio"].isUndefined())
} else if (!serverObject["ratio"].isUndefined()) { {
logList << QObject::tr("warning: invalid ratio encountered. using fallback value."); logList << QObject::tr("warning: invalid ratio encountered. using fallback value.");
} }

View File

@ -1,4 +1,5 @@
#include "Serialization.hpp" #include "Serialization.hpp"
#include "Generation.hpp" #include "Generation.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp" #include "core/CoreUtils.hpp"
@ -12,11 +13,13 @@ namespace Qv2ray::core::connection
{ {
CONFIGROOT config; CONFIGROOT config;
if (link.startsWith("vmess://")) { if (link.startsWith("vmess://")) { config = ConvertConfigFromVMessString(link, alias, errMessage); }
config = ConvertConfigFromVMessString(link, alias, errMessage); else if (link.startsWith("ss://"))
} else if (link.startsWith("ss://")) { {
config = ConvertConfigFromSSString(link, alias, errMessage); config = ConvertConfigFromSSString(link, alias, errMessage);
} else { }
else
{
*errMessage = QObject::tr("Unsupported share link format."); *errMessage = QObject::tr("Unsupported share link format.");
} }
@ -31,21 +34,29 @@ namespace Qv2ray::core::connection
auto type = outbound["protocol"].toString(); auto type = outbound["protocol"].toString();
QString sharelink = ""; QString sharelink = "";
if (type == "vmess") { if (type == "vmess")
auto vmessServer = StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject())); {
auto vmessServer =
StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject())); auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
sharelink = ConvertConfigToVMessString(transport, vmessServer, alias); sharelink = ConvertConfigToVMessString(transport, vmessServer, alias);
} else if (type == "shadowsocks") { }
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject())); else if (type == "shadowsocks")
{
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(
JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject()));
sharelink = ConvertConfigToSSString(ssServer, alias, isSip002); sharelink = ConvertConfigToSSString(ssServer, alias, isSip002);
} else { }
else
{
LOG(MODULE_CONNECTION, "Unsupported outbound type: " + type) LOG(MODULE_CONNECTION, "Unsupported outbound type: " + type)
} }
return sharelink; return sharelink;
} }
// From https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) // From
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &serverConfig, const QString &alias) QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &serverConfig, const QString &alias)
{ {
QJsonObject vmessUriRoot; QJsonObject vmessUriRoot;
@ -59,15 +70,19 @@ namespace Qv2ray::core::connection
vmessUriRoot["net"] = transfer.network; vmessUriRoot["net"] = transfer.network;
vmessUriRoot["tls"] = transfer.security; vmessUriRoot["tls"] = transfer.security;
if (transfer.network == "tcp") { if (transfer.network == "tcp") { vmessUriRoot["type"] = transfer.tcpSettings.header.type; }
vmessUriRoot["type"] = transfer.tcpSettings.header.type; else if (transfer.network == "kcp")
} else if (transfer.network == "kcp") { {
vmessUriRoot["type"] = transfer.kcpSettings.header.type; vmessUriRoot["type"] = transfer.kcpSettings.header.type;
} else if (transfer.network == "quic") { }
else if (transfer.network == "quic")
{
vmessUriRoot["type"] = transfer.quicSettings.header.type; vmessUriRoot["type"] = transfer.quicSettings.header.type;
vmessUriRoot["host"] = transfer.quicSettings.security; vmessUriRoot["host"] = transfer.quicSettings.security;
vmessUriRoot["path"] = transfer.quicSettings.key; vmessUriRoot["path"] = transfer.quicSettings.key;
} else if (transfer.network == "ws") { }
else if (transfer.network == "ws")
{
auto x = transfer.wsSettings.headers; auto x = transfer.wsSettings.headers;
auto host = x.contains("host"); auto host = x.contains("host");
auto CapHost = x.contains("Host"); auto CapHost = x.contains("Host");
@ -75,7 +90,9 @@ namespace Qv2ray::core::connection
// //
vmessUriRoot["host"] = realHost; vmessUriRoot["host"] = realHost;
vmessUriRoot["path"] = transfer.wsSettings.path; vmessUriRoot["path"] = transfer.wsSettings.path;
} else if (transfer.network == "h2" || transfer.network == "http") { }
else if (transfer.network == "h2" || transfer.network == "http")
{
vmessUriRoot["host"] = transfer.httpSettings.host.join(","); vmessUriRoot["host"] = transfer.httpSettings.host.join(",");
vmessUriRoot["path"] = transfer.httpSettings.path; vmessUriRoot["path"] = transfer.httpSettings.path;
} }
@ -89,8 +106,8 @@ namespace Qv2ray::core::connection
{ {
// String may start with: vmess:// and ss:// // String may start with: vmess:// and ss://
// We only process vmess:// here // We only process vmess:// here
// Some subscription providers may use plain vmess:// saperated by lines // Some subscription providers may use plain vmess:// saperated by
// But others may use base64 of above. // lines But others may use base64 of above.
auto result = QString::fromUtf8(arr).trimmed(); auto result = QString::fromUtf8(arr).trimmed();
return result.startsWith("vmess://") ? result : Base64Decode(result); return result.startsWith("vmess://") ? result : Base64Decode(result);
} }
@ -100,8 +117,9 @@ namespace Qv2ray::core::connection
ShadowSocksServerObject server; ShadowSocksServerObject server;
QString d_name; QString d_name;
//auto ssUri = _ssUri.toStdString(); // auto ssUri = _ssUri.toStdString();
if (ssUri.length() < 5) { if (ssUri.length() < 5)
{
LOG(MODULE_CONNECTION, "ss:// string too short") LOG(MODULE_CONNECTION, "ss:// string too short")
*errMessage = QObject::tr("SS URI is too short"); *errMessage = QObject::tr("SS URI is too short");
} }
@ -110,53 +128,51 @@ namespace Qv2ray::core::connection
auto hashPos = uri.lastIndexOf("#"); auto hashPos = uri.lastIndexOf("#");
DEBUG(MODULE_CONNECTION, "Hash sign position: " + QSTRN(hashPos)) DEBUG(MODULE_CONNECTION, "Hash sign position: " + QSTRN(hashPos))
if (hashPos >= 0) { if (hashPos >= 0)
{
// Get the name/remark // Get the name/remark
d_name = uri.mid(uri.lastIndexOf("#") + 1); d_name = uri.mid(uri.lastIndexOf("#") + 1);
uri.truncate(hashPos); uri.truncate(hashPos);
} }
// No plugins for Qv2ray so disable those lnes.i // No plugins for Qv2ray so disable those lnes.i
//size_t pluginPos = uri.find_first_of('/'); // size_t pluginPos = uri.find_first_of('/');
// //
//if (pluginPos != std::string::npos) { // if (pluginPos != std::string::npos) {
// // TODO: support plugins. For now, just ignore them // // TODO: support plugins. For now, just ignore them
// uri.erase(pluginPos); // uri.erase(pluginPos);
//} //}
auto atPos = uri.indexOf('@'); auto atPos = uri.indexOf('@');
DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos)) DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos))
if (atPos < 0) { if (atPos < 0)
{
// Old URI scheme // Old URI scheme
QString decoded = QByteArray::fromBase64(uri.toUtf8(), QByteArray::Base64Option::OmitTrailingEquals); QString decoded = QByteArray::fromBase64(uri.toUtf8(), QByteArray::Base64Option::OmitTrailingEquals);
auto colonPos = decoded.indexOf(':'); auto colonPos = decoded.indexOf(':');
DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos)) DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos))
if (colonPos < 0) { if (colonPos < 0) { *errMessage = QObject::tr("Can't find the colon separator between method and password"); }
*errMessage = QObject::tr("Can't find the colon separator between method and password");
}
server.method = decoded.left(colonPos); server.method = decoded.left(colonPos);
decoded.remove(0, colonPos + 1); decoded.remove(0, colonPos + 1);
atPos = decoded.lastIndexOf('@'); atPos = decoded.lastIndexOf('@');
DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos)) DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos))
if (atPos < 0) { if (atPos < 0) { *errMessage = QObject::tr("Can't find the at separator between password and hostname"); }
*errMessage = QObject::tr("Can't find the at separator between password and hostname");
}
server.password = decoded.mid(0, atPos); server.password = decoded.mid(0, atPos);
decoded.remove(0, atPos + 1); decoded.remove(0, atPos + 1);
colonPos = decoded.lastIndexOf(':'); colonPos = decoded.lastIndexOf(':');
DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos)) DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos))
if (colonPos < 0) { if (colonPos < 0) { *errMessage = QObject::tr("Can't find the colon separator between hostname and port"); }
*errMessage = QObject::tr("Can't find the colon separator between hostname and port");
}
server.address = decoded.mid(0, colonPos); server.address = decoded.mid(0, colonPos);
server.port = decoded.mid(colonPos + 1).toInt(); server.port = decoded.mid(colonPos + 1).toInt();
} else { }
else
{
// SIP002 URI scheme // SIP002 URI scheme
auto x = QUrl::fromUserInput(uri); auto x = QUrl::fromUserInput(uri);
server.address = x.host(); server.address = x.host();
@ -166,9 +182,7 @@ namespace Qv2ray::core::connection
// //
DEBUG(MODULE_CONNECTION, "Userinfo splitter position: " + QSTRN(userInfoSp)) DEBUG(MODULE_CONNECTION, "Userinfo splitter position: " + QSTRN(userInfoSp))
if (userInfoSp < 0) { if (userInfoSp < 0) { *errMessage = QObject::tr("Can't find the colon separator between method and password"); }
*errMessage = QObject::tr("Can't find the colon separator between method and password");
}
QString method = userInfo.mid(0, userInfoSp); QString method = userInfo.mid(0, userInfoSp);
server.method = method; server.method = method;
@ -178,7 +192,8 @@ namespace Qv2ray::core::connection
d_name = QUrl::fromPercentEncoding(d_name.toUtf8()); d_name = QUrl::fromPercentEncoding(d_name.toUtf8());
CONFIGROOT root; CONFIGROOT root;
OUTBOUNDS outbounds; OUTBOUNDS outbounds;
outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>() << server), QJsonObject())); outbounds.append(
GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>() << server), QJsonObject()));
JADD(outbounds) JADD(outbounds)
*alias = alias->isEmpty() ? d_name : *alias + "_" + d_name; *alias = alias->isEmpty() ? d_name : *alias + "_" + d_name;
LOG(MODULE_CONNECTION, "Deduced alias: " + *alias) LOG(MODULE_CONNECTION, "Deduced alias: " + *alias)
@ -189,12 +204,15 @@ namespace Qv2ray::core::connection
{ {
auto myAlias = QUrl::toPercentEncoding(alias); auto myAlias = QUrl::toPercentEncoding(alias);
if (isSip002) { if (isSip002)
{
LOG(MODULE_CONNECTION, "Converting an ss-server config to Sip002 ss:// format") LOG(MODULE_CONNECTION, "Converting an ss-server config to Sip002 ss:// format")
QString plainUserInfo = server.method + ":" + server.password; QString plainUserInfo = server.method + ":" + server.password;
QString userinfo(plainUserInfo.toUtf8().toBase64(QByteArray::Base64Option::Base64UrlEncoding).data()); QString userinfo(plainUserInfo.toUtf8().toBase64(QByteArray::Base64Option::Base64UrlEncoding).data());
return "ss://" + userinfo + "@" + server.address + ":" + QSTRN(server.port) + "#" + myAlias; return "ss://" + userinfo + "@" + server.address + ":" + QSTRN(server.port) + "#" + myAlias;
} else { }
else
{
LOG(MODULE_CONNECTION, "Converting an ss-server config to old ss:// string format") LOG(MODULE_CONNECTION, "Converting an ss-server config to old ss:// string format")
QString ssUri = server.method + ":" + server.password + "@" + server.address + ":" + QSTRN(server.port); QString ssUri = server.method + ":" + server.password + "@" + server.address + ":" + QSTRN(server.port);
return "ss://" + ssUri.toUtf8().toBase64(QByteArray::Base64Option::OmitTrailingEquals) + "#" + myAlias; return "ss://" + ssUri.toUtf8().toBase64(QByteArray::Base64Option::OmitTrailingEquals) + "#" + myAlias;
@ -209,7 +227,8 @@ namespace Qv2ray::core::connection
LOG(MODULE_SETTINGS, "Trying to convert from a vmess string.") LOG(MODULE_SETTINGS, "Trying to convert from a vmess string.")
QString vmess = vmessStr; QString vmess = vmessStr;
if (vmess.trimmed() != vmess) { if (vmess.trimmed() != vmess)
{
LOG(MODULE_SETTINGS, "VMess string has some prefix/postfix spaces, trimming.") LOG(MODULE_SETTINGS, "VMess string has some prefix/postfix spaces, trimming.")
vmess = vmessStr.trimmed(); vmess = vmessStr.trimmed();
} }
@ -217,16 +236,19 @@ namespace Qv2ray::core::connection
// Reset errMessage // Reset errMessage
*errMessage = ""; *errMessage = "";
if (!vmess.toLower().startsWith("vmess://")) { if (!vmess.toLower().startsWith("vmess://"))
{
*errMessage = QObject::tr("VMess string should start with 'vmess://'"); *errMessage = QObject::tr("VMess string should start with 'vmess://'");
return default; return default;
} }
try { try
{
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8); QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
auto b64Str = vmessJsonB64.toString(); auto b64Str = vmessJsonB64.toString();
if (b64Str.isEmpty()) { if (b64Str.isEmpty())
{
*errMessage = QObject::tr("VMess string should be a valid base64 string"); *errMessage = QObject::tr("VMess string should be a valid base64 string");
return default; return default;
} }
@ -234,14 +256,16 @@ namespace Qv2ray::core::connection
auto vmessString = Base64Decode(b64Str); auto vmessString = Base64Decode(b64Str);
auto jsonErr = VerifyJsonString(vmessString); auto jsonErr = VerifyJsonString(vmessString);
if (!jsonErr.isEmpty()) { if (!jsonErr.isEmpty())
{
*errMessage = jsonErr; *errMessage = jsonErr;
return default; return default;
} }
auto vmessConf = JsonFromString(vmessString); auto vmessConf = JsonFromString(vmessString);
if (vmessConf.isEmpty()) { if (vmessConf.isEmpty())
{
*errMessage = QObject::tr("JSON should not be empty"); *errMessage = QObject::tr("JSON should not be empty");
return default; return default;
} }
@ -249,21 +273,23 @@ namespace Qv2ray::core::connection
bool flag = true; bool flag = true;
// C is a quick hack... // C is a quick hack...
#define C(k) vmessConf.contains(k) #define C(k) vmessConf.contains(k)
// id, aid, port and add are mandatory fields of a vmess:// link. // id, aid, port and add are mandatory fields of a vmess://
// link.
flag = flag && C("id") && C("aid") && C("port") && C("add"); flag = flag && C("id") && C("aid") && C("port") && C("add");
// Stream Settings // Stream Settings
auto net = C("net") ? vmessConf["net"].toString() : "tcp"; auto net = C("net") ? vmessConf["net"].toString() : "tcp";
if (net == "http" || net == "ws") if (net == "http" || net == "ws") flag = flag && C("host") && C("path");
flag = flag && C("host") && C("path");
else if (net == "domainsocket") else if (net == "domainsocket")
flag = flag && C("path"); flag = flag && C("path");
else if (net == "quic") else if (net == "quic")
flag = flag && C("host") && C("type") && C("path"); flag = flag && C("host") && C("type") && C("path");
#undef C #undef C
//return flag ? 0 : 1; // return flag ? 0 : 1;
} catch (exception *e) { }
catch (exception *e)
{
*errMessage = e->what(); *errMessage = e->what();
LOG(MODULE_IMPORT, "Failed to decode vmess string: " + *errMessage) LOG(MODULE_IMPORT, "Failed to decode vmess string: " + *errMessage)
delete e; delete e;
@ -279,44 +305,62 @@ namespace Qv2ray::core::connection
int port, aid; int port, aid;
// //
// key = key in JSON and the variable name. // key = key in JSON and the variable name.
// values = Candidate variable list, if not match, the first one is used as default. // values = Candidate variable list, if not match, the first one is
// [[val.size() <= 1]] is used when only the default value exists. // used as default.
// - It can be empty, if so, if the key is not in the JSON, or the value is empty, it'll report an error. // [[val.size() <= 1]] is used when only the default value
// - Else if it contains one thing. if the key is not in the JSON, or the value is empty, it'll use that one. // exists.
// - Else if it contains many things, when the key IS in the JSON but not in those THINGS, it'll use the first one in the THINGS // - It can be empty, if so, if the key is not in
// the JSON, or the value is empty, it'll report an error.
// - Else if it contains one thing. if the key is not in
// the JSON, or the value is empty, it'll use that one.
// - Else if it contains many things, when the key IS in
// the JSON but not in those THINGS, it'll use the first
// one in the THINGS
// - Else, it'll use the value found from the JSON object. // - Else, it'll use the value found from the JSON object.
// //
#define empty_arg #define empty_arg
#define __vmess_checker__func(key, values) \ #define __vmess_checker__func(key, values) \
{\ { \
auto val = QStringList() values;\ auto val = QStringList() values; \
if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() \ if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() && \
&& (val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) {\ (val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) \
key = vmessConf[#key].toVariant().toString();\ { \
DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.")\ key = vmessConf[#key].toVariant().toString(); \
} else if (!val.isEmpty()) {\ DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.") \
key = val.first(); \ } \
DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key)\ else if (!val.isEmpty()) \
} else{\ { \
*errMessage = QObject::tr(#key " does not exist."); \ key = val.first(); \
LOG(MODULE_IMPORT, "Cannot process \"" #key "\" since it's not included in the json object." ) \ DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key) \
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \ } \
LOG(MODULE_IMPORT, " --> PS: " + ps) \ else \
}\ { \
*errMessage = QObject::tr(#key " does not exist."); \
LOG(MODULE_IMPORT, "Cannot process \"" #key "\" since it's not included in the json object.") \
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \
LOG(MODULE_IMPORT, " --> PS: " + ps) \
} \
} }
// Strict check of VMess protocol, to check if the specified value is in the correct range. // Strict check of VMess protocol, to check if the specified value
// is in the correct range.
// //
// Get Alias (AKA ps) from address and port. // Get Alias (AKA ps) from address and port.
__vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString()); __vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString());
__vmess_checker__func(add, empty_arg) __vmess_checker__func(add, empty_arg) __vmess_checker__func(id, empty_arg) __vmess_checker__func(net, << "tcp"
__vmess_checker__func(id, empty_arg) << "http"
__vmess_checker__func(net, << "tcp" << "http" << "h2" << "ws" << "kcp" << "domainsocket" << "quic") << "h2"
__vmess_checker__func(type, << "none" << "http" << "srtp" << "utp" << "wechat-video") << "ws"
__vmess_checker__func(path, << "") << "kcp"
__vmess_checker__func(host, << "") << "domainsocket"
__vmess_checker__func(tls, << "") << "quic")
// __vmess_checker__func(type, << "none"
port = vmessConf["port"].toVariant().toInt(); << "http"
<< "srtp"
<< "utp"
<< "wechat-video") __vmess_checker__func(path, << "") __vmess_checker__func(host, << "")
__vmess_checker__func(tls, << "")
//
port = vmessConf["port"].toVariant().toInt();
aid = vmessConf["aid"].toVariant().toInt(); aid = vmessConf["aid"].toVariant().toInt();
// Apply the settings. // Apply the settings.
// //
@ -340,23 +384,29 @@ namespace Qv2ray::core::connection
// Stream Settings // Stream Settings
StreamSettingsObject streaming; StreamSettingsObject streaming;
if (net == "tcp") { if (net == "tcp") { streaming.tcpSettings.header.type = type; }
streaming.tcpSettings.header.type = type; else if (net == "http" || net == "h2")
} else if (net == "http" || net == "h2") { {
// Fill hosts for HTTP // Fill hosts for HTTP
for (auto _host : host.split(',')) { for (auto _host : host.split(',')) { streaming.httpSettings.host.push_back(_host.trimmed()); }
streaming.httpSettings.host.push_back(_host.trimmed());
}
streaming.httpSettings.path = path; streaming.httpSettings.path = path;
} else if (net == "ws") { }
else if (net == "ws")
{
streaming.wsSettings.headers["Host"] = host; streaming.wsSettings.headers["Host"] = host;
streaming.wsSettings.path = path; streaming.wsSettings.path = path;
} else if (net == "kcp") { }
else if (net == "kcp")
{
streaming.kcpSettings.header.type = type; streaming.kcpSettings.header.type = type;
} else if (net == "domainsocket") { }
else if (net == "domainsocket")
{
streaming.dsSettings.path = path; streaming.dsSettings.path = path;
} else if (net == "quic") { }
else if (net == "quic")
{
streaming.quicSettings.security = host; streaming.quicSettings.security = host;
streaming.quicSettings.header.type = type; streaming.quicSettings.header.type = type;
streaming.quicSettings.key = path; streaming.quicSettings.key = path;
@ -364,7 +414,8 @@ namespace Qv2ray::core::connection
// FIXME: makeshift patch for #290. // FIXME: makeshift patch for #290.
// to be rewritten after refactoring. // to be rewritten after refactoring.
if (tls == "tls" && host != "" && (net == "tcp" || net == "ws")) { if (tls == "tls" && host != "" && (net == "tcp" || net == "ws"))
{
streaming.tlsSettings.serverName = host; streaming.tlsSettings.serverName = host;
streaming.tlsSettings.allowInsecure = false; streaming.tlsSettings.allowInsecure = false;
} }
@ -378,10 +429,11 @@ namespace Qv2ray::core::connection
auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_PROXY); auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_PROXY);
// //
root["outbounds"] = QJsonArray() << outbound; root["outbounds"] = QJsonArray() << outbound;
// If previous alias is empty, just the PS is needed, else, append a "_" // If previous alias is empty, just the PS is needed, else, append a
// "_"
*alias = alias->trimmed().isEmpty() ? ps : *alias + "_" + ps; *alias = alias->trimmed().isEmpty() ? ps : *alias + "_" + ps;
#undef default #undef default
return root; return root;
} }
} } // namespace Serialization
} } // namespace Qv2ray::core::connection

View File

@ -5,7 +5,7 @@ namespace Qv2ray::core::connection
{ {
namespace Serialization namespace Serialization
{ {
//int VerifyVMessProtocolString(QString vmess); // int VerifyVMessProtocolString(QString vmess);
QString DecodeSubscriptionString(QByteArray arr); QString DecodeSubscriptionString(QByteArray arr);
// General // General
@ -19,8 +19,8 @@ namespace Qv2ray::core::connection
// SS URI Protocol // SS URI Protocol
CONFIGROOT ConvertConfigFromSSString(const QString &ss, QString *alias, QString *errMessage); CONFIGROOT ConvertConfigFromSSString(const QString &ss, QString *alias, QString *errMessage);
QString ConvertConfigToSSString(const ShadowSocksServerObject &server, const QString &alias, bool isSip002); QString ConvertConfigToSSString(const ShadowSocksServerObject &server, const QString &alias, bool isSip002);
} } // namespace Serialization
} } // namespace Qv2ray::core::connection
using namespace Qv2ray::core; using namespace Qv2ray::core;
using namespace Qv2ray::core::connection; using namespace Qv2ray::core::connection;

View File

@ -1,6 +1,8 @@
#include "ConnectionHandler.hpp" #include "ConnectionHandler.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/config/ConfigBackend.hpp" #include "core/config/ConfigBackend.hpp"
#include "core/connection/Serialization.hpp"
namespace Qv2ray::core::handlers namespace Qv2ray::core::handlers
{ {
@ -10,26 +12,23 @@ namespace Qv2ray::core::handlers
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.") DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
// Do we need to check how many of them are loaded? // Do we need to check how many of them are loaded?
for (auto i = 0; i < GlobalConfig.connections.count(); i++) { for (auto i = 0; i < GlobalConfig.connections.count(); i++)
connections[ConnectionId(GlobalConfig.connections.keys()[i])] = GlobalConfig.connections.values()[i]; { connections[ConnectionId(GlobalConfig.connections.keys()[i])] = GlobalConfig.connections.values()[i]; }
}
for (auto key : GlobalConfig.subscriptions.keys()) { for (auto key : GlobalConfig.subscriptions.keys())
{
auto val = GlobalConfig.subscriptions[key]; auto val = GlobalConfig.subscriptions[key];
groups[GroupId(key)] = val; groups[GroupId(key)] = val;
for (auto conn : val.connections) { for (auto conn : val.connections) { connections[ConnectionId(conn)].groupId = GroupId(key); }
connections[ConnectionId(conn)].groupId = GroupId(key);
}
} }
for (auto key : GlobalConfig.groups.keys()) { for (auto key : GlobalConfig.groups.keys())
{
auto val = GlobalConfig.groups[key]; auto val = GlobalConfig.groups[key];
groups[GroupId(key)] = val; groups[GroupId(key)] = val;
for (auto conn : val.connections) { for (auto conn : val.connections) { connections[ConnectionId(conn)].groupId = GroupId(key); }
connections[ConnectionId(conn)].groupId = GroupId(key);
}
} }
vCoreInstance = new V2rayKernelInstance(); vCoreInstance = new V2rayKernelInstance();
@ -47,7 +46,7 @@ namespace Qv2ray::core::handlers
pingConnectionTimerId = startTimer(60 * 1000); pingConnectionTimerId = startTimer(60 * 1000);
} }
void QvConnectionHandler::CHSaveConnectionData_p() void QvConnectionHandler::CHSaveConfigData_p()
{ {
// Copy // Copy
auto newGlobalConfig = GlobalConfig; auto newGlobalConfig = GlobalConfig;
@ -55,18 +54,21 @@ namespace Qv2ray::core::handlers
newGlobalConfig.groups.clear(); newGlobalConfig.groups.clear();
newGlobalConfig.subscriptions.clear(); newGlobalConfig.subscriptions.clear();
for (auto i = 0; i < connections.count(); i++) { for (auto i = 0; i < connections.count(); i++)
newGlobalConfig.connections[connections.keys()[i].toString()] = connections.values()[i]; { newGlobalConfig.connections[connections.keys()[i].toString()] = connections.values()[i]; }
}
for (auto i = 0; i < groups.count(); i++) { for (auto i = 0; i < groups.count(); i++)
{
QStringList connections = IdListToStrings(groups.values()[i].connections); QStringList connections = IdListToStrings(groups.values()[i].connections);
if (groups.values()[i].isSubscription) { if (groups.values()[i].isSubscription)
{
SubscriptionObject_Config o = groups.values()[i]; SubscriptionObject_Config o = groups.values()[i];
o.connections = connections; o.connections = connections;
newGlobalConfig.subscriptions[groups.keys()[i].toString()] = o; newGlobalConfig.subscriptions[groups.keys()[i].toString()] = o;
} else { }
else
{
GroupObject_Config o = groups.values()[i]; GroupObject_Config o = groups.values()[i];
o.connections = connections; o.connections = connections;
newGlobalConfig.groups[groups.keys()[i].toString()] = o; newGlobalConfig.groups[groups.keys()[i].toString()] = o;
@ -78,29 +80,25 @@ namespace Qv2ray::core::handlers
void QvConnectionHandler::timerEvent(QTimerEvent *event) void QvConnectionHandler::timerEvent(QTimerEvent *event)
{ {
if (event->timerId() == saveTimerId) { if (event->timerId() == saveTimerId) { CHSaveConfigData_p(); }
CHSaveConnectionData_p(); else if (event->timerId() == pingAllTimerId)
} else if (event->timerId() == pingAllTimerId) { {
StartLatencyTest(); StartLatencyTest();
} else if (event->timerId() == pingConnectionTimerId) { }
if (currentConnectionId != NullConnectionId) { else if (event->timerId() == pingConnectionTimerId)
StartLatencyTest(currentConnectionId); {
} if (currentConnectionId != NullConnectionId) { StartLatencyTest(currentConnectionId); }
} }
} }
void QvConnectionHandler::StartLatencyTest() void QvConnectionHandler::StartLatencyTest()
{ {
for (auto connection : connections.keys()) { for (auto connection : connections.keys()) { StartLatencyTest(connection); }
StartLatencyTest(connection);
}
} }
void QvConnectionHandler::StartLatencyTest(const GroupId &id) void QvConnectionHandler::StartLatencyTest(const GroupId &id)
{ {
for (auto connection : groups[id].connections) { for (auto connection : groups[id].connections) { StartLatencyTest(connection); }
StartLatencyTest(connection);
}
} }
void QvConnectionHandler::StartLatencyTest(const ConnectionId &id) void QvConnectionHandler::StartLatencyTest(const ConnectionId &id)
@ -123,10 +121,9 @@ namespace Qv2ray::core::handlers
{ {
QList<GroupId> subsList; QList<GroupId> subsList;
for (auto group : groups.keys()) { for (auto group : groups.keys())
if (groups[group].isSubscription) { {
subsList.push_back(group); if (groups[group].isSubscription) { subsList.push_back(group); }
}
} }
return subsList; return subsList;
@ -149,10 +146,9 @@ namespace Qv2ray::core::handlers
const ConnectionId QvConnectionHandler::GetConnectionIdByDisplayName(const QString &displayName) const const ConnectionId QvConnectionHandler::GetConnectionIdByDisplayName(const QString &displayName) const
{ {
for (auto conn : connections.keys()) { for (auto conn : connections.keys())
if (connections[conn].displayName == displayName) { {
return conn; if (connections[conn].displayName == displayName) { return conn; }
}
} }
return NullConnectionId; return NullConnectionId;
@ -160,10 +156,9 @@ namespace Qv2ray::core::handlers
const GroupId QvConnectionHandler::GetGroupIdByDisplayName(const QString &displayName) const const GroupId QvConnectionHandler::GetGroupIdByDisplayName(const QString &displayName) const
{ {
for (auto group : groups.keys()) { for (auto group : groups.keys())
if (groups[group].displayName == displayName) { {
return group; if (groups[group].displayName == displayName) { return group; }
}
} }
return NullGroupId; return NullGroupId;
@ -171,48 +166,57 @@ namespace Qv2ray::core::handlers
const GroupId QvConnectionHandler::GetConnectionGroupId(const ConnectionId &id) const const GroupId QvConnectionHandler::GetConnectionGroupId(const ConnectionId &id) const
{ {
if (!connections.contains(id)) { if (!connections.contains(id)) { LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString()); }
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
}
return connections[id].groupId; return connections[id].groupId;
} }
double QvConnectionHandler::GetConnectionLatency(const ConnectionId &id) const double QvConnectionHandler::GetConnectionLatency(const ConnectionId &id) const
{ {
if (!connections.contains(id)) { if (!connections.contains(id)) { LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString()); }
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
}
return connections[id].latency; return connections[id].latency;
} }
const optional<QString> QvConnectionHandler::DeleteConnection(const ConnectionId &id) const optional<QString> QvConnectionHandler::DeleteConnection(const ConnectionId &id)
{ {
// TODO //
Q_UNUSED(id) auto groupId = connections[id].groupId;
return ""; QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
id.toString() + QV2RAY_CONFIG_FILE_EXTENSION);
//
bool exists = connectionFile.exists();
if (exists)
{
bool removed = connectionFile.remove();
if (removed)
{
connections.remove(id);
groups[groupId].connections.removeAll(id);
return {};
}
else
return "Failed to remove file";
}
else
return tr("File does not exist.");
} }
const optional<QString> QvConnectionHandler::StartConnection(const ConnectionId &identifier) const optional<QString> QvConnectionHandler::StartConnection(const ConnectionId &identifier)
{ {
if (!connections.contains(identifier)) { if (!connections.contains(identifier)) { return tr("No connection selected!") + NEWLINE + tr("Please select a config from the list."); }
return tr("No connection selected!") + NEWLINE + tr("Please select a config from the list.");
}
if (currentConnectionId != NullConnectionId) { if (currentConnectionId != NullConnectionId) { StopConnection(); }
StopConnection();
}
CONFIGROOT root = GetConnectionRoot(connections[identifier].groupId, identifier); CONFIGROOT root = GetConnectionRoot(connections[identifier].groupId, identifier);
return CHStartConnection_p(identifier, root); return CHStartConnection_p(identifier, root);
} }
void QvConnectionHandler::StopConnection() //const ConnectionId &id void QvConnectionHandler::StopConnection() // const ConnectionId &id
{ {
// Currently just simply stop it. // Currently just simply stop it.
//_UNUSED(id) //_UNUSED(id)
//if (currentConnectionId == id) { // if (currentConnectionId == id) {
//} //}
CHStopConnection_p(); CHStopConnection_p();
} }
@ -226,9 +230,7 @@ namespace Qv2ray::core::handlers
{ {
QString result; QString result;
if (!connections.contains(id)) { if (!connections.contains(id)) { result = tr("N/A"); }
result = tr("N/A");
}
CONFIGROOT root = GetConnectionRoot(connections[id].groupId, id); CONFIGROOT root = GetConnectionRoot(connections[id].groupId, id);
QStringList protocols; QStringList protocols;
@ -236,12 +238,12 @@ namespace Qv2ray::core::handlers
auto outbound = root["outbounds"].toArray().first().toObject(); auto outbound = root["outbounds"].toArray().first().toObject();
result.append(outbound["protocol"].toString()); result.append(outbound["protocol"].toString());
if (outbound.contains("streamSettings")) { if (outbound.contains("streamSettings"))
result.append(" + " + outbound["streamSettings"].toObject()["network"].toString()); {
result.append(" / " + outbound["streamSettings"].toObject()["network"].toString());
if (outbound["streamSettings"].toObject().contains("tls")) { if (outbound["streamSettings"].toObject().contains("tls"))
result.append(outbound["streamSettings"].toObject()["tls"].toBool() ? " + tls" : ""); { result.append(outbound["streamSettings"].toObject()["tls"].toBool() ? " / tls" : ""); }
}
} }
return result; return result;
@ -249,23 +251,23 @@ namespace Qv2ray::core::handlers
const tuple<quint64, quint64> QvConnectionHandler::GetConnectionUsageAmount(const ConnectionId &id) const const tuple<quint64, quint64> QvConnectionHandler::GetConnectionUsageAmount(const ConnectionId &id) const
{ {
if (!connections.contains(id)) { if (!connections.contains(id)) { return make_tuple(0, 0); }
return make_tuple(0, 0);
}
return make_tuple(connections[id].upLinkData, connections[id].downLinkData); return make_tuple(connections[id].upLinkData, connections[id].downLinkData);
} }
//const GroupMetaObject QvConnectionHandler::GetGroup(const GroupId &id) const // const GroupMetaObject QvConnectionHandler::GetGroup(const GroupId &id)
// const
//{ //{
// return groups[id]; // return groups[id];
//} //}
QvConnectionHandler::~QvConnectionHandler() QvConnectionHandler::~QvConnectionHandler()
{ {
CHSaveConnectionData_p(); CHSaveConfigData_p();
if (vCoreInstance->KernelStarted) { if (vCoreInstance->KernelStarted)
{
vCoreInstance->StopConnection(); vCoreInstance->StopConnection();
LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.") LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.")
} }
@ -285,36 +287,49 @@ namespace Qv2ray::core::handlers
return CONFIGROOT(JsonFromString(StringFromFile(path))); return CONFIGROOT(JsonFromString(StringFromFile(path)));
} }
const tuple<QString, int> QvConnectionHandler::GetConnectionInfo(const ConnectionId &id) const const tuple<QString, QString, int> QvConnectionHandler::GetConnectionData(const ConnectionId &id) const
{ {
// TODO, what if is complex?
auto root = GetConnectionRoot(id); auto root = GetConnectionRoot(id);
bool validOutboundFound = false;
QString host;
int port;
for (auto item : root["outbounds"].toArray()) { bool isSucceed = false;
OUTBOUND outBoundRoot = OUTBOUND(item.toObject()); auto result = CHGetOutboundData_p(root, &isSucceed);
QString outboundType = ""; return result;
validOutboundFound = GetOutboundData(outBoundRoot, &host, &port, &outboundType);
if (validOutboundFound) {
return make_tuple(host, port);
} else {
LOG(MODULE_CORE_HANDLER, "Unknown outbound entry: " + outboundType + ", cannot deduce host and port.")
}
}
return make_tuple(QObject::tr("N/A"), 0);
} }
const tuple<QString, QString, int> QvConnectionHandler::CHGetOutboundData_p(const CONFIGROOT &root, bool *ok) const
{
*ok = false;
for (auto item : root["outbounds"].toArray())
{
OUTBOUND outBoundRoot = OUTBOUND(item.toObject());
QString host;
int port;
QString outboundType = "";
if (GetOutboundData(outBoundRoot, &host, &port, &outboundType))
{
*ok = true;
return make_tuple(outboundType, host, port);
}
else
{
LOG(MODULE_CORE_HANDLER, "Unknown outbound type: " + outboundType + ", cannot deduce host and port.")
}
}
return make_tuple(tr("N/A"), tr("N/A"), 0);
}
void QvConnectionHandler::OnLatencyDataArrived(const QvTCPingResultObject &result) void QvConnectionHandler::OnLatencyDataArrived(const QvTCPingResultObject &result)
{ {
if (connections.contains(result.connectionId)) { if (connections.contains(result.connectionId))
{
connections[result.connectionId].latency = result.avg; connections[result.connectionId].latency = result.avg;
emit OnLatencyTestFinished(result.connectionId, result.avg); emit OnLatencyTestFinished(result.connectionId, result.avg);
} else { }
else
{
LOG(MODULE_CORE_HANDLER, "Received a latecy result with non-exist connection id.") LOG(MODULE_CORE_HANDLER, "Received a latecy result with non-exist connection id.")
} }
} }
@ -322,8 +337,8 @@ namespace Qv2ray::core::handlers
bool QvConnectionHandler::UpdateConnection(const ConnectionId &id, const CONFIGROOT &root) bool QvConnectionHandler::UpdateConnection(const ConnectionId &id, const CONFIGROOT &root)
{ {
auto groupId = connections[id].groupId; auto groupId = connections[id].groupId;
auto path = (groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) auto path = (groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
+ groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; id.toString() + QV2RAY_CONFIG_FILE_EXTENSION;
auto content = JsonToString(root); auto content = JsonToString(root);
emit OnConnectionChanged(id); emit OnConnectionChanged(id);
return StringToFile(content, path); return StringToFile(content, path);
@ -333,10 +348,101 @@ namespace Qv2ray::core::handlers
{ {
tuple<QString, int64_t, float> result; tuple<QString, int64_t, float> result;
if (!groups[id].isSubscription) { if (!groups[id].isSubscription) { return result; }
return result;
}
return make_tuple(groups[id].address, groups[id].lastUpdated, groups[id].updateInterval); return make_tuple(groups[id].address, groups[id].lastUpdated, groups[id].updateInterval);
} }
}
bool QvConnectionHandler::UpdateSubscription(const GroupId &id, bool useSystemProxy)
{
if (isHttpRequestInProgress) { return false; }
isHttpRequestInProgress = true;
auto data = httpHelper->syncget(groups[id].address, useSystemProxy);
isHttpRequestInProgress = false;
return CHUpdateSubscription_p(id, data);
}
bool QvConnectionHandler::CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData)
{
if (!groups.contains(id)) { return false; }
bool isAutoConnectionContainedWithin = groups[id].connections.contains(ConnectionId(GlobalConfig.autoStartId));
Q_UNUSED(isAutoConnectionContainedWithin)
//
// Anyway, we try our best to preserve the connection id.
QMap<QString, ConnectionId> nameMap;
QMap<tuple<QString, QString, int>, ConnectionId> typeMap;
for (auto conn : groups[id].connections)
{
nameMap[GetDisplayName(conn)] = conn;
auto [protocol, host, port] = GetConnectionData(conn);
if (port != 0) { typeMap[make_tuple(protocol, host, port)] = conn; }
}
//
/// List that is holding connection IDs to be updated.
auto connectionsOrig = groups[id].connections;
auto str = DecodeSubscriptionString(subscriptionData);
if (str.isEmpty()) return false;
//
auto subsList = SplitLines(str);
QDir(QV2RAY_SUBSCRIPTION_DIR + id.toString()).removeRecursively();
QDir().mkpath(QV2RAY_SUBSCRIPTION_DIR + id.toString());
bool hasErrorOccured = false;
for (auto vmess : subsList)
{
QString errMessage;
QString _alias;
auto config = ConvertConfigFromString(vmess.trimmed(), &_alias, &errMessage);
if (!errMessage.isEmpty())
{
LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage)
hasErrorOccured = true;
continue;
}
bool canGetOutboundData = false;
auto outboundData = CHGetOutboundData_p(config, &canGetOutboundData);
//
// Begin guessing new ConnectionId
if (nameMap.contains(_alias))
{
// Just go and save the connection...
LOG(MODULE_CORE_HANDLER, "Guessed id from name: " + _alias + ", connectionId: " + nameMap[_alias].toString())
UpdateConnection(nameMap[_alias], config);
// Remove Connection Id from the list.
connectionsOrig.removeAll(nameMap[_alias]);
}
else if (canGetOutboundData && typeMap.contains(outboundData))
{
LOG(MODULE_CORE_HANDLER, "Guessed id from protocol/host/port pair for connectionId: " + typeMap[outboundData].toString())
UpdateConnection(typeMap[outboundData], config);
// Update displayName
connections[typeMap[outboundData]].displayName = _alias;
// Remove Connection Id from the list.
connectionsOrig.removeAll(typeMap[outboundData]);
}
else
{
// New connection id is required since nothing matched found...
ConnectionId newId(GenerateUuid());
connections[newId].groupId = id;
connections[newId].importDate = system_clock::to_time_t(system_clock::now());
connections[newId].displayName = _alias;
LOG(MODULE_CORE_HANDLER, "Generated new connectionId.")
UpdateConnection(newId, config);
}
// End guessing connectionId
}
// Check if anything left behind (not being updated or changed significantly)
LOG(MODULE_CORE_HANDLER, "Removed old connections not have been matched.")
for (auto conn : connectionsOrig)
{
LOG(MODULE_CORE_HANDLER, "Removing: " + conn.toString())
DeleteConnection(conn);
}
return hasErrorOccured;
}
} // namespace Qv2ray::core::handlers

View File

@ -1,134 +1,136 @@
#pragma once #pragma once
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "common/HTTPRequestHelper.hpp"
#include "core/CoreSafeTypes.hpp"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/kernel/KernelInteractions.hpp" #include "core/kernel/KernelInteractions.hpp"
#include "core/tcping/QvTCPing.hpp" #include "core/tcping/QvTCPing.hpp"
#include "core/CoreSafeTypes.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/CoreUtils.hpp"
#include "common/HTTPRequestHelper.hpp"
namespace Qv2ray::core::handlers namespace Qv2ray::core::handlers
{ {
class QvConnectionHandler : public QObject class QvConnectionHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvConnectionHandler(); explicit QvConnectionHandler();
~QvConnectionHandler(); ~QvConnectionHandler();
// //
const QList<ConnectionId> Connections() const; const QList<ConnectionId> Connections() const;
// //
const QList<GroupId> AllGroups() const; const QList<GroupId> AllGroups() const;
const QList<GroupId> Subscriptions() const; const QList<GroupId> Subscriptions() const;
const QList<ConnectionId> Connections(const GroupId &groupId) const; const QList<ConnectionId> Connections(const GroupId &groupId) const;
// //
// Generic Get Options // Generic Get Options
const QString GetDisplayName(const GroupId &id, int limit = -1) const; const QString GetDisplayName(const GroupId &id, int limit = -1) const;
const QString GetDisplayName(const ConnectionId &id, int limit = -1) const; const QString GetDisplayName(const ConnectionId &id, int limit = -1) const;
const GroupId GetGroupIdByDisplayName(const QString &displayName) const; const GroupId GetGroupIdByDisplayName(const QString &displayName) const;
const ConnectionId GetConnectionIdByDisplayName(const QString &displayName) const; const ConnectionId GetConnectionIdByDisplayName(const QString &displayName) const;
// //
// Connectivity Operationss // Connectivity Operationss
bool IsConnected(const ConnectionId &id) const; bool IsConnected(const ConnectionId &id) const;
const optional<QString> StartConnection(const ConnectionId &identifier); const optional<QString> StartConnection(const ConnectionId &identifier);
void StopConnection(); //const ConnectionId &id void StopConnection(); // const ConnectionId &id
// //
// Connection Operations. // Connection Operations.
const GroupId GetConnectionGroupId(const ConnectionId &id) const; const GroupId GetConnectionGroupId(const ConnectionId &id) const;
double GetConnectionLatency(const ConnectionId &id) const; double GetConnectionLatency(const ConnectionId &id) const;
const ConnectionId &CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root); const ConnectionId &CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root);
const optional<QString> DeleteConnection(const ConnectionId &id); const optional<QString> DeleteConnection(const ConnectionId &id);
bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root); bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root);
const optional<QString> RenameConnection(const ConnectionId &id, const QString &newName); const optional<QString> RenameConnection(const ConnectionId &id, const QString &newName);
const ConnectionId DuplicateConnection(const ConnectionId &id); const ConnectionId DuplicateConnection(const ConnectionId &id);
const optional<QString> MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); const optional<QString> MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId);
// //
const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const; const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const;
const CONFIGROOT GetConnectionRoot(const GroupId &group, const ConnectionId &id) const; const CONFIGROOT GetConnectionRoot(const GroupId &group, const ConnectionId &id) const;
// //
// Get Conncetion Property // Get Conncetion Property
const QString GetConnectionProtocolString(const ConnectionId &id) const; const QString GetConnectionProtocolString(const ConnectionId &id) const;
const tuple<QString, int> GetConnectionInfo(const ConnectionId &connectionId) const; const tuple<QString, QString, int> GetConnectionData(const ConnectionId &connectionId) const;
const tuple<quint64, quint64> GetConnectionUsageAmount(const ConnectionId &id) const; const tuple<quint64, quint64> GetConnectionUsageAmount(const ConnectionId &id) const;
// //
// Misc Connection Operations // Misc Connection Operations
void StartLatencyTest(); void StartLatencyTest();
void StartLatencyTest(const GroupId &id); void StartLatencyTest(const GroupId &id);
void StartLatencyTest(const ConnectionId &id); void StartLatencyTest(const ConnectionId &id);
// //
// Group Operations // Group Operations
const optional<QString> DeleteGroup(const GroupId &id); const optional<QString> DeleteGroup(const GroupId &id);
const optional<QString> DuplicateGroup(const GroupId &id); const optional<QString> DuplicateGroup(const GroupId &id);
const GroupId &CreateGroup(const QString displayName, bool isSubscription); const GroupId &CreateGroup(const QString displayName, bool isSubscription);
const optional<QString> RenameGroup(const GroupId &id, const QString &newName); const optional<QString> RenameGroup(const GroupId &id, const QString &newName);
// //
// Subscriptions // Subscriptions
const optional<QString> UpdateSubscription(const GroupId &id, bool useSystemProxy); bool UpdateSubscription(const GroupId &id, bool useSystemProxy);
const optional<QString> UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy); bool UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy);
const tuple<QString, int64_t, float> GetSubscriptionData(const GroupId &id); const tuple<QString, int64_t, float> GetSubscriptionData(const GroupId &id);
signals: signals:
void OnCrashed(); void OnCrashed();
void OnConnected(const ConnectionId &id); void OnConnected(const ConnectionId &id);
void OnDisConnected(const ConnectionId &id); void OnDisConnected(const ConnectionId &id);
void OnVCoreLogAvailable(const ConnectionId &id, const QString &log); void OnVCoreLogAvailable(const ConnectionId &id, const QString &log);
void OnStatsAvailable(const ConnectionId &id, void OnStatsAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed, const quint64 totalUpload,
const quint64 uploadSpeed, const quint64 downloadSpeed, const quint64 totalDownload);
const quint64 totalUpload, const quint64 totalDownload); //
// void OnConnectionCreated(const ConnectionId &id, const QString &displayName);
void OnConnectionCreated(const ConnectionId &id, const QString &displayName); void OnConnectionRenamed(const ConnectionId &id, const QString &originalName, const QString &newName);
void OnConnectionRenamed(const ConnectionId &id, const QString &originalName, const QString &newName); void OnConnectionChanged(const ConnectionId &id);
void OnConnectionChanged(const ConnectionId &id); void OnConnectionGroupChanged(const ConnectionId &id, const QString &originalGroup, const QString &newGroup);
void OnConnectionGroupChanged(const ConnectionId &id, const QString &originalGroup, const QString &newGroup); //
// void OnLatencyTestStarted(const ConnectionId &id);
void OnLatencyTestStarted(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average);
void OnLatencyTestFinished(const ConnectionId &id, const uint average); //
// void OnGroupCreated(const GroupId &id, const QString &displayName);
void OnGroupCreated(const GroupId &id, const QString &displayName); void OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName);
void OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName); void OnGroupDeleted(const GroupId &id, const QString &displayName);
void OnGroupDeleted(const GroupId &id, const QString &displayName); //
// // void OnSubscriptionCreated(const GroupId &id, const QString &displayName, const QString &address);
//void OnSubscriptionCreated(const GroupId &id, const QString &displayName, const QString &address); // void OnSubscriptionDeleted(const GroupId &id, const QString &oldName, const QString &newName);
//void OnSubscriptionDeleted(const GroupId &id, const QString &oldName, const QString &newName); void OnSubscriptionUpdateFinished(const GroupId &id);
void OnSubscriptionUpdateFinished(const GroupId &id);
private slots: private slots:
void OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); void OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
void OnVCoreCrashed(const ConnectionId &id); void OnVCoreCrashed(const ConnectionId &id);
void OnLatencyDataArrived(const QvTCPingResultObject &data); void OnLatencyDataArrived(const QvTCPingResultObject &data);
protected: protected:
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
private: private:
void CHSaveConnectionData_p(); void CHSaveConfigData_p();
// //
optional<QString> CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root); optional<QString> CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root);
void CHStopConnection_p(); void CHStopConnection_p();
bool CHSaveConnectionConfig_p(CONFIGROOT obj, const ConnectionId &id, bool override); bool CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData);
// bool CHSaveConnectionConfig_p(CONFIGROOT obj, const ConnectionId &id, bool override);
const tuple<QString, QString, int> CHGetOutboundData_p(const CONFIGROOT &obj, bool *succeed) const;
private: private:
int saveTimerId; int saveTimerId;
int pingAllTimerId; int pingAllTimerId;
int pingConnectionTimerId; int pingConnectionTimerId;
QHash<GroupId, GroupMetaObject> groups; QHash<GroupId, GroupMetaObject> groups;
QHash<ConnectionId, ConnectionMetaObject> connections; QHash<ConnectionId, ConnectionMetaObject> connections;
//QHash<ConnectionId, CONFIGROOT> connectionRootCache; // QHash<ConnectionId, CONFIGROOT> connectionRootCache;
private: private:
QvHttpRequestHelper *httpHelper; QvHttpRequestHelper *httpHelper;
QvTCPingHelper *tcpingHelper; bool isHttpRequestInProgress = false;
// We only support one cuncurrent connection currently. QvTCPingHelper *tcpingHelper;
// We only support one cuncurrent connection currently.
#ifdef QV2RAY_MULTIPlE_ONNECTION #ifdef QV2RAY_MULTIPlE_ONNECTION
QHash<ConnectionId, *V2rayKernelInstance> kernelInstances; QHash<ConnectionId, *V2rayKernelInstance> kernelInstances;
#else #else
ConnectionId currentConnectionId = NullConnectionId; ConnectionId currentConnectionId = NullConnectionId;
V2rayKernelInstance *vCoreInstance = nullptr; V2rayKernelInstance *vCoreInstance = nullptr;
#endif #endif
}; };
inline ::Qv2ray::core::handlers::QvConnectionHandler *ConnectionManager = nullptr; inline ::Qv2ray::core::handlers::QvConnectionHandler *ConnectionManager = nullptr;
} } // namespace Qv2ray::core::handlers
using namespace Qv2ray::core::handlers; using namespace Qv2ray::core::handlers;

View File

@ -8,7 +8,8 @@ optional<QString> QvConnectionHandler::CHStartConnection_p(const ConnectionId &i
auto fullConfig = GenerateRuntimeConfig(root); auto fullConfig = GenerateRuntimeConfig(root);
auto result = vCoreInstance->StartConnection(id, fullConfig); auto result = vCoreInstance->StartConnection(id, fullConfig);
if (!result.has_value()) { if (!result.has_value())
{
currentConnectionId = id; currentConnectionId = id;
emit OnConnected(currentConnectionId); emit OnConnected(currentConnectionId);
} }
@ -18,13 +19,16 @@ optional<QString> QvConnectionHandler::CHStartConnection_p(const ConnectionId &i
void QvConnectionHandler::CHStopConnection_p() void QvConnectionHandler::CHStopConnection_p()
{ {
if (vCoreInstance->KernelStarted) { if (vCoreInstance->KernelStarted)
{
vCoreInstance->StopConnection(); vCoreInstance->StopConnection();
// Copy // Copy
ConnectionId id = currentConnectionId; ConnectionId id = currentConnectionId;
currentConnectionId = NullConnectionId; currentConnectionId = NullConnectionId;
emit OnDisConnected(id); emit OnDisConnected(id);
} else { }
else
{
LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting") LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting")
} }
} }

View File

@ -6,7 +6,7 @@ using grpc::Channel;
using grpc::ClientContext; using grpc::ClientContext;
using grpc::Status; using grpc::Status;
#else #else
#include "libs/libqvb/build/libqvb.h" #include "libs/libqvb/build/libqvb.h"
#endif #endif
namespace Qv2ray::core::kernel namespace Qv2ray::core::kernel
@ -22,9 +22,7 @@ namespace Qv2ray::core::kernel
DEBUG(MODULE_VCORE, "API Worker initialised.") DEBUG(MODULE_VCORE, "API Worker initialised.")
connect(this, SIGNAL(error(QString)), this, SLOT(errorString(QString))); connect(this, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), this, SLOT(process())); connect(thread, SIGNAL(started()), this, SLOT(process()));
connect(thread, &QThread::finished, []() { connect(thread, &QThread::finished, []() { LOG(MODULE_VCORE, "API thread stopped") });
LOG(MODULE_VCORE, "API thread stopped")
});
started = true; started = true;
thread->start(); thread->start();
DEBUG(MODULE_VCORE, "API Worker started.") DEBUG(MODULE_VCORE, "API Worker started.")
@ -52,21 +50,22 @@ namespace Qv2ray::core::kernel
thread->wait(); thread->wait();
// Although thread shouldnot be null, we'll add this check to be safe. // Although thread shouldnot be null, we'll add this check to be safe.
if (thread) { if (thread) { delete thread; }
delete thread;
}
} }
// API Core Operations // API Core Operations
// Start processing data. // Start processing data.
void APIWorker::process() void APIWorker::process()
{ {
while (started) { while (started)
{
QThread::msleep(1000); QThread::msleep(1000);
bool dialed = false; bool dialed = false;
while (running) { while (running)
if (!dialed) { {
if (!dialed)
{
auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.apiConfig.statsPort); auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.apiConfig.statsPort);
#ifdef WITH_LIB_GRPCPP #ifdef WITH_LIB_GRPCPP
Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials()); Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials());
@ -85,36 +84,44 @@ namespace Qv2ray::core::kernel
qint64 value_up = 0; qint64 value_up = 0;
qint64 value_down = 0; qint64 value_down = 0;
for (auto tag : inboundTags) { for (auto tag : inboundTags)
{
value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink"); value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink"); value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
} }
if (value_up < 0 || value_down < 0) { if (value_up < 0 || value_down < 0)
{
dialed = false; dialed = false;
break; break;
} }
if (running) { if (running)
{
apiFailedCounter = 0; apiFailedCounter = 0;
emit OnDataReady(value_up, value_down); emit OnDataReady(value_up, value_down);
} }
#else #else
for (auto tag : inboundTags) { for (auto tag : inboundTags)
{
auto valup = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink"); auto valup = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
auto valdown = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink"); auto valdown = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
if (valup < 0 || valdown < 0) { if (valup < 0 || valdown < 0)
{
dialed = false; dialed = false;
break; break;
} }
if (running) { if (running)
{
apiFailedCounter = 0; apiFailedCounter = 0;
emit OnDataReady(tag, valup, valdown); emit OnDataReady(tag, valup, valdown);
} else { }
else
{
// If the connection has stopped, just quit. // If the connection has stopped, just quit.
break; break;
} }
@ -123,19 +130,22 @@ namespace Qv2ray::core::kernel
#endif #endif
QThread::msleep(1000); QThread::msleep(1000);
} // end while running } // end while running
} // end while started } // end while started
thread->exit(); thread->exit();
} }
qint64 APIWorker::CallStatsAPIByName(const QString &name) qint64 APIWorker::CallStatsAPIByName(const QString &name)
{ {
if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) { if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD)
{
LOG(MODULE_VCORE, "API call failure threshold reached, cancelling further API aclls.") LOG(MODULE_VCORE, "API call failure threshold reached, cancelling further API aclls.")
emit error("Failed to get statistics data, please check if V2ray is running properly"); emit error("Failed to get statistics data, please check if V2ray is running properly");
apiFailedCounter++; apiFailedCounter++;
return 0; return 0;
} else if (apiFailedCounter > QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) { }
else if (apiFailedCounter > QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD)
{
return 0; return 0;
} }
@ -147,7 +157,8 @@ namespace Qv2ray::core::kernel
ClientContext context; ClientContext context;
Status status = Stub->GetStats(&context, request, &response); Status status = Stub->GetStats(&context, request, &response);
if (!status.ok()) { if (!status.ok())
{
LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")") LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")")
apiFailedCounter++; apiFailedCounter++;
} }
@ -157,7 +168,8 @@ namespace Qv2ray::core::kernel
qint64 data = GetStats(const_cast<char *>(name.toStdString().c_str()), 1000); qint64 data = GetStats(const_cast<char *>(name.toStdString().c_str()), 1000);
#endif #endif
if (data < 0) { if (data < 0)
{
LOG(MODULE_VCORE, "API call returns: " + QSTRN(data)) LOG(MODULE_VCORE, "API call returns: " + QSTRN(data))
apiFailedCounter++; apiFailedCounter++;
return 0; return 0;
@ -165,4 +177,4 @@ namespace Qv2ray::core::kernel
return data; return data;
} }
} } // namespace Qv2ray::core::kernel

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#ifdef WITH_LIB_GRPCPP #ifdef WITH_LIB_GRPCPP
#include <grpc++/grpc++.h> #include "libs/gen/v2ray_api.grpc.pb.h"
#include "libs/gen/v2ray_api.pb.h" #include "libs/gen/v2ray_api.pb.h"
#include "libs/gen/v2ray_api.grpc.pb.h"
#include <grpc++/grpc++.h>
#endif #endif
// Check 10 times before telling user that API has failed. // Check 10 times before telling user that API has failed.
@ -13,34 +14,34 @@ namespace Qv2ray::core::kernel
{ {
class APIWorker : public QObject class APIWorker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
APIWorker(); APIWorker();
~APIWorker(); ~APIWorker();
void StartAPI(const QStringList &tags); void StartAPI(const QStringList &tags);
void StopAPI(); void StopAPI();
public slots: public slots:
void process(); void process();
signals: signals:
void OnDataReady(const quint64 _totalUp, const quint64 _totalDown); void OnDataReady(const quint64 _totalUp, const quint64 _totalDown);
void error(const QString &err); void error(const QString &err);
private: private:
qint64 CallStatsAPIByName(const QString &name); qint64 CallStatsAPIByName(const QString &name);
QStringList inboundTags; QStringList inboundTags;
QThread *thread; QThread *thread;
// //
bool started = false; bool started = false;
bool running = false; bool running = false;
uint apiFailedCounter = 0; uint apiFailedCounter = 0;
#ifdef WITH_LIB_GRPCPP #ifdef WITH_LIB_GRPCPP
std::shared_ptr<::grpc::Channel> Channel; std::shared_ptr<::grpc::Channel> Channel;
std::unique_ptr<::v2ray::core::app::stats::command::StatsService::Stub> Stub; std::unique_ptr<::v2ray::core::app::stats::command::StatsService::Stub> Stub;
#endif #endif
}; };
} } // namespace Qv2ray::core::kernel
using namespace Qv2ray::core::kernel; using namespace Qv2ray::core::kernel;

View File

@ -1,10 +1,12 @@
#include "KernelInteractions.hpp"
#include "APIBackend.hpp"
#include "common/QvHelpers.hpp"
#include "core/connection/ConnectionIO.hpp"
#include <QDesktopServices>
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QDesktopServices>
#include "common/QvHelpers.hpp"
#include "KernelInteractions.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "APIBackend.hpp"
namespace Qv2ray::core::kernel namespace Qv2ray::core::kernel
{ {
@ -12,14 +14,17 @@ namespace Qv2ray::core::kernel
{ {
QFile coreFile(vCorePath); QFile coreFile(vCorePath);
if (!coreFile.exists()) { if (!coreFile.exists())
{
DEBUG(MODULE_VCORE, "V2ray core file cannot be found.") DEBUG(MODULE_VCORE, "V2ray core file cannot be found.")
*message = tr("V2ray core executable not found."); *message = tr("V2ray core executable not found.");
return false; return false;
} }
// Use open() here to prevent `executing` a folder, which may have the same name as the V2ray core. // Use open() here to prevent `executing` a folder, which may have the
if (!coreFile.open(QFile::ReadOnly)) { // same name as the V2ray core.
if (!coreFile.open(QFile::ReadOnly))
{
DEBUG(MODULE_VCORE, "V2ray core file cannot be opened, possibly be a folder?") DEBUG(MODULE_VCORE, "V2ray core file cannot be opened, possibly be a folder?")
*message = tr("V2ray core file cannot be opened, please ensure there's a file instead of a folder."); *message = tr("V2ray core file cannot be opened, please ensure there's a file instead of a folder.");
return false; return false;
@ -33,28 +38,32 @@ namespace Qv2ray::core::kernel
bool hasGeoIP = FileExistsIn(QDir(vAssetsPath), "geoip.dat"); bool hasGeoIP = FileExistsIn(QDir(vAssetsPath), "geoip.dat");
bool hasGeoSite = FileExistsIn(QDir(vAssetsPath), "geosite.dat"); bool hasGeoSite = FileExistsIn(QDir(vAssetsPath), "geosite.dat");
if (!hasGeoIP && !hasGeoSite) { if (!hasGeoIP && !hasGeoSite)
{
DEBUG(MODULE_VCORE, "V2ray assets path contains none of those two files.") DEBUG(MODULE_VCORE, "V2ray assets path contains none of those two files.")
*message = tr("V2ray assets path is not valid."); *message = tr("V2ray assets path is not valid.");
return false; return false;
} }
if (!hasGeoIP) { if (!hasGeoIP)
{
DEBUG(MODULE_VCORE, "No geoip.dat in assets path, aborting.") DEBUG(MODULE_VCORE, "No geoip.dat in assets path, aborting.")
*message = tr("No geoip.dat in assets path."); *message = tr("No geoip.dat in assets path.");
return false; return false;
} }
if (!hasGeoSite) { if (!hasGeoSite)
{
DEBUG(MODULE_VCORE, "No geosite.dat in assets path, aborting.") DEBUG(MODULE_VCORE, "No geosite.dat in assets path, aborting.")
*message = tr("No geosite.dat in assets path."); *message = tr("No geosite.dat in assets path.");
return false; return false;
} }
// Check if V2ray core returns a version number correctly. // Check if V2ray core returns a version number correctly.
QProcess proc; QProcess proc;
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
// nativeArguments are required for Windows platform, without a reason... // nativeArguments are required for Windows platform, without a
// reason...
proc.setProcessChannelMode(QProcess::MergedChannels); proc.setProcessChannelMode(QProcess::MergedChannels);
proc.setProgram(vCorePath); proc.setProgram(vCorePath);
proc.setNativeArguments("--version"); proc.setNativeArguments("--version");
@ -66,7 +75,8 @@ namespace Qv2ray::core::kernel
proc.waitForFinished(); proc.waitForFinished();
auto exitCode = proc.exitCode(); auto exitCode = proc.exitCode();
if (exitCode != 0) { if (exitCode != 0)
{
DEBUG(MODULE_VCORE, "VCore failed with an exit code: " + QSTRN(exitCode)) DEBUG(MODULE_VCORE, "VCore failed with an exit code: " + QSTRN(exitCode))
*message = tr("V2ray core failed with an exit code: ") + QSTRN(exitCode); *message = tr("V2ray core failed with an exit code: ") + QSTRN(exitCode);
return false; return false;
@ -75,7 +85,8 @@ namespace Qv2ray::core::kernel
QString output = proc.readAllStandardOutput(); QString output = proc.readAllStandardOutput();
LOG(MODULE_VCORE, "V2ray output: " + SplitLines(output).join(";")) LOG(MODULE_VCORE, "V2ray output: " + SplitLines(output).join(";"))
if (SplitLines(output).isEmpty()) { if (SplitLines(output).isEmpty())
{
*message = tr("V2ray core returns empty string."); *message = tr("V2ray core returns empty string.");
return false; return false;
} }
@ -84,12 +95,12 @@ namespace Qv2ray::core::kernel
return true; return true;
} }
bool V2rayKernelInstance::ValidateConfig(const QString &path) bool V2rayKernelInstance::ValidateConfig(const QString &path)
{ {
QString v2rayCheckResult; QString v2rayCheckResult;
if (ValidateKernel(GlobalConfig.v2CorePath, GlobalConfig.v2AssetsPath, &v2rayCheckResult)) { if (ValidateKernel(GlobalConfig.v2CorePath, GlobalConfig.v2AssetsPath, &v2rayCheckResult))
{
DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult) DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult)
// Append assets location env. // Append assets location env.
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@ -98,21 +109,28 @@ namespace Qv2ray::core::kernel
QProcess process; QProcess process;
process.setProcessEnvironment(env); process.setProcessEnvironment(env);
DEBUG(MODULE_VCORE, "Starting V2ray core with test options") DEBUG(MODULE_VCORE, "Starting V2ray core with test options")
process.start(GlobalConfig.v2CorePath, QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text); process.start(GlobalConfig.v2CorePath,
QStringList() << "-test"
<< "-config" << path,
QIODevice::ReadWrite | QIODevice::Text);
process.waitForFinished(); process.waitForFinished();
if (process.exitCode() != 0) { if (process.exitCode() != 0)
{
QString output = QString(process.readAllStandardOutput()); QString output = QString(process.readAllStandardOutput());
QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17)); QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
return false; return false;
} else { }
else
{
DEBUG(MODULE_VCORE, "Config file check passed.") DEBUG(MODULE_VCORE, "Config file check passed.")
return true; return true;
} }
} else { }
else
{
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("The error is: ") + NEWLINE + v2rayCheckResult);
tr("The error is: ") + NEWLINE + v2rayCheckResult);
return false; return false;
} }
} }
@ -120,14 +138,14 @@ namespace Qv2ray::core::kernel
V2rayKernelInstance::V2rayKernelInstance() V2rayKernelInstance::V2rayKernelInstance()
{ {
vProcess = new QProcess(); vProcess = new QProcess();
connect(vProcess, &QProcess::readyReadStandardOutput, this, [&]() { connect(vProcess, &QProcess::readyReadStandardOutput, this,
emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); [&]() { emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); });
});
connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) { connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString()) DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
// If V2ray crashed AFTER we start it. // If V2ray crashed AFTER we start it.
if (KernelStarted && state == QProcess::NotRunning) { if (KernelStarted && state == QProcess::NotRunning)
{
LOG(MODULE_VCORE, "V2ray kernel crashed.") LOG(MODULE_VCORE, "V2ray kernel crashed.")
StopConnection(); StopConnection();
emit OnProcessErrored(id); emit OnProcessErrored(id);
@ -140,7 +158,8 @@ namespace Qv2ray::core::kernel
optional<QString> V2rayKernelInstance::StartConnection(const ConnectionId &id, const CONFIGROOT &root) optional<QString> V2rayKernelInstance::StartConnection(const ConnectionId &id, const CONFIGROOT &root)
{ {
if (KernelStarted) { if (KernelStarted)
{
LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartConnection") LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartConnection")
return tr("Invalid V2ray Instance Status."); return tr("Invalid V2ray Instance Status.");
} }
@ -151,7 +170,8 @@ namespace Qv2ray::core::kernel
// //
auto filePath = QV2RAY_GENERATED_FILE_PATH; auto filePath = QV2RAY_GENERATED_FILE_PATH;
if (ValidateConfig(filePath)) { if (ValidateConfig(filePath))
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", GlobalConfig.v2AssetsPath); env.insert("V2RAY_LOCATION_ASSET", GlobalConfig.v2AssetsPath);
vProcess->setProcessEnvironment(env); vProcess->setProcessEnvironment(env);
@ -163,10 +183,12 @@ namespace Qv2ray::core::kernel
this->id = id; this->id = id;
QStringList inboundTags; QStringList inboundTags;
for (auto item : root["inbounds"].toArray()) { for (auto item : root["inbounds"].toArray())
{
auto tag = item.toObject()["tag"].toString(""); auto tag = item.toObject()["tag"].toString("");
if (tag.isEmpty() || tag == API_TAG_INBOUND) { if (tag.isEmpty() || tag == API_TAG_INBOUND)
{
// Ignore API tag and empty tags. // Ignore API tag and empty tags.
continue; continue;
} }
@ -178,20 +200,26 @@ namespace Qv2ray::core::kernel
apiEnabled = false; apiEnabled = false;
// //
if (StartupOption.noAPI) { if (StartupOption.noAPI) { LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"") }
LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"") else if (!GlobalConfig.apiConfig.enableAPI)
} else if (!GlobalConfig.apiConfig.enableAPI) { {
LOG(MODULE_VCORE, "API has been disabled by the global config option") LOG(MODULE_VCORE, "API has been disabled by the global config option")
} else if (inboundTags.isEmpty()) { }
else if (inboundTags.isEmpty())
{
LOG(MODULE_VCORE, "API is disabled since no inbound tags configured. This is probably caused by a bad complex config.") LOG(MODULE_VCORE, "API is disabled since no inbound tags configured. This is probably caused by a bad complex config.")
} else { }
else
{
apiWorker->StartAPI(inboundTags); apiWorker->StartAPI(inboundTags);
apiEnabled = true; apiEnabled = true;
DEBUG(MODULE_VCORE, "Qv2ray API started") DEBUG(MODULE_VCORE, "Qv2ray API started")
} }
return { }; return {};
} else { }
else
{
KernelStarted = false; KernelStarted = false;
return tr("V2ray kernel failed to start."); return tr("V2ray kernel failed to start.");
} }
@ -199,12 +227,14 @@ namespace Qv2ray::core::kernel
void V2rayKernelInstance::StopConnection() void V2rayKernelInstance::StopConnection()
{ {
if (apiEnabled) { if (apiEnabled)
{
apiWorker->StopAPI(); apiWorker->StopAPI();
apiEnabled = false; apiEnabled = false;
} }
// Set this to false BEFORE close the Process, since we need this flag to capture the real kernel CRASH // Set this to false BEFORE close the Process, since we need this flag
// to capture the real kernel CRASH
KernelStarted = false; KernelStarted = false;
vProcess->close(); vProcess->close();
// Block until V2ray core exits // Block until V2ray core exits
@ -214,9 +244,7 @@ namespace Qv2ray::core::kernel
V2rayKernelInstance::~V2rayKernelInstance() V2rayKernelInstance::~V2rayKernelInstance()
{ {
if (KernelStarted) { if (KernelStarted) { StopConnection(); }
StopConnection();
}
delete apiWorker; delete apiWorker;
delete vProcess; delete vProcess;
@ -226,4 +254,4 @@ namespace Qv2ray::core::kernel
{ {
emit OnNewStatsDataArrived(id, _totalUp, _totalDown); emit OnNewStatsDataArrived(id, _totalUp, _totalDown);
} }
} } // namespace Qv2ray::core::kernel

View File

@ -1,46 +1,47 @@
#pragma once #pragma once
#include <QProcess>
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "core/CoreSafeTypes.hpp" #include "core/CoreSafeTypes.hpp"
#include <QProcess>
namespace Qv2ray::core::kernel namespace Qv2ray::core::kernel
{ {
class APIWorker; class APIWorker;
class V2rayKernelInstance : public QObject class V2rayKernelInstance : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit V2rayKernelInstance(); explicit V2rayKernelInstance();
~V2rayKernelInstance() override; ~V2rayKernelInstance() override;
// //
// Speed // Speed
qulonglong getTagSpeedUp(const QString &tag); qulonglong getTagSpeedUp(const QString &tag);
qulonglong getTagSpeedDown(const QString &tag); qulonglong getTagSpeedDown(const QString &tag);
qulonglong getAllSpeedUp(); qulonglong getAllSpeedUp();
qulonglong getAllSpeedDown(); qulonglong getAllSpeedDown();
// //
optional<QString> StartConnection(const ConnectionId &id, const CONFIGROOT &root); optional<QString> StartConnection(const ConnectionId &id, const CONFIGROOT &root);
void StopConnection(); void StopConnection();
bool KernelStarted = false; bool KernelStarted = false;
// //
static bool ValidateConfig(const QString &path); static bool ValidateConfig(const QString &path);
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message); static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
signals: signals:
void OnProcessErrored(const ConnectionId &id); void OnProcessErrored(const ConnectionId &id);
void OnProcessOutputReadyRead(const ConnectionId &id, const QString &output); void OnProcessOutputReadyRead(const ConnectionId &id, const QString &output);
void OnNewStatsDataArrived(const ConnectionId &id, const quint64 _totalUp, const quint64 _totalDown); void OnNewStatsDataArrived(const ConnectionId &id, const quint64 _totalUp, const quint64 _totalDown);
public slots: public slots:
void onAPIDataReady(const quint64 _totalUp, const quint64 _totalDown); void onAPIDataReady(const quint64 _totalUp, const quint64 _totalDown);
private: private:
APIWorker *apiWorker; APIWorker *apiWorker;
QProcess *vProcess; QProcess *vProcess;
bool apiEnabled; bool apiEnabled;
// //
ConnectionId id = NullConnectionId; ConnectionId id = NullConnectionId;
}; };
} } // namespace Qv2ray::core::kernel
using namespace Qv2ray::core::kernel; using namespace Qv2ray::core::kernel;

View File

@ -1,15 +1,15 @@
#ifdef _WIN32 #ifdef _WIN32
#include <WinSock2.h> #include <WS2tcpip.h>
#include <WS2tcpip.h> #include <WinSock2.h>
#else #else
#include <sys/socket.h> #include <netdb.h>
#include <netdb.h> #include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "QtConcurrent/QtConcurrent"
#include "QvTCPing.hpp" #include "QvTCPing.hpp"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
#include "QtConcurrent/QtConcurrent"
namespace Qv2ray::core::tcping namespace Qv2ray::core::tcping
{ {
@ -23,7 +23,8 @@ namespace Qv2ray::core::tcping
void QvTCPingHelper::StopAllLatenceTest() void QvTCPingHelper::StopAllLatenceTest()
{ {
while (!pingWorkingThreads.isEmpty()) { while (!pingWorkingThreads.isEmpty())
{
auto worker = pingWorkingThreads.dequeue(); auto worker = pingWorkingThreads.dequeue();
worker->future().cancel(); worker->future().cancel();
worker->cancel(); worker->cancel();
@ -35,13 +36,11 @@ namespace Qv2ray::core::tcping
watcher->setFuture(QtConcurrent::run(&QvTCPingHelper::TestLatency_p, id, count)); watcher->setFuture(QtConcurrent::run(&QvTCPingHelper::TestLatency_p, id, count));
pingWorkingThreads.enqueue(watcher); pingWorkingThreads.enqueue(watcher);
// //
connect(watcher, &QFutureWatcher<QvTCPingResultObject>::finished, this, [ = ]() { connect(watcher, &QFutureWatcher<QvTCPingResultObject>::finished, this, [=]() {
auto result = watcher->result(); auto result = watcher->result();
this->pingWorkingThreads.removeOne(watcher); this->pingWorkingThreads.removeOne(watcher);
if (!result.errorMessage.isEmpty()) { if (!result.errorMessage.isEmpty()) { LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage) }
LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage)
}
emit this->OnLatencyTestCompleted(result); emit this->OnLatencyTestCompleted(result);
}); });
@ -54,12 +53,14 @@ namespace Qv2ray::core::tcping
if (isExiting) return data; if (isExiting) return data;
auto [host, port] = ConnectionManager->GetConnectionInfo(id); auto [protocol, host, port] = ConnectionManager->GetConnectionData(id);
Q_UNUSED(protocol)
double successCount = 0, errorCount = 0; double successCount = 0, errorCount = 0;
addrinfo *resolved; addrinfo *resolved;
int errcode; int errcode;
if ((errcode = resolveHost(host.toStdString(), port, &resolved)) != 0) { if ((errcode = resolveHost(host.toStdString(), port, &resolved)) != 0)
{
#ifdef _WIN32 #ifdef _WIN32
data.errorMessage = QString::fromStdWString(gai_strerror(errcode)); data.errorMessage = QString::fromStdWString(gai_strerror(errcode));
#else #else
@ -71,26 +72,35 @@ namespace Qv2ray::core::tcping
bool noAddress = false; bool noAddress = false;
int currentCount = 0; int currentCount = 0;
while (currentCount < count) { while (currentCount < count)
{
if (isExiting) return QvTCPingResultObject(); if (isExiting) return QvTCPingResultObject();
system_clock::time_point start; system_clock::time_point start;
system_clock::time_point end; system_clock::time_point end;
if ((errcode = testLatency(resolved, &start, &end)) != 0) { if ((errcode = testLatency(resolved, &start, &end)) != 0)
if (errcode != -EADDRNOTAVAIL) { {
//LOG(MODULE_NETWORK, "Error connecting to host: " + data.hostName + ":" + QSTRN(data.port) + " " + strerror(-errcode)) if (errcode != -EADDRNOTAVAIL)
{
// LOG(MODULE_NETWORK, "Error connecting to host: " +
// data.hostName + ":" + QSTRN(data.port) + " " +
// strerror(-errcode))
errorCount++; errorCount++;
} else { }
if (noAddress) { else
LOG(MODULE_NETWORK, ".") {
} else { if (noAddress) { LOG(MODULE_NETWORK, ".") }
else
{
LOG(MODULE_NETWORK, "error connecting to host: " + QSTRN(-errcode) + " " + strerror(-errcode)) LOG(MODULE_NETWORK, "error connecting to host: " + QSTRN(-errcode) + " " + strerror(-errcode))
} }
noAddress = true; noAddress = true;
} }
} else { }
else
{
noAddress = false; noAddress = false;
successCount++; successCount++;
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
@ -99,7 +109,8 @@ namespace Qv2ray::core::tcping
data.worst = min(data.worst, ms); data.worst = min(data.worst, ms);
data.best = max(data.best, ms); data.best = max(data.best, ms);
if (ms > 1000) { if (ms > 1000)
{
LOG(MODULE_NETWORK, "Stop the test on the first long connect()") LOG(MODULE_NETWORK, "Stop the test on the first long connect()")
break; /* Stop the test on the first long connect() */ break; /* Stop the test on the first long connect() */
} }
@ -146,20 +157,19 @@ namespace Qv2ray::core::tcping
int rv = 0; int rv = 0;
/* try to connect for each of the entries: */ /* try to connect for each of the entries: */
while (addr != nullptr) { while (addr != nullptr)
{
if (isExiting) return 0; if (isExiting) return 0;
/* create socket */ /* create socket */
fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (!fd) { if (!fd) { goto next_addr0; }
goto next_addr0;
}
#ifdef _WIN32 #ifdef _WIN32
// Windows needs special conversion. // Windows needs special conversion.
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) < 0)
#else #else
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
#endif #endif
@ -172,7 +182,8 @@ namespace Qv2ray::core::tcping
/* connect to peer */ /* connect to peer */
// Qt has its own connect() function in QObject.... // Qt has its own connect() function in QObject....
// So we add "::" here // So we add "::" here
if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) { if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
{
*end = system_clock::now(); *end = system_clock::now();
#ifdef _WIN32 #ifdef _WIN32
closesocket(fd); closesocket(fd);
@ -182,17 +193,17 @@ namespace Qv2ray::core::tcping
return 0; return 0;
} }
next_addr1: next_addr1:
#ifdef _WIN32 #ifdef _WIN32
closesocket(fd); closesocket(fd);
#else #else
close(fd); close(fd);
#endif #endif
next_addr0: next_addr0:
addr = addr->ai_next; addr = addr->ai_next;
} }
rv = rv ? rv : -errno; rv = rv ? rv : -errno;
return rv; return rv;
} }
} } // namespace Qv2ray::core::tcping

View File

@ -4,7 +4,8 @@
namespace Qv2ray::core::tcping namespace Qv2ray::core::tcping
{ {
struct QvTCPingResultObject { struct QvTCPingResultObject
{
ConnectionId connectionId = NullConnectionId; ConnectionId connectionId = NullConnectionId;
QString errorMessage; QString errorMessage;
int total, succeed, failed; int total, succeed, failed;
@ -13,19 +14,20 @@ namespace Qv2ray::core::tcping
class QvTCPingHelper : public QObject class QvTCPingHelper : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvTCPingHelper(const int defaultCount = 5, QObject *parent = nullptr); explicit QvTCPingHelper(const int defaultCount = 5, QObject *parent = nullptr);
void TestLatency(const ConnectionId &connectionId); void TestLatency(const ConnectionId &connectionId);
void StopAllLatenceTest(); void StopAllLatenceTest();
signals: signals:
void OnLatencyTestCompleted(const QvTCPingResultObject &data); void OnLatencyTestCompleted(const QvTCPingResultObject &data);
private:
static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count); private:
int count; static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count);
QQueue<QFutureWatcher<QvTCPingResultObject>*> pingWorkingThreads; int count;
QQueue<QFutureWatcher<QvTCPingResultObject> *> pingWorkingThreads;
}; };
} } // namespace Qv2ray::core::tcping
using namespace Qv2ray::core::tcping; using namespace Qv2ray::core::tcping;

View File

@ -1,37 +1,36 @@
#include <QFileInfo> #include <QFileInfo>
#include <QStandardPaths> #include "common/CommandArgs.hpp"
#include <QTranslator> #include "common/QvHelpers.hpp"
#include <QStyle> #include "common/QvTranslator.hpp"
#include <QLocale>
#include <QObject>
#include <QStyleFactory>
#include <QApplication>
#include <singleapplication.h>
#include <csignal>
#include "ui/w_MainWindow.hpp"
#include "core/config/ConfigBackend.hpp" #include "core/config/ConfigBackend.hpp"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
#include "common/QvHelpers.hpp" #include "ui/w_MainWindow.hpp"
#include "common/CommandArgs.hpp"
#include "common/QvTranslator.hpp" #include <QApplication>
#include <QLocale>
#include <QObject>
#include <QStandardPaths>
#include <QStyle>
#include <QStyleFactory>
#include <QTranslator>
#include <csignal>
#include <singleapplication.h>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
// For unix root user check // For unix root user check
#include "unistd.h" #include "unistd.h"
#endif #endif
void signalHandler(int signum) void signalHandler(int signum)
{ {
cout << "Interrupt signal (" << signum << ") received." << endl; cout << "Interrupt signal (" << signum << ") received." << endl;
//if (MainWindow::mwInstance && MainWindow::mwInstance->vinstance) { // if (MainWindow::mwInstance && MainWindow::mwInstance->vinstance) {
// cout << "Trying to stop connection..." << endl; // cout << "Trying to stop connection..." << endl;
// MainWindow::mwInstance->vinstance->StopConnection(); // MainWindow::mwInstance->vinstance->StopConnection();
//} //}
qApp->exit(-99); qApp->exit(-99);
} }
bool verifyConfigAvaliability(QString path, bool checkExistingConfig) bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{ {
// Does not exist. // Does not exist.
@ -41,17 +40,21 @@ bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
QFile testFile(path + ".qv2ray_file_write_test_file" + QSTRN(QTime::currentTime().msecsSinceStartOfDay())); QFile testFile(path + ".qv2ray_file_write_test_file" + QSTRN(QTime::currentTime().msecsSinceStartOfDay()));
bool opened = testFile.open(QFile::OpenModeFlag::ReadWrite); bool opened = testFile.open(QFile::OpenModeFlag::ReadWrite);
if (!opened) { if (!opened)
{
LOG(MODULE_SETTINGS, "Directory at: " + path + " cannot be used as a valid config file path.") LOG(MODULE_SETTINGS, "Directory at: " + path + " cannot be used as a valid config file path.")
LOG(MODULE_INIT, "---> Cannot create a new file or openwrite a file.") LOG(MODULE_INIT, "---> Cannot create a new file or openwrite a file.")
return false; return false;
} else { }
else
{
testFile.write("Qv2ray test file, feel free to remove."); testFile.write("Qv2ray test file, feel free to remove.");
testFile.flush(); testFile.flush();
testFile.close(); testFile.close();
bool removed = testFile.remove(); bool removed = testFile.remove();
if (!removed) { if (!removed)
{
// This is rare, as we can create a file but failed to remove it. // This is rare, as we can create a file but failed to remove it.
LOG(MODULE_SETTINGS, "Directory at: " + path + " cannot be used as a valid config file path.") LOG(MODULE_SETTINGS, "Directory at: " + path + " cannot be used as a valid config file path.")
LOG(MODULE_INIT, "---> Cannot remove a file.") LOG(MODULE_INIT, "---> Cannot remove a file.")
@ -59,7 +62,8 @@ bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
} }
} }
if (checkExistingConfig) { if (checkExistingConfig)
{
// Check if an existing config is found. // Check if an existing config is found.
QFile configFile(path + "Qv2ray.conf"); QFile configFile(path + "Qv2ray.conf");
@ -68,37 +72,52 @@ bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
bool opened2 = configFile.open(QIODevice::ReadWrite); bool opened2 = configFile.open(QIODevice::ReadWrite);
try { try
if (opened2) { {
if (opened2)
{
// Verify if the config can be loaded. // Verify if the config can be loaded.
// Failed to parse if we changed the file structure... // Failed to parse if we changed the file structure...
// Original: // Original:
// -- auto conf = StructFromJsonString<Qv2rayConfig>(configFile.readAll()); // -- auto conf =
// StructFromJsonString<Qv2rayConfig>(configFile.readAll());
// //
// Verify JSON file format. (only) because this file version may not be upgraded and may contain unsupported structure. // Verify JSON file format. (only) because this file version may
// not be upgraded and may contain unsupported structure.
auto err = VerifyJsonString(StringFromFile(&configFile)); auto err = VerifyJsonString(StringFromFile(&configFile));
if (!err.isEmpty()) { if (!err.isEmpty())
{
LOG(MODULE_INIT, "Json parse returns: " + err) LOG(MODULE_INIT, "Json parse returns: " + err)
return false; return false;
} else { }
else
{
// If the file format is valid. // If the file format is valid.
auto conf = JsonFromString(StringFromFile(&configFile)); auto conf = JsonFromString(StringFromFile(&configFile));
LOG(MODULE_SETTINGS, "Path: " + path + " contains a config file, in version " + conf["config_version"].toVariant().toString()) LOG(MODULE_SETTINGS,
"Path: " + path + " contains a config file, in version " + conf["config_version"].toVariant().toString())
configFile.close(); configFile.close();
return true; return true;
} }
} else { }
LOG(MODULE_SETTINGS, "File: " + configFile.fileName() + " cannot be opened!") else
{
LOG(MODULE_SETTINGS, "File: " + configFile.fileName() + " cannot be opened!")
return false; return false;
} }
} catch (...) { }
catch (...)
{
LOG(MODULE_SETTINGS, "Exception raised when checking config: " + configFile.fileName()) LOG(MODULE_SETTINGS, "Exception raised when checking config: " + configFile.fileName())
//LOG(INIT, e->what()) // LOG(INIT, e->what())
QvMessageBoxWarn(nullptr, QObject::tr("Warning"), QObject::tr("Qv2ray cannot load the config file from here:") + NEWLINE + configFile.fileName()); QvMessageBoxWarn(nullptr, QObject::tr("Warning"),
QObject::tr("Qv2ray cannot load the config file from here:") + NEWLINE + configFile.fileName());
return false; return false;
} }
} else return true; }
else
return true;
} }
bool initialiseQv2ray() bool initialiseQv2ray()
@ -109,12 +128,14 @@ bool initialiseQv2ray()
const QString homeQv2ray = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX; const QString homeQv2ray = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
// //
// //
// Some built-in search paths for Qv2ray to find configs. (load the first one if possible). // Some built-in search paths for Qv2ray to find configs. (load the first
// one if possible).
// //
QStringList configFilePaths; QStringList configFilePaths;
configFilePaths << currentPathConfig; configFilePaths << currentPathConfig;
#ifdef WITH_FLATHUB_CONFIG_PATH #ifdef WITH_FLATHUB_CONFIG_PATH
// AppConfigLocation uses 'Q'v2ray instead of `q`v2ray. Keep here as backward compatibility. // AppConfigLocation uses 'Q'v2ray instead of `q`v2ray. Keep here as
// backward compatibility.
configFilePaths << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QV2RAY_CONFIG_DIR_SUFFIX; configFilePaths << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QV2RAY_CONFIG_DIR_SUFFIX;
#endif #endif
configFilePaths << configQv2ray; configFilePaths << configQv2ray;
@ -123,31 +144,40 @@ bool initialiseQv2ray()
QString configPath = ""; QString configPath = "";
bool hasExistingConfig = false; bool hasExistingConfig = false;
for (auto path : configFilePaths) { for (auto path : configFilePaths)
// Verify the config path, check if the config file exists and in the correct JSON format. {
// True means we check for config existance as well. --|HERE | // Verify the config path, check if the config file exists and in the
// correct JSON format. True means we check for config existance as
// well. --|HERE |
bool isValidConfigPath = verifyConfigAvaliability(path, true); bool isValidConfigPath = verifyConfigAvaliability(path, true);
// If we already found a valid config file. just simply load it... // If we already found a valid config file. just simply load it...
if (hasExistingConfig) break; if (hasExistingConfig) break;
if (isValidConfigPath) { if (isValidConfigPath)
{
DEBUG(MODULE_INIT, "Path: " + path + " is valid.") DEBUG(MODULE_INIT, "Path: " + path + " is valid.")
configPath = path; configPath = path;
hasExistingConfig = true; hasExistingConfig = true;
} else { }
else
{
LOG(MODULE_INIT, "Path: " + path + " does not contain a valid config file.") LOG(MODULE_INIT, "Path: " + path + " does not contain a valid config file.")
} }
} }
// If there's no existing config. // If there's no existing config.
if (hasExistingConfig) { if (hasExistingConfig)
{
// Use the config path found by the checks above // Use the config path found by the checks above
SetConfigDirPath(configPath); SetConfigDirPath(configPath);
LOG(MODULE_INIT, "Using " + QV2RAY_CONFIG_DIR + " as the config path.") LOG(MODULE_INIT, "Using " + QV2RAY_CONFIG_DIR + " as the config path.")
} else { }
else
{
// //
// Create new config at these dirs, these are default values for each platform. // Create new config at these dirs, these are default values for each
// platform.
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
configPath = currentPathConfig; configPath = currentPathConfig;
#else #else
@ -156,23 +186,26 @@ bool initialiseQv2ray()
bool mkpathResult = QDir().mkpath(configPath); bool mkpathResult = QDir().mkpath(configPath);
// Check if the dirs are write-able // Check if the dirs are write-able
if (mkpathResult && verifyConfigAvaliability(configPath, false)) { if (mkpathResult && verifyConfigAvaliability(configPath, false))
// Found a valid config dir, with write permission, but assume no config is located in it. {
// Found a valid config dir, with write permission, but assume no
// config is located in it.
LOG(MODULE_INIT, "Set " + configPath + " as the config path.") LOG(MODULE_INIT, "Set " + configPath + " as the config path.")
SetConfigDirPath(configPath); SetConfigDirPath(configPath);
if (QFile::exists(QV2RAY_CONFIG_FILE)) { if (QFile::exists(QV2RAY_CONFIG_FILE))
{
// As we already tried to load config from every possible dir. // As we already tried to load config from every possible dir.
// This condition branch (!hasExistingConfig check) holds the fact that // This condition branch (!hasExistingConfig check) holds the
// current config dir, should NOT contain any valid file (at least in the same name) // fact that current config dir, should NOT contain any valid
// It usually means that QV2RAY_CONFIG_FILE here is corrupted, in JSON format. // file (at least in the same name) It usually means that
// Otherwise Qv2ray would have loaded this config already instead of notifying to // QV2RAY_CONFIG_FILE here is corrupted, in JSON format.
// create a new config in this folder. // Otherwise Qv2ray would have loaded this config already
// instead of notifying to create a new config in this folder.
LOG(MODULE_INIT, "This should not occur: Qv2ray config exists but failed to load.") LOG(MODULE_INIT, "This should not occur: Qv2ray config exists but failed to load.")
QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"), QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"),
QObject::tr("Failed to determine the location of config file.") + NEWLINE + QObject::tr("Failed to determine the location of config file.") + NEWLINE +
QObject::tr("Qv2ray will now exit.") + NEWLINE + QObject::tr("Qv2ray will now exit.") + NEWLINE + QObject::tr("Please report if you think it's a bug."));
QObject::tr("Please report if you think it's a bug."));
return false; return false;
} }
@ -184,7 +217,9 @@ bool initialiseQv2ray()
// Save initial config. // Save initial config.
SaveGlobalConfig(conf); SaveGlobalConfig(conf);
LOG(MODULE_INIT, "Created initial config file.") LOG(MODULE_INIT, "Created initial config file.")
} else { }
else
{
// None of the path above can be used as a dir for storing config. // None of the path above can be used as a dir for storing config.
// Even the last folder failed to pass the check. // Even the last folder failed to pass the check.
LOG(MODULE_INIT, "FATAL") LOG(MODULE_INIT, "FATAL")
@ -192,14 +227,14 @@ bool initialiseQv2ray()
QString searchPath = configFilePaths.join(NEWLINE); QString searchPath = configFilePaths.join(NEWLINE);
QvMessageBoxWarn(nullptr, QObject::tr("Cannot Start Qv2ray"), QvMessageBoxWarn(nullptr, QObject::tr("Cannot Start Qv2ray"),
QObject::tr("Cannot find a place to store config files.") + NEWLINE + QObject::tr("Cannot find a place to store config files.") + NEWLINE +
QObject::tr("Qv2ray has searched these paths below:") + QObject::tr("Qv2ray has searched these paths below:") + NEWLINE + NEWLINE + searchPath + NEWLINE +
NEWLINE + NEWLINE + searchPath + NEWLINE + QObject::tr("Qv2ray will now exit."));
QObject::tr("Qv2ray will now exit."));
return false; return false;
} }
} }
if (!QDir(QV2RAY_GENERATED_DIR).exists()) { if (!QDir(QV2RAY_GENERATED_DIR).exists())
{
// The dir used to generate final config file, for V2ray interaction. // The dir used to generate final config file, for V2ray interaction.
QDir().mkdir(QV2RAY_GENERATED_DIR); QDir().mkdir(QV2RAY_GENERATED_DIR);
LOG(MODULE_INIT, "Created config generation dir at: " + QV2RAY_GENERATED_DIR) LOG(MODULE_INIT, "Created config generation dir at: " + QV2RAY_GENERATED_DIR)
@ -208,7 +243,6 @@ bool initialiseQv2ray()
return true; return true;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
@ -228,9 +262,9 @@ int main(int argc, char *argv[])
QvCommandArgParser parser; QvCommandArgParser parser;
QString errorMessage; QString errorMessage;
switch (parser.ParseCommandLine(&errorMessage)) { switch (parser.ParseCommandLine(&errorMessage))
case CommandLineOk: {
break; case CommandLineOk: break;
case CommandLineError: case CommandLineError:
cout << errorMessage.toStdString() << endl; cout << errorMessage.toStdString() << endl;
@ -243,16 +277,16 @@ int main(int argc, char *argv[])
LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO) LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO)
return 0; return 0;
case CommandLineHelpRequested: case CommandLineHelpRequested: cout << parser.Parser()->helpText().toStdString() << endl; return 0;
cout << parser.Parser()->helpText().toStdString() << endl;
return 0;
} }
} }
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
// Unix OS root user check. // Unix OS root user check.
// Do not use getuid() here since it's installed as owned by the root, someone may accidently setuid to it. // Do not use getuid() here since it's installed as owned by the root,
if (!StartupOption.forceRunAsRootUser && geteuid() == 0) { // someone may accidently setuid to it.
if (!StartupOption.forceRunAsRootUser && geteuid() == 0)
{
LOG("ERROR", QObject::tr("You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so.")) LOG("ERROR", QObject::tr("You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so."))
LOG("ERROR", QObject::tr(" --> USE IT AT YOUR OWN RISK!")) LOG("ERROR", QObject::tr(" --> USE IT AT YOUR OWN RISK!"))
return 1; return 1;
@ -263,9 +297,11 @@ int main(int argc, char *argv[])
// finished: command line parsing // finished: command line parsing
LOG("QV2RAY_BUILD_INFO", QV2RAY_BUILD_INFO) LOG("QV2RAY_BUILD_INFO", QV2RAY_BUILD_INFO)
LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO) LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO)
LOG(MODULE_INIT, "Qv2ray " QV2RAY_VERSION_STRING " running on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture() + NEWLINE) LOG(MODULE_INIT,
"Qv2ray " QV2RAY_VERSION_STRING " running on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture() + NEWLINE)
// //
// This line must be called before any other ones, since we are using these values to identify instances. // This line must be called before any other ones, since we are using these
// values to identify instances.
SingleApplication::setApplicationName("Qv2ray"); SingleApplication::setApplicationName("Qv2ray");
SingleApplication::setApplicationVersion(QV2RAY_VERSION_STRING); SingleApplication::setApplicationVersion(QV2RAY_VERSION_STRING);
SingleApplication::setApplicationDisplayName("Qv2ray"); SingleApplication::setApplicationDisplayName("Qv2ray");
@ -275,7 +311,8 @@ int main(int argc, char *argv[])
// ----------------------------> For debug build... // ----------------------------> For debug build...
SingleApplication::setApplicationName("Qv2ray - DEBUG"); SingleApplication::setApplicationName("Qv2ray - DEBUG");
#endif #endif
SingleApplication _qApp(argc, argv, false, SingleApplication::Mode::User | SingleApplication::Mode::ExcludeAppPath | SingleApplication::Mode::ExcludeAppVersion); SingleApplication _qApp(
argc, argv, false, SingleApplication::Mode::User | SingleApplication::Mode::ExcludeAppPath | SingleApplication::Mode::ExcludeAppVersion);
_qApp.setQuitOnLastWindowClosed(false); _qApp.setQuitOnLastWindowClosed(false);
// Early initialisation // Early initialisation
// //
@ -288,14 +325,11 @@ int main(int argc, char *argv[])
bool _result_ = _qApp.installTranslator(Qv2rayTranslator.get()); bool _result_ = _qApp.installTranslator(Qv2rayTranslator.get());
LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed")) LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed"))
// //
LOG("LICENCE", NEWLINE "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE LOG("LICENCE", NEWLINE
"This is free software, and you are welcome to redistribute it" NEWLINE "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE "This is free software, and you are welcome to redistribute it" NEWLINE
"under certain conditions." NEWLINE NEWLINE "under certain conditions." NEWLINE NEWLINE "Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE NEWLINE NEWLINE
"Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE
NEWLINE NEWLINE
"Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE "Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE
"Copyright (c) 2020 dridk (@dridk): X2Struct (Apache)" NEWLINE "Copyright (c) 2020 dridk (@dridk): X2Struct (Apache)" NEWLINE "Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE
"Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE
"Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE "Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE
"Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE "Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE
"Copyright (c) 2016 Nikhil Marathe (@nikhilm): QHttpServer (MIT)" NEWLINE "Copyright (c) 2016 Nikhil Marathe (@nikhilm): QHttpServer (MIT)" NEWLINE
@ -304,48 +338,50 @@ int main(int argc, char *argv[])
"Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE "Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE
"Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE "Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE
"Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE "Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE
"Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE "Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE NEWLINE)
NEWLINE)
// //
LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay()))
// //
#ifdef QT_DEBUG #ifdef QT_DEBUG
cout << "WARNING: ============================== This is a debug build, many features are not stable enough. ==============================" << endl; cout << "WARNING: ============================== This is a debug build, many features are not stable enough. =============================="
<< endl;
#endif #endif
// //
// Load the language translation list. // Load the language translation list.
auto langs = GetFileList(QDir(":/translations")); auto langs = GetFileList(QDir(":/translations"));
if (langs.empty()) { if (langs.empty())
{
LOG(MODULE_INIT, "FAILED to find any translations. THIS IS A BUILD ERROR.") LOG(MODULE_INIT, "FAILED to find any translations. THIS IS A BUILD ERROR.")
QvMessageBoxWarn(nullptr, QObject::tr("Cannot load languages"), QObject::tr("Qv2ray will continue running, but you cannot change the UI language.")); QvMessageBoxWarn(nullptr, QObject::tr("Cannot load languages"),
} else { QObject::tr("Qv2ray will continue running, but you cannot change the UI language."));
for (auto lang : langs) { }
LOG(MODULE_INIT, "Found Translator: " + lang) else
} {
for (auto lang : langs) { LOG(MODULE_INIT, "Found Translator: " + lang) }
} }
// Qv2ray Initialize, find possible config paths and verify them. // Qv2ray Initialize, find possible config paths and verify them.
if (!initialiseQv2ray()) { if (!initialiseQv2ray()) { return -1; }
return -1;
}
// Load the config for upgrade, but do not parse it to the struct. // Load the config for upgrade, but do not parse it to the struct.
auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE)); auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE));
auto confVersion = conf["config_version"].toVariant().toString().toInt(); auto confVersion = conf["config_version"].toVariant().toString().toInt();
if (confVersion > QV2RAY_CONFIG_VERSION) { if (confVersion > QV2RAY_CONFIG_VERSION)
{
// Config version is larger than the current version... // Config version is larger than the current version...
// This is rare but it may happen.... // This is rare but it may happen....
QvMessageBoxWarn(nullptr, QObject::tr("Qv2ray Cannot Continue"), QvMessageBoxWarn(nullptr, QObject::tr("Qv2ray Cannot Continue"),
QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE + QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE +
QObject::tr("Please check if there's an issue explaining about it.") + NEWLINE + QObject::tr("Please check if there's an issue explaining about it.") + NEWLINE +
QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE + QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE +
QObject::tr("Qv2ray will now exit.")); QObject::tr("Qv2ray will now exit."));
return -2; return -2;
} }
if (confVersion < QV2RAY_CONFIG_VERSION) { if (confVersion < QV2RAY_CONFIG_VERSION)
{
// That is, config file needs to be upgraded. // That is, config file needs to be upgraded.
conf = Qv2ray::UpgradeConfig(confVersion, QV2RAY_CONFIG_VERSION, conf); conf = Qv2ray::UpgradeConfig(confVersion, QV2RAY_CONFIG_VERSION, conf);
} }
@ -356,7 +392,8 @@ int main(int argc, char *argv[])
qApp->removeTranslator(Qv2rayTranslator.get()); qApp->removeTranslator(Qv2rayTranslator.get());
LOG(MODULE_INIT, "Removed system translations") LOG(MODULE_INIT, "Removed system translations")
if (confObject.uiConfig.language.isEmpty()) { if (confObject.uiConfig.language.isEmpty())
{
// Prevent empty. // Prevent empty.
LOG(MODULE_UI, "Setting default UI language to en-US") LOG(MODULE_UI, "Setting default UI language to en-US")
confObject.uiConfig.language = "en-US"; confObject.uiConfig.language = "en-US";
@ -364,15 +401,15 @@ int main(int argc, char *argv[])
Qv2rayTranslator = std::move(QvTranslator(confObject.uiConfig.language).pTranslator); Qv2rayTranslator = std::move(QvTranslator(confObject.uiConfig.language).pTranslator);
if (qApp->installTranslator(Qv2rayTranslator.get())) { if (qApp->installTranslator(Qv2rayTranslator.get()))
LOG(MODULE_INIT, "Successfully installed a translator for " + confObject.uiConfig.language) { LOG(MODULE_INIT, "Successfully installed a translator for " + confObject.uiConfig.language) }
} else { else
{
// Do not translate these..... // Do not translate these.....
// If a translator fails to load, pop up a message. // If a translator fails to load, pop up a message.
QvMessageBoxWarn( QvMessageBoxWarn(nullptr, "Translation Failed",
nullptr, "Translation Failed", "Cannot load translation for " + confObject.uiConfig.language + ", English is now used." + NEWLINE + NEWLINE +
"Cannot load translation for " + confObject.uiConfig.language + ", English is now used." + NEWLINE + NEWLINE + "Please go to Preferences Window to change language or open an Issue");
"Please go to Preferences Window to change language or open an Issue");
} }
// Let's save the config. // Let's save the config.
@ -383,16 +420,16 @@ int main(int argc, char *argv[])
auto osslCurVersion = QSslSocket::sslLibraryVersionString(); auto osslCurVersion = QSslSocket::sslLibraryVersionString();
LOG(MODULE_NETWORK, "Current OpenSSL version: " + osslCurVersion) LOG(MODULE_NETWORK, "Current OpenSSL version: " + osslCurVersion)
if (!QSslSocket::supportsSsl()) { if (!QSslSocket::supportsSsl())
{
LOG(MODULE_NETWORK, "Required OpenSSL version: " + osslReqVersion) LOG(MODULE_NETWORK, "Required OpenSSL version: " + osslReqVersion)
LOG(MODULE_NETWORK, "OpenSSL library MISSING, Quitting.") LOG(MODULE_NETWORK, "OpenSSL library MISSING, Quitting.")
QvMessageBoxWarn(nullptr, QObject::tr("Dependency Missing"), QvMessageBoxWarn(nullptr, QObject::tr("Dependency Missing"),
QObject::tr("Cannot find openssl libs") + NEWLINE + QObject::tr("Cannot find openssl libs") + NEWLINE +
QObject::tr("This could be caused by a missing of `openssl` package in your system.") + NEWLINE + QObject::tr("This could be caused by a missing of `openssl` package in your system.") + NEWLINE +
QObject::tr("If you are using an AppImage from Github Action, please report a bug.") + NEWLINE + NEWLINE + QObject::tr("If you are using an AppImage from Github Action, please report a bug.") + NEWLINE + NEWLINE +
QObject::tr("Technical Details") + NEWLINE + QObject::tr("Technical Details") + NEWLINE + "OSsl.Rq.V=" + osslReqVersion + NEWLINE +
"OSsl.Rq.V=" + osslReqVersion + NEWLINE + "OSsl.Cr.V=" + osslCurVersion);
"OSsl.Cr.V=" + osslCurVersion);
return -3; return -3;
} }
@ -406,7 +443,8 @@ int main(int argc, char *argv[])
#ifdef QV2RAY_USE_BUILTIN_DARKTHEME #ifdef QV2RAY_USE_BUILTIN_DARKTHEME
LOG(MODULE_UI, "Using built-in theme.") LOG(MODULE_UI, "Using built-in theme.")
if (confObject.uiConfig.useDarkTheme) { if (confObject.uiConfig.useDarkTheme)
{
LOG(MODULE_UI, " --> Using built-in dark theme.") LOG(MODULE_UI, " --> Using built-in dark theme.")
// From https://forum.qt.io/topic/101391/windows-10-dark-theme/4 // From https://forum.qt.io/topic/101391/windows-10-dark-theme/4
_qApp.setStyle("Fusion"); _qApp.setStyle("Fusion");
@ -441,7 +479,8 @@ int main(int argc, char *argv[])
QStringList themes = QStyleFactory::keys(); QStringList themes = QStyleFactory::keys();
//_qApp.setDesktopFileName("qv2ray.desktop"); //_qApp.setDesktopFileName("qv2ray.desktop");
if (themes.contains(confObject.uiConfig.theme)) { if (themes.contains(confObject.uiConfig.theme))
{
_qApp.setStyle(confObject.uiConfig.theme); _qApp.setStyle(confObject.uiConfig.theme);
LOG(MODULE_INIT + " " + MODULE_UI, "Setting Qv2ray UI themes: " + confObject.uiConfig.theme) LOG(MODULE_INIT + " " + MODULE_UI, "Setting Qv2ray UI themes: " + confObject.uiConfig.theme)
} }
@ -449,7 +488,8 @@ int main(int argc, char *argv[])
#endif #endif
#ifndef QT_DEBUG #ifndef QT_DEBUG
try { try
{
#endif #endif
//_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar); //_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar);
// Initialise Connection Handler // Initialise Connection Handler
@ -465,23 +505,19 @@ int main(int argc, char *argv[])
// Handler for session logout, shutdown, etc. // Handler for session logout, shutdown, etc.
// Will not block. // Will not block.
QGuiApplication::setFallbackSessionManagementEnabled(false); QGuiApplication::setFallbackSessionManagementEnabled(false);
QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, []() { QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, []() { LOG(MODULE_INIT, "Quit triggered by session manager.") });
LOG(MODULE_INIT, "Quit triggered by session manager.")
});
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
signal(SIGUSR1, [](int) { signal(SIGUSR1, [](int) { emit MainWindow::mwInstance->Connect(); });
emit MainWindow::mwInstance->Connect(); signal(SIGUSR2, [](int) { emit MainWindow::mwInstance->DisConnect(); });
});
signal(SIGUSR2, [](int) {
emit MainWindow::mwInstance->DisConnect();
});
#endif #endif
auto rcode = _qApp.exec(); auto rcode = _qApp.exec();
delete ConnectionManager; delete ConnectionManager;
LOG(MODULE_INIT, "Quitting normally") LOG(MODULE_INIT, "Quitting normally")
return rcode; return rcode;
#ifndef QT_DEBUG #ifndef QT_DEBUG
} catch (...) { }
catch (...)
{
QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now."); QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
LOG(MODULE_INIT, "EXCEPTION THROWN: " __FILE__) LOG(MODULE_INIT, "EXCEPTION THROWN: " __FILE__)
return -99; return -99;

View File

@ -1,36 +1,45 @@
#include "w_InboundEditor.hpp" #include "w_InboundEditor.hpp"
#include "core/CoreUtils.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp" #include "core/connection/ConnectionIO.hpp"
static bool isLoading = false; static bool isLoading = false;
#define CHECKLOADING if(isLoading) return; #define CHECKLOADING \
if (isLoading) return;
InboundEditor::InboundEditor(INBOUND root, QWidget *parent) : InboundEditor::InboundEditor(INBOUND root, QWidget *parent) : QDialog(parent), original(root)
QDialog(parent),
original(root)
{ {
QvMessageBusConnect(InboundEditor); QvMessageBusConnect(InboundEditor);
setupUi(this); setupUi(this);
this->root = root; this->root = root;
auto inboundType = root["protocol"].toString(); auto inboundType = root["protocol"].toString();
allocate = root["allocate"].toObject(); allocate = root["allocate"].toObject();
sniffing = root["sniffing"].toObject(); sniffing = root["sniffing"].toObject();
if (inboundType == "http") { if (inboundType == "http") { httpSettings = root["settings"].toObject(); }
httpSettings = root["settings"].toObject(); else if (inboundType == "socks")
} else if (inboundType == "socks") { {
socksSettings = root["settings"].toObject(); socksSettings = root["settings"].toObject();
} else if (inboundType == "dokodemo-door") { }
else if (inboundType == "dokodemo-door")
{
dokoSettings = root["settings"].toObject(); dokoSettings = root["settings"].toObject();
} else if (inboundType == "mtproto") { }
else if (inboundType == "mtproto")
{
mtSettings = root["settings"].toObject(); mtSettings = root["settings"].toObject();
} else { }
if (!root["protocol"].toString().isEmpty()) { else
{
if (!root["protocol"].toString().isEmpty())
{
LOG(MODULE_UI, "Unsupported inbound type: " + inboundType) LOG(MODULE_UI, "Unsupported inbound type: " + inboundType)
QvMessageBoxWarn(this, tr("Inbound type not supported"), tr("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" + QvMessageBoxWarn(this, tr("Inbound type not supported"),
tr("Inbound: ") + inboundType); tr("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" +
} else { tr("Inbound: ") + inboundType);
}
else
{
LOG(MODULE_UI, "Creating new inbound config") LOG(MODULE_UI, "Creating new inbound config")
root["protocol"] = inboundType = "http"; root["protocol"] = inboundType = "http";
} }
@ -41,10 +50,9 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
QvMessageBusSlotImpl(InboundEditor) QvMessageBusSlotImpl(InboundEditor)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl\ {
MBHideDefaultImpl\ MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl\
} }
} }
@ -59,27 +67,28 @@ INBOUND InboundEditor::GenerateNewRoot()
INBOUND _newRoot = root; INBOUND _newRoot = root;
auto inboundType = root["protocol"].toString(); auto inboundType = root["protocol"].toString();
if (inboundType.isNull() || inboundType.isEmpty()) { if (inboundType.isNull() || inboundType.isEmpty()) { inboundType = "http"; }
inboundType = "http";
}
if (inboundType == "http") { if (inboundType == "http")
{
// Remove useless, misleading 'accounts' array. // Remove useless, misleading 'accounts' array.
if (httpAccountListBox->count() == 0) { if (httpAccountListBox->count() == 0) { httpSettings.remove("accounts"); }
httpSettings.remove("accounts");
}
_newRoot["settings"] = httpSettings; _newRoot["settings"] = httpSettings;
} else if (inboundType == "socks") { }
else if (inboundType == "socks")
{
// See above // See above
if (socksAccountListBox->count() == 0) { if (socksAccountListBox->count() == 0) { socksSettings.remove("accounts"); }
socksSettings.remove("accounts");
}
_newRoot["settings"] = socksSettings; _newRoot["settings"] = socksSettings;
} else if (inboundType == "dokodemo-door") { }
else if (inboundType == "dokodemo-door")
{
_newRoot["settings"] = dokoSettings; _newRoot["settings"] = dokoSettings;
} else if (inboundType == "mtproto") { }
else if (inboundType == "mtproto")
{
_newRoot["settings"] = mtSettings; _newRoot["settings"] = mtSettings;
} }
@ -102,7 +111,8 @@ void InboundEditor::LoadUIData()
// //
destOverrideList->setEnabled(sniffing["enabled"].toBool()); destOverrideList->setEnabled(sniffing["enabled"].toBool());
for (auto item : sniffing["destOverride"].toArray()) { for (auto item : sniffing["destOverride"].toArray())
{
if (item.toString().toLower() == "http") destOverrideList->item(0)->setCheckState(Qt::Checked); if (item.toString().toLower() == "http") destOverrideList->item(0)->setCheckState(Qt::Checked);
if (item.toString().toLower() == "tls") destOverrideList->item(1)->setCheckState(Qt::Checked); if (item.toString().toLower() == "tls") destOverrideList->item(1)->setCheckState(Qt::Checked);
@ -118,9 +128,8 @@ void InboundEditor::LoadUIData()
httpUserLevelSB->setValue(httpSettings["userLevel"].toInt()); httpUserLevelSB->setValue(httpSettings["userLevel"].toInt());
httpAccountListBox->clear(); httpAccountListBox->clear();
for (auto user : httpSettings["accounts"].toArray()) { for (auto user : httpSettings["accounts"].toArray())
httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); { httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); }
}
// SOCKS // SOCKS
socksAuthCombo->setCurrentText(socksSettings["auth"].toString()); socksAuthCombo->setCurrentText(socksSettings["auth"].toString());
@ -128,9 +137,8 @@ void InboundEditor::LoadUIData()
socksUDPIPAddrTxt->setText(socksSettings["ip"].toString()); socksUDPIPAddrTxt->setText(socksSettings["ip"].toString());
socksUserLevelSB->setValue(socksSettings["userLevel"].toInt()); socksUserLevelSB->setValue(socksSettings["userLevel"].toInt());
for (auto user : socksSettings["accounts"].toArray()) { for (auto user : socksSettings["accounts"].toArray())
socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); { socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); }
}
// Dokodemo-Door // Dokodemo-Door
dokoFollowRedirectCB->setChecked(dokoSettings["followRedirect"].toBool()); dokoFollowRedirectCB->setChecked(dokoSettings["followRedirect"].toBool());
@ -190,15 +198,18 @@ void InboundEditor::on_httpRemoveUserBtn_clicked()
{ {
CHECKLOADING CHECKLOADING
if (httpAccountListBox->currentRow() != -1) { if (httpAccountListBox->currentRow() != -1)
{
auto item = httpAccountListBox->currentItem(); auto item = httpAccountListBox->currentItem();
auto list = httpSettings["accounts"].toArray(); auto list = httpSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) { for (int i = 0; i < list.count(); i++)
{
auto user = list[i].toObject(); auto user = list[i].toObject();
auto entry = user["user"].toString() + ":" + user["pass"].toString(); auto entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) { if (entry == item->text().trimmed())
{
list.removeAt(i); list.removeAt(i);
httpSettings["accounts"] = list; httpSettings["accounts"] = list;
LOG(MODULE_UI, "Removed http inbound user " + entry) LOG(MODULE_UI, "Removed http inbound user " + entry)
@ -206,8 +217,11 @@ void InboundEditor::on_httpRemoveUserBtn_clicked()
} }
} }
//QvMessageBox(this, tr("Removing a user"), tr("No user has been removed. Why?")); // QvMessageBox(this, tr("Removing a user"), tr("No user has been
} else { // removed. Why?"));
}
else
{
QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet.")); QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet."));
} }
} }
@ -220,10 +234,12 @@ void InboundEditor::on_httpAddUserBtn_clicked()
// //
auto list = httpSettings["accounts"].toArray(); auto list = httpSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) { for (int i = 0; i < list.count(); i++)
{
auto _user = list[i].toObject(); auto _user = list[i].toObject();
if (_user["user"].toString() == user) { if (_user["user"].toString() == user)
{
QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already.")); QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already."));
return; return;
} }
@ -243,15 +259,18 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
{ {
CHECKLOADING CHECKLOADING
if (socksAccountListBox->currentRow() != -1) { if (socksAccountListBox->currentRow() != -1)
{
auto item = socksAccountListBox->currentItem(); auto item = socksAccountListBox->currentItem();
auto list = socksSettings["accounts"].toArray(); auto list = socksSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) { for (int i = 0; i < list.count(); i++)
{
auto user = list[i].toObject(); auto user = list[i].toObject();
auto entry = user["user"].toString() + ":" + user["pass"].toString(); auto entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) { if (entry == item->text().trimmed())
{
list.removeAt(i); list.removeAt(i);
socksSettings["accounts"] = list; socksSettings["accounts"] = list;
LOG(MODULE_UI, "Removed http inbound user " + entry) LOG(MODULE_UI, "Removed http inbound user " + entry)
@ -259,7 +278,9 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
return; return;
} }
} }
} else { }
else
{
QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet.")); QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet."));
} }
} }
@ -272,10 +293,12 @@ void InboundEditor::on_socksAddUserBtn_clicked()
// //
auto list = socksSettings["accounts"].toArray(); auto list = socksSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) { for (int i = 0; i < list.count(); i++)
{
auto _user = list[i].toObject(); auto _user = list[i].toObject();
if (_user["user"].toString() == user) { if (_user["user"].toString() == user)
{
QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already.")); QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already."));
return; return;
} }
@ -322,12 +345,11 @@ void InboundEditor::on_destOverrideList_itemChanged(QListWidgetItem *item)
Q_UNUSED(item) Q_UNUSED(item)
QJsonArray list; QJsonArray list;
for (int i = 0; i < destOverrideList->count(); i++) { for (int i = 0; i < destOverrideList->count(); i++)
{
auto _item = destOverrideList->item(i); auto _item = destOverrideList->item(i);
if (_item->checkState() == Qt::Checked) { if (_item->checkState() == Qt::Checked) { list.append(_item->text().toLower()); }
list.append(_item->text().toLower());
}
} }
sniffing["destOverride"] = list; sniffing["destOverride"] = list;
@ -360,7 +382,7 @@ void InboundEditor::on_dokoIPAddrTxt_textEdited(const QString &arg1)
void InboundEditor::on_dokoPortSB_valueChanged(int arg1) void InboundEditor::on_dokoPortSB_valueChanged(int arg1)
{ {
CHECKLOADING CHECKLOADING
dokoSettings["port"] = arg1; dokoSettings["port"] = arg1;
} }
void InboundEditor::on_dokoTCPCB_stateChanged(int arg1) void InboundEditor::on_dokoTCPCB_stateChanged(int arg1)
@ -461,5 +483,5 @@ void InboundEditor::on_inboundPortTxt_textEdited(const QString &arg1)
void InboundEditor::on_socksAuthCombo_currentIndexChanged(const QString &arg1) void InboundEditor::on_socksAuthCombo_currentIndexChanged(const QString &arg1)
{ {
CHECKLOADING CHECKLOADING
socksSettings["auth"] = arg1.toLower(); socksSettings["auth"] = arg1.toLower();
} }

View File

@ -1,97 +1,100 @@
#pragma once #pragma once
#include "base/Qv2rayBase.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_InboundEditor.h"
#include <QDialog> #include <QDialog>
#include <QJsonObject> #include <QJsonObject>
#include <QListWidgetItem> #include <QListWidgetItem>
#include "ui_w_InboundEditor.h"
#include "base/Qv2rayBase.hpp"
#include "ui/messaging/QvMessageBus.hpp"
class InboundEditor : public QDialog, private Ui::InboundEditor class InboundEditor
: public QDialog
, private Ui::InboundEditor
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InboundEditor(INBOUND root, QWidget *parent = nullptr); explicit InboundEditor(INBOUND root, QWidget *parent = nullptr);
~InboundEditor(); ~InboundEditor();
INBOUND OpenEditor(); INBOUND OpenEditor();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_inboundProtocolCombo_currentIndexChanged(const QString &arg1); void on_inboundProtocolCombo_currentIndexChanged(const QString &arg1);
void on_inboundProtocolCombo_currentIndexChanged(int index); void on_inboundProtocolCombo_currentIndexChanged(int index);
void on_inboundTagTxt_textEdited(const QString &arg1); void on_inboundTagTxt_textEdited(const QString &arg1);
void on_httpTimeoutSpinBox_valueChanged(int arg1); void on_httpTimeoutSpinBox_valueChanged(int arg1);
void on_httpTransparentCB_stateChanged(int arg1); void on_httpTransparentCB_stateChanged(int arg1);
void on_httpUserLevelSB_valueChanged(int arg1); void on_httpUserLevelSB_valueChanged(int arg1);
void on_httpRemoveUserBtn_clicked(); void on_httpRemoveUserBtn_clicked();
void on_httpAddUserBtn_clicked(); void on_httpAddUserBtn_clicked();
void on_strategyCombo_currentIndexChanged(const QString &arg1); void on_strategyCombo_currentIndexChanged(const QString &arg1);
void on_refreshNumberBox_valueChanged(int arg1); void on_refreshNumberBox_valueChanged(int arg1);
void on_concurrencyNumberBox_valueChanged(int arg1); void on_concurrencyNumberBox_valueChanged(int arg1);
void on_enableSniffingCB_stateChanged(int arg1); void on_enableSniffingCB_stateChanged(int arg1);
void on_destOverrideList_itemChanged(QListWidgetItem *item); void on_destOverrideList_itemChanged(QListWidgetItem *item);
void on_socksUDPCB_stateChanged(int arg1); void on_socksUDPCB_stateChanged(int arg1);
void on_socksUDPIPAddrTxt_textEdited(const QString &arg1); void on_socksUDPIPAddrTxt_textEdited(const QString &arg1);
void on_socksUserLevelSB_valueChanged(int arg1); void on_socksUserLevelSB_valueChanged(int arg1);
void on_socksRemoveUserBtn_clicked(); void on_socksRemoveUserBtn_clicked();
void on_socksAddUserBtn_clicked(); void on_socksAddUserBtn_clicked();
void on_dokoIPAddrTxt_textEdited(const QString &arg1); void on_dokoIPAddrTxt_textEdited(const QString &arg1);
void on_dokoPortSB_valueChanged(int arg1); void on_dokoPortSB_valueChanged(int arg1);
void on_dokoTCPCB_stateChanged(int arg1); void on_dokoTCPCB_stateChanged(int arg1);
void on_dokoUDPCB_stateChanged(int arg1); void on_dokoUDPCB_stateChanged(int arg1);
void on_dokoTimeoutSB_valueChanged(int arg1); void on_dokoTimeoutSB_valueChanged(int arg1);
void on_dokoFollowRedirectCB_stateChanged(int arg1); void on_dokoFollowRedirectCB_stateChanged(int arg1);
void on_dokoUserLevelSB_valueChanged(int arg1); void on_dokoUserLevelSB_valueChanged(int arg1);
void on_mtEMailTxt_textEdited(const QString &arg1); void on_mtEMailTxt_textEdited(const QString &arg1);
void on_mtSecretTxt_textEdited(const QString &arg1); void on_mtSecretTxt_textEdited(const QString &arg1);
void on_mtUserLevelSB_valueChanged(int arg1); void on_mtUserLevelSB_valueChanged(int arg1);
void on_inboundHostTxt_textEdited(const QString &arg1); void on_inboundHostTxt_textEdited(const QString &arg1);
void on_inboundPortTxt_textEdited(const QString &arg1); void on_inboundPortTxt_textEdited(const QString &arg1);
void on_socksAuthCombo_currentIndexChanged(const QString &arg1); void on_socksAuthCombo_currentIndexChanged(const QString &arg1);
private: private:
INBOUND GenerateNewRoot(); INBOUND GenerateNewRoot();
void LoadUIData(); void LoadUIData();
INBOUND original; INBOUND original;
INBOUND root; INBOUND root;
// //
QJsonObject httpSettings; QJsonObject httpSettings;
QJsonObject socksSettings; QJsonObject socksSettings;
QJsonObject mtSettings; QJsonObject mtSettings;
QJsonObject dokoSettings; QJsonObject dokoSettings;
// //
QJsonObject sniffing; QJsonObject sniffing;
QJsonObject allocate; QJsonObject allocate;
}; };

View File

@ -2,20 +2,22 @@
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) : JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) : QDialog(parent)
QDialog(parent)
{ {
QvMessageBusConnect(JsonEditor); QvMessageBusConnect(JsonEditor);
setupUi(this); setupUi(this);
original = rootObject; original = rootObject;
final = rootObject; final = rootObject;
QString jsonString = JsonToString(rootObject); QString jsonString = JsonToString(rootObject);
if (VerifyJsonString(jsonString).isEmpty()) { if (VerifyJsonString(jsonString).isEmpty())
{
LOG(MODULE_UI, "Begin loading Json Model") LOG(MODULE_UI, "Begin loading Json Model")
jsonTree->setModel(&model); jsonTree->setModel(&model);
model.loadJson(QJsonDocument(rootObject).toJson()); model.loadJson(QJsonDocument(rootObject).toJson());
} else { }
else
{
QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("Original Json may contain syntax errors. Json tree is disabled.")); QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("Original Json may contain syntax errors. Json tree is disabled."));
} }
@ -26,10 +28,9 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) :
QvMessageBusSlotImpl(JsonEditor) QvMessageBusSlotImpl(JsonEditor)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -38,7 +39,8 @@ QJsonObject JsonEditor::OpenEditor()
int resultCode = this->exec(); int resultCode = this->exec();
auto string = jsonEditor->toPlainText(); auto string = jsonEditor->toPlainText();
while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) { while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty())
{
QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("You must correct these errors before continue.")); QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("You must correct these errors before continue."));
resultCode = this->exec(); resultCode = this->exec();
string = jsonEditor->toPlainText(); string = jsonEditor->toPlainText();
@ -57,13 +59,16 @@ void JsonEditor::on_jsonEditor_textChanged()
auto VerifyResult = VerifyJsonString(string); auto VerifyResult = VerifyJsonString(string);
jsonValidateStatus->setText(VerifyResult); jsonValidateStatus->setText(VerifyResult);
if (VerifyResult.isEmpty()) { if (VerifyResult.isEmpty())
{
BLACK(jsonEditor) BLACK(jsonEditor)
final = JsonFromString(string); final = JsonFromString(string);
model.loadJson(QJsonDocument(final).toJson()); model.loadJson(QJsonDocument(final).toJson());
jsonTree->expandAll(); jsonTree->expandAll();
jsonTree->resizeColumnToContents(0); jsonTree->resizeColumnToContents(0);
} else { }
else
{
RED(jsonEditor) RED(jsonEditor)
} }
} }
@ -74,10 +79,13 @@ void JsonEditor::on_formatJsonBtn_clicked()
auto VerifyResult = VerifyJsonString(string); auto VerifyResult = VerifyJsonString(string);
jsonValidateStatus->setText(VerifyResult); jsonValidateStatus->setText(VerifyResult);
if (VerifyResult.isEmpty()) { if (VerifyResult.isEmpty())
{
BLACK(jsonEditor) BLACK(jsonEditor)
jsonEditor->setPlainText(JsonToString(JsonFromString(string))); jsonEditor->setPlainText(JsonToString(JsonFromString(string)));
} else { }
else
{
RED(jsonEditor) RED(jsonEditor)
QvMessageBoxWarn(this, tr("Syntax Errors"), tr("Please fix the JSON errors before continue")); QvMessageBoxWarn(this, tr("Syntax Errors"), tr("Please fix the JSON errors before continue"));
} }

View File

@ -1,30 +1,33 @@
#pragma once #pragma once
#include "base/Qv2rayBase.hpp"
#include "common/QJsonModel.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_JsonEditor.h"
#include <QDialog> #include <QDialog>
#include <QtCore> #include <QtCore>
#include "common/QJsonModel.hpp"
#include "base/Qv2rayBase.hpp"
#include "ui_w_JsonEditor.h"
#include "ui/messaging/QvMessageBus.hpp"
class JsonEditor : public QDialog, private Ui::JsonEditor class JsonEditor
: public QDialog
, private Ui::JsonEditor
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JsonEditor(QJsonObject rootObject, QWidget *parent = nullptr); explicit JsonEditor(QJsonObject rootObject, QWidget *parent = nullptr);
~JsonEditor(); ~JsonEditor();
QJsonObject OpenEditor(); QJsonObject OpenEditor();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_jsonEditor_textChanged(); void on_jsonEditor_textChanged();
void on_formatJsonBtn_clicked(); void on_formatJsonBtn_clicked();
private: private:
QJsonModel model; QJsonModel model;
QJsonObject original; QJsonObject original;
QJsonObject final; QJsonObject final;
}; };

View File

@ -1,20 +1,16 @@
#include <QDebug> #include <QDebug>
#include "w_OutboundEditor.hpp"
#include "core/connection/Generation.hpp"
#include "ui/editors/w_JsonEditor.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
#include "ui/w_MainWindow.hpp"
#include <QFile> #include <QFile>
#include <QIntValidator> #include <QIntValidator>
#include <iostream> #include <iostream>
#include "w_OutboundEditor.hpp" OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), Tag(""), Mux(), vmess(), shadowsocks()
#include "ui/w_MainWindow.hpp"
#include "ui/editors/w_JsonEditor.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
#include "core/connection/Generation.hpp"
OutboundEditor::OutboundEditor(QWidget *parent)
: QDialog(parent),
Tag(""),
Mux(),
vmess(),
shadowsocks()
{ {
QvMessageBusConnect(OutboundEditor); QvMessageBusConnect(OutboundEditor);
setupUi(this); setupUi(this);
@ -40,10 +36,9 @@ OutboundEditor::OutboundEditor(QWidget *parent)
QvMessageBusSlotImpl(OutboundEditor) QvMessageBusSlotImpl(OutboundEditor)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -57,20 +52,28 @@ OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : Outbou
useFProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false); useFProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false);
ssWidget->SetStreamObject(StructFromJsonString<StreamSettingsObject>(JsonToString(outboundEntry["streamSettings"].toObject()))); ssWidget->SetStreamObject(StructFromJsonString<StreamSettingsObject>(JsonToString(outboundEntry["streamSettings"].toObject())));
if (OutboundType == "vmess") { if (OutboundType == "vmess")
vmess = StructFromJsonString<VMessServerObject>(JsonToString(outboundEntry["settings"].toObject()["vnext"].toArray().first().toObject())); {
vmess =
StructFromJsonString<VMessServerObject>(JsonToString(outboundEntry["settings"].toObject()["vnext"].toArray().first().toObject()));
shadowsocks.port = vmess.port; shadowsocks.port = vmess.port;
shadowsocks.address = vmess.address; shadowsocks.address = vmess.address;
socks.address = vmess.address; socks.address = vmess.address;
socks.port = vmess.port; socks.port = vmess.port;
} else if (OutboundType == "shadowsocks") { }
shadowsocks = StructFromJsonString<ShadowSocksServerObject>(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); else if (OutboundType == "shadowsocks")
{
shadowsocks = StructFromJsonString<ShadowSocksServerObject>(
JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject()));
vmess.address = shadowsocks.address; vmess.address = shadowsocks.address;
vmess.port = shadowsocks.port; vmess.port = shadowsocks.port;
socks.address = shadowsocks.address; socks.address = shadowsocks.address;
socks.port = shadowsocks.port; socks.port = shadowsocks.port;
} else if (OutboundType == "socks") { }
socks = StructFromJsonString<SocksServerObject>(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); else if (OutboundType == "socks")
{
socks =
StructFromJsonString<SocksServerObject>(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject()));
vmess.address = socks.address; vmess.address = socks.address;
vmess.port = socks.port; vmess.port = socks.port;
shadowsocks.address = socks.address; shadowsocks.address = socks.address;
@ -81,7 +84,6 @@ OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : Outbou
Result = GenerateConnectionJson(); Result = GenerateConnectionJson();
} }
OutboundEditor::~OutboundEditor() OutboundEditor::~OutboundEditor()
{ {
} }
@ -106,18 +108,23 @@ OUTBOUND OutboundEditor::GenerateConnectionJson()
OUTBOUNDSETTING settings; OUTBOUNDSETTING settings;
auto streaming = GetRootObject(ssWidget->GetStreamSettings()); auto streaming = GetRootObject(ssWidget->GetStreamSettings());
if (OutboundType == "vmess") { if (OutboundType == "vmess")
{
// VMess is only a ServerObject, and we need an array { "vnext": [] } // VMess is only a ServerObject, and we need an array { "vnext": [] }
QJsonArray vnext; QJsonArray vnext;
vnext.append(GetRootObject(vmess)); vnext.append(GetRootObject(vmess));
settings.insert("vnext", vnext); settings.insert("vnext", vnext);
} else if (OutboundType == "shadowsocks") { }
else if (OutboundType == "shadowsocks")
{
streaming = QJsonObject(); streaming = QJsonObject();
LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.") LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.")
QJsonArray servers; QJsonArray servers;
servers.append(GetRootObject(shadowsocks)); servers.append(GetRootObject(shadowsocks));
settings["servers"] = servers; settings["servers"] = servers;
} else if (OutboundType == "socks") { }
else if (OutboundType == "socks")
{
streaming = QJsonObject(); streaming = QJsonObject();
LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.") LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.")
QJsonArray servers; QJsonArray servers;
@ -132,14 +139,17 @@ OUTBOUND OutboundEditor::GenerateConnectionJson()
void OutboundEditor::ReloadGUI() void OutboundEditor::ReloadGUI()
{ {
if (OutboundType == "vmess") { if (OutboundType == "vmess")
{
outBoundTypeCombo->setCurrentIndex(0); outBoundTypeCombo->setCurrentIndex(0);
ipLineEdit->setText(vmess.address); ipLineEdit->setText(vmess.address);
portLineEdit->setText(QSTRN(vmess.port)); portLineEdit->setText(QSTRN(vmess.port));
idLineEdit->setText(vmess.users.front().id); idLineEdit->setText(vmess.users.front().id);
alterLineEdit->setValue(vmess.users.front().alterId); alterLineEdit->setValue(vmess.users.front().alterId);
securityCombo->setCurrentText(vmess.users.front().security); securityCombo->setCurrentText(vmess.users.front().security);
} else if (OutboundType == "shadowsocks") { }
else if (OutboundType == "shadowsocks")
{
outBoundTypeCombo->setCurrentIndex(1); outBoundTypeCombo->setCurrentIndex(1);
// ShadowSocks Configs // ShadowSocks Configs
ipLineEdit->setText(shadowsocks.address); ipLineEdit->setText(shadowsocks.address);
@ -149,7 +159,9 @@ void OutboundEditor::ReloadGUI()
ss_otaCheckBox->setChecked(shadowsocks.ota); ss_otaCheckBox->setChecked(shadowsocks.ota);
ss_passwordTxt->setText(shadowsocks.password); ss_passwordTxt->setText(shadowsocks.password);
ss_encryptionMethod->setCurrentText(shadowsocks.method); ss_encryptionMethod->setCurrentText(shadowsocks.method);
} else if (OutboundType == "socks") { }
else if (OutboundType == "socks")
{
outBoundTypeCombo->setCurrentIndex(2); outBoundTypeCombo->setCurrentIndex(2);
ipLineEdit->setText(socks.address); ipLineEdit->setText(socks.address);
portLineEdit->setText(QSTRN(socks.port)); portLineEdit->setText(QSTRN(socks.port));
@ -165,7 +177,6 @@ void OutboundEditor::ReloadGUI()
muxConcurrencyTxt->setValue(Mux["concurrency"].toInt()); muxConcurrencyTxt->setValue(Mux["concurrency"].toInt());
} }
void OutboundEditor::on_buttonBox_accepted() void OutboundEditor::on_buttonBox_accepted()
{ {
Result = GenerateConnectionJson(); Result = GenerateConnectionJson();
@ -180,7 +191,8 @@ void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1)
void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1) void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1)
{ {
if (arg1 != "") { if (arg1 != "")
{
vmess.port = arg1.toInt(); vmess.port = arg1.toInt();
shadowsocks.port = arg1.toInt(); shadowsocks.port = arg1.toInt();
socks.port = arg1.toInt(); socks.port = arg1.toInt();

View File

@ -1,76 +1,79 @@
#pragma once #pragma once
#include <QtCore>
#include <QDialog>
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "ui_w_OutboundEditor.h"
#include "ui/widgets/StreamSettingsWidget.hpp"
#include "ui/messaging/QvMessageBus.hpp" #include "ui/messaging/QvMessageBus.hpp"
#include "ui/widgets/StreamSettingsWidget.hpp"
#include "ui_w_OutboundEditor.h"
class OutboundEditor : public QDialog, private Ui::OutboundEditor #include <QDialog>
#include <QtCore>
class OutboundEditor
: public QDialog
, private Ui::OutboundEditor
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OutboundEditor(QWidget *parent = nullptr); explicit OutboundEditor(QWidget *parent = nullptr);
explicit OutboundEditor(OUTBOUND outboundEntry, QWidget *parent = nullptr); explicit OutboundEditor(OUTBOUND outboundEntry, QWidget *parent = nullptr);
~OutboundEditor(); ~OutboundEditor();
OUTBOUND OpenEditor(); OUTBOUND OpenEditor();
QString GetFriendlyName(); QString GetFriendlyName();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
signals: signals:
void s_reload_config(bool need_restart); void s_reload_config(bool need_restart);
private slots: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_ipLineEdit_textEdited(const QString &arg1); void on_ipLineEdit_textEdited(const QString &arg1);
void on_portLineEdit_textEdited(const QString &arg1); void on_portLineEdit_textEdited(const QString &arg1);
void on_idLineEdit_textEdited(const QString &arg1); void on_idLineEdit_textEdited(const QString &arg1);
void on_tagTxt_textEdited(const QString &arg1); void on_tagTxt_textEdited(const QString &arg1);
void on_muxEnabledCB_stateChanged(int arg1); void on_muxEnabledCB_stateChanged(int arg1);
void on_muxConcurrencyTxt_valueChanged(int arg1); void on_muxConcurrencyTxt_valueChanged(int arg1);
void on_alterLineEdit_valueChanged(int arg1); void on_alterLineEdit_valueChanged(int arg1);
void on_useFPCB_stateChanged(int arg1); void on_useFPCB_stateChanged(int arg1);
void on_outBoundTypeCombo_currentIndexChanged(int index); void on_outBoundTypeCombo_currentIndexChanged(int index);
void on_ss_emailTxt_textEdited(const QString &arg1); void on_ss_emailTxt_textEdited(const QString &arg1);
void on_ss_passwordTxt_textEdited(const QString &arg1); void on_ss_passwordTxt_textEdited(const QString &arg1);
void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1); void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1);
void on_ss_levelSpin_valueChanged(int arg1); void on_ss_levelSpin_valueChanged(int arg1);
void on_ss_otaCheckBox_stateChanged(int arg1); void on_ss_otaCheckBox_stateChanged(int arg1);
void on_socks_UserNameTxt_textEdited(const QString &arg1); void on_socks_UserNameTxt_textEdited(const QString &arg1);
void on_socks_PasswordTxt_textEdited(const QString &arg1); void on_socks_PasswordTxt_textEdited(const QString &arg1);
void on_securityCombo_currentIndexChanged(const QString &arg1); void on_securityCombo_currentIndexChanged(const QString &arg1);
private: private:
QString Tag; QString Tag;
void ReloadGUI(); void ReloadGUI();
bool useFProxy; bool useFProxy;
OUTBOUND GenerateConnectionJson(); OUTBOUND GenerateConnectionJson();
OUTBOUND Original; OUTBOUND Original;
OUTBOUND Result; OUTBOUND Result;
QJsonObject Mux; QJsonObject Mux;
// //
// Connection Configs // Connection Configs
QString OutboundType; QString OutboundType;
// //
VMessServerObject vmess; VMessServerObject vmess;
ShadowSocksServerObject shadowsocks; ShadowSocksServerObject shadowsocks;
SocksServerObject socks; SocksServerObject socks;
// //
StreamSettingsWidget *ssWidget; StreamSettingsWidget *ssWidget;
}; };

View File

@ -1,36 +1,40 @@
// WARNING // WARNING
// Since it's required for *extra.cpp to know the content of those macros defined below. // Since it's required for *extra.cpp to know the content of those macros
// We include this CPP file instead of the proper HPP file. Adding #pragma once to prevent duplicate function instances // defined below. We include this CPP file instead of the proper HPP file.
// Adding #pragma once to prevent duplicate function instances
#pragma once #pragma once
#include "w_RoutesEditor.hpp" #include "w_RoutesEditor.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/connection/Generation.hpp"
#include "w_OutboundEditor.hpp"
#include "w_JsonEditor.hpp"
#include "w_InboundEditor.hpp"
#include "ui/w_ImportConfig.hpp"
#include "core/CoreUtils.hpp"
#include "ui/models/RuleNodeModel.hpp"
#include "ui/models/InboundNodeModel.hpp"
#include "ui/models/OutboundNodeModel.hpp"
#include "NodeStyle.hpp"
#include "FlowView.hpp" #include "FlowView.hpp"
#include "FlowViewStyle.hpp" #include "FlowViewStyle.hpp"
#include "NodeStyle.hpp"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/connection/Generation.hpp"
#include "ui/models/InboundNodeModel.hpp"
#include "ui/models/OutboundNodeModel.hpp"
#include "ui/models/RuleNodeModel.hpp"
#include "ui/w_ImportConfig.hpp"
#include "w_InboundEditor.hpp"
#include "w_JsonEditor.hpp"
#include "w_OutboundEditor.hpp"
using QtNodes::FlowView; using QtNodes::FlowView;
using namespace Qv2ray::ui::nodemodels; using namespace Qv2ray::ui::nodemodels;
static bool isLoading = false; static bool isLoading = false;
#define CurrentRule this->rules[this->currentRuleTag] #define CurrentRule this->rules[this->currentRuleTag]
#define LOADINGCHECK if(isLoading) return; #define LOADINGCHECK \
#define GetFirstNodeData(node, nodeModel, dataModel) (static_cast<dataModel *>(static_cast<nodeModel *>((node).nodeDataModel())->outData(0).get())) if (isLoading) return;
#define GetFirstNodeData(node, nodeModel, dataModel) \
(static_cast<dataModel *>(static_cast<nodeModel *>((node).nodeDataModel())->outData(0).get()))
#define CHECKEMPTYRULES if (this->rules.isEmpty()) { \ #define CHECKEMPTYRULES \
LOG(MODULE_UI, "No rules currently, we add one.") \ if (this->rules.isEmpty()) \
AddNewRule(); \ { \
LOG(MODULE_UI, "No rules currently, we add one.") \
AddNewRule(); \
} }
#define GRAPH_GLOBAL_OFFSET_X -80 #define GRAPH_GLOBAL_OFFSET_X -80
@ -41,18 +45,25 @@ static bool isLoading = false;
void RouteEditor::SetupNodeWidget() void RouteEditor::SetupNodeWidget()
{ {
if (GlobalConfig.uiConfig.useDarkTheme) { if (GlobalConfig.uiConfig.useDarkTheme)
ConnectionStyle::setConnectionStyle(R"({"ConnectionStyle": {"ConstructionColor": "gray","NormalColor": "black","SelectedColor": "gray", {
ConnectionStyle::setConnectionStyle(
R"({"ConnectionStyle": {"ConstructionColor": "gray","NormalColor": "black","SelectedColor": "gray",
"SelectedHaloColor": "deepskyblue","HoveredColor": "deepskyblue","LineWidth": 3.0, "SelectedHaloColor": "deepskyblue","HoveredColor": "deepskyblue","LineWidth": 3.0,
"ConstructionLineWidth": 2.0,"PointDiameter": 10.0,"UseDataDefinedColors": true}})"); "ConstructionLineWidth": 2.0,"PointDiameter": 10.0,"UseDataDefinedColors": true}})");
} else { }
QtNodes::NodeStyle::setNodeStyle(R"({"NodeStyle": {"NormalBoundaryColor": "darkgray","SelectedBoundaryColor": "deepskyblue", else
{
QtNodes::NodeStyle::setNodeStyle(
R"({"NodeStyle": {"NormalBoundaryColor": "darkgray","SelectedBoundaryColor": "deepskyblue",
"GradientColor0": "mintcream","GradientColor1": "mintcream","GradientColor2": "mintcream", "GradientColor0": "mintcream","GradientColor1": "mintcream","GradientColor2": "mintcream",
"GradientColor3": "mintcream","ShadowColor": [200, 200, 200],"FontColor": [10, 10, 10], "GradientColor3": "mintcream","ShadowColor": [200, 200, 200],"FontColor": [10, 10, 10],
"FontColorFaded": [100, 100, 100],"ConnectionPointColor": "white","PenWidth": 2.0,"HoveredPenWidth": 2.5, "FontColorFaded": [100, 100, 100],"ConnectionPointColor": "white","PenWidth": 2.0,"HoveredPenWidth": 2.5,
"ConnectionPointDiameter": 10.0,"Opacity": 1.0}})"); "ConnectionPointDiameter": 10.0,"Opacity": 1.0}})");
QtNodes::FlowViewStyle::setStyle(R"({"FlowViewStyle": {"BackgroundColor": [255, 255, 240],"FineGridColor": [245, 245, 230],"CoarseGridColor": [235, 235, 220]}})"); QtNodes::FlowViewStyle::setStyle(
ConnectionStyle::setConnectionStyle(R"({"ConnectionStyle": {"ConstructionColor": "gray","NormalColor": "black","SelectedColor": "gray", R"({"FlowViewStyle": {"BackgroundColor": [255, 255, 240],"FineGridColor": [245, 245, 230],"CoarseGridColor": [235, 235, 220]}})");
ConnectionStyle::setConnectionStyle(
R"({"ConnectionStyle": {"ConstructionColor": "gray","NormalColor": "black","SelectedColor": "gray",
"SelectedHaloColor": "deepskyblue","HoveredColor": "deepskyblue","LineWidth": 3.0,"ConstructionLineWidth": 2.0, "SelectedHaloColor": "deepskyblue","HoveredColor": "deepskyblue","LineWidth": 3.0,"ConstructionLineWidth": 2.0,
"PointDiameter": 10.0,"UseDataDefinedColors": false}})"); "PointDiameter": 10.0,"UseDataDefinedColors": false}})");
} }
@ -91,17 +102,20 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QDialog(pare
domainStrategyCombo->setCurrentText(domainStrategy); domainStrategyCombo->setCurrentText(domainStrategy);
// Show connections in the node graph // Show connections in the node graph
for (auto in : root["inbounds"].toArray()) { for (auto in : root["inbounds"].toArray())
{
INBOUND _in = INBOUND(in.toObject()); INBOUND _in = INBOUND(in.toObject());
AddInbound(_in); AddInbound(_in);
} }
for (auto out : root["outbounds"].toArray()) { for (auto out : root["outbounds"].toArray())
{
OUTBOUND _out = OUTBOUND(out.toObject()); OUTBOUND _out = OUTBOUND(out.toObject());
AddOutbound(_out); AddOutbound(_out);
} }
for (auto item : root["routing"].toObject()["rules"].toArray()) { for (auto item : root["routing"].toObject()["rules"].toArray())
{
auto _rule = StructFromJsonString<RuleObject>(JsonToString(item.toObject())); auto _rule = StructFromJsonString<RuleObject>(JsonToString(item.toObject()));
AddRule(_rule); AddRule(_rule);
} }
@ -110,12 +124,12 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QDialog(pare
defaultOutboundCombo->setCurrentText(root["outbounds"].toArray().first().toObject()["tag"].toString()); defaultOutboundCombo->setCurrentText(root["outbounds"].toArray().first().toObject()["tag"].toString());
// Find and add balancers. // Find and add balancers.
for (auto _balancer : root["routing"].toObject()["balancers"].toArray()) { for (auto _balancer : root["routing"].toObject()["balancers"].toArray())
{
auto _balancerObject = _balancer.toObject(); auto _balancerObject = _balancer.toObject();
if (!_balancerObject["tag"].toString().isEmpty()) { if (!_balancerObject["tag"].toString().isEmpty())
balancers[_balancerObject["tag"].toString()] = _balancerObject["selector"].toVariant().toStringList(); { balancers[_balancerObject["tag"].toString()] = _balancerObject["selector"].toVariant().toStringList(); }
}
} }
isLoading = false; isLoading = false;
@ -123,10 +137,9 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QDialog(pare
QvMessageBusSlotImpl(RouteEditor) QvMessageBusSlotImpl(RouteEditor)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -140,25 +153,31 @@ void RouteEditor::onNodeClicked(Node &n)
auto isIn = inboundNodes.values().contains(&n); auto isIn = inboundNodes.values().contains(&n);
auto isRule = ruleNodes.values().contains(&n); auto isRule = ruleNodes.values().contains(&n);
if (isRule) { if (isRule)
{
// It's a rule object // It's a rule object
currentRuleTag = GetFirstNodeData(n, QvRuleNodeDataModel, RuleNodeData)->GetRuleTag(); currentRuleTag = GetFirstNodeData(n, QvRuleNodeDataModel, RuleNodeData)->GetRuleTag();
DEBUG(MODULE_GRAPH, "Selecting rule: " + currentRuleTag) DEBUG(MODULE_GRAPH, "Selecting rule: " + currentRuleTag)
ShowCurrentRuleDetail(); ShowCurrentRuleDetail();
toolBox->setCurrentIndex(1); toolBox->setCurrentIndex(1);
} else if (isOut || isIn) { }
else if (isOut || isIn)
{
// It's an inbound or an outbound. // It's an inbound or an outbound.
QString alias; QString alias;
QString host; QString host;
int port; int port;
QString protocol; QString protocol;
if (isOut) { if (isOut)
{
alias = GetFirstNodeData(n, QvOutboundNodeModel, OutboundNodeData)->GetOutbound(); alias = GetFirstNodeData(n, QvOutboundNodeModel, OutboundNodeData)->GetOutbound();
QJsonObject _root = outbounds[alias].raw(); QJsonObject _root = outbounds[alias].raw();
throw new runtime_error("Not implemented"); throw new runtime_error("Not implemented");
GetOutboundData(OUTBOUND(_root), &host, &port, &protocol); GetOutboundData(OUTBOUND(_root), &host, &port, &protocol);
} else { }
else
{
alias = GetFirstNodeData(n, QvInboundNodeModel, InboundNodeData)->GetInbound(); alias = GetFirstNodeData(n, QvInboundNodeModel, InboundNodeData)->GetInbound();
QJsonObject _root = inbounds[alias].raw(); QJsonObject _root = inbounds[alias].raw();
host = _root["listen"].toString(); host = _root["listen"].toString();
@ -170,7 +189,9 @@ void RouteEditor::onNodeClicked(Node &n)
protocolLabel->setText(protocol); protocolLabel->setText(protocol);
portLabel->setNum(port); portLabel->setNum(port);
hostLabel->setText(host); hostLabel->setText(host);
} else { }
else
{
LOG(MODULE_GRAPH, "Selected an unknown node, RARE.") LOG(MODULE_GRAPH, "Selected an unknown node, RARE.")
} }
} }
@ -185,7 +206,8 @@ void RouteEditor::onConnectionCreated(QtNodes::Connection const &c)
auto const sourceNode = c.getNode(PortType::Out); auto const sourceNode = c.getNode(PortType::Out);
auto const targetNode = c.getNode(PortType::In); auto const targetNode = c.getNode(PortType::In);
if (inboundNodes.values().contains(sourceNode) && ruleNodes.values().contains(targetNode)) { if (inboundNodes.values().contains(sourceNode) && ruleNodes.values().contains(targetNode))
{
// It's a inbound-rule connection // It's a inbound-rule connection
onNodeClicked(*sourceNode); onNodeClicked(*sourceNode);
onNodeClicked(*targetNode); onNodeClicked(*targetNode);
@ -194,14 +216,16 @@ void RouteEditor::onConnectionCreated(QtNodes::Connection const &c)
// QStringList has an helper to let us remove duplicates, see below. // QStringList has an helper to let us remove duplicates, see below.
QStringList _inbounds; QStringList _inbounds;
for (auto &&[_, conn] : nodeScene->connections()) { for (auto &&[_, conn] : nodeScene->connections())
{
auto _connection = conn.get(); auto _connection = conn.get();
if (_connection->getNode(PortType::In) == targetNode && _connection->getNode(PortType::Out) == sourceNode && _connection->id() != c.id()) { if (_connection->getNode(PortType::In) == targetNode && _connection->getNode(PortType::Out) == sourceNode &&
nodeScene->deleteConnection(*_connection); _connection->id() != c.id())
} { nodeScene->deleteConnection(*_connection); }
// Append all inbounds // Append all inbounds
else if (_connection->getNode(PortType::In) == targetNode) { else if (_connection->getNode(PortType::In) == targetNode)
{
_inbounds.append(GetFirstNodeData(*_connection->getNode(PortType::Out), QvInboundNodeModel, InboundNodeData)->GetInbound()); _inbounds.append(GetFirstNodeData(*_connection->getNode(PortType::Out), QvInboundNodeModel, InboundNodeData)->GetInbound());
} }
} }
@ -209,7 +233,9 @@ void RouteEditor::onConnectionCreated(QtNodes::Connection const &c)
// caused by multi-in connection // caused by multi-in connection
_inbounds.removeDuplicates(); _inbounds.removeDuplicates();
CurrentRule.inboundTag = _inbounds; CurrentRule.inboundTag = _inbounds;
} else if (ruleNodes.values().contains(sourceNode) && outboundNodes.values().contains(targetNode)) { }
else if (ruleNodes.values().contains(sourceNode) && outboundNodes.values().contains(targetNode))
{
// It's a rule-outbound connection // It's a rule-outbound connection
onNodeClicked(*sourceNode); onNodeClicked(*sourceNode);
onNodeClicked(*targetNode); onNodeClicked(*targetNode);
@ -219,7 +245,9 @@ void RouteEditor::onConnectionCreated(QtNodes::Connection const &c)
// Update balancer settings. // Update balancer settings.
ShowCurrentRuleDetail(); ShowCurrentRuleDetail();
LOG(MODULE_GRAPH, "Updated outbound: " + CurrentRule.outboundTag) LOG(MODULE_GRAPH, "Updated outbound: " + CurrentRule.outboundTag)
} else { }
else
{
// It's an impossible connection // It's an impossible connection
LOG(MODULE_GRAPH, "Unrecognized connection, RARE.") LOG(MODULE_GRAPH, "Unrecognized connection, RARE.")
} }
@ -235,7 +263,8 @@ void RouteEditor::onConnectionDeleted(QtNodes::Connection const &c)
auto const source = c.getNode(PortType::Out); auto const source = c.getNode(PortType::Out);
auto const target = c.getNode(PortType::In); auto const target = c.getNode(PortType::In);
if (inboundNodes.values().contains(source) && ruleNodes.values().contains(target)) { if (inboundNodes.values().contains(source) && ruleNodes.values().contains(target))
{
// It's a inbound-rule connection // It's a inbound-rule connection
onNodeClicked(*source); onNodeClicked(*source);
onNodeClicked(*target); onNodeClicked(*target);
@ -243,19 +272,21 @@ void RouteEditor::onConnectionDeleted(QtNodes::Connection const &c)
auto _inboundTag = GetFirstNodeData(*source, QvInboundNodeModel, InboundNodeData)->GetInbound(); auto _inboundTag = GetFirstNodeData(*source, QvInboundNodeModel, InboundNodeData)->GetInbound();
LOG(MODULE_UI, "Removing inbound: " + _inboundTag + " from rule: " + currentRuleTag) LOG(MODULE_UI, "Removing inbound: " + _inboundTag + " from rule: " + currentRuleTag)
CurrentRule.inboundTag.removeAll(_inboundTag); CurrentRule.inboundTag.removeAll(_inboundTag);
} else if (ruleNodes.values().contains(source) && outboundNodes.values().contains(target)) { }
else if (ruleNodes.values().contains(source) && outboundNodes.values().contains(target))
{
// It's a rule-outbound connection // It's a rule-outbound connection
onNodeClicked(*source); onNodeClicked(*source);
onNodeClicked(*target); onNodeClicked(*target);
currentRuleTag = GetFirstNodeData(*source, QvRuleNodeDataModel, RuleNodeData)->GetRuleTag(); currentRuleTag = GetFirstNodeData(*source, QvRuleNodeDataModel, RuleNodeData)->GetRuleTag();
auto _outboundTag = GetFirstNodeData(*target, QvOutboundNodeModel, OutboundNodeData)->GetOutbound(); auto _outboundTag = GetFirstNodeData(*target, QvOutboundNodeModel, OutboundNodeData)->GetOutbound();
if (!CurrentRule.QV2RAY_RULE_USE_BALANCER && CurrentRule.outboundTag == _outboundTag) { if (!CurrentRule.QV2RAY_RULE_USE_BALANCER && CurrentRule.outboundTag == _outboundTag) { CurrentRule.outboundTag.clear(); }
CurrentRule.outboundTag.clear();
}
LOG(MODULE_GRAPH, "Removing an outbound: " + _outboundTag) LOG(MODULE_GRAPH, "Removing an outbound: " + _outboundTag)
} else { }
else
{
// It's an impossible connection // It's an impossible connection
LOG(MODULE_GRAPH, "Selected an unknown node, RARE.") LOG(MODULE_GRAPH, "Selected an unknown node, RARE.")
} }
@ -265,30 +296,35 @@ CONFIGROOT RouteEditor::OpenEditor()
{ {
auto result = this->exec(); auto result = this->exec();
if (rules.isEmpty()) { if (rules.isEmpty())
// Prevent empty rule list causing mis-detection of config type to simple. {
// Prevent empty rule list causing mis-detection of config type to
// simple.
on_addRouteBtn_clicked(); on_addRouteBtn_clicked();
} }
// If clicking OK // If clicking OK
if (result == QDialog::Accepted) { if (result == QDialog::Accepted)
{
QJsonArray rulesArray; QJsonArray rulesArray;
QJsonArray _balancers; QJsonArray _balancers;
// Append rules by order // Append rules by order
for (auto i = 0; i < ruleListWidget->count(); i++) { for (auto i = 0; i < ruleListWidget->count(); i++)
{
auto _rule = rules[ruleListWidget->item(i)->text()]; auto _rule = rules[ruleListWidget->item(i)->text()];
auto ruleJsonObject = GetRootObject(_rule); auto ruleJsonObject = GetRootObject(_rule);
// Process balancer for a rule // Process balancer for a rule
if (_rule.QV2RAY_RULE_USE_BALANCER) { if (_rule.QV2RAY_RULE_USE_BALANCER)
{
// Do not use outbound tag. // Do not use outbound tag.
ruleJsonObject.remove("outboundTag"); ruleJsonObject.remove("outboundTag");
// Find balancer list // Find balancer list
if (!balancers.contains(_rule.balancerTag)) { if (!balancers.contains(_rule.balancerTag)) { LOG(MODULE_UI, "Cannot find a balancer for tag: " + _rule.balancerTag) }
LOG(MODULE_UI, "Cannot find a balancer for tag: " + _rule.balancerTag) else
} else { {
auto _balancerList = balancers[_rule.balancerTag]; auto _balancerList = balancers[_rule.balancerTag];
QJsonObject balancerEntry; QJsonObject balancerEntry;
balancerEntry["tag"] = _rule.balancerTag; balancerEntry["tag"] = _rule.balancerTag;
@ -298,13 +334,9 @@ CONFIGROOT RouteEditor::OpenEditor()
} }
// Remove some empty fields. // Remove some empty fields.
if (_rule.port.isEmpty()) { if (_rule.port.isEmpty()) { ruleJsonObject.remove("port"); }
ruleJsonObject.remove("port");
}
if (_rule.network.isEmpty()) { if (_rule.network.isEmpty()) { ruleJsonObject.remove("network"); }
ruleJsonObject.remove("network");
}
rulesArray.append(ruleJsonObject); rulesArray.append(ruleJsonObject);
} }
@ -318,22 +350,25 @@ CONFIGROOT RouteEditor::OpenEditor()
QJsonArray _outbounds; QJsonArray _outbounds;
// Convert our internal data format to QJsonArray // Convert our internal data format to QJsonArray
for (auto x : inbounds) { for (auto x : inbounds)
if (x.isEmpty()) {
continue; if (x.isEmpty()) continue;
_inbounds.append(x.raw()); _inbounds.append(x.raw());
} }
for (auto x : outbounds) { for (auto x : outbounds)
if (x.isEmpty()) {
continue; if (x.isEmpty()) continue;
if (getTag(x) == defaultOutbound) { if (getTag(x) == defaultOutbound)
{
LOG(MODULE_CONNECTION, "Pushing default outbound to the front.") LOG(MODULE_CONNECTION, "Pushing default outbound to the front.")
// Put the default outbound to the first. // Put the default outbound to the first.
_outbounds.push_front(x.raw()); _outbounds.push_front(x.raw());
} else { }
else
{
_outbounds.push_back(x.raw()); _outbounds.push_back(x.raw());
} }
} }
@ -342,7 +377,9 @@ CONFIGROOT RouteEditor::OpenEditor()
root["outbounds"] = _outbounds; root["outbounds"] = _outbounds;
root["routing"] = routing; root["routing"] = routing;
return root; return root;
} else { }
else
{
return original; return original;
} }
} }
@ -355,18 +392,22 @@ RouteEditor::~RouteEditor()
disconnect(nodeScene, &FlowScene::connectionCreated, this, &RouteEditor::onConnectionCreated); disconnect(nodeScene, &FlowScene::connectionCreated, this, &RouteEditor::onConnectionCreated);
disconnect(nodeScene, &FlowScene::nodeClicked, this, &RouteEditor::onNodeClicked); disconnect(nodeScene, &FlowScene::nodeClicked, this, &RouteEditor::onNodeClicked);
} }
void RouteEditor::on_buttonBox_accepted() {} void RouteEditor::on_buttonBox_accepted()
{
}
void RouteEditor::ShowCurrentRuleDetail() void RouteEditor::ShowCurrentRuleDetail()
{ {
LOADINGCHECK LOADINGCHECK
if (currentRuleTag.isEmpty()) { if (currentRuleTag.isEmpty())
{
LOG(MODULE_UI, "WARNING, trying to access a non-exist rule entry. return.") LOG(MODULE_UI, "WARNING, trying to access a non-exist rule entry. return.")
return; return;
} }
if (!rules.contains(currentRuleTag)) { if (!rules.contains(currentRuleTag))
{
QvMessageBoxWarn(this, tr("Show rule details"), tr("A rule cannot be found: ") + currentRuleTag); QvMessageBoxWarn(this, tr("Show rule details"), tr("A rule cannot be found: ") + currentRuleTag);
LOG(MODULE_UI, "WARNING, trying to access a non-exist rule entry. return.") LOG(MODULE_UI, "WARNING, trying to access a non-exist rule entry. return.")
return; return;
@ -384,16 +425,15 @@ void RouteEditor::ShowCurrentRuleDetail()
balancerSelectionCombo->clear(); balancerSelectionCombo->clear();
// BUG added the wrong items, should be outbound list. // BUG added the wrong items, should be outbound list.
for (auto out : outbounds) { for (auto out : outbounds) { balancerSelectionCombo->addItem((out)["tag"].toString()); }
balancerSelectionCombo->addItem((out)["tag"].toString());
}
// //
// Balancers combo and balancer list. // Balancers combo and balancer list.
enableBalancerCB->setChecked(CurrentRule.QV2RAY_RULE_USE_BALANCER); enableBalancerCB->setChecked(CurrentRule.QV2RAY_RULE_USE_BALANCER);
balancersWidget->setEnabled(CurrentRule.QV2RAY_RULE_USE_BALANCER); balancersWidget->setEnabled(CurrentRule.QV2RAY_RULE_USE_BALANCER);
if (!CurrentRule.balancerTag.isEmpty()) { if (!CurrentRule.balancerTag.isEmpty())
{
balancerList->clear(); balancerList->clear();
balancerList->addItems(balancers[CurrentRule.balancerTag]); balancerList->addItems(balancers[CurrentRule.balancerTag]);
} }
@ -490,12 +530,15 @@ void RouteEditor::on_balancerAddBtn_clicked()
LOADINGCHECK LOADINGCHECK
auto balancerTx = balancerSelectionCombo->currentText(); auto balancerTx = balancerSelectionCombo->currentText();
if (!balancerTx.isEmpty()) { if (!balancerTx.isEmpty())
{
this->balancers[CurrentRule.balancerTag].append(balancerSelectionCombo->currentText()); this->balancers[CurrentRule.balancerTag].append(balancerSelectionCombo->currentText());
balancerList->addItem(balancerTx); balancerList->addItem(balancerTx);
balancerSelectionCombo->setEditText(""); balancerSelectionCombo->setEditText("");
statusLabel->setText(tr("OK")); statusLabel->setText(tr("OK"));
} else { }
else
{
statusLabel->setText(tr("Balancer is empty, not processing.")); statusLabel->setText(tr("Balancer is empty, not processing."));
} }
} }
@ -503,9 +546,7 @@ void RouteEditor::on_balancerDelBtn_clicked()
{ {
LOADINGCHECK LOADINGCHECK
if (balancerList->currentRow() < 0) { if (balancerList->currentRow() < 0) { return; }
return;
}
balancers[CurrentRule.balancerTag].removeAt(balancerList->currentRow()); balancers[CurrentRule.balancerTag].removeAt(balancerList->currentRow());
balancerList->takeItem(balancerList->currentRow()); balancerList->takeItem(balancerList->currentRow());
@ -568,7 +609,8 @@ void RouteEditor::on_enableBalancerCB_stateChanged(int arg1)
CurrentRule.QV2RAY_RULE_USE_BALANCER = useBalancer; CurrentRule.QV2RAY_RULE_USE_BALANCER = useBalancer;
balancersWidget->setEnabled(useBalancer); balancersWidget->setEnabled(useBalancer);
if (CurrentRule.balancerTag.isEmpty()) { if (CurrentRule.balancerTag.isEmpty())
{
LOG(MODULE_UI, "Creating a new balancer tag.") LOG(MODULE_UI, "Creating a new balancer tag.")
CurrentRule.balancerTag = GenerateRandomString(6); CurrentRule.balancerTag = GenerateRandomString(6);
balancers[CurrentRule.balancerTag] = QStringList(); balancers[CurrentRule.balancerTag] = QStringList();
@ -576,16 +618,18 @@ void RouteEditor::on_enableBalancerCB_stateChanged(int arg1)
DEBUG(MODULE_UI, "Balancer: " + CurrentRule.balancerTag) DEBUG(MODULE_UI, "Balancer: " + CurrentRule.balancerTag)
if (useBalancer) { if (useBalancer)
{
LOG(MODULE_UI, "A rule has been set to use balancer, disconnect it to any outbound.") LOG(MODULE_UI, "A rule has been set to use balancer, disconnect it to any outbound.")
auto ruleNode = ruleNodes[currentRuleTag]; auto ruleNode = ruleNodes[currentRuleTag];
for (auto &&[_, conn] : nodeScene->connections()) { for (auto &&[_, conn] : nodeScene->connections())
if (conn.get()->getNode(PortType::Out) == ruleNode) { {
nodeScene->deleteConnection(*conn); if (conn.get()->getNode(PortType::Out) == ruleNode) { nodeScene->deleteConnection(*conn); }
}
} }
} else { }
else
{
QvMessageBoxWarn(this, tr("Route Editor"), tr("To make this rule ready to use, you need to connect it to an outbound node.")); QvMessageBoxWarn(this, tr("Route Editor"), tr("To make this rule ready to use, you need to connect it to an outbound node."));
} }
} }
@ -597,8 +641,7 @@ void RouteEditor::on_addDefaultBtn_clicked()
auto _Inconfig = GlobalConfig.inboundConfig; auto _Inconfig = GlobalConfig.inboundConfig;
// //
auto _in_httpConf = GenerateHTTPIN(QList<AccountObject>() << _Inconfig.httpAccount); auto _in_httpConf = GenerateHTTPIN(QList<AccountObject>() << _Inconfig.httpAccount);
auto _in_socksConf = GenerateSocksIN((_Inconfig.socks_useAuth ? "password" : "noauth"), auto _in_socksConf = GenerateSocksIN((_Inconfig.socks_useAuth ? "password" : "noauth"), QList<AccountObject>() << _Inconfig.socksAccount,
QList<AccountObject>() << _Inconfig.socksAccount,
_Inconfig.socksUDP, _Inconfig.socksLocalIP); _Inconfig.socksUDP, _Inconfig.socksLocalIP);
// //
auto _in_HTTP = GenerateInboundEntry(_Inconfig.listenip, _Inconfig.http_port, "http", _in_httpConf, "HTTP_gConf"); auto _in_HTTP = GenerateInboundEntry(_Inconfig.listenip, _Inconfig.http_port, "http", _in_httpConf, "HTTP_gConf");
@ -622,9 +665,7 @@ void RouteEditor::on_addInboundBtn_clicked()
InboundEditor w(INBOUND(), this); InboundEditor w(INBOUND(), this);
auto _result = w.OpenEditor(); auto _result = w.OpenEditor();
if (w.result() == QDialog::Accepted) { if (w.result() == QDialog::Accepted) { AddInbound(_result); }
AddInbound(_result);
}
CHECKEMPTYRULES CHECKEMPTYRULES
} }
@ -635,19 +676,17 @@ void RouteEditor::on_addOutboundBtn_clicked()
// True here for not keep the inbounds. // True here for not keep the inbounds.
auto configs = w.OpenImport(true); auto configs = w.OpenImport(true);
for (auto i = 0; i < configs.count(); i++) { for (auto i = 0; i < configs.count(); i++)
{
auto conf = configs.values()[i]; auto conf = configs.values()[i];
auto name = configs.key(conf, ""); auto name = configs.key(conf, "");
if (name.isEmpty()) if (name.isEmpty()) continue;
continue;
// conf is rootObject, needs to unwrap it. // conf is rootObject, needs to unwrap it.
auto confList = conf["outbounds"].toArray(); auto confList = conf["outbounds"].toArray();
for (int i = 0; i < confList.count(); i++) { for (int i = 0; i < confList.count(); i++) { AddOutbound(OUTBOUND(confList[i].toObject())); }
AddOutbound(OUTBOUND(confList[i].toObject()));
}
} }
CHECKEMPTYRULES CHECKEMPTYRULES
@ -661,7 +700,8 @@ void RouteEditor::on_ruleEnableCB_stateChanged(int arg1)
} }
void RouteEditor::on_delBtn_clicked() void RouteEditor::on_delBtn_clicked()
{ {
if (nodeScene->selectedNodes().empty()) { if (nodeScene->selectedNodes().empty())
{
QvMessageBoxWarn(this, tr("Remove Items"), tr("Please select a node from the graph to continue.")); QvMessageBoxWarn(this, tr("Remove Items"), tr("Please select a node from the graph to continue."));
return; return;
} }
@ -671,40 +711,46 @@ void RouteEditor::on_delBtn_clicked()
auto isOutbound = outboundNodes.values().contains(firstNode); auto isOutbound = outboundNodes.values().contains(firstNode);
auto isRule = ruleNodes.values().contains(firstNode); auto isRule = ruleNodes.values().contains(firstNode);
// Get the tag first, and call inbounds/outbounds/rules container variable remove() // Get the tag first, and call inbounds/outbounds/rules container variable
// Remove the node last since some events may trigger. // remove() Remove the node last since some events may trigger. Then remove
// Then remove the node container. // the node container.
if (isInbound) { if (isInbound)
{
currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvInboundNodeModel, InboundNodeData)->GetInbound(); currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvInboundNodeModel, InboundNodeData)->GetInbound();
nodeScene->removeNode(*inboundNodes[currentInboundOutboundTag]); nodeScene->removeNode(*inboundNodes[currentInboundOutboundTag]);
inboundNodes.remove(currentInboundOutboundTag); inboundNodes.remove(currentInboundOutboundTag);
// Remove corresponded inbound tags from the rules. // Remove corresponded inbound tags from the rules.
for (auto k : rules.keys()) { for (auto k : rules.keys())
{
auto v = rules[k]; auto v = rules[k];
v.inboundTag.removeAll(currentInboundOutboundTag); v.inboundTag.removeAll(currentInboundOutboundTag);
rules[k] = v; rules[k] = v;
} }
inbounds.remove(currentInboundOutboundTag); inbounds.remove(currentInboundOutboundTag);
} else if (isOutbound) { }
else if (isOutbound)
{
currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvOutboundNodeModel, OutboundNodeData)->GetOutbound(); currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvOutboundNodeModel, OutboundNodeData)->GetOutbound();
outbounds.remove(currentInboundOutboundTag); outbounds.remove(currentInboundOutboundTag);
ResolveDefaultOutboundTag(currentInboundOutboundTag, ""); ResolveDefaultOutboundTag(currentInboundOutboundTag, "");
// Remove corresponded outbound tags from the rules. // Remove corresponded outbound tags from the rules.
for (auto k : rules.keys()) { for (auto k : rules.keys())
{
auto v = rules[k]; auto v = rules[k];
if (v.outboundTag == currentInboundOutboundTag) if (v.outboundTag == currentInboundOutboundTag) v.outboundTag.clear();
v.outboundTag.clear();
rules[k] = v; rules[k] = v;
} }
nodeScene->removeNode(*outboundNodes[currentInboundOutboundTag]); nodeScene->removeNode(*outboundNodes[currentInboundOutboundTag]);
outboundNodes.remove(currentInboundOutboundTag); outboundNodes.remove(currentInboundOutboundTag);
} else if (isRule) { }
else if (isRule)
{
ruleEnableCB->setEnabled(false); ruleEnableCB->setEnabled(false);
ruleTagLineEdit->setEnabled(false); ruleTagLineEdit->setEnabled(false);
ruleRenameBtn->setEnabled(false); ruleRenameBtn->setEnabled(false);
@ -719,27 +765,30 @@ void RouteEditor::on_delBtn_clicked()
// Remove item from the rule order list widget. // Remove item from the rule order list widget.
ruleListWidget->takeItem(ruleListWidget->row(ruleListWidget->findItems(RuleTag, Qt::MatchExactly).first())); ruleListWidget->takeItem(ruleListWidget->row(ruleListWidget->findItems(RuleTag, Qt::MatchExactly).first()));
CHECKEMPTYRULES CHECKEMPTYRULES
//currentRuleTag = rules.firstKey(); // currentRuleTag = rules.firstKey();
//ShowCurrentRuleDetail(); // ShowCurrentRuleDetail();
} else { }
else
{
LOG(MODULE_UI, "Unknown node selected.") LOG(MODULE_UI, "Unknown node selected.")
QvMessageBoxWarn(this, tr("Error"), tr("Qv2ray entered an unknown state.")); QvMessageBoxWarn(this, tr("Error"), tr("Qv2ray entered an unknown state."));
} }
} }
void RouteEditor::on_editBtn_clicked() void RouteEditor::on_editBtn_clicked()
{ {
if (nodeScene->selectedNodes().empty()) { if (nodeScene->selectedNodes().empty())
QvMessageBoxWarn(this, tr("Edit Inbound/Outbound"), tr("Please select a node from the graph to continue.")); { QvMessageBoxWarn(this, tr("Edit Inbound/Outbound"), tr("Please select a node from the graph to continue.")); }
}
auto firstNode = nodeScene->selectedNodes().at(0); auto firstNode = nodeScene->selectedNodes().at(0);
auto isInbound = inboundNodes.values().contains(firstNode); auto isInbound = inboundNodes.values().contains(firstNode);
auto isOutbound = outboundNodes.values().contains(firstNode); auto isOutbound = outboundNodes.values().contains(firstNode);
if (isInbound) { if (isInbound)
{
currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvInboundNodeModel, InboundNodeData)->GetInbound(); currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvInboundNodeModel, InboundNodeData)->GetInbound();
if (!inbounds.contains(currentInboundOutboundTag)) { if (!inbounds.contains(currentInboundOutboundTag))
{
QvMessageBoxWarn(this, tr("Edit Inbound"), tr("No inbound tag found: ") + currentInboundOutboundTag); QvMessageBoxWarn(this, tr("Edit Inbound"), tr("No inbound tag found: ") + currentInboundOutboundTag);
return; return;
} }
@ -749,14 +798,18 @@ void RouteEditor::on_editBtn_clicked()
auto protocol = _in["protocol"].toString(); auto protocol = _in["protocol"].toString();
int _code; int _code;
if (protocol != "http" && protocol != "mtproto" && protocol != "socks" && protocol != "dokodemo-door") { if (protocol != "http" && protocol != "mtproto" && protocol != "socks" && protocol != "dokodemo-door")
QvMessageBoxWarn(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor.") + "\r\n" + {
tr("We will launch Json Editor instead.")); QvMessageBoxWarn(this, tr("Cannot Edit"),
tr("Currently, this type of outbound is not supported by the editor.") + "\r\n" +
tr("We will launch Json Editor instead."));
statusLabel->setText(tr("Opening JSON editor")); statusLabel->setText(tr("Opening JSON editor"));
JsonEditor w(_in, this); JsonEditor w(_in, this);
_result = INBOUND(w.OpenEditor()); _result = INBOUND(w.OpenEditor());
_code = w.result(); _code = w.result();
} else { }
else
{
InboundEditor w(_in, this); InboundEditor w(_in, this);
statusLabel->setText(tr("Opening default inbound editor")); statusLabel->setText(tr("Opening default inbound editor"));
_result = w.OpenEditor(); _result = w.OpenEditor();
@ -765,22 +818,24 @@ void RouteEditor::on_editBtn_clicked()
statusLabel->setText(tr("OK")); statusLabel->setText(tr("OK"));
if (_code == QDialog::Accepted) { if (_code == QDialog::Accepted)
{
bool isTagChanged = getTag(_in) != getTag(_result); bool isTagChanged = getTag(_in) != getTag(_result);
if (isTagChanged) { if (isTagChanged) { RenameItemTag(RENAME_INBOUND, getTag(_in), getTag(_result)); }
RenameItemTag(RENAME_INBOUND, getTag(_in), getTag(_result));
}
DEBUG(MODULE_UI, "Removed old tag: " + getTag(_in)) DEBUG(MODULE_UI, "Removed old tag: " + getTag(_in))
inbounds.remove(getTag(_in)); inbounds.remove(getTag(_in));
DEBUG(MODULE_UI, "Adding new tag: " + getTag(_result)) DEBUG(MODULE_UI, "Adding new tag: " + getTag(_result))
inbounds[getTag(_result)] = _result; inbounds[getTag(_result)] = _result;
} }
} else if (isOutbound) { }
else if (isOutbound)
{
currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvOutboundNodeModel, OutboundNodeData)->GetOutbound(); currentInboundOutboundTag = GetFirstNodeData(*firstNode, QvOutboundNodeModel, OutboundNodeData)->GetOutbound();
if (!outbounds.contains(currentInboundOutboundTag)) { if (!outbounds.contains(currentInboundOutboundTag))
{
QvMessageBoxWarn(this, tr("Edit Inbound"), tr("No inbound tag found: ") + currentInboundOutboundTag); QvMessageBoxWarn(this, tr("Edit Inbound"), tr("No inbound tag found: ") + currentInboundOutboundTag);
return; return;
} }
@ -790,25 +845,30 @@ void RouteEditor::on_editBtn_clicked()
auto protocol = _out["protocol"].toString().toLower(); auto protocol = _out["protocol"].toString().toLower();
int _code; int _code;
if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") { if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks")
{
QvMessageBoxWarn(this, tr("Unsupported Outbound Type"), QvMessageBoxWarn(this, tr("Unsupported Outbound Type"),
tr("This outbound entry is not supported by the GUI editor.") + NEWLINE + tr("This outbound entry is not supported by the GUI editor.") + NEWLINE +
tr("We will launch Json Editor instead.")); tr("We will launch Json Editor instead."));
JsonEditor w(_out, this); JsonEditor w(_out, this);
statusLabel->setText(tr("Opening JSON editor")); statusLabel->setText(tr("Opening JSON editor"));
_result = OUTBOUND(w.OpenEditor()); _result = OUTBOUND(w.OpenEditor());
_code = w.result(); _code = w.result();
} else { }
else
{
OutboundEditor w(_out, this); OutboundEditor w(_out, this);
statusLabel->setText(tr("Opening default outbound editor.")); statusLabel->setText(tr("Opening default outbound editor."));
_result = w.OpenEditor(); _result = w.OpenEditor();
_code = w.result(); _code = w.result();
} }
if (_code == QDialog::Accepted) { if (_code == QDialog::Accepted)
{
bool isTagChanged = getTag(_out) != getTag(_result); bool isTagChanged = getTag(_out) != getTag(_result);
if (isTagChanged) { if (isTagChanged)
{
DEBUG(MODULE_UI, "Outbound tag is changed: " + QString(isTagChanged)) DEBUG(MODULE_UI, "Outbound tag is changed: " + QString(isTagChanged))
RenameItemTag(RENAME_OUTBOUND, getTag(_out), getTag(_result)); RenameItemTag(RENAME_OUTBOUND, getTag(_out), getTag(_result));
DEBUG(MODULE_UI, "Removed old tag: " + getTag(_out)) DEBUG(MODULE_UI, "Removed old tag: " + getTag(_out))
@ -819,7 +879,9 @@ void RouteEditor::on_editBtn_clicked()
outbounds[getTag(_result)] = _result; outbounds[getTag(_result)] = _result;
statusLabel->setText(tr("OK")); statusLabel->setText(tr("OK"));
} }
} else { }
else
{
LOG(MODULE_UI, "Cannot apply 'edit' operation to non-inbound and non-outbound") LOG(MODULE_UI, "Cannot apply 'edit' operation to non-inbound and non-outbound")
} }
} }
@ -840,16 +902,23 @@ void RouteEditor::on_ruleRenameBtn_clicked()
{ {
auto newTag = ruleTagLineEdit->text(); auto newTag = ruleTagLineEdit->text();
if (newTag.isEmpty()) { if (newTag.isEmpty())
{
LOG(MODULE_UI, "Tag is empty, this is ILLEGAL!") LOG(MODULE_UI, "Tag is empty, this is ILLEGAL!")
QvMessageBoxWarn(this, tr("Renaming a tag"), tr("New tag is empty, please try another.")); QvMessageBoxWarn(this, tr("Renaming a tag"), tr("New tag is empty, please try another."));
} else if (newTag == CurrentRule.QV2RAY_RULE_TAG) { }
else if (newTag == CurrentRule.QV2RAY_RULE_TAG)
{
LOG(MODULE_UI, "No tag changed, returning.") LOG(MODULE_UI, "No tag changed, returning.")
QvMessageBoxInfo(this, tr("Renaming a tag"), tr("New tag is the same as the original one.")); QvMessageBoxInfo(this, tr("Renaming a tag"), tr("New tag is the same as the original one."));
} else if (rules.contains(newTag)) { }
else if (rules.contains(newTag))
{
LOG(MODULE_UI, "Tag duplicate detected.") LOG(MODULE_UI, "Tag duplicate detected.")
QvMessageBoxWarn(this, tr("Renaming a tag"), tr("Duplicate rule tag detected, please try another.")); QvMessageBoxWarn(this, tr("Renaming a tag"), tr("Duplicate rule tag detected, please try another."));
} else { }
else
{
RenameItemTag(RENAME_RULE, CurrentRule.QV2RAY_RULE_TAG, newTag); RenameItemTag(RENAME_RULE, CurrentRule.QV2RAY_RULE_TAG, newTag);
} }
} }

View File

@ -1,131 +1,134 @@
#pragma once #pragma once
#include <list> #include "ConnectionStyle.hpp"
#include "FlowScene.hpp"
#include "Node.hpp"
#include "NodeData.hpp"
#include "common/QvHelpers.hpp"
#include <QDialog> #include <QDialog>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QListWidgetItem> #include <QListWidgetItem>
#include "common/QvHelpers.hpp" #include <list>
#include "Node.hpp"
#include "NodeData.hpp"
#include "ConnectionStyle.hpp"
#include "FlowScene.hpp"
using QtNodes::Node;
using QtNodes::FlowScene;
using QtNodes::ConnectionStyle; using QtNodes::ConnectionStyle;
using QtNodes::FlowScene;
using QtNodes::Node;
#include "ui_w_RoutesEditor.h"
#include "ui/messaging/QvMessageBus.hpp" #include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_RoutesEditor.h"
enum ROUTE_EDIT_MODE { enum ROUTE_EDIT_MODE
{
RENAME_INBOUND, RENAME_INBOUND,
RENAME_OUTBOUND, RENAME_OUTBOUND,
RENAME_RULE, RENAME_RULE,
}; };
class RouteEditor : public QDialog, private Ui::RouteEditor class RouteEditor
: public QDialog
, private Ui::RouteEditor
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit RouteEditor(QJsonObject connection, QWidget *parent = nullptr); explicit RouteEditor(QJsonObject connection, QWidget *parent = nullptr);
~RouteEditor(); ~RouteEditor();
CONFIGROOT OpenEditor(); CONFIGROOT OpenEditor();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_insertDirectBtn_clicked(); void on_insertDirectBtn_clicked();
void on_routeProtocolHTTPCB_stateChanged(int arg1); void on_routeProtocolHTTPCB_stateChanged(int arg1);
void on_routeProtocolTLSCB_stateChanged(int arg1); void on_routeProtocolTLSCB_stateChanged(int arg1);
void on_routeProtocolBTCB_stateChanged(int arg1); void on_routeProtocolBTCB_stateChanged(int arg1);
void on_balancerAddBtn_clicked(); void on_balancerAddBtn_clicked();
void on_balancerDelBtn_clicked(); void on_balancerDelBtn_clicked();
void on_hostList_textChanged(); void on_hostList_textChanged();
void on_ipList_textChanged(); void on_ipList_textChanged();
void on_routePortTxt_textEdited(const QString &arg1); void on_routePortTxt_textEdited(const QString &arg1);
void on_routeUserTxt_textEdited(const QString &arg1); void on_routeUserTxt_textEdited(const QString &arg1);
void on_addRouteBtn_clicked(); void on_addRouteBtn_clicked();
void on_netBothRB_clicked(); void on_netBothRB_clicked();
void on_netUDPRB_clicked(); void on_netUDPRB_clicked();
void on_netTCPRB_clicked(); void on_netTCPRB_clicked();
void on_routeUserTxt_textChanged(); void on_routeUserTxt_textChanged();
void on_sourceIPList_textChanged(); void on_sourceIPList_textChanged();
void on_enableBalancerCB_stateChanged(int arg1); void on_enableBalancerCB_stateChanged(int arg1);
void on_addDefaultBtn_clicked(); void on_addDefaultBtn_clicked();
void on_insertBlackBtn_clicked(); void on_insertBlackBtn_clicked();
void on_addInboundBtn_clicked(); void on_addInboundBtn_clicked();
void on_addOutboundBtn_clicked(); void on_addOutboundBtn_clicked();
void on_ruleEnableCB_stateChanged(int arg1); void on_ruleEnableCB_stateChanged(int arg1);
void on_delBtn_clicked(); void on_delBtn_clicked();
void on_editBtn_clicked(); void on_editBtn_clicked();
void on_domainStrategyCombo_currentIndexChanged(const QString &arg1); void on_domainStrategyCombo_currentIndexChanged(const QString &arg1);
void on_defaultOutboundCombo_currentIndexChanged(const QString &arg1); void on_defaultOutboundCombo_currentIndexChanged(const QString &arg1);
void on_ruleRenameBtn_clicked(); void on_ruleRenameBtn_clicked();
public slots: public slots:
void onNodeClicked(QtNodes::Node &n); void onNodeClicked(QtNodes::Node &n);
void onConnectionCreated(QtNodes::Connection const &c); void onConnectionCreated(QtNodes::Connection const &c);
void onConnectionDeleted(QtNodes::Connection const &c); void onConnectionDeleted(QtNodes::Connection const &c);
private: private:
void RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag, const QString newTag); void RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag, const QString newTag);
void ShowCurrentRuleDetail(); void ShowCurrentRuleDetail();
// //
QString currentRuleTag; QString currentRuleTag;
QString currentInboundOutboundTag; QString currentInboundOutboundTag;
QMap<QString, QStringList> balancers; QMap<QString, QStringList> balancers;
QString domainStrategy; QString domainStrategy;
QString defaultOutbound; QString defaultOutbound;
// //
QMap<QString, INBOUND> inbounds; QMap<QString, INBOUND> inbounds;
QMap<QString, OUTBOUND> outbounds; QMap<QString, OUTBOUND> outbounds;
QMap<QString, RuleObject> rules; QMap<QString, RuleObject> rules;
// //
CONFIGROOT root; CONFIGROOT root;
CONFIGROOT original; CONFIGROOT original;
// //
// ---------------------------- Node Graph Impl -------------------------- // ---------------------------- Node Graph Impl --------------------------
void SetupNodeWidget(); void SetupNodeWidget();
QMap<QString, Node *> inboundNodes; QMap<QString, Node *> inboundNodes;
QMap<QString, Node *> outboundNodes; QMap<QString, Node *> outboundNodes;
QMap<QString, Node *> ruleNodes; QMap<QString, Node *> ruleNodes;
// //
FlowScene *nodeScene; FlowScene *nodeScene;
// ---------------------------- Extra Source File Headers ---------------- // ---------------------------- Extra Source File Headers ----------------
void AddInbound(INBOUND in); void AddInbound(INBOUND in);
void AddOutbound(OUTBOUND out); void AddOutbound(OUTBOUND out);
void AddRule(RuleObject rule); void AddRule(RuleObject rule);
QString AddNewRule(); QString AddNewRule();
void ResolveDefaultOutboundTag(QString original, QString newTag); void ResolveDefaultOutboundTag(QString original, QString newTag);
}; };

View File

@ -1,18 +1,16 @@
// WARNING // WARNING
// Since it's required for this file to know the content of the macros defined in another CPP file. // Since it's required for this file to know the content of the macros defined
// We included an CPP file instead of the proper HPP file. // in another CPP file. We included an CPP file instead of the proper HPP file.
#include "w_RoutesEditor.cpp" #include "w_RoutesEditor.cpp"
// Supplementary source file for Routes editor, basically providing routes-related operations. // Supplementary source file for Routes editor, basically providing
// routes-related operations.
void RouteEditor::AddInbound(INBOUND in) void RouteEditor::AddInbound(INBOUND in)
{ {
QString tag = getTag(in); QString tag = getTag(in);
if (inbounds.contains(tag)) { if (inbounds.contains(tag)) { tag = tag + "_" + GenerateRandomString(5); }
tag = tag + "_" + GenerateRandomString(5);
}
in["tag"] = tag; in["tag"] = tag;
auto _nodeData = make_unique<QvInboundNodeModel>(make_shared<InboundNodeData>(tag)); auto _nodeData = make_unique<QvInboundNodeModel>(make_shared<InboundNodeData>(tag));
@ -29,9 +27,7 @@ void RouteEditor::AddOutbound(OUTBOUND out)
{ {
QString tag = getTag(out); QString tag = getTag(out);
if (outbounds.contains(tag)) { if (outbounds.contains(tag)) { tag = tag + "_" + GenerateRandomString(5); }
tag = tag + "_" + GenerateRandomString(5);
}
out["tag"] = tag; out["tag"] = tag;
auto _nodeData = make_unique<QvOutboundNodeModel>(make_shared<OutboundNodeData>(tag)); auto _nodeData = make_unique<QvOutboundNodeModel>(make_shared<OutboundNodeData>(tag));
@ -64,9 +60,7 @@ QString RouteEditor::AddNewRule()
void RouteEditor::AddRule(RuleObject rule) void RouteEditor::AddRule(RuleObject rule)
{ {
// Prevent duplicate // Prevent duplicate
if (ruleNodes.contains(rule.QV2RAY_RULE_TAG)) { if (ruleNodes.contains(rule.QV2RAY_RULE_TAG)) { rule.QV2RAY_RULE_TAG += "-" + GenerateRandomString(5); }
rule.QV2RAY_RULE_TAG += "-" + GenerateRandomString(5);
}
rules[rule.QV2RAY_RULE_TAG] = rule; rules[rule.QV2RAY_RULE_TAG] = rule;
auto pos = nodeGraphWidget->pos(); auto pos = nodeGraphWidget->pos();
@ -76,25 +70,34 @@ void RouteEditor::AddRule(RuleObject rule)
auto &node = nodeScene->createNode(std::move(_nodeData)); auto &node = nodeScene->createNode(std::move(_nodeData));
nodeScene->setNodePosition(node, pos); nodeScene->setNodePosition(node, pos);
for (auto inTag : rule.inboundTag) { for (auto inTag : rule.inboundTag)
if (!inboundNodes.contains(inTag)) { {
if (!inboundNodes.contains(inTag))
{
LOG(MODULE_UI, "No inbound tag found for rule: " + rule.QV2RAY_RULE_TAG + ", inbound tag: " + inTag) LOG(MODULE_UI, "No inbound tag found for rule: " + rule.QV2RAY_RULE_TAG + ", inbound tag: " + inTag)
QvMessageBoxWarn(this, tr("No Inbound"), tr("No inbound item found: ") + inTag); QvMessageBoxWarn(this, tr("No Inbound"), tr("No inbound item found: ") + inTag);
rule.inboundTag.removeAll(inTag); rule.inboundTag.removeAll(inTag);
} else { }
else
{
auto inboundNode = inboundNodes[inTag]; auto inboundNode = inboundNodes[inTag];
nodeScene->createConnection(node, 0, *inboundNode, 0); nodeScene->createConnection(node, 0, *inboundNode, 0);
} }
} }
// If not using balancers (use outbound tag) // If not using balancers (use outbound tag)
if (!rule.QV2RAY_RULE_USE_BALANCER) { if (!rule.QV2RAY_RULE_USE_BALANCER)
if (outboundNodes.contains(rule.outboundTag)) { {
if (outboundNodes.contains(rule.outboundTag))
{
DEBUG(MODULE_GRAPH, "Found outbound tag: " + rule.outboundTag + ", for rule: " + rule.QV2RAY_RULE_TAG) DEBUG(MODULE_GRAPH, "Found outbound tag: " + rule.outboundTag + ", for rule: " + rule.QV2RAY_RULE_TAG)
nodeScene->createConnection(*outboundNodes[rule.outboundTag], 0, node, 0); nodeScene->createConnection(*outboundNodes[rule.outboundTag], 0, node, 0);
} else { }
else
{
LOG(MODULE_GRAPH, "Outbound tag not found: " + rule.outboundTag + ", for: " + rule.QV2RAY_RULE_TAG) LOG(MODULE_GRAPH, "Outbound tag not found: " + rule.outboundTag + ", for: " + rule.QV2RAY_RULE_TAG)
//QvMessageBoxWarn(this, tr("No outbound tag"), tr("Please connect the rule with an outbound.")); // QvMessageBoxWarn(this, tr("No outbound tag"), tr("Please connect
// the rule with an outbound."));
} }
} }
@ -105,19 +108,20 @@ void RouteEditor::AddRule(RuleObject rule)
// Do not use reference here, we need deep // Do not use reference here, we need deep
void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag, QString newTag) void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag, QString newTag)
{ {
switch (mode) { switch (mode)
{
case RENAME_RULE: case RENAME_RULE:
if (rules.contains(originalTag) && ruleNodes.contains(originalTag)) { if (rules.contains(originalTag) && ruleNodes.contains(originalTag))
if (rules.contains(newTag) && rules.contains(newTag)) { {
if (rules.contains(newTag) && rules.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix.")); QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5); newTag += "_" + GenerateRandomString(5);
} }
auto node = static_cast<QvRuleNodeDataModel *>(ruleNodes[originalTag]->nodeDataModel()); auto node = static_cast<QvRuleNodeDataModel *>(ruleNodes[originalTag]->nodeDataModel());
if (node == nullptr) { if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
node->setData(newTag); node->setData(newTag);
// //
@ -125,27 +129,30 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
rules[newTag].QV2RAY_RULE_TAG = newTag; rules[newTag].QV2RAY_RULE_TAG = newTag;
ruleNodes[newTag] = ruleNodes.take(originalTag); ruleNodes[newTag] = ruleNodes.take(originalTag);
// //
// No other operation needed, but need to rename the one in the ruleOrder list widget. // No other operation needed, but need to rename the one in the
// ruleOrder list widget.
auto items = ruleListWidget->findItems(originalTag, Qt::MatchExactly); auto items = ruleListWidget->findItems(originalTag, Qt::MatchExactly);
if (items.isEmpty()) { if (items.isEmpty()) { LOG(MODULE_UI, "Cannot find a node: " + originalTag) }
LOG(MODULE_UI, "Cannot find a node: " + originalTag) else
} else { {
items.first()->setText(newTag); items.first()->setText(newTag);
} }
if (currentRuleTag == originalTag) { if (currentRuleTag == originalTag) { currentRuleTag = newTag; }
currentRuleTag = newTag; }
} else
} else { {
LOG(MODULE_UI, "There's nothing match " + originalTag + " in the containers.") LOG(MODULE_UI, "There's nothing match " + originalTag + " in the containers.")
} }
break; break;
case RENAME_OUTBOUND: case RENAME_OUTBOUND:
if (outbounds.contains(originalTag) && outboundNodes.contains(originalTag)) { if (outbounds.contains(originalTag) && outboundNodes.contains(originalTag))
if (outbounds.contains(newTag) && outboundNodes.contains(newTag)) { {
if (outbounds.contains(newTag) && outboundNodes.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix.")); QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5); newTag += "_" + GenerateRandomString(5);
} }
@ -154,34 +161,39 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
outboundNodes[newTag] = outboundNodes.take(originalTag); outboundNodes[newTag] = outboundNodes.take(originalTag);
auto node = static_cast<QvOutboundNodeModel *>(outboundNodes[newTag]->nodeDataModel()); auto node = static_cast<QvOutboundNodeModel *>(outboundNodes[newTag]->nodeDataModel());
if (node == nullptr) { if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
node->setData(newTag); node->setData(newTag);
// Change outbound tag in rules accordingly. // Change outbound tag in rules accordingly.
for (auto k : rules.keys()) { for (auto k : rules.keys())
{
auto v = rules[k]; auto v = rules[k];
if (v.outboundTag == originalTag) { if (v.outboundTag == originalTag)
{
v.outboundTag = newTag; v.outboundTag = newTag;
// Put this inside the if block since no need an extra operation if the condition is false. // Put this inside the if block since no need an extra
// operation if the condition is false.
rules[k] = v; rules[k] = v;
} }
} }
// Resolve default outbound. // Resolve default outbound.
ResolveDefaultOutboundTag(originalTag, newTag); ResolveDefaultOutboundTag(originalTag, newTag);
} else { }
else
{
LOG(MODULE_UI, "Failed to rename an outbound --> Item not found.") LOG(MODULE_UI, "Failed to rename an outbound --> Item not found.")
} }
break; break;
case RENAME_INBOUND: case RENAME_INBOUND:
if (inbounds.contains(originalTag) && inboundNodes.contains(originalTag)) { if (inbounds.contains(originalTag) && inboundNodes.contains(originalTag))
if (inbounds.contains(newTag) && inboundNodes.contains(newTag)) { {
if (inbounds.contains(newTag) && inboundNodes.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix.")); QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5); newTag += "_" + GenerateRandomString(5);
} }
@ -190,26 +202,29 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
inboundNodes[newTag] = inboundNodes.take(originalTag); inboundNodes[newTag] = inboundNodes.take(originalTag);
auto node = static_cast<QvInboundNodeModel *>(inboundNodes[newTag]->nodeDataModel()); auto node = static_cast<QvInboundNodeModel *>(inboundNodes[newTag]->nodeDataModel());
if (node == nullptr) { if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
node->setData(newTag); node->setData(newTag);
// Change inbound tag in rules accordingly. // Change inbound tag in rules accordingly.
// k -> rule tag // k -> rule tag
// v -> rule object // v -> rule object
for (auto k : rules.keys()) { for (auto k : rules.keys())
{
auto v = rules[k]; auto v = rules[k];
if (v.inboundTag.contains(originalTag)) { if (v.inboundTag.contains(originalTag))
{
v.inboundTag.append(newTag); v.inboundTag.append(newTag);
v.inboundTag.removeAll(originalTag); v.inboundTag.removeAll(originalTag);
// Put this inside the if block since no need an extra operation if the condition is false. // Put this inside the if block since no need an extra
// operation if the condition is false.
rules[k] = v; rules[k] = v;
} }
} }
} else { }
else
{
LOG(MODULE_UI, "Failed to rename an outbound --> Item not found.") LOG(MODULE_UI, "Failed to rename an outbound --> Item not found.")
} }
@ -219,26 +234,31 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
void RouteEditor::ResolveDefaultOutboundTag(QString original, QString newTag) void RouteEditor::ResolveDefaultOutboundTag(QString original, QString newTag)
{ {
LOG(MODULE_UI, "Resolving default outbound settings: default=" + defaultOutbound + " original=" + original + " new=" + newTag) LOG(MODULE_UI, "Resolving default outbound settings: default=" + defaultOutbound + " original=" + original + " new=" + newTag)
auto isDefaultChanged = original == defaultOutbound; auto isDefaultChanged = original == defaultOutbound;
defaultOutboundCombo->clear(); defaultOutboundCombo->clear();
defaultOutboundCombo->addItems(outbounds.keys()); defaultOutboundCombo->addItems(outbounds.keys());
if (!isDefaultChanged) { if (!isDefaultChanged)
{
LOG(MODULE_UI, "Default outbound is not changed: retaining: " + defaultOutbound) LOG(MODULE_UI, "Default outbound is not changed: retaining: " + defaultOutbound)
// Just simply restore the default one. // Just simply restore the default one.
defaultOutboundCombo->setCurrentText(defaultOutbound); defaultOutboundCombo->setCurrentText(defaultOutbound);
} else if (newTag.isEmpty()) { }
else if (newTag.isEmpty())
{
LOG(MODULE_UI, "Default outbound is removed, using first key from the outbounds as the default one.") LOG(MODULE_UI, "Default outbound is removed, using first key from the outbounds as the default one.")
// Removed the default one, so set the first one as the default. // Removed the default one, so set the first one as the default.
if (outbounds.isEmpty()) { if (outbounds.isEmpty()) { defaultOutbound.clear(); }
defaultOutbound.clear(); else
} else { {
defaultOutbound = getTag(outbounds.first()); defaultOutbound = getTag(outbounds.first());
defaultOutboundCombo->addItem(outbounds.firstKey()); defaultOutboundCombo->addItem(outbounds.firstKey());
} }
} else { }
else
{
LOG(MODULE_UI, "Default outbound is renamed, ") LOG(MODULE_UI, "Default outbound is renamed, ")
defaultOutboundCombo->setCurrentText(newTag); defaultOutboundCombo->setCurrentText(newTag);
defaultOutbound = newTag; defaultOutbound = newTag;

View File

@ -1,8 +1,9 @@
#include <QMetaEnum>
#include "QvMessageBus.hpp" #include "QvMessageBus.hpp"
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include <QMetaEnum>
namespace Qv2ray::ui::messaging namespace Qv2ray::ui::messaging
{ {
QvMessageBusObject::QvMessageBusObject() QvMessageBusObject::QvMessageBusObject()
@ -15,4 +16,4 @@ namespace Qv2ray::ui::messaging
LOG(MODULE_MESSAGING, "Emitting signal: " + QString(metaEnum.valueToKey(msg))); LOG(MODULE_MESSAGING, "Emitting signal: " + QString(metaEnum.valueToKey(msg)));
emit QvSendMessage(msg); emit QvSendMessage(msg);
} }
} } // namespace Qv2ray::ui::messaging

View File

@ -6,28 +6,27 @@
#define QvMessageBusSlotSig const QvMBMessage &msg #define QvMessageBusSlotSig const QvMBMessage &msg
#define QvMessageBusSlotIdentifier on_QvMessageReceived #define QvMessageBusSlotIdentifier on_QvMessageReceived
#define QvMessageBusSlotDecl void QvMessageBusSlotIdentifier(QvMessageBusSlotSig); #define QvMessageBusSlotDecl void QvMessageBusSlotIdentifier(QvMessageBusSlotSig)
#define QvMessageBusSlotImpl(CLASSNAME) void CLASSNAME::QvMessageBusSlotIdentifier(QvMessageBusSlotSig) #define QvMessageBusSlotImpl(CLASSNAME) void CLASSNAME::QvMessageBusSlotIdentifier(QvMessageBusSlotSig)
#define MBShowDefaultImpl \ #define MBShowDefaultImpl \
case SHOW_WINDOWS:\ case SHOW_WINDOWS: \
this->setWindowOpacity(1);\ this->setWindowOpacity(1); \
break; break;
#define MBHideDefaultImpl \ #define MBHideDefaultImpl \
case HIDE_WINDOWS:\ case HIDE_WINDOWS: \
this->setWindowOpacity(0);\ this->setWindowOpacity(0); \
break; break;
#define MBRetranslateDefaultImpl \ #define MBRetranslateDefaultImpl \
case RETRANSLATE:\ case RETRANSLATE: this->retranslateUi(this); break;
this->retranslateUi(this);\
break;
namespace Qv2ray::ui::messaging namespace Qv2ray::ui::messaging
{ {
Q_NAMESPACE Q_NAMESPACE
enum QvMBMessage { enum QvMBMessage
{
/// Show all windows. /// Show all windows.
SHOW_WINDOWS, SHOW_WINDOWS,
/// Hide all windows. /// Hide all windows.
@ -43,20 +42,20 @@ namespace Qv2ray::ui::messaging
// //
class QvMessageBusObject : public QObject class QvMessageBusObject : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvMessageBusObject(); explicit QvMessageBusObject();
// //
void EmitGlobalSignal(const QvMBMessage &msg); void EmitGlobalSignal(const QvMBMessage &msg);
signals: signals:
void QvSendMessage(const QvMBMessage &msg); void QvSendMessage(const QvMBMessage &msg);
//private slots: // private slots:
// void on_QvMessageReceived(QvMessage msg); // void on_QvMessageReceived(QvMessage msg);
}; };
// Danger, new is used here. Possible memory leak (hope not so much leak) // Danger, new is used here. Possible memory leak (hope not so much leak)
inline QvMessageBusObject messageBus = QvMessageBusObject(); inline QvMessageBusObject messageBus = QvMessageBusObject();
} } // namespace Qv2ray::ui::messaging
using namespace Qv2ray::ui::messaging; using namespace Qv2ray::ui::messaging;

View File

@ -1,6 +1,6 @@
#include "ui/models/InboundNodeModel.hpp" #include "ui/models/InboundNodeModel.hpp"
QvInboundNodeModel::QvInboundNodeModel(std::shared_ptr<InboundNodeData> data): NodeDataModel() QvInboundNodeModel::QvInboundNodeModel(std::shared_ptr<InboundNodeData> data) : NodeDataModel()
{ {
_in = data; _in = data;
_label = new QLabel(); _label = new QLabel();

View File

@ -1,68 +1,72 @@
#pragma once #pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp" #include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvInboundNodeModel : public NodeDataModel class QvInboundNodeModel : public NodeDataModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvInboundNodeModel(std::shared_ptr<InboundNodeData> data); explicit QvInboundNodeModel(std::shared_ptr<InboundNodeData> data);
~QvInboundNodeModel() ~QvInboundNodeModel()
{ {
//if (_label) { // if (_label) {
// delete _label; // delete _label;
//} //}
} }
QString caption() const override QString caption() const override
{ {
return "Nothing"; return "Nothing";
} }
bool captionVisible() const override bool captionVisible() const override
{ {
return false; return false;
} }
unsigned int nPorts(PortType portType) const override unsigned int nPorts(PortType portType) const override
{ {
return portType == PortType::Out ? 1 : 0; return portType == PortType::Out ? 1 : 0;
} }
QString name() const override QString name() const override
{ {
return "InboundNode"; return "InboundNode";
} }
NodeDataType dataType(PortType portType, PortIndex portIndex) const override NodeDataType dataType(PortType portType, PortIndex portIndex) const override
{ {
Q_UNUSED(portType); Q_UNUSED(portType);
Q_UNUSED(portIndex); Q_UNUSED(portIndex);
return inboundType; return inboundType;
} }
std::shared_ptr<NodeData> outData(PortIndex) override std::shared_ptr<NodeData> outData(PortIndex) override
{ {
return _in; return _in;
} }
void setInData(std::shared_ptr<NodeData>, int) override {} void setInData(std::shared_ptr<NodeData>, int) override
void setData(const QString &data) {
{ }
_in = make_shared<InboundNodeData>(data); void setData(const QString &data)
_label->setText(data); {
_label->adjustSize(); _in = make_shared<InboundNodeData>(data);
} _label->setText(data);
_label->adjustSize();
}
QWidget *embeddedWidget() override QWidget *embeddedWidget() override
{ {
return _label; return _label;
} }
private:
NodeValidationState modelValidationState = NodeValidationState::Warning; private:
QString modelValidationError = tr("Missing or incorrect inputs"); NodeValidationState modelValidationState = NodeValidationState::Warning;
// QString modelValidationError = tr("Missing or incorrect inputs");
std::shared_ptr<InboundNodeData> _in; //
QLabel *_label; std::shared_ptr<InboundNodeData> _in;
QLabel *_label;
}; };

View File

@ -1,101 +1,99 @@
#pragma once #pragma once
#include <QLabel>
#include "NodeDataModel.hpp" #include "NodeDataModel.hpp"
#include "PortType.hpp" #include "PortType.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
using QtNodes::PortType; #include <QLabel>
using QtNodes::PortIndex;
using QtNodes::NodeData; using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel; using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
using QtNodes::NodeDataType; using QtNodes::NodeDataType;
using QtNodes::NodeValidationState;
using QtNodes::PortIndex;
using QtNodes::PortType;
using QtNodes::NodeData; using QtNodes::NodeData;
using QtNodes::NodeDataType;
const int GRAPH_NODE_LABEL_FONTSIZE_INCREMENT = 3; const int GRAPH_NODE_LABEL_FONTSIZE_INCREMENT = 3;
namespace Qv2ray::ui::nodemodels namespace Qv2ray::ui::nodemodels
{ {
const NodeDataType outboundType = {"outbound", "Outbound Object"}; const NodeDataType outboundType = { "outbound", "Outbound Object" };
const NodeDataType inboundType = {"inbound", "Inbound Object"}; const NodeDataType inboundType = { "inbound", "Inbound Object" };
/// The class can potentially incapsulate any user data /// The class can potentially incapsulate any user data
/// need to be transferred within the Node Editor graph /// need to be transferred within the Node Editor graph
class InboundNodeData : public NodeData class InboundNodeData : public NodeData
{ {
public: public:
InboundNodeData() InboundNodeData(){ DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") } InboundNodeData(QString in)
{ : _inboundTag(in)
DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") {
} }
InboundNodeData(QString in) : _inboundTag(in) { }
NodeDataType type() const override NodeDataType type() const override
{ {
return inboundType; return inboundType;
} }
QString GetInbound() QString GetInbound()
{ {
return _inboundTag; return _inboundTag;
} }
private:
QString _inboundTag; private:
QString _inboundTag;
}; };
/// The class can potentially incapsulate any user data /// The class can potentially incapsulate any user data
/// need to be transferred within the Node Editor graph /// need to be transferred within the Node Editor graph
class OutboundNodeData : public NodeData class OutboundNodeData : public NodeData
{ {
public: public:
OutboundNodeData() : _outboundTag() OutboundNodeData()
{ : _outboundTag(){ DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") } OutboundNodeData(QString out)
DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") : _outboundTag(out)
} {
OutboundNodeData(QString out) : _outboundTag(out) { } }
NodeDataType type() const override NodeDataType type() const override
{ {
return outboundType; return outboundType;
} }
QString GetOutbound() QString GetOutbound()
{ {
return _outboundTag; return _outboundTag;
} }
private:
QString _outboundTag; private:
QString _outboundTag;
}; };
/// The class can potentially incapsulate any user data /// The class can potentially incapsulate any user data
/// need to be transferred within the Node Editor graph /// need to be transferred within the Node Editor graph
class RuleNodeData : public NodeData class RuleNodeData : public NodeData
{ {
public: public:
RuleNodeData() : _ruleTag() RuleNodeData()
{ : _ruleTag(){ DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") } RuleNodeData(QString out) : _ruleTag(out)
DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.") {
} }
RuleNodeData(QString out) : _ruleTag(out) { }
NodeDataType type() const override NodeDataType type() const override
{ {
return outboundType; return outboundType;
} }
QString GetRuleTag() QString GetRuleTag()
{ {
return _ruleTag; return _ruleTag;
} }
private:
QString _ruleTag; private:
QString _ruleTag;
}; };
} } // namespace Qv2ray::ui::nodemodels
using namespace Qv2ray::ui::nodemodels; using namespace Qv2ray::ui::nodemodels;

View File

@ -1,6 +1,6 @@
#include "ui/models/OutboundNodeModel.hpp" #include "ui/models/OutboundNodeModel.hpp"
QvOutboundNodeModel::QvOutboundNodeModel(std::shared_ptr<OutboundNodeData> data): NodeDataModel() QvOutboundNodeModel::QvOutboundNodeModel(std::shared_ptr<OutboundNodeData> data) : NodeDataModel()
{ {
_out = data; _out = data;
_label = new QLabel(); _label = new QLabel();

View File

@ -1,74 +1,80 @@
#pragma once #pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp" #include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvOutboundNodeModel : public NodeDataModel class QvOutboundNodeModel : public NodeDataModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QvOutboundNodeModel(std::shared_ptr<OutboundNodeData> data); explicit QvOutboundNodeModel(std::shared_ptr<OutboundNodeData> data);
~QvOutboundNodeModel() ~QvOutboundNodeModel()
{ {
//if (_label) { // if (_label) {
// delete _label; // delete _label;
//} //}
} }
QString caption() const override QString caption() const override
{ {
return "Nothing"; return "Nothing";
} }
bool captionVisible() const override bool captionVisible() const override
{ {
return false; return false;
} }
unsigned int nPorts(PortType portType) const override unsigned int nPorts(PortType portType) const override
{ {
return portType == PortType::In ? 1 : 0; return portType == PortType::In ? 1 : 0;
} }
QString name() const override QString name() const override
{ {
return "OutboundNode"; return "OutboundNode";
} }
NodeDataType dataType(PortType portType, PortIndex portIndex) const override NodeDataType dataType(PortType portType, PortIndex portIndex) const override
{ {
Q_UNUSED(portType); Q_UNUSED(portType);
Q_UNUSED(portIndex); Q_UNUSED(portIndex);
return outboundType; return outboundType;
} }
std::shared_ptr<NodeData> outData(PortIndex) override std::shared_ptr<NodeData> outData(PortIndex) override
{ {
return _out; return _out;
} }
void setInData(shared_ptr<NodeData>, int) override {} void setInData(shared_ptr<NodeData>, int) override
{
}
void setInData(vector<shared_ptr<NodeData>>, int) override {} void setInData(vector<shared_ptr<NodeData>>, int) override
void setData(const QString &data) {
{ }
_out = make_shared<OutboundNodeData>(data); void setData(const QString &data)
_label->setText(_out->GetOutbound()); {
_label->adjustSize(); _out = make_shared<OutboundNodeData>(data);
} _label->setText(_out->GetOutbound());
_label->adjustSize();
}
QWidget *embeddedWidget() override QWidget *embeddedWidget() override
{ {
return _label; return _label;
} }
ConnectionPolicy portInConnectionPolicy(PortIndex) const override ConnectionPolicy portInConnectionPolicy(PortIndex) const override
{ {
return ConnectionPolicy::Many; return ConnectionPolicy::Many;
} }
private:
NodeValidationState modelValidationState = NodeValidationState::Warning; private:
QString modelValidationError = tr("Missing or incorrect inputs"); NodeValidationState modelValidationState = NodeValidationState::Warning;
// QString modelValidationError = tr("Missing or incorrect inputs");
std::shared_ptr<OutboundNodeData> _out; //
QLabel *_label; std::shared_ptr<OutboundNodeData> _out;
QLabel *_label;
}; };

View File

@ -1,6 +1,6 @@
#include "ui/models/RuleNodeModel.hpp" #include "ui/models/RuleNodeModel.hpp"
QvRuleNodeDataModel::QvRuleNodeDataModel(std::shared_ptr<RuleNodeData> data): NodeDataModel() QvRuleNodeDataModel::QvRuleNodeDataModel(std::shared_ptr<RuleNodeData> data) : NodeDataModel()
{ {
_ruleTag = data; _ruleTag = data;
_label = new QLabel(); _label = new QLabel();

View File

@ -1,98 +1,103 @@
#pragma once #pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp" #include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvRuleNodeDataModel : public NodeDataModel class QvRuleNodeDataModel : public NodeDataModel
{ {
Q_OBJECT Q_OBJECT
public: public:
QvRuleNodeDataModel(std::shared_ptr<RuleNodeData> data); QvRuleNodeDataModel(std::shared_ptr<RuleNodeData> data);
~QvRuleNodeDataModel() ~QvRuleNodeDataModel()
{
// if (_label) {
// delete _label;
//}
}
QString caption() const override
{
return "Nothing";
}
bool captionVisible() const override
{
return false;
}
unsigned int nPorts(PortType portType) const override
{
if (portType == PortType::In) { return 1; }
else if (portType == PortType::Out)
{ {
//if (_label) { return 1;
// delete _label; }
//} else
{
return 0;
}
}
QString name() const override
{
return "RuleNode";
}
NodeDataType dataType(PortType portType, PortIndex portIndex) const override
{
Q_UNUSED(portIndex)
switch (portType)
{
case PortType::In: return inboundType;
case PortType::Out: return outboundType;
case PortType::None: break;
} }
QString caption() const override return NodeDataType();
{ }
return "Nothing";
}
bool captionVisible() const override std::shared_ptr<NodeData> outData(PortIndex port) override
{ {
return false; Q_UNUSED(port)
} return _ruleTag;
}
unsigned int nPorts(PortType portType) const override void setInData(std::shared_ptr<NodeData>, int) override
{ {
if (portType == PortType::In) { }
return 1; void setInData(vector<shared_ptr<NodeData>>, int) override
} else if (portType == PortType::Out) { {
return 1; }
} else { void setData(const QString &data)
return 0; {
} _ruleTag = make_shared<RuleNodeData>(data);
} _label->setText(_ruleTag->GetRuleTag());
_label->adjustSize();
}
QString name() const override QWidget *embeddedWidget() override
{ {
return "RuleNode"; return _label;
} }
NodeDataType dataType(PortType portType, PortIndex portIndex) const override ConnectionPolicy portInConnectionPolicy(PortIndex) const override
{ {
Q_UNUSED(portIndex) return ConnectionPolicy::Many;
}
switch (portType) { ConnectionPolicy portOutConnectionPolicy(PortIndex) const override
case PortType::In: {
return inboundType; return ConnectionPolicy::One;
}
case PortType::Out: private:
return outboundType; NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs");
case PortType::None: //
break; shared_ptr<RuleNodeData> _ruleTag;
} QLabel *_label;
return NodeDataType();
}
std::shared_ptr<NodeData> outData(PortIndex port) override
{
Q_UNUSED(port)
return _ruleTag;
}
void setInData(std::shared_ptr<NodeData>, int) override {}
void setInData(vector<shared_ptr<NodeData>>, int) override {}
void setData(const QString &data)
{
_ruleTag = make_shared<RuleNodeData>(data);
_label->setText(_ruleTag->GetRuleTag());
_label->adjustSize();
}
QWidget *embeddedWidget() override
{
return _label;
}
ConnectionPolicy portInConnectionPolicy(PortIndex) const override
{
return ConnectionPolicy::Many;
}
ConnectionPolicy portOutConnectionPolicy(PortIndex) const override
{
return ConnectionPolicy::One;
}
private:
NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs");
//
shared_ptr<RuleNodeData> _ruleTag;
QLabel *_label;
}; };

View File

@ -1,12 +1,11 @@
#include "w_ExportConfig.hpp" #include "w_ExportConfig.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/connection/Serialization.hpp" #include "core/connection/Serialization.hpp"
#include <QFileDialog> #include <QFileDialog>
// Private initialiser // Private initialiser
ConfigExporter::ConfigExporter(QWidget *parent) : ConfigExporter::ConfigExporter(QWidget *parent) : QDialog(parent), qzxing(this)
QDialog(parent),
qzxing(this)
{ {
setupUi(this); setupUi(this);
QvMessageBusConnect(ConfigExporter); QvMessageBusConnect(ConfigExporter);
@ -14,10 +13,9 @@ ConfigExporter::ConfigExporter(QWidget *parent) :
QvMessageBusSlotImpl(ConfigExporter) QvMessageBusSlotImpl(ConfigExporter)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -25,7 +23,7 @@ ConfigExporter::~ConfigExporter()
{ {
} }
//ConfigExporter::ConfigExporter(QWidget *parent) : ConfigExporter(parent) // ConfigExporter::ConfigExporter(QWidget *parent) : ConfigExporter(parent)
//{ //{
// // WIP // // WIP
// // /auto &x = connection; // // /auto &x = connection;
@ -49,17 +47,13 @@ void ConfigExporter::changeEvent(QEvent *e)
{ {
QDialog::changeEvent(e); QDialog::changeEvent(e);
switch (e->type()) { switch (e->type())
case QEvent::LanguageChange: {
retranslateUi(this); case QEvent::LanguageChange: retranslateUi(this); break;
break;
case QEvent::Resize: case QEvent::Resize: imageLabel->setPixmap(QPixmap::fromImage(image)); break;
imageLabel->setPixmap(QPixmap::fromImage(image));
break;
default: default: break;
break;
} }
} }

View File

@ -1,32 +1,36 @@
#pragma once #pragma once
#include "ui_w_ExportConfig.h"
#include "base/Qv2rayBase.hpp"
#include "3rdparty/qzxing/src/QZXing.h" #include "3rdparty/qzxing/src/QZXing.h"
#include "base/Qv2rayBase.hpp"
#include "ui/messaging/QvMessageBus.hpp" #include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_ExportConfig.h"
class ConfigExporter : public QDialog, private Ui::ExportConfigWindow class ConfigExporter
: public QDialog
, private Ui::ExportConfigWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigExporter(QWidget *parent = nullptr); explicit ConfigExporter(QWidget *parent = nullptr);
~ConfigExporter(); ~ConfigExporter();
void OpenExport(); void OpenExport();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
protected:
void changeEvent(QEvent *e);
private slots:
void on_closeBtn_clicked();
void on_saveBtn_clicked(); protected:
void changeEvent(QEvent *e);
private slots:
void on_closeBtn_clicked();
void on_copyImageBtn_clicked(); void on_saveBtn_clicked();
void on_copyVMessBtn_clicked(); void on_copyImageBtn_clicked();
private:
QZXing qzxing; void on_copyVMessBtn_clicked();
QImage image;
QString message; private:
QZXing qzxing;
QImage image;
QString message;
}; };

View File

@ -1,26 +1,25 @@
#include <QDebug> #include <QDebug>
#include "w_ImportConfig.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/connection/Serialization.hpp"
#include "core/kernel/KernelInteractions.hpp"
#include "ui/editors/w_JsonEditor.hpp"
#include "ui/editors/w_OutboundEditor.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
#include "ui/w_SubscriptionManager.hpp"
#include "w_ScreenShot_Core.hpp"
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QThread> #include <QThread>
#include "3rdparty/qzxing/src/QZXing.h"
#include "core/CoreUtils.hpp" ImportConfigWindow::ImportConfigWindow(QWidget *parent) : QDialog(parent)
#include "core/kernel/KernelInteractions.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/connection/Serialization.hpp"
#include "w_ScreenShot_Core.hpp"
#include "ui/editors/w_OutboundEditor.hpp"
#include "ui/editors/w_JsonEditor.hpp"
#include "w_ImportConfig.hpp"
#include "ui/w_SubscriptionManager.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
ImportConfigWindow::ImportConfigWindow(QWidget *parent)
: QDialog(parent)
{ {
setupUi(this); setupUi(this);
nameTxt->setText(QDateTime::currentDateTime().toString("MMdd_hhmm")); nameTxt->setText(QDateTime::currentDateTime().toString("MMdd_hhmm"));
@ -30,10 +29,9 @@ ImportConfigWindow::ImportConfigWindow(QWidget *parent)
QvMessageBusSlotImpl(ImportConfigWindow) QvMessageBusSlotImpl(ImportConfigWindow)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -43,7 +41,8 @@ ImportConfigWindow::~ImportConfigWindow()
QMap<QString, CONFIGROOT> ImportConfigWindow::OpenImport(bool partialImport) QMap<QString, CONFIGROOT> ImportConfigWindow::OpenImport(bool partialImport)
{ {
// partial import means only import as an outbound, will set keepImported to false and disable the checkbox // partial import means only import as an outbound, will set keepImported to
// false and disable the checkbox
// keepImportedInboundCheckBox->setChecked(!outboundsOnly); // keepImportedInboundCheckBox->setChecked(!outboundsOnly);
keepImportedInboundCheckBox->setEnabled(!partialImport); keepImportedInboundCheckBox->setEnabled(!partialImport);
routeEditBtn->setEnabled(!partialImport); routeEditBtn->setEnabled(!partialImport);
@ -61,9 +60,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
{ {
bool hideQv2ray = hideQv2rayCB->isChecked(); bool hideQv2ray = hideQv2rayCB->isChecked();
if (hideQv2ray) { if (hideQv2ray) { messageBus.EmitGlobalSignal(QvMBMessage::HIDE_WINDOWS); }
messageBus.EmitGlobalSignal(QvMBMessage::HIDE_WINDOWS);
}
QApplication::processEvents(); QApplication::processEvents();
QThread::msleep(static_cast<ulong>(doubleSpinBox->value() * 1000)); QThread::msleep(static_cast<ulong>(doubleSpinBox->value() * 1000));
@ -72,24 +69,29 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
auto _r = w.result(); auto _r = w.result();
// Explicitly delete w to call UNREGISTER_WINDOW // Explicitly delete w to call UNREGISTER_WINDOW
if (hideQv2ray) { if (hideQv2ray)
{
messageBus.EmitGlobalSignal(QvMBMessage::SHOW_WINDOWS); messageBus.EmitGlobalSignal(QvMBMessage::SHOW_WINDOWS);
//ShowAllGlobalWindow(); // ShowAllGlobalWindow();
} }
if (_r == QDialog::Accepted) { if (_r == QDialog::Accepted)
{
QZXing decoder; QZXing decoder;
decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13); decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13);
auto str = decoder.decodeImage(pix); auto str = decoder.decodeImage(pix);
//auto str = QZXing().decodeImage(pix); // auto str = QZXing().decodeImage(pix);
if (str.trimmed().isEmpty()) { if (str.trimmed().isEmpty())
{
LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + QSTRN(pix.width()) + ", v=" + QSTRN(pix.height())) LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + QSTRN(pix.width()) + ", v=" + QSTRN(pix.height()))
QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region.")); QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region."));
} else { }
else
{
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE); vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
//QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Successfully imported a QR code form the screen.")); // QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Successfully
//this->show(); // imported a QR code form the screen.")); this->show();
} }
} }
} }
@ -97,26 +99,30 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
void ImportConfigWindow::on_beginImportBtn_clicked() void ImportConfigWindow::on_beginImportBtn_clicked()
{ {
QString aliasPrefix = nameTxt->text(); QString aliasPrefix = nameTxt->text();
//auto conf = GetGlobalConfig(); // auto conf = GetGlobalConfig();
switch (tabWidget->currentIndex()) { switch (tabWidget->currentIndex())
case 0: { {
case 0:
{
//// From File... //// From File...
//bool ImportAsComplex = keepImportedInboundCheckBox->isChecked(); // bool ImportAsComplex = keepImportedInboundCheckBox->isChecked();
//QString path = fileLineTxt->text(); // QString path = fileLineTxt->text();
// //
//if (!V2rayKernelInstance::ValidateConfig(path)) { // if (!V2rayKernelInstance::ValidateConfig(path)) {
// QvMessageBoxWarn(this, tr("Import config file"), tr("Failed to check the validity of the config file.")); // QvMessageBoxWarn(this, tr("Import config file"), tr("Failed to
// return; // check the validity of the config file.")); return;
//} //}
// //
//aliasPrefix += "_" + QFileInfo(path).fileName(); // aliasPrefix += "_" + QFileInfo(path).fileName();
////CONFIGROOT config = ConvertConfigFromFile(path, ImportAsComplex); ////CONFIGROOT config = ConvertConfigFromFile(path,
//connections[aliasPrefix] = config; /// ImportAsComplex);
//break; // connections[aliasPrefix] = config;
// break;
} }
case 1: { case 1:
{
QStringList linkList = SplitLines(vmessConnectionStringTxt->toPlainText()); QStringList linkList = SplitLines(vmessConnectionStringTxt->toPlainText());
// //
// Clear UI and error lists // Clear UI and error lists
@ -126,24 +132,30 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
// //
LOG(MODULE_IMPORT, QSTRN(linkList.count()) + " string found in vmess box.") LOG(MODULE_IMPORT, QSTRN(linkList.count()) + " string found in vmess box.")
while (!linkList.isEmpty()) { while (!linkList.isEmpty())
{
aliasPrefix = nameTxt->text(); aliasPrefix = nameTxt->text();
auto link = linkList.takeFirst(); auto link = linkList.takeFirst();
QString errMessage; QString errMessage;
CONFIGROOT config = ConvertConfigFromString(link, &aliasPrefix, &errMessage); CONFIGROOT config = ConvertConfigFromString(link, &aliasPrefix, &errMessage);
// If the config is empty or we have any err messages. // If the config is empty or we have any err messages.
if (config.isEmpty() || !errMessage.isEmpty()) { if (config.isEmpty() || !errMessage.isEmpty())
{
// To prevent duplicated values. // To prevent duplicated values.
linkErrors[link] = QSTRN(linkErrors.count() + 1) + ": " + errMessage; linkErrors[link] = QSTRN(linkErrors.count() + 1) + ": " + errMessage;
continue; continue;
} else { }
else
{
connections[aliasPrefix] = config; connections[aliasPrefix] = config;
} }
} }
if (!linkErrors.isEmpty()) { if (!linkErrors.isEmpty())
for (auto item : linkErrors) { {
for (auto item : linkErrors)
{
vmessConnectionStringTxt->appendPlainText(linkErrors.key(item)); vmessConnectionStringTxt->appendPlainText(linkErrors.key(item));
errorsList->addItem(item); errorsList->addItem(item);
} }
@ -173,10 +185,13 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13); decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13);
auto str = decoder.decodeImage(QImage::fromData(buf)); auto str = decoder.decodeImage(QImage::fromData(buf));
if (str.isEmpty()) { if (str.isEmpty())
{
QvMessageBoxWarn(this, tr("QRCode scanning failed"), tr("Cannot find any QRCode from the image.")); QvMessageBoxWarn(this, tr("QRCode scanning failed"), tr("Cannot find any QRCode from the image."));
return; return;
} else { }
else
{
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE); vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
} }
} }
@ -184,9 +199,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
{ {
Q_UNUSED(previous) Q_UNUSED(previous)
if (current == nullptr) { if (current == nullptr) { return; }
return;
}
auto currentErrorText = current->text(); auto currentErrorText = current->text();
auto vmessEntry = linkErrors.key(currentErrorText); auto vmessEntry = linkErrors.key(currentErrorText);
@ -194,9 +207,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
auto startPos = vmessConnectionStringTxt->toPlainText().indexOf(vmessEntry); auto startPos = vmessConnectionStringTxt->toPlainText().indexOf(vmessEntry);
auto endPos = startPos + vmessEntry.length(); auto endPos = startPos + vmessEntry.length();
if (startPos < 0) { if (startPos < 0) { return; }
return;
}
// Select vmess string that is invalid. // Select vmess string that is invalid.
QTextCursor c = vmessConnectionStringTxt->textCursor(); QTextCursor c = vmessConnectionStringTxt->textCursor();
@ -208,36 +219,43 @@ void ImportConfigWindow::on_editFileBtn_clicked()
{ {
QFile file(fileLineTxt->text()); QFile file(fileLineTxt->text());
if (!file.exists()) { if (!file.exists())
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Provided file not found: ") + fileLineTxt->text()); {
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Provided file not found: ") + fileLineTxt->text());
return; return;
} }
auto jsonString = StringFromFile(&file); auto jsonString = StringFromFile(&file);
auto jsonCheckingError = VerifyJsonString(jsonString); auto jsonCheckingError = VerifyJsonString(jsonString);
if (!jsonCheckingError.isEmpty()) { if (!jsonCheckingError.isEmpty())
{
LOG(MODULE_FILEIO, "Currupted JSON file detected") LOG(MODULE_FILEIO, "Currupted JSON file detected")
if (QvMessageBoxAsk(this, tr("Edit file as JSON"), tr("The file you selected has json syntax error. Continue editing may make you lose data. Would you like to continue?") + NEWLINE + jsonCheckingError) != QMessageBox::Yes) { if (QvMessageBoxAsk(
return; this, tr("Edit file as JSON"),
} else { tr("The file you selected has json syntax error. Continue editing may make you lose data. Would you like to continue?") +
NEWLINE + jsonCheckingError) != QMessageBox::Yes)
{ return; }
else
{
LOG(MODULE_FILEIO, "Continue editing curruped json file, data loss is expected.") LOG(MODULE_FILEIO, "Continue editing curruped json file, data loss is expected.")
} }
} }
auto json = JsonFromString(jsonString); auto json = JsonFromString(jsonString);
JsonEditor editor(json, this); JsonEditor editor(json, this);
json = editor.OpenEditor(); json = editor.OpenEditor();
if (editor.result() == QDialog::Accepted) { if (editor.result() == QDialog::Accepted)
{
auto str = JsonToString(json); auto str = JsonToString(json);
bool result = StringToFile(str, file); bool result = StringToFile(str, file);
if (!result) { if (!result) { QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Failed to save file, please check if you have proper permissions")); }
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Failed to save file, please check if you have proper permissions")); }
} else
} else { {
LOG(MODULE_FILEIO, "Canceled saving a file.") LOG(MODULE_FILEIO, "Canceled saving a file.")
} }
} }
@ -249,7 +267,8 @@ void ImportConfigWindow::on_connectionEditBtn_clicked()
bool isChanged = w.result() == QDialog::Accepted; bool isChanged = w.result() == QDialog::Accepted;
QString alias = w.GetFriendlyName(); QString alias = w.GetFriendlyName();
if (isChanged) { if (isChanged)
{
OUTBOUNDS outboundsList; OUTBOUNDS outboundsList;
outboundsList.push_back(outboundEntry); outboundsList.push_back(outboundEntry);
CONFIGROOT root; CONFIGROOT root;
@ -257,7 +276,9 @@ void ImportConfigWindow::on_connectionEditBtn_clicked()
// //
connections[alias] = root; connections[alias] = root;
accept(); accept();
} else { }
else
{
return; return;
} }
} }
@ -275,7 +296,8 @@ void ImportConfigWindow::on_subscriptionButton_clicked()
auto importToComplex = !keepImportedInboundCheckBox->isEnabled(); auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
connections.clear(); connections.clear();
if (importToComplex) { if (importToComplex)
{
auto _result = w.GetSelectedConfig(); auto _result = w.GetSelectedConfig();
connections[_result.first] = _result.second; connections[_result.first] = _result.second;
} }
@ -290,10 +312,13 @@ void ImportConfigWindow::on_routeEditBtn_clicked()
bool isChanged = w.result() == QDialog::Accepted; bool isChanged = w.result() == QDialog::Accepted;
QString alias = nameTxt->text(); QString alias = nameTxt->text();
if (isChanged) { if (isChanged)
{
connections[alias] = result; connections[alias] = result;
accept(); accept();
} else { }
else
{
return; return;
} }
} }

View File

@ -1,44 +1,47 @@
#pragma once #pragma once
#include <QDialog>
#include <QString>
#include <QJsonObject>
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "ui_w_ImportConfig.h"
#include "ui/messaging/QvMessageBus.hpp" #include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_ImportConfig.h"
class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow #include <QDialog>
#include <QJsonObject>
#include <QString>
class ImportConfigWindow
: public QDialog
, private Ui::ImportConfigWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ImportConfigWindow(QWidget *parent = nullptr); explicit ImportConfigWindow(QWidget *parent = nullptr);
~ImportConfigWindow(); ~ImportConfigWindow();
QMap<QString, CONFIGROOT> OpenImport(bool outboundsOnly = false); QMap<QString, CONFIGROOT> OpenImport(bool outboundsOnly = false);
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_selectFileBtn_clicked(); void on_selectFileBtn_clicked();
void on_qrFromScreenBtn_clicked(); void on_qrFromScreenBtn_clicked();
void on_beginImportBtn_clicked(); void on_beginImportBtn_clicked();
void on_selectImageBtn_clicked(); void on_selectImageBtn_clicked();
void on_errorsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_errorsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
void on_editFileBtn_clicked(); void on_editFileBtn_clicked();
void on_connectionEditBtn_clicked(); void on_connectionEditBtn_clicked();
void on_cancelImportBtn_clicked(); void on_cancelImportBtn_clicked();
void on_subscriptionButton_clicked(); void on_subscriptionButton_clicked();
void on_routeEditBtn_clicked(); void on_routeEditBtn_clicked();
void on_hideQv2rayCB_stateChanged(int arg1); void on_hideQv2rayCB_stateChanged(int arg1);
private: private:
QMap<QString, CONFIGROOT> connections; QMap<QString, CONFIGROOT> connections;
QMap<QString, QString> linkErrors; QMap<QString, QString> linkErrors;
}; };

View File

@ -1,76 +1,86 @@
#pragma once #pragma once
#include "w_MainWindow.hpp" #include "w_MainWindow.hpp"
#include "ui/editors/w_JsonEditor.hpp"
#include "ui/editors/w_OutboundEditor.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
#include "w_ExportConfig.hpp"
#include "w_ImportConfig.hpp" #include "w_ImportConfig.hpp"
#include "w_PreferencesWindow.hpp" #include "w_PreferencesWindow.hpp"
#include "w_SubscriptionManager.hpp" #include "w_SubscriptionManager.hpp"
#include "w_ExportConfig.hpp"
#include "ui/editors/w_OutboundEditor.hpp"
#include "ui/editors/w_RoutesEditor.hpp"
#include "ui/editors/w_JsonEditor.hpp"
//#include <QAction> //#include <QAction>
#include "components/pac/QvPACHandler.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "ui/widgets/ConnectionInfoWidget.hpp"
#include <QCloseEvent> #include <QCloseEvent>
#include <QDebug> #include <QDebug>
#include <QDesktopServices>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QHeaderView> #include <QHeaderView>
#include <QInputDialog> #include <QInputDialog>
#include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QDesktopServices>
#include <QUrl> #include <QUrl>
#include <QVersionNumber> #include <QVersionNumber>
#include <QKeyEvent>
#include "components/plugins/toolbar/QvToolbar.hpp" // MainWindow.cpp --> Main MainWindow source file, handles mostly UI-related
#include "components/pac/QvPACHandler.hpp" // operations.
#include "core/connection/ConnectionIO.hpp"
#include "ui/widgets/ConnectionInfoWidget.hpp"
// MainWindow.cpp --> Main MainWindow source file, handles mostly UI-related operations.
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING #define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
// //
#define GetItemWidget(item) (qobject_cast<ConnectionItemWidget*>(connectionListWidget->itemWidget(item, 0))) #define GetItemWidget(item) (qobject_cast<ConnectionItemWidget *>(connectionListWidget->itemWidget(item, 0)))
/* /*
#define ItemConnectionIdentifier(__item__) (__item__->data(0, Qt::UserRole).value<ConnectionIdentifier>()) #define ItemConnectionIdentifier(__item__) (__item__->data(0,
Qt::UserRole).value<ConnectionIdentifier>())
#define CheckConfigType(_item_, TYPE) (connections.contains(ItemConnectionIdentifier(_item_)) && connections[ItemConnectionIdentifier(_item_)].configType == CONNECTION_ ## TYPE) #define CheckConfigType(_item_, TYPE)
(connections.contains(ItemConnectionIdentifier(_item_)) &&
connections[ItemConnectionIdentifier(_item_)].configType == CONNECTION_ ## TYPE)
#define SUBSCRIPTION_CONFIG_MODIFY_ASK(_item_) \ #define SUBSCRIPTION_CONFIG_MODIFY_ASK(_item_) \
if (!CheckConfigType(_item_, REGULAR)) { \ if (!CheckConfigType(_item_, REGULAR)) { \
if (QvMessageBoxAsk(this, QObject::tr("Editing a subscription config"), QObject::tr("You are trying to edit a config loaded from subscription.") + \ if (QvMessageBoxAsk(this, QObject::tr("Editing a subscription config"),
NEWLINE + QObject::tr("All changes will be overwritten when the subscriptions are updated next time.") + \ QObject::tr("You are trying to edit a config loaded from subscription.") + \
NEWLINE + QObject::tr("Are you still going to do so?")) != QMessageBox::Yes) { \ NEWLINE + QObject::tr("All changes will be
return; \ overwritten when the subscriptions are updated next time.") + \
} \ NEWLINE + QObject::tr("Are you still going to do
} \ so?")) != QMessageBox::Yes) { \
return; \
} \
} \
#define SUBSCRIPTION_CONFIG_MODIFY_DENY(_item_) \ #define SUBSCRIPTION_CONFIG_MODIFY_DENY(_item_) \
if (!CheckConfigType(_item_, REGULAR)) { \ if (!CheckConfigType(_item_, REGULAR)) { \
QvMessageBoxWarn(this, QObject::tr("Editing a subscription config"), QObject::tr("You should not modity this property of a config from a subscription")); \ QvMessageBoxWarn(this, QObject::tr("Editing a subscription config"),
return; \ QObject::tr("You should not modity this property of a config from a
} \ subscription")); \
return; \
} \
#define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 && (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION))) #define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 &&
#define IsSelectionConnectable (!connectionListWidget->selectedItems().empty() && IsConnectableItem(connectionListWidget->selectedItems().first())) (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION))) #define
IsSelectionConnectable (!connectionListWidget->selectedItems().empty() &&
IsConnectableItem(connectionListWidget->selectedItems().first()))
*/ */
MainWindow *MainWindow::mwInstance = nullptr; MainWindow *MainWindow::mwInstance = nullptr;
QvMessageBusSlotImpl(MainWindow) QvMessageBusSlotImpl(MainWindow)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTray(this), tcpingHelper(3, this) MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) //, vinstance(), hTray(this), tcpingHelper(3, this)
{ {
setupUi(this); setupUi(this);
MainWindow::mwInstance = this; MainWindow::mwInstance = this;
@ -86,8 +96,8 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
this->setWindowIcon(QIcon(":/assets/icons/qv2ray.png")); this->setWindowIcon(QIcon(":/assets/icons/qv2ray.png"));
hTray.setIcon(QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/tray.png" : ":/assets/icons/ui_light/tray.png")); hTray.setIcon(QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/tray.png" : ":/assets/icons/ui_light/tray.png"));
importConfigButton->setIcon(QICON_R("import.png")); importConfigButton->setIcon(QICON_R("import.png"));
//pingTestBtn->setIcon(QICON_R("ping_gauge.png")); // pingTestBtn->setIcon(QICON_R("ping_gauge.png"));
//shareBtn->setIcon(QICON_R("share.png")); // shareBtn->setIcon(QICON_R("share.png"));
updownImageBox->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)"); updownImageBox->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)");
updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)"); updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)");
// //
@ -96,7 +106,7 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
this->show(); this->show();
QvMessageBoxWarn(this, tr("V2ray vcore terminated."), QvMessageBoxWarn(this, tr("V2ray vcore terminated."),
tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE + tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE +
tr("To solve the problem, read the V2ray log in the log text browser.")); tr("To solve the problem, read the V2ray log in the log text browser."));
}); });
connect(ConnectionManager, &QvConnectionHandler::OnConnected, this, &MainWindow::OnConnected); connect(ConnectionManager, &QvConnectionHandler::OnConnected, this, &MainWindow::OnConnected);
connect(ConnectionManager, &QvConnectionHandler::OnDisConnected, this, &MainWindow::OnDisConnected); connect(ConnectionManager, &QvConnectionHandler::OnDisConnected, this, &MainWindow::OnDisConnected);
@ -141,9 +151,11 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
// //
connect(action_Tray_ShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility); connect(action_Tray_ShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility);
connect(action_Tray_ShowPreferencesWindow, &QAction::triggered, this, &MainWindow::on_preferencesBtn_clicked); connect(action_Tray_ShowPreferencesWindow, &QAction::triggered, this, &MainWindow::on_preferencesBtn_clicked);
//connect(action_Tray_Start, &QAction::triggered, this, &MainWindow::on_startButton_clicked); // connect(action_Tray_Start, &QAction::triggered, this,
//connect(action_Tray_Stop, &QAction::triggered, this, &MainWindow::on_stopButton_clicked); // &MainWindow::on_startButton_clicked); connect(action_Tray_Stop,
//connect(action_Tray_Reconnect, &QAction::triggered, this, &MainWindow::on_reconnectButton_clicked); // &QAction::triggered, this, &MainWindow::on_stopButton_clicked);
// connect(action_Tray_Reconnect, &QAction::triggered, this,
// &MainWindow::on_reconnectButton_clicked);
connect(action_Tray_Quit, &QAction::triggered, this, &MainWindow::on_actionExit_triggered); connect(action_Tray_Quit, &QAction::triggered, this, &MainWindow::on_actionExit_triggered);
connect(action_Tray_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy); connect(action_Tray_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy);
connect(action_Tray_ClearSystemProxy, &QAction::triggered, this, &MainWindow::MWClearSystemProxy); connect(action_Tray_ClearSystemProxy, &QAction::triggered, this, &MainWindow::MWClearSystemProxy);
@ -166,9 +178,11 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
connect(action_RCM_ShareQR, &QAction::triggered, this, &MainWindow::on_action_RCM_ShareQR_triggered); connect(action_RCM_ShareQR, &QAction::triggered, this, &MainWindow::on_action_RCM_ShareQR_triggered);
// //
// Globally invokable signals. // Globally invokable signals.
//connect(this, &MainWindow::Connect, this, &MainWindow::on_startButton_clicked); // connect(this, &MainWindow::Connect, this,
//connect(this, &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked); // &MainWindow::on_startButton_clicked); connect(this,
//connect(this, &MainWindow::ReConnect, this, &MainWindow::on_reconnectButton_clicked); // &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked);
// connect(this, &MainWindow::ReConnect, this,
// &MainWindow::on_reconnectButton_clicked);
// //
hTray.setContextMenu(tray_RootMenu); hTray.setContextMenu(tray_RootMenu);
hTray.show(); hTray.show();
@ -183,13 +197,15 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
LOG(MODULE_UI, "Loading data...") LOG(MODULE_UI, "Loading data...")
auto groups = ConnectionManager->AllGroups(); auto groups = ConnectionManager->AllGroups();
for (auto group : groups) { for (auto group : groups)
{
auto groupItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(group)); auto groupItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(group));
connectionListWidget->addTopLevelItem(groupItem); connectionListWidget->addTopLevelItem(groupItem);
connectionListWidget->setItemWidget(groupItem, 0, new ConnectionItemWidget(group, connectionListWidget)); connectionListWidget->setItemWidget(groupItem, 0, new ConnectionItemWidget(group, connectionListWidget));
auto connections = ConnectionManager->Connections(group); auto connections = ConnectionManager->Connections(group);
for (auto connection : connections) { for (auto connection : connections)
{
auto connectionItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(connection)); auto connectionItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(connection));
groupItem->addChild(connectionItem); groupItem->addChild(connectionItem);
auto widget = new ConnectionItemWidget(connection, connectionListWidget); auto widget = new ConnectionItemWidget(connection, connectionListWidget);
@ -202,18 +218,16 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
// Find and start if there is an auto-connection // Find and start if there is an auto-connection
auto needShowWindow = true; auto needShowWindow = true;
if (!GlobalConfig.autoStartId.isEmpty()) { if (!GlobalConfig.autoStartId.isEmpty())
{
auto id = ConnectionId(GlobalConfig.autoStartId); auto id = ConnectionId(GlobalConfig.autoStartId);
needShowWindow = ConnectionManager->StartConnection(id).has_value(); needShowWindow = ConnectionManager->StartConnection(id).has_value();
} }
if (needShowWindow) { if (needShowWindow) { this->show(); }
this->show();
}
//// If we are not connected to anything, show the MainWindow. //// If we are not connected to anything, show the MainWindow.
if (needShowWindow) { if (needShowWindow) {}
}
#ifndef DISABLE_AUTO_UPDATE #ifndef DISABLE_AUTO_UPDATE
requestHelper = new QvHttpRequestHelper(); requestHelper = new QvHttpRequestHelper();
@ -221,7 +235,8 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
requestHelper->get("https://api.github.com/repos/Qv2ray/Qv2ray/releases/latest"); requestHelper->get("https://api.github.com/repos/Qv2ray/Qv2ray/releases/latest");
#endif #endif
if (StartupOption.enableToolbarPlguin) { if (StartupOption.enableToolbarPlguin)
{
LOG(MODULE_UI, "Plugin daemon is enabled.") LOG(MODULE_UI, "Plugin daemon is enabled.")
StartProcessingPlugins(); StartProcessingPlugins();
} }
@ -231,33 +246,35 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
splitter->setSizes(QList<int>() << 100 << 300); splitter->setSizes(QList<int>() << 100 << 300);
} }
void MainWindow::keyPressEvent(QKeyEvent *e) void MainWindow::keyPressEvent(QKeyEvent *e)
{ {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
{
// If pressed enter or return on connectionListWidget. // If pressed enter or return on connectionListWidget.
// Try to connect to the selected connection. // Try to connect to the selected connection.
//if (focusWidget() == connectionListWidget) { // if (focusWidget() == connectionListWidget) {
// if (!IsSelectionConnectable) return; // if (!IsSelectionConnectable) return;
// //
// auto selections = connectionListWidget->selectedItems(); // auto selections = connectionListWidget->selectedItems();
// QVariant v; // QVariant v;
// auto vv = v.value<QvConnectionObject>(); // auto vv = v.value<QvConnectionObject>();
// ShowAndSetConnection(ItemConnectionIdentifier(selections.first()), true, true); // ShowAndSetConnection(ItemConnectionIdentifier(selections.first()),
// true, true);
//} //}
} }
} }
void MainWindow::on_action_StartThis_triggered() void MainWindow::on_action_StartThis_triggered()
{ {
//if (!IsSelectionConnectable) { // if (!IsSelectionConnectable) {
// QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please select a config from the list.")); // QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please
// return; // select a config from the list.")); return;
//} //}
// //
//CurrentSelectedItemPtr = connectionListWidget->selectedItems().first(); // CurrentSelectedItemPtr = connectionListWidget->selectedItems().first();
//CurrentConnectionIdentifier = ItemConnectionIdentifier(CurrentSelectedItemPtr); // CurrentConnectionIdentifier =
//on_reconnectButton_clicked(); // ItemConnectionIdentifier(CurrentSelectedItemPtr);
// on_reconnectButton_clicked();
} }
#ifndef DISABLE_AUTO_UPDATE #ifndef DISABLE_AUTO_UPDATE
@ -269,28 +286,27 @@ void MainWindow::VersionUpdate(QByteArray &data)
QVersionNumber newVersion = QVersionNumber::fromString(root["tag_name"].toString("v").remove(0, 1)); QVersionNumber newVersion = QVersionNumber::fromString(root["tag_name"].toString("v").remove(0, 1));
QVersionNumber currentVersion = QVersionNumber::fromString(QString(QV2RAY_VERSION_STRING).remove(0, 1)); QVersionNumber currentVersion = QVersionNumber::fromString(QString(QV2RAY_VERSION_STRING).remove(0, 1));
QVersionNumber ignoredVersion = QVersionNumber::fromString(GlobalConfig.ignoredVersion); QVersionNumber ignoredVersion = QVersionNumber::fromString(GlobalConfig.ignoredVersion);
LOG(MODULE_UPDATE, "Received update info, Latest: " + newVersion.toString() + " Current: " + currentVersion.toString() + " Ignored: " + ignoredVersion.toString()) LOG(MODULE_UPDATE, "Received update info, Latest: " + newVersion.toString() + " Current: " + currentVersion.toString() +
" Ignored: " + ignoredVersion.toString())
// If the version is newer than us. // If the version is newer than us.
// And new version is newer than the ignored version. // And new version is newer than the ignored version.
if (newVersion > currentVersion && newVersion > ignoredVersion) { if (newVersion > currentVersion && newVersion > ignoredVersion)
{
LOG(MODULE_UPDATE, "New version detected.") LOG(MODULE_UPDATE, "New version detected.")
auto link = root["html_url"].toString(""); auto link = root["html_url"].toString("");
auto result = QvMessageBoxAsk(this, tr("Update"), auto result =
tr("Found a new version: ") + root["tag_name"].toString("") + QvMessageBoxAsk(this, tr("Update"),
"\r\n" + tr("Found a new version: ") + root["tag_name"].toString("") + "\r\n" + root["name"].toString("") +
root["name"].toString("") + "\r\n------------\r\n" + root["body"].toString("") + "\r\n------------\r\n" + tr("Download Link: ") + link,
"\r\n------------\r\n" + QMessageBox::Ignore);
root["body"].toString("") +
"\r\n------------\r\n" +
tr("Download Link: ") + link, QMessageBox::Ignore);
if (result == QMessageBox::Yes) { if (result == QMessageBox::Yes) { QDesktopServices::openUrl(QUrl::fromUserInput(link)); }
QDesktopServices::openUrl(QUrl::fromUserInput(link)); else if (result == QMessageBox::Ignore)
} else if (result == QMessageBox::Ignore) { {
// Set and save ingored version. // Set and save ingored version.
GlobalConfig.ignoredVersion = newVersion.toString(); GlobalConfig.ignoredVersion = newVersion.toString();
//SaveGlobalConfig(GlobalConfig); // SaveGlobalConfig(GlobalConfig);
} }
} }
} }
@ -309,12 +325,14 @@ void MainWindow::closeEvent(QCloseEvent *event)
} }
void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason) void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
{ {
switch (reason) { switch (reason)
{
case QSystemTrayIcon::Trigger: case QSystemTrayIcon::Trigger:
// Toggle Show/Hide // Toggle Show/Hide
#ifndef __APPLE__ #ifndef __APPLE__
// Every single click will trigger the Show/Hide toggling. // Every single click will trigger the Show/Hide toggling.
// So, as what common macOS Apps do, we don't toggle visibility here. // So, as what common macOS Apps do, we don't toggle visibility
// here.
ToggleVisibility(); ToggleVisibility();
#endif #endif
break; break;
@ -325,13 +343,13 @@ void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
#endif #endif
break; break;
default: default: break;
break;
} }
} }
void MainWindow::ToggleVisibility() void MainWindow::ToggleVisibility()
{ {
if (this->isHidden()) { if (this->isHidden())
{
this->show(); this->show();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
setWindowState(Qt::WindowNoState); setWindowState(Qt::WindowNoState);
@ -340,7 +358,9 @@ void MainWindow::ToggleVisibility()
SetWindowPos(HWND(this->winId()), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); SetWindowPos(HWND(this->winId()), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#endif #endif
tray_RootMenu->actions()[0]->setText(tr("Hide")); tray_RootMenu->actions()[0]->setText(tr("Hide"));
} else { }
else
{
this->hide(); this->hide();
tray_RootMenu->actions()[0]->setText(tr("Show")); tray_RootMenu->actions()[0]->setText(tr("Show"));
} }
@ -348,9 +368,7 @@ void MainWindow::ToggleVisibility()
void MainWindow::on_actionExit_triggered() void MainWindow::on_actionExit_triggered()
{ {
if (StartupOption.enableToolbarPlguin) { if (StartupOption.enableToolbarPlguin) { StopProcessingPlugins(); }
StopProcessingPlugins();
}
ExitQv2ray(); ExitQv2ray();
} }
@ -366,13 +384,14 @@ void MainWindow::on_clearlogButton_clicked()
void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{ {
Q_UNUSED(previous) Q_UNUSED(previous)
//CurrentItem = current; // CurrentItem = current;
//isRenamingInProgress = false; // isRenamingInProgress = false;
// //
//if (!IsConnectableItem(current)) return; // if (!IsConnectableItem(current)) return;
// //
//// no need to check !isRenamingInProgress since it's always true. //// no need to check !isRenamingInProgress since it's always true.
//ShowAndSetConnection(ItemConnectionIdentifier(current), !vinstance->KernelStarted, false); // ShowAndSetConnection(ItemConnectionIdentifier(current),
// !vinstance->KernelStarted, false);
////on_connectionListWidget_itemClicked(current, 0); ////on_connectionListWidget_itemClicked(current, 0);
} }
void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos) void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos)
@ -381,52 +400,55 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint
auto _pos = QCursor::pos(); auto _pos = QCursor::pos();
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos)); auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
if (GetItemWidget(item)->IsConnection()) { if (GetItemWidget(item)->IsConnection()) { connectionListMenu->popup(_pos); }
connectionListMenu->popup(_pos);
}
} }
void MainWindow::on_action_RCM_RenameConnection_triggered() void MainWindow::on_action_RCM_RenameConnection_triggered()
{ {
//auto item = connectionListWidget->currentItem(); // auto item = connectionListWidget->currentItem();
//SUBSCRIPTION_CONFIG_MODIFY_DENY(item) // SUBSCRIPTION_CONFIG_MODIFY_DENY(item)
//item->setFlags(item->flags() | Qt::ItemIsEditable); // item->setFlags(item->flags() | Qt::ItemIsEditable);
//isRenamingInProgress = true; // isRenamingInProgress = true;
//connectionListWidget->editItem(item); // connectionListWidget->editItem(item);
//renameOriginalIdentifier = ItemConnectionIdentifier(item); // renameOriginalIdentifier = ItemConnectionIdentifier(item);
} }
void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int) void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
{ {
//DEBUG(UI, "A connection ListViewItem is changed. This should ONLY occur when renaming an connection.") // DEBUG(UI, "A connection ListViewItem is changed. This should ONLY occur
// when renaming an connection.")
// //
//if (!isRenamingInProgress) { // if (!isRenamingInProgress) {
// return; // return;
//} //}
// //
//isRenamingInProgress = false; // isRenamingInProgress = false;
//// In this case it's after we entered the name. //// In this case it's after we entered the name.
//// and tell user you should not rename a config from subscription. //// and tell user you should not rename a config from subscription.
//auto newIdentifier = renameOriginalIdentifier; // auto newIdentifier = renameOriginalIdentifier;
//newIdentifier.connectionName = item->text(0); // newIdentifier.connectionName = item->text(0);
//LOG(CONNECTION, "RENAME: " + renameOriginalIdentifier.IdentifierString() + " -> " + newIdentifier.IdentifierString()) // LOG(CONNECTION, "RENAME: " + renameOriginalIdentifier.IdentifierString()
// + " -> " + newIdentifier.IdentifierString())
// //
//// If I really did some changes. //// If I really did some changes.
//if (renameOriginalIdentifier != newIdentifier) { // if (renameOriginalIdentifier != newIdentifier) {
// bool canContinueRename = true; // bool canContinueRename = true;
// //
// if (newIdentifier.connectionName.trimmed().isEmpty()) { // if (newIdentifier.connectionName.trimmed().isEmpty()) {
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name cannot be empty")); // QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name
// canContinueRename = false; // cannot be empty")); canContinueRename = false;
// } // }
// //
// QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP"); // QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP");
// //if (GlobalConfig.configs.contains(newIdentifier.connectionName)) { // //if (GlobalConfig.configs.contains(newIdentifier.connectionName)) {
// // QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name has been used already, Please choose another.")); // // QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name
// has been used already, Please choose another."));
// // canContinueRename = false; // // canContinueRename = false;
// //} // //}
// //
// if (!IsValidFileName(newIdentifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION)) { // if (!IsValidFileName(newIdentifier.connectionName +
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name you suggested is not valid, please try another.")); // QV2RAY_CONFIG_FILE_EXTENSION)) {
// canContinueRename = false; // QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name you
// suggested is not valid, please try another.")); canContinueRename
// = false;
// } // }
// //
// if (!canContinueRename) { // if (!canContinueRename) {
@ -448,8 +470,10 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
// //GlobalConfig.configs.removeOne(renameOriginalIdentifier.connectionName); // //GlobalConfig.configs.removeOne(renameOriginalIdentifier.connectionName);
// //GlobalConfig.configs.push_back(newIdentifier.connectionName); // //GlobalConfig.configs.push_back(newIdentifier.connectionName);
// // // //
// //connections[newIdentifier] = connections.take(renameOriginalIdentifier); // //connections[newIdentifier] =
// //RenameConnection(renameOriginalIdentifier.connectionName, newIdentifier.connectionName); // connections.take(renameOriginalIdentifier);
// //RenameConnection(renameOriginalIdentifier.connectionName,
// newIdentifier.connectionName);
// //LOG(UI, "Saving a global config") // //LOG(UI, "Saving a global config")
// //SaveGlobalConfig(GlobalConfig); // //SaveGlobalConfig(GlobalConfig);
// //// // ////
@ -462,35 +486,38 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
// // on_reconnectButton_clicked(); // // on_reconnectButton_clicked();
// // } // // }
// //} // //}
// //OnConfigListChanged(CurrentConnectionIdentifier.connectionName == renameOriginalName); // //OnConfigListChanged(CurrentConnectionIdentifier.connectionName ==
// renameOriginalName);
//} //}
} }
void MainWindow::on_removeConfigButton_clicked() void MainWindow::on_removeConfigButton_clicked()
{ {
QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP"); QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP");
//QList<ConnectionIdentifier> connlist; // QList<ConnectionIdentifier> connlist;
// //
//for (auto item : connectionListWidget->selectedItems()) { // for (auto item : connectionListWidget->selectedItems()) {
// if (IsConnectableItem(item)) { // if (IsConnectableItem(item)) {
// connlist.append(ItemConnectionIdentifier(item)); // connlist.append(ItemConnectionIdentifier(item));
// } // }
//} //}
// //
//LOG(UI, "Selected " + QSTRN(connlist.count()) + " items") // LOG(UI, "Selected " + QSTRN(connlist.count()) + " items")
// //
//if (connlist.isEmpty()) { // if (connlist.isEmpty()) {
// // Remove nothing means doing nothing. // // Remove nothing means doing nothing.
// return; // return;
//} //}
// //
//if (QvMessageBoxAsk(this, tr("Removing Connection(s)"), tr("Are you sure to remove selected connection(s)?")) != QMessageBox::Yes) { // if (QvMessageBoxAsk(this, tr("Removing Connection(s)"), tr("Are you sure
// to remove selected connection(s)?")) != QMessageBox::Yes) {
// return; // return;
//} //}
// //
//// A triple-state flag which indicates if the user wants to remove the configs loaded from a subscription. //// A triple-state flag which indicates if the user wants to remove the
//int subscriptionRemovalCheckStatus = -1; /// configs loaded from a subscription.
// int subscriptionRemovalCheckStatus = -1;
// //
//for (auto conn : connlist) { // for (auto conn : connlist) {
// if (conn == CurrentConnectionIdentifier) { // if (conn == CurrentConnectionIdentifier) {
// on_stopButton_clicked(); // on_stopButton_clicked();
// CurrentConnectionIdentifier = ConnectionIdentifier(); // CurrentConnectionIdentifier = ConnectionIdentifier();
@ -499,8 +526,10 @@ void MainWindow::on_removeConfigButton_clicked()
// auto connData = connections[conn]; // auto connData = connections[conn];
// //
// // Remove auto start config. // // Remove auto start config.
// if (GlobalConfig.autoStartConfig.subscriptionName == connData.subscriptionName && // if (GlobalConfig.autoStartConfig.subscriptionName ==
// GlobalConfig.autoStartConfig.connectionName == connData.connectionName) // connData.subscriptionName &&
// GlobalConfig.autoStartConfig.connectionName ==
// connData.connectionName)
// // If all those settings match. // // If all those settings match.
// { // {
// GlobalConfig.autoStartConfig.subscriptionName.clear(); // GlobalConfig.autoStartConfig.subscriptionName.clear();
@ -510,44 +539,53 @@ void MainWindow::on_removeConfigButton_clicked()
// if (connData.configType == CONNECTION_REGULAR) { // if (connData.configType == CONNECTION_REGULAR) {
// // Just remove the regular configs. // // Just remove the regular configs.
// if (!connData.subscriptionName.isEmpty()) { // if (!connData.subscriptionName.isEmpty()) {
// LOG(UI, "Unexpected subscription name in a single regular config.") // LOG(UI, "Unexpected subscription name in a single regular
// connData.subscriptionName.clear(); // config.") connData.subscriptionName.clear();
// } // }
// //
// GlobalConfig.configs.removeOne(conn.connectionName); // GlobalConfig.configs.removeOne(conn.connectionName);
// //
// if (!RemoveConnection(conn.connectionName)) { // if (!RemoveConnection(conn.connectionName)) {
// QvMessageBoxWarn(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually.")); // QvMessageBoxWarn(this, tr("Removing this Connection"),
// tr("Failed to delete connection file, please delete
// manually."));
// } // }
// } else if (connData.configType == CONNECTION_SUBSCRIPTION) { // } else if (connData.configType == CONNECTION_SUBSCRIPTION) {
// if (subscriptionRemovalCheckStatus == -1) { // if (subscriptionRemovalCheckStatus == -1) {
// subscriptionRemovalCheckStatus = (QvMessageBoxAsk(this, tr("Removing a subscription config"), tr("Do you want to remove the config loaded from a subscription?")) == QMessageBox::Yes) // subscriptionRemovalCheckStatus = (QvMessageBoxAsk(this,
// tr("Removing a subscription config"), tr("Do you want to
// remove the config loaded from a subscription?")) ==
// QMessageBox::Yes)
// ? 1 // Yes i want // ? 1 // Yes i want
// : 0; // No please keep // : 0; // No please keep
// } // }
// //
// if (subscriptionRemovalCheckStatus == 1) { // if (subscriptionRemovalCheckStatus == 1) {
// if (!RemoveSubscriptionConnection(connData.subscriptionName, connData.connectionName)) { // if (!RemoveSubscriptionConnection(connData.subscriptionName,
// QvMessageBoxWarn(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually.")); // connData.connectionName)) {
// QvMessageBoxWarn(this, tr("Removing this Connection"),
// tr("Failed to delete connection file, please delete
// manually."));
// } // }
// } // }
// } else { // } else {
// LOG(SETTINGS, "Unknown config type -> Not regular nor subscription...") // LOG(SETTINGS, "Unknown config type -> Not regular nor
// subscription...")
// } // }
//} //}
// //
//LOG(UI, "Saving GlobalConfig") // LOG(UI, "Saving GlobalConfig")
//SaveGlobalConfig(GlobalConfig); // SaveGlobalConfig(GlobalConfig);
//OnConfigListChanged(false); // OnConfigListChanged(false);
//ShowAndSetConnection(CurrentConnectionIdentifier, false, false); // ShowAndSetConnection(CurrentConnectionIdentifier, false, false);
} }
void MainWindow::on_importConfigButton_clicked() void MainWindow::on_importConfigButton_clicked()
{ {
QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP"); QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP");
//ImportConfigWindow w(this); // ImportConfigWindow w(this);
//auto configs = w.OpenImport(); // auto configs = w.OpenImport();
//if (!configs.isEmpty()) { // if (!configs.isEmpty()) {
// for (auto conf : configs) { // for (auto conf : configs) {
// auto name = configs.key(conf, ""); // auto name = configs.key(conf, "");
// //
@ -569,26 +607,26 @@ void MainWindow::on_editConfigButton_clicked()
void MainWindow::on_action_RCM_ConvToComplex_triggered() void MainWindow::on_action_RCM_ConvToComplex_triggered()
{ {
//// Check if we have a connection selected... //// Check if we have a connection selected...
//if (!IsSelectionConnectable) { // if (!IsSelectionConnectable) {
// QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a Config")); // QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a
// return; // Config")); return;
//} //}
// //
//auto selectedFirst = connectionListWidget->currentItem(); // auto selectedFirst = connectionListWidget->currentItem();
//auto _identifier = ItemConnectionIdentifier(selectedFirst); // auto _identifier = ItemConnectionIdentifier(selectedFirst);
//SUBSCRIPTION_CONFIG_MODIFY_DENY(selectedFirst) // SUBSCRIPTION_CONFIG_MODIFY_DENY(selectedFirst)
//// ////
//auto outBoundRoot = connections[_identifier].config; // auto outBoundRoot = connections[_identifier].config;
//CONFIGROOT root; // CONFIGROOT root;
//bool isChanged = false; // bool isChanged = false;
//// ////
//LOG(UI, "INFO: Opening route editor.") // LOG(UI, "INFO: Opening route editor.")
//RouteEditor routeWindow(outBoundRoot, this); // RouteEditor routeWindow(outBoundRoot, this);
//root = routeWindow.OpenEditor(); // root = routeWindow.OpenEditor();
//isChanged = routeWindow.result() == QDialog::Accepted; // isChanged = routeWindow.result() == QDialog::Accepted;
//QString alias = _identifier.connectionName; // QString alias = _identifier.connectionName;
// //
//if (isChanged) { // if (isChanged) {
// connections[_identifier].config = root; // connections[_identifier].config = root;
// // true indicates the alias will NOT change // // true indicates the alias will NOT change
// SaveConnectionConfig(root, &alias, true); // SaveConnectionConfig(root, &alias, true);
@ -603,7 +641,7 @@ void MainWindow::on_action_RCM_EditJson_triggered()
void MainWindow::on_action_RCM_ShareQR_triggered() void MainWindow::on_action_RCM_ShareQR_triggered()
{ {
//on_shareBtn_clicked(); // on_shareBtn_clicked();
} }
void MainWindow::on_subsButton_clicked() void MainWindow::on_subsButton_clicked()
@ -616,9 +654,7 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item
Q_UNUSED(column) Q_UNUSED(column)
auto widget = GetItemWidget(item); auto widget = GetItemWidget(item);
if (widget->IsConnection()) { if (widget->IsConnection()) { widget->BeginConnection(); }
widget->BeginConnection();
}
} }
void MainWindow::OnDisConnected(const ConnectionId &id) void MainWindow::OnDisConnected(const ConnectionId &id)
@ -628,14 +664,13 @@ void MainWindow::OnDisConnected(const ConnectionId &id)
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE); hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE);
connetionStatusLabel->setText(tr("Disconnected")); connetionStatusLabel->setText(tr("Disconnected"));
if (systemProxyEnabled) { if (systemProxyEnabled) { MWClearSystemProxy(false); }
MWClearSystemProxy(false);
}
//QFile(QV2RAY_GENERATED_FILE_PATH).remove(); // QFile(QV2RAY_GENERATED_FILE_PATH).remove();
if (GlobalConfig.inboundConfig.pacConfig.enablePAC) { if (GlobalConfig.inboundConfig.pacConfig.enablePAC)
//pacServer.StopServer(); {
// pacServer.StopServer();
LOG(MODULE_UI, "Stopping PAC server") LOG(MODULE_UI, "Stopping PAC server")
} }
} }
@ -655,56 +690,64 @@ void MainWindow::OnConnected(const ConnectionId &id)
bool httpEnabled = GlobalConfig.inboundConfig.useHTTP; bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
bool socksEnabled = GlobalConfig.inboundConfig.useSocks; bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
if (usePAC) { if (usePAC)
{
bool canStartPAC = true; bool canStartPAC = true;
QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT; http://proxy:8080 QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS
// 127.0.0.1:1080; DIRECT; http://proxy:8080
auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP; auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP;
if (pacIP.isEmpty()) { if (pacIP.isEmpty())
{
LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1") LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1")
pacIP = "127.0.0.1"; pacIP = "127.0.0.1";
} }
if (pacUseSocks) { if (pacUseSocks)
if (socksEnabled) { {
pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port); if (socksEnabled) { pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port); }
} else { else
{
LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled") LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled")
QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled")); QvMessageBoxWarn(this, tr("Configuring PAC"),
tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
canStartPAC = false; canStartPAC = false;
} }
} else { }
if (httpEnabled) { else
pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port); {
} else { if (httpEnabled) { pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port); }
else
{
LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled") LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled")
QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled")); QvMessageBoxWarn(this, tr("Configuring PAC"),
tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
canStartPAC = false; canStartPAC = false;
} }
} }
if (canStartPAC) { if (canStartPAC)
//pacServer.SetProxyString(pacProxyString); {
//pacServer.StartListen(); // pacServer.SetProxyString(pacProxyString);
} else { // pacServer.StartListen();
}
else
{
LOG(MODULE_PROXY, "Not starting PAC due to previous error.") LOG(MODULE_PROXY, "Not starting PAC due to previous error.")
} }
} }
if (GlobalConfig.inboundConfig.setSystemProxy) { if (GlobalConfig.inboundConfig.setSystemProxy) { MWSetSystemProxy(); }
MWSetSystemProxy();
}
} }
void MainWindow::onConnectionWidgetFocusRequested(const ConnectionItemWidget *_widget) void MainWindow::onConnectionWidgetFocusRequested(const ConnectionItemWidget *_widget)
{ {
if (_widget == nullptr) { if (_widget == nullptr) { return; }
return;
}
for (auto _item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive)) { for (auto _item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive))
if (GetItemWidget(_item_) == _widget) { {
if (GetItemWidget(_item_) == _widget)
{
LOG(MODULE_UI, "Setting current item.") LOG(MODULE_UI, "Setting current item.")
connectionListWidget->setCurrentItem(_item_); connectionListWidget->setCurrentItem(_item_);
connectionListWidget->scrollToItem(_item_); connectionListWidget->scrollToItem(_item_);
@ -717,28 +760,31 @@ void MainWindow::onConnectionWidgetFocusRequested(const ConnectionItemWidget *_w
void MainWindow::on_connectionFilterTxt_textEdited(const QString &arg1) void MainWindow::on_connectionFilterTxt_textEdited(const QString &arg1)
{ {
// No recursive since we only need top level item // No recursive since we only need top level item
for (auto _top_item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard)) { for (auto _top_item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard))
//auto topWidget = GetItemWidget(_top_item_); {
// auto topWidget = GetItemWidget(_top_item_);
bool isTotallyHide = true; bool isTotallyHide = true;
for (auto i = 0; i < _top_item_->childCount(); i++) { for (auto i = 0; i < _top_item_->childCount(); i++)
{
auto _child_ = _top_item_->child(i); auto _child_ = _top_item_->child(i);
if (GetItemWidget(_child_)->NameMatched(arg1)) { if (GetItemWidget(_child_)->NameMatched(arg1))
{
LOG(MODULE_UI, "Setting current item.") LOG(MODULE_UI, "Setting current item.")
// Show the child // Show the child
_child_->setHidden(false); _child_->setHidden(false);
isTotallyHide = false; isTotallyHide = false;
} else { }
else
{
_child_->setHidden(true); _child_->setHidden(true);
} }
} }
_top_item_->setHidden(isTotallyHide); _top_item_->setHidden(isTotallyHide);
if (!isTotallyHide) { if (!isTotallyHide) { connectionListWidget->expandItem(_top_item_); }
connectionListWidget->expandItem(_top_item_);
}
} }
} }
@ -748,11 +794,12 @@ void MainWindow::on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int
infoWidget->ShowDetails(GetItemWidget(item)->Identifier()); infoWidget->ShowDetails(GetItemWidget(item)->Identifier());
} }
void MainWindow::onConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp, const quint64 totalDown) void MainWindow::onConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp,
const quint64 totalDown)
{ {
Q_UNUSED(id); Q_UNUSED(id);
// This may not be, or may not precisely be, speed per second if low-level has "any" latency. // This may not be, or may not precisely be, speed per second if low-level
// (Hope not...) // has "any" latency. (Hope not...)
speedChartWidget->AddPointData(upSpeed, downSpeed); speedChartWidget->AddPointData(upSpeed, downSpeed);
// //
auto totalSpeedUp = FormatBytes(upSpeed) + "/s"; auto totalSpeedUp = FormatBytes(upSpeed) + "/s";
@ -763,7 +810,8 @@ void MainWindow::onConnectionStatsArrived(const ConnectionId &id, const quint64
netspeedLabel->setText(totalSpeedUp + NEWLINE + totalSpeedDown); netspeedLabel->setText(totalSpeedUp + NEWLINE + totalSpeedDown);
dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown); dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown);
// //
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + ConnectionManager->GetDisplayName(id) + NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown); hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + ConnectionManager->GetDisplayName(id) + NEWLINE "Up: " + totalSpeedUp +
" Down: " + totalSpeedDown);
} }
void MainWindow::onVCoreLogArrived(const ConnectionId &id, const QString &log) void MainWindow::onVCoreLogArrived(const ConnectionId &id, const QString &log)
@ -777,35 +825,40 @@ void MainWindow::onVCoreLogArrived(const ConnectionId &id, const QString &log)
auto maxLines = GlobalConfig.uiConfig.maximumLogLines; auto maxLines = GlobalConfig.uiConfig.maximumLogLines;
QTextBlock block = masterLogBrowser->document()->begin(); QTextBlock block = masterLogBrowser->document()->begin();
while (block.isValid()) { while (block.isValid())
if (masterLogBrowser->document()->blockCount() > maxLines) { {
if (masterLogBrowser->document()->blockCount() > maxLines)
{
QTextCursor cursor(block); QTextCursor cursor(block);
block = block.next(); block = block.next();
cursor.select(QTextCursor::BlockUnderCursor); cursor.select(QTextCursor::BlockUnderCursor);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
cursor.removeSelectedText(); cursor.removeSelectedText();
} else { }
else
{
break; break;
} }
} }
if (val >= max * 0.8 || val >= max - 20) if (val >= max * 0.8 || val >= max - 20) bar->setValue(max);
bar->setValue(max);
} }
void MainWindow::OnEditRequested(const ConnectionId &id) void MainWindow::OnEditRequested(const ConnectionId &id)
{ {
auto outBoundRoot = ConnectionManager->GetConnectionRoot(id); auto outBoundRoot = ConnectionManager->GetConnectionRoot(id);
CONFIGROOT root; CONFIGROOT root;
bool isChanged = false; bool isChanged = false;
if (IsComplexConfig(outBoundRoot)) { if (IsComplexConfig(outBoundRoot))
{
LOG(MODULE_UI, "INFO: Opening route editor.") LOG(MODULE_UI, "INFO: Opening route editor.")
RouteEditor routeWindow(outBoundRoot, this); RouteEditor routeWindow(outBoundRoot, this);
root = routeWindow.OpenEditor(); root = routeWindow.OpenEditor();
isChanged = routeWindow.result() == QDialog::Accepted; isChanged = routeWindow.result() == QDialog::Accepted;
} else { }
else
{
LOG(MODULE_UI, "INFO: Opening single connection edit window.") LOG(MODULE_UI, "INFO: Opening single connection edit window.")
auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject()); auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject());
OutboundEditor w(out, this); OutboundEditor w(out, this);
@ -816,18 +869,21 @@ void MainWindow::OnEditRequested(const ConnectionId &id)
root.insert("outbounds", outboundsList); root.insert("outbounds", outboundsList);
} }
if (isChanged) { if (isChanged)
//if (CheckConfigType(firstSelected, SUBSCRIPTION)) { {
// if (CheckConfigType(firstSelected, SUBSCRIPTION)) {
// auto name = connections[_identifier].connectionName; // auto name = connections[_identifier].connectionName;
// // Assume name will not change. // // Assume name will not change.
// SaveSubscriptionConfig(root, connections[_identifier].subscriptionName, &name); // SaveSubscriptionConfig(root,
// connections[_identifier].subscriptionName, &name);
//} else { //} else {
// connections[_identifier].config = root; // connections[_identifier].config = root;
// // true indicates the alias will NOT change // // true indicates the alias will NOT change
// SaveConnectionConfig(root, &alias, true); // SaveConnectionConfig(root, &alias, true);
//} //}
ConnectionManager->UpdateConnection(id, root); ConnectionManager->UpdateConnection(id, root);
//OnConfigListChanged(alias == CurrentConnectionIdentifier.connectionName); // OnConfigListChanged(alias ==
// CurrentConnectionIdentifier.connectionName);
} }
} }
void MainWindow::OnJsonEditRequested(const ConnectionId &id) void MainWindow::OnJsonEditRequested(const ConnectionId &id)
@ -835,7 +891,5 @@ void MainWindow::OnJsonEditRequested(const ConnectionId &id)
JsonEditor w(ConnectionManager->GetConnectionRoot(id), this); JsonEditor w(ConnectionManager->GetConnectionRoot(id), this);
auto root = CONFIGROOT(w.OpenEditor()); auto root = CONFIGROOT(w.OpenEditor());
if (w.result() == QDialog::Accepted) { if (w.result() == QDialog::Accepted) { ConnectionManager->UpdateConnection(id, root); }
ConnectionManager->UpdateConnection(id, root);
}
} }

View File

@ -1,114 +1,116 @@
#pragma once #pragma once
#include "common/HTTPRequestHelper.hpp"
#include "common/LogHighlighter.hpp"
#include "components/speedchart/speedwidget.hpp"
#include "core/handler/ConnectionHandler.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_MainWindow.h"
#include <QMainWindow> #include <QMainWindow>
#include <QMenu> #include <QMenu>
#include <QScrollBar> #include <QScrollBar>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include "ui_w_MainWindow.h"
#include "common/LogHighlighter.hpp"
#include "common/HTTPRequestHelper.hpp"
#include "components/speedchart/speedwidget.hpp"
#include "core/handler/ConnectionHandler.hpp"
#include "ui/messaging/QvMessageBus.hpp"
// ========================================================================================== // ==========================================================================================
#include "ui/widgets/ConnectionItemWidget.hpp"
#include "ui/widgets/ConnectionInfoWidget.hpp" #include "ui/widgets/ConnectionInfoWidget.hpp"
#include "ui/widgets/ConnectionItemWidget.hpp"
class MainWindow : public QMainWindow, Ui::MainWindow class MainWindow
: public QMainWindow
, Ui::MainWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override; ~MainWindow() override;
signals: signals:
void Connect() const; void Connect() const;
void DisConnect() const; void DisConnect() const;
void ReConnect() const; void ReConnect() const;
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_action_RCM_ShareQR_triggered(); void on_action_RCM_ShareQR_triggered();
void on_activatedTray(QSystemTrayIcon::ActivationReason reason); void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
void on_actionExit_triggered(); void on_actionExit_triggered();
void on_preferencesBtn_clicked(); void on_preferencesBtn_clicked();
void on_clearlogButton_clicked(); void on_clearlogButton_clicked();
void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_connectionListWidget_customContextMenuRequested(const QPoint &pos); void on_connectionListWidget_customContextMenuRequested(const QPoint &pos);
void on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int column); void on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int column);
void on_removeConfigButton_clicked(); void on_removeConfigButton_clicked();
void on_importConfigButton_clicked(); void on_importConfigButton_clicked();
void on_editConfigButton_clicked(); void on_editConfigButton_clicked();
void on_subsButton_clicked(); void on_subsButton_clicked();
// //
void ToggleVisibility(); void ToggleVisibility();
#ifndef DISABLE_AUTO_UPDATE #ifndef DISABLE_AUTO_UPDATE
void VersionUpdate(QByteArray &data); void VersionUpdate(QByteArray &data);
#endif #endif
// //
public: public:
static MainWindow *mwInstance; static MainWindow *mwInstance;
protected: protected:
void keyPressEvent(QKeyEvent *e) override; void keyPressEvent(QKeyEvent *e) override;
void closeEvent(QCloseEvent *) override; void closeEvent(QCloseEvent *) override;
private slots: private slots:
// //
void OnConnected(const ConnectionId &id); void OnConnected(const ConnectionId &id);
void OnDisConnected(const ConnectionId &id); void OnDisConnected(const ConnectionId &id);
void OnEditRequested(const ConnectionId &id); void OnEditRequested(const ConnectionId &id);
void OnJsonEditRequested(const ConnectionId &id); void OnJsonEditRequested(const ConnectionId &id);
void onConnectionWidgetFocusRequested(const ConnectionItemWidget *widget); void onConnectionWidgetFocusRequested(const ConnectionItemWidget *widget);
//void onConnectionConnected(const ConnectionId &id); // void onConnectionConnected(const ConnectionId &id);
//void onConnectionDisConnected(const ConnectionId &id); // void onConnectionDisConnected(const ConnectionId &id);
void onConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp, const quint64 totalDown); void onConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp,
void onVCoreLogArrived(const ConnectionId &id, const QString &log); const quint64 totalDown);
// void onVCoreLogArrived(const ConnectionId &id, const QString &log);
void on_action_StartThis_triggered(); //
void on_action_RCM_EditJson_triggered(); void on_action_StartThis_triggered();
void on_action_RCM_ConvToComplex_triggered(); void on_action_RCM_EditJson_triggered();
void on_action_RCM_RenameConnection_triggered(); void on_action_RCM_ConvToComplex_triggered();
void on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_action_RCM_RenameConnection_triggered();
void on_connectionFilterTxt_textEdited(const QString &arg1); void on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int column); void on_connectionFilterTxt_textEdited(const QString &arg1);
void on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int column);
private: private:
QTreeWidgetItem *CurrentItem; QTreeWidgetItem *CurrentItem;
// Charts // Charts
SpeedWidget *speedChartWidget; SpeedWidget *speedChartWidget;
QMenu *connectionListMenu; QMenu *connectionListMenu;
#ifndef DISABLE_AUTO_UPDATE #ifndef DISABLE_AUTO_UPDATE
QvHttpRequestHelper *requestHelper; QvHttpRequestHelper *requestHelper;
#endif #endif
QSystemTrayIcon hTray; QSystemTrayIcon hTray;
//PACServer pacServer; // PACServer pacServer;
//QvTCPingModel tcpingHelper; // QvTCPingModel tcpingHelper;
SyntaxHighlighter *vCoreLogHighlighter; SyntaxHighlighter *vCoreLogHighlighter;
ConnectionInfoWidget *infoWidget; ConnectionInfoWidget *infoWidget;
// //
// Actions in the system tray menu // Actions in the system tray menu
// //
QMenu *tray_RootMenu = new QMenu(this); QMenu *tray_RootMenu = new QMenu(this);
QAction *action_Tray_ShowHide; QAction *action_Tray_ShowHide;
QAction *action_Tray_ShowPreferencesWindow; QAction *action_Tray_ShowPreferencesWindow;
QAction *action_Tray_Quit; QAction *action_Tray_Quit;
// --> Connectivities // --> Connectivities
QAction *action_Tray_Start; QAction *action_Tray_Start;
QAction *action_Tray_Reconnect ; QAction *action_Tray_Reconnect;
QAction *action_Tray_Stop; QAction *action_Tray_Stop;
// --> System proxy settings // --> System proxy settings
QMenu *tray_SystemProxyMenu = new QMenu(this); QMenu *tray_SystemProxyMenu = new QMenu(this);
QAction *action_Tray_SetSystemProxy; QAction *action_Tray_SetSystemProxy;
QAction *action_Tray_ClearSystemProxy; QAction *action_Tray_ClearSystemProxy;
// //
// ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling V2ray Connectivities. // ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling V2ray Connectivities.
bool systemProxyEnabled; bool systemProxyEnabled;
void MWSetSystemProxy(); void MWSetSystemProxy();
void MWClearSystemProxy(bool); void MWClearSystemProxy(bool);
void CheckSubscriptionsUpdate(); void CheckSubscriptionsUpdate();
}; };

View File

@ -1,16 +1,19 @@
// Supplementary file for MainWindow -- Basically the handler for connectivity management // Supplementary file for MainWindow -- Basically the handler for connectivity
// and components interactions. // management and components interactions. We NEED to include the cpp file to
// We NEED to include the cpp file to define the macros. // define the macros.
#include "w_MainWindow.cpp"
#include "components/proxy/QvProxyConfigurator.hpp" #include "components/proxy/QvProxyConfigurator.hpp"
#include "w_MainWindow.cpp"
//QTreeWidgetItem *MainWindow::FindItemByIdentifier(QvConnectionObject identifier) // QTreeWidgetItem *MainWindow::FindItemByIdentifier(QvConnectionObject
// identifier)
//{ //{
// //// First filter out all items with our config name. // //// First filter out all items with our config name.
// //auto items = connectionListWidget->findItems(identifier.connectionName, Qt::MatchExactly | Qt::MatchRecursive); // //auto items = connectionListWidget->findItems(identifier.connectionName,
// Qt::MatchExactly | Qt::MatchRecursive);
// // // //
// //for (auto item : items) { // //for (auto item : items) {
// // // This connectable prevents the an item with (which is the parent node of a subscription, having the same // // // This connectable prevents the an item with (which is the parent
// node of a subscription, having the same
// // // -- name as our current connected name) // // // -- name as our current connected name)
// // if (!IsConnectableItem(item)) { // // if (!IsConnectableItem(item)) {
// // LOG(UI, "Invalid Item found: " + item->text(0)) // // LOG(UI, "Invalid Item found: " + item->text(0))
@ -25,8 +28,8 @@
// // } // // }
// //} // //}
// // // //
// //LOG(UI, "Warning: Failed to find an item named: " + identifier.IdentifierString()) // //LOG(UI, "Warning: Failed to find an item named: " +
// return nullptr; // identifier.IdentifierString()) return nullptr;
//} //}
void MainWindow::MWClearSystemProxy(bool showMessage) void MainWindow::MWClearSystemProxy(bool showMessage)
@ -35,9 +38,7 @@ void MainWindow::MWClearSystemProxy(bool showMessage)
LOG(MODULE_UI, "Clearing System Proxy") LOG(MODULE_UI, "Clearing System Proxy")
systemProxyEnabled = false; systemProxyEnabled = false;
if (showMessage) { if (showMessage) { hTray.showMessage("Qv2ray", tr("System proxy cleared."), windowIcon()); }
hTray.showMessage("Qv2ray", tr("System proxy cleared."), windowIcon());
}
} }
void MainWindow::MWSetSystemProxy() void MainWindow::MWSetSystemProxy()
@ -48,45 +49,58 @@ void MainWindow::MWSetSystemProxy()
bool socksEnabled = GlobalConfig.inboundConfig.useSocks; bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
// //
// Set system proxy if necessary // Set system proxy if necessary
//bool isComplex = IsComplexConfig(connections[CurrentConnectionIdentifier].config); // bool isComplex =
// IsComplexConfig(connections[CurrentConnectionIdentifier].config);
bool isComplex = true; bool isComplex = true;
if (!isComplex) { if (!isComplex)
{
// Is simple config and we will try to set system proxy. // Is simple config and we will try to set system proxy.
LOG(MODULE_UI, "Preparing to set system proxy") LOG(MODULE_UI, "Preparing to set system proxy")
// //
QString proxyAddress; QString proxyAddress;
bool canSetSystemProxy = true; bool canSetSystemProxy = true;
if (usePAC) { if (usePAC)
if ((httpEnabled && !pacUseSocks) || (socksEnabled && pacUseSocks)) { {
if ((httpEnabled && !pacUseSocks) || (socksEnabled && pacUseSocks))
{
// If we use PAC and socks/http are properly configured for PAC // If we use PAC and socks/http are properly configured for PAC
LOG(MODULE_PROXY, "System proxy uses PAC") LOG(MODULE_PROXY, "System proxy uses PAC")
proxyAddress = "http://" + GlobalConfig.inboundConfig.listenip + ":" + QSTRN(GlobalConfig.inboundConfig.pacConfig.port) + "/pac"; proxyAddress = "http://" + GlobalConfig.inboundConfig.listenip + ":" + QSTRN(GlobalConfig.inboundConfig.pacConfig.port) + "/pac";
} else { }
else
{
// Not properly configured // Not properly configured
LOG(MODULE_PROXY, "Failed to process pac due to following reasons:") LOG(MODULE_PROXY, "Failed to process pac due to following reasons:")
LOG(MODULE_PROXY, " --> PAC is configured to use socks but socks is not enabled.") LOG(MODULE_PROXY, " --> PAC is configured to use socks but socks is not enabled.")
LOG(MODULE_PROXY, " --> PAC is configuted to use http but http is not enabled.") LOG(MODULE_PROXY, " --> PAC is configuted to use http but http is not enabled.")
QvMessageBoxWarn(this, tr("PAC Processing Failed"), tr("HTTP or SOCKS inbound is not properly configured for PAC") + QvMessageBoxWarn(this, tr("PAC Processing Failed"),
NEWLINE + tr("Qv2ray will continue, but will not set system proxy.")); tr("HTTP or SOCKS inbound is not properly configured for PAC") + NEWLINE +
tr("Qv2ray will continue, but will not set system proxy."));
canSetSystemProxy = false; canSetSystemProxy = false;
} }
} else { }
else
{
// Not using PAC // Not using PAC
if (httpEnabled || socksEnabled) { if (httpEnabled || socksEnabled)
{
// Not use PAC, System proxy should use HTTP or SOCKS // Not use PAC, System proxy should use HTTP or SOCKS
LOG(MODULE_PROXY, "Setting up system proxy.") LOG(MODULE_PROXY, "Setting up system proxy.")
// A 'proxy host' should be a host WITHOUT `http://` uri scheme // A 'proxy host' should be a host WITHOUT `http://` uri scheme
proxyAddress = "localhost"; proxyAddress = "localhost";
} else { }
else
{
LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.") LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.")
QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled")); QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled"));
canSetSystemProxy = false; canSetSystemProxy = false;
} }
} }
if (canSetSystemProxy) { if (canSetSystemProxy)
{
LOG(MODULE_UI, "Setting system proxy for simple config.") LOG(MODULE_UI, "Setting system proxy for simple config.")
auto httpPort = GlobalConfig.inboundConfig.useHTTP ? GlobalConfig.inboundConfig.http_port : 0; auto httpPort = GlobalConfig.inboundConfig.useHTTP ? GlobalConfig.inboundConfig.http_port : 0;
auto socksPort = GlobalConfig.inboundConfig.useSocks ? GlobalConfig.inboundConfig.socks_port : 0; auto socksPort = GlobalConfig.inboundConfig.useSocks ? GlobalConfig.inboundConfig.socks_port : 0;
@ -96,7 +110,9 @@ void MainWindow::MWSetSystemProxy()
systemProxyEnabled = true; systemProxyEnabled = true;
hTray.showMessage("Qv2ray", tr("System proxy settings applied."), windowIcon()); hTray.showMessage("Qv2ray", tr("System proxy settings applied."), windowIcon());
} }
} else { }
else
{
hTray.showMessage("Qv2ray", tr("Cannot set proxy for complex config."), windowIcon()); hTray.showMessage("Qv2ray", tr("Cannot set proxy for complex config."), windowIcon());
} }
} }
@ -105,28 +121,29 @@ void MainWindow::CheckSubscriptionsUpdate()
{ {
QStringList updateList; QStringList updateList;
for (auto index = 0; index < GlobalConfig.subscriptions.count(); index++) { for (auto index = 0; index < GlobalConfig.subscriptions.count(); index++)
{
auto subs = GlobalConfig.subscriptions.values()[index]; auto subs = GlobalConfig.subscriptions.values()[index];
auto key = GlobalConfig.subscriptions.keys()[index]; auto key = GlobalConfig.subscriptions.keys()[index];
// //
auto lastRenewDate = QDateTime::fromTime_t(subs.lastUpdated); auto lastRenewDate = QDateTime::fromTime_t(subs.lastUpdated);
auto renewTime = lastRenewDate.addSecs(subs.updateInterval * 86400); auto renewTime = lastRenewDate.addSecs(subs.updateInterval * 86400);
LOG(MODULE_SUBSCRIPTION, "Subscription \"" + key + "\": " + NEWLINE + LOG(MODULE_SUBSCRIPTION, "Subscription \"" + key + "\": " + NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + NEWLINE +
" --> Last renewal time: " + lastRenewDate.toString() + NEWLINE + " --> Renew interval: " + QSTRN(subs.updateInterval) + NEWLINE +
" --> Renew interval: " + QSTRN(subs.updateInterval) + NEWLINE + " --> Ideal renew time: " + renewTime.toString())
" --> Ideal renew time: " + renewTime.toString())
if (renewTime <= QDateTime::currentDateTime()) { if (renewTime <= QDateTime::currentDateTime())
{
LOG(MODULE_SUBSCRIPTION, "Subscription: " + key + " needs to be updated.") LOG(MODULE_SUBSCRIPTION, "Subscription: " + key + " needs to be updated.")
updateList.append(key); updateList.append(key);
} }
} }
if (!updateList.isEmpty()) { if (!updateList.isEmpty())
{
QvMessageBoxWarn(this, tr("Update Subscriptions"), QvMessageBoxWarn(this, tr("Update Subscriptions"),
tr("There are subscriptions need to be updated, please go to subscriptions window to update them.") + NEWLINE + NEWLINE + tr("There are subscriptions need to be updated, please go to subscriptions window to update them.") + NEWLINE +
tr("These subscriptions are out-of-date: ") + NEWLINE + updateList.join(";")); NEWLINE + tr("These subscriptions are out-of-date: ") + NEWLINE + updateList.join(";"));
on_subsButton_clicked(); on_subsButton_clicked();
} }
} }

View File

@ -1,27 +1,28 @@
#include "w_PreferencesWindow.hpp" #include "w_PreferencesWindow.hpp"
#include <QFileDialog>
#include <QColorDialog>
#include <QStyleFactory>
#include <QStyle>
#include <QDesktopServices>
#include "common/QvHelpers.hpp"
#include "common/HTTPRequestHelper.hpp" #include "common/HTTPRequestHelper.hpp"
#include "common/QvHelpers.hpp"
#include "common/QvTranslator.hpp"
#include "components/autolaunch/QvAutoLaunch.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp"
#include "core/config/ConfigBackend.hpp" #include "core/config/ConfigBackend.hpp"
#include "core/connection/ConnectionIO.hpp" #include "core/connection/ConnectionIO.hpp"
#include "core/kernel/KernelInteractions.hpp"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp" #include "core/kernel/KernelInteractions.hpp"
#include "components/autolaunch/QvAutoLaunch.hpp"
#include "common/QvTranslator.hpp" #include <QColorDialog>
#include <QDesktopServices>
#include <QFileDialog>
#include <QStyle>
#include <QStyleFactory>
using Qv2ray::common::validation::IsValidIPAddress; using Qv2ray::common::validation::IsValidIPAddress;
#define LOADINGCHECK if(!finishedLoading) return; #define LOADINGCHECK \
#define NEEDRESTART if(finishedLoading) IsConnectionPropertyChanged = true; if (!finishedLoading) return;
#define NEEDRESTART \
if (finishedLoading) IsConnectionPropertyChanged = true;
PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), CurrentConfig()
CurrentConfig()
{ {
setupUi(this); setupUi(this);
QvMessageBusConnect(PreferencesWindow); QvMessageBusConnect(PreferencesWindow);
@ -31,17 +32,14 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
// Set network Toolbar page state. // Set network Toolbar page state.
networkToolbarPage->setEnabled(StartupOption.enableToolbarPlguin); networkToolbarPage->setEnabled(StartupOption.enableToolbarPlguin);
if (!StartupOption.enableToolbarPlguin) { if (!StartupOption.enableToolbarPlguin)
networkToolbarInfoLabel->setText(tr("Qv2ray Network Toolbar is disabled and still under test. Add --withToolbarPlugin to enable.")); { networkToolbarInfoLabel->setText(tr("Qv2ray Network Toolbar is disabled and still under test. Add --withToolbarPlugin to enable.")); }
}
// We add locales // We add locales
languageComboBox->clear(); languageComboBox->clear();
QDirIterator it(":/translations"); QDirIterator it(":/translations");
while (it.hasNext()) { while (it.hasNext()) { languageComboBox->addItem(it.next().split("/").last().split(".").first()); }
languageComboBox->addItem(it.next().split("/").last().split(".").first());
}
// Set auto start button state // Set auto start button state
SetAutoStartButtonsState(GetLaunchAtLoginStatus()); SetAutoStartButtonsState(GetLaunchAtLoginStatus());
@ -120,26 +118,30 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
// //
DNSListTxt->clear(); DNSListTxt->clear();
for (auto dnsStr : CurrentConfig.connectionConfig.dnsList) { for (auto dnsStr : CurrentConfig.connectionConfig.dnsList)
{
auto str = dnsStr.trimmed(); auto str = dnsStr.trimmed();
if (!str.isEmpty()) { if (!str.isEmpty()) { DNSListTxt->appendPlainText(str); }
DNSListTxt->appendPlainText(str);
}
} }
// //
cancelIgnoreVersionBtn->setEnabled(CurrentConfig.ignoredVersion != ""); cancelIgnoreVersionBtn->setEnabled(CurrentConfig.ignoredVersion != "");
ignoredNextVersion->setText(CurrentConfig.ignoredVersion); ignoredNextVersion->setText(CurrentConfig.ignoredVersion);
for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++) { for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++)
nsBarPagesList->addItem(tr("Page") + QSTRN(i + 1) + ": " + QSTRN(CurrentConfig.toolBarConfig.Pages[i].Lines.size()) + " " + tr("Item(s)")); {
nsBarPagesList->addItem(tr("Page") + QSTRN(i + 1) + ": " + QSTRN(CurrentConfig.toolBarConfig.Pages[i].Lines.size()) + " " +
tr("Item(s)"));
} }
if (CurrentConfig.toolBarConfig.Pages.size() > 0) { if (CurrentConfig.toolBarConfig.Pages.size() > 0)
{
nsBarPagesList->setCurrentRow(0); nsBarPagesList->setCurrentRow(0);
on_nsBarPagesList_currentRowChanged(0); on_nsBarPagesList_currentRowChanged(0);
} else { }
else
{
networkToolbarSettingsFrame->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
nsBarLinesList->setEnabled(false); nsBarLinesList->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
@ -153,22 +155,17 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
auto autoStartConnId = ConnectionId(CurrentConfig.autoStartId); auto autoStartConnId = ConnectionId(CurrentConfig.autoStartId);
auto autoStartGroupId = ConnectionManager->GetConnectionGroupId(autoStartConnId); auto autoStartGroupId = ConnectionManager->GetConnectionGroupId(autoStartConnId);
for (auto group : ConnectionManager->AllGroups()) { for (auto group : ConnectionManager->AllGroups()) { autoStartSubsCombo->addItem(ConnectionManager->GetDisplayName(group)); }
autoStartSubsCombo->addItem(ConnectionManager->GetDisplayName(group));
}
autoStartSubsCombo->setCurrentText(ConnectionManager->GetDisplayName(autoStartGroupId)); autoStartSubsCombo->setCurrentText(ConnectionManager->GetDisplayName(autoStartGroupId));
for (auto conn : ConnectionManager->Connections(autoStartGroupId)) { for (auto conn : ConnectionManager->Connections(autoStartGroupId)) { autoStartConnCombo->addItem(ConnectionManager->GetDisplayName(conn)); }
autoStartConnCombo->addItem(ConnectionManager->GetDisplayName(conn));
}
autoStartConnCombo->setCurrentText(ConnectionManager->GetDisplayName(autoStartConnId)); autoStartConnCombo->setCurrentText(ConnectionManager->GetDisplayName(autoStartConnId));
// FP Settings // FP Settings
if (CurrentConfig.connectionConfig.forwardProxyConfig.type.trimmed().isEmpty()) { if (CurrentConfig.connectionConfig.forwardProxyConfig.type.trimmed().isEmpty())
CurrentConfig.connectionConfig.forwardProxyConfig.type = "http"; { CurrentConfig.connectionConfig.forwardProxyConfig.type = "http"; }
}
fpGroupBox->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy); fpGroupBox->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy);
fpUsernameTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.username); fpUsernameTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.username);
@ -182,18 +179,17 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
// //
maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines); maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines);
// //
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac"); pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
QSTRN(pacPortSB->value()) + "/pac");
// //
finishedLoading = true; finishedLoading = true;
} }
QvMessageBusSlotImpl(PreferencesWindow) QvMessageBusSlotImpl(PreferencesWindow)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
@ -204,45 +200,57 @@ PreferencesWindow::~PreferencesWindow()
void PreferencesWindow::on_buttonBox_accepted() void PreferencesWindow::on_buttonBox_accepted()
{ {
// Note: // Note:
// A signal-slot connection from buttonbox_accpted to QDialog::accepted() has been removed. // A signal-slot connection from buttonbox_accpted to QDialog::accepted()
// To prevent closing this Dialog. // has been removed. To prevent closing this Dialog.
QSet<int> ports; QSet<int> ports;
auto size = 0; auto size = 0;
if (CurrentConfig.inboundConfig.useHTTP) { if (CurrentConfig.inboundConfig.useHTTP)
size ++; {
size++;
ports << CurrentConfig.inboundConfig.http_port; ports << CurrentConfig.inboundConfig.http_port;
} }
if (CurrentConfig.inboundConfig.useSocks) { if (CurrentConfig.inboundConfig.useSocks)
size ++; {
size++;
ports << CurrentConfig.inboundConfig.socks_port; ports << CurrentConfig.inboundConfig.socks_port;
} }
if (CurrentConfig.inboundConfig.pacConfig.enablePAC) { if (CurrentConfig.inboundConfig.pacConfig.enablePAC)
size ++; {
size++;
ports << CurrentConfig.inboundConfig.pacConfig.port; ports << CurrentConfig.inboundConfig.pacConfig.port;
} }
if (!StartupOption.noAPI) { if (!StartupOption.noAPI)
size ++; {
size++;
ports << CurrentConfig.apiConfig.statsPort; ports << CurrentConfig.apiConfig.statsPort;
} }
if (ports.size() != size) { if (ports.size() != size)
{
// Duplicates detected. // Duplicates detected.
QvMessageBoxWarn(this, tr("Preferences"), tr("Duplicated port numbers detected, please check the port number settings.")); QvMessageBoxWarn(this, tr("Preferences"), tr("Duplicated port numbers detected, please check the port number settings."));
} else if (CurrentConfig.inboundConfig.listenip.toLower() != "localhost" && !IsValidIPAddress(CurrentConfig.inboundConfig.listenip)) { }
QvMessageBoxWarn(this, tr("Preferences"), tr("Invalid inbound listening address."));; else if (CurrentConfig.inboundConfig.listenip.toLower() != "localhost" && !IsValidIPAddress(CurrentConfig.inboundConfig.listenip))
} else { {
if (CurrentConfig.uiConfig.language != GlobalConfig.uiConfig.language) { QvMessageBoxWarn(this, tr("Preferences"), tr("Invalid inbound listening address."));
;
}
else
{
if (CurrentConfig.uiConfig.language != GlobalConfig.uiConfig.language)
{
qApp->removeTranslator(Qv2rayTranslator.get()); qApp->removeTranslator(Qv2rayTranslator.get());
Qv2rayTranslator = std::move(QvTranslator(CurrentConfig.uiConfig.language).pTranslator); Qv2rayTranslator = std::move(QvTranslator(CurrentConfig.uiConfig.language).pTranslator);
// Install translator // Install translator
if (!qApp->installTranslator(Qv2rayTranslator.get())) { if (!qApp->installTranslator(Qv2rayTranslator.get()))
LOG(MODULE_UI, "Failed to translate UI to: " + CurrentConfig.uiConfig.language) { LOG(MODULE_UI, "Failed to translate UI to: " + CurrentConfig.uiConfig.language) }
} else { else
{
messageBus.EmitGlobalSignal(QvMBMessage::RETRANSLATE); messageBus.EmitGlobalSignal(QvMBMessage::RETRANSLATE);
QApplication::processEvents(); QApplication::processEvents();
} }
@ -296,13 +304,14 @@ void PreferencesWindow::on_listenIPTxt_textEdited(const QString &arg1)
NEEDRESTART NEEDRESTART
CurrentConfig.inboundConfig.listenip = arg1; CurrentConfig.inboundConfig.listenip = arg1;
if (IsValidIPAddress(arg1)) { if (IsValidIPAddress(arg1)) { BLACK(listenIPTxt) }
BLACK(listenIPTxt) else
} else { {
RED(listenIPTxt) RED(listenIPTxt)
} }
//pacAccessPathTxt->setText("http://" + arg1 + ":" + QSTRN(pacPortSB->value()) + "/pac"); // pacAccessPathTxt->setText("http://" + arg1 + ":" +
// QSTRN(pacPortSB->value()) + "/pac");
} }
void PreferencesWindow::on_httpAuthUsernameTxt_textEdited(const QString &arg1) void PreferencesWindow::on_httpAuthUsernameTxt_textEdited(const QString &arg1)
@ -346,7 +355,8 @@ void PreferencesWindow::on_selectVAssetBtn_clicked()
NEEDRESTART NEEDRESTART
QString dir = QFileDialog::getExistingDirectory(this, tr("Open V2ray assets folder"), QDir::currentPath()); QString dir = QFileDialog::getExistingDirectory(this, tr("Open V2ray assets folder"), QDir::currentPath());
if (!dir.isEmpty()) { if (!dir.isEmpty())
{
vCoreAssetsPathTxt->setText(dir); vCoreAssetsPathTxt->setText(dir);
on_vCoreAssetsPathTxt_textEdited(dir); on_vCoreAssetsPathTxt_textEdited(dir);
} }
@ -356,7 +366,8 @@ void PreferencesWindow::on_selectVCoreBtn_clicked()
{ {
QString core = QFileDialog::getOpenFileName(this, tr("Open V2ray core file"), QDir::currentPath()); QString core = QFileDialog::getOpenFileName(this, tr("Open V2ray core file"), QDir::currentPath());
if (!core.isEmpty()) { if (!core.isEmpty())
{
vCorePathTxt->setText(core); vCorePathTxt->setText(core);
on_vCorePathTxt_textEdited(core); on_vCorePathTxt_textEdited(core);
} }
@ -370,13 +381,17 @@ void PreferencesWindow::on_vCorePathTxt_textEdited(const QString &arg1)
void PreferencesWindow::on_DNSListTxt_textChanged() void PreferencesWindow::on_DNSListTxt_textChanged()
{ {
if (finishedLoading) { if (finishedLoading)
try { {
try
{
QStringList hosts = DNSListTxt->toPlainText().replace("\r", "").split("\n"); QStringList hosts = DNSListTxt->toPlainText().replace("\r", "").split("\n");
CurrentConfig.connectionConfig.dnsList.clear(); CurrentConfig.connectionConfig.dnsList.clear();
foreach (auto host, hosts) { foreach (auto host, hosts)
if (host != "" && host != "\r") { {
if (host != "" && host != "\r")
{
// Not empty, so we save. // Not empty, so we save.
CurrentConfig.connectionConfig.dnsList.push_back(host); CurrentConfig.connectionConfig.dnsList.push_back(host);
NEEDRESTART NEEDRESTART
@ -384,7 +399,9 @@ void PreferencesWindow::on_DNSListTxt_textChanged()
} }
BLACK(DNSListTxt) BLACK(DNSListTxt)
} catch (...) { }
catch (...)
{
RED(DNSListTxt) RED(DNSListTxt)
} }
} }
@ -408,19 +425,25 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
// Setting up tProxy for linux // Setting up tProxy for linux
// Steps: // Steps:
// --> 1. Copy V2ray core files to the QV2RAY_TPROXY_VCORE_PATH and QV2RAY_TPROXY_VCTL_PATH dir. // --> 1. Copy V2ray core files to the QV2RAY_TPROXY_VCORE_PATH and
// QV2RAY_TPROXY_VCTL_PATH dir.
// --> 2. Change GlobalConfig.v2CorePath. // --> 2. Change GlobalConfig.v2CorePath.
// --> 3. Call `pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip` on the V2ray core. // --> 3. Call `pkexec setcap
if (arg1 == Qt::Checked) { // CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip` on the V2ray core.
if (arg1 == Qt::Checked)
{
// We enable it! // We enable it!
if (QvMessageBoxAsk(this, tr("Enable tProxy Support"), if (QvMessageBoxAsk(this, tr("Enable tProxy Support"),
tr("This will append capabilities to the V2ray executable.") + NEWLINE + NEWLINE + tr("This will append capabilities to the V2ray executable.") + NEWLINE + NEWLINE +
tr("Qv2ray will copy your V2ray core to this path: ") + NEWLINE + QV2RAY_TPROXY_VCORE_PATH + NEWLINE + NEWLINE + tr("Qv2ray will copy your V2ray core to this path: ") + NEWLINE + QV2RAY_TPROXY_VCORE_PATH + NEWLINE + NEWLINE +
tr("If anything goes wrong after enabling this, please check issue #57 or the link below:") + NEWLINE + tr("If anything goes wrong after enabling this, please check issue #57 or the link below:") + NEWLINE +
" https://github.com/Qv2ray/Qv2ray/wiki/FAQ ") != QMessageBox::Yes) { " https://github.com/Qv2ray/Qv2ray/wiki/FAQ ") != QMessageBox::Yes)
{
tProxyCheckBox->setChecked(false); tProxyCheckBox->setChecked(false);
LOG(MODULE_UI, "Canceled enabling tProxy feature.") LOG(MODULE_UI, "Canceled enabling tProxy feature.")
} else { }
else
{
LOG(MODULE_VCORE, "ENABLING tProxy Support") LOG(MODULE_VCORE, "ENABLING tProxy Support")
LOG(MODULE_FILEIO, " --> Origin V2ray core file is at: " + CurrentConfig.v2CorePath) LOG(MODULE_FILEIO, " --> Origin V2ray core file is at: " + CurrentConfig.v2CorePath)
auto v2ctlPath = QFileInfo(CurrentConfig.v2CorePath).absolutePath() + "/v2ctl"; auto v2ctlPath = QFileInfo(CurrentConfig.v2CorePath).absolutePath() + "/v2ctl";
@ -433,17 +456,22 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
// //
LOG(MODULE_FILEIO, " --> Copying files....") LOG(MODULE_FILEIO, " --> Copying files....")
if (QFileInfo(CurrentConfig.v2CorePath).absoluteFilePath() != QFileInfo(QV2RAY_TPROXY_VCORE_PATH).absoluteFilePath()) { if (QFileInfo(CurrentConfig.v2CorePath).absoluteFilePath() != QFileInfo(QV2RAY_TPROXY_VCORE_PATH).absoluteFilePath())
// Only trying to remove file when they are not in the default dir. {
// (In other words...) Keep using the current files. <Because we don't know where else we can copy the file from...> // Only trying to remove file when they are not in the default
// dir. (In other words...) Keep using the current files.
// <Because we don't know where else we can copy the file
// from...>
// //
if (QFile(QV2RAY_TPROXY_VCORE_PATH).exists()) { if (QFile(QV2RAY_TPROXY_VCORE_PATH).exists())
{
LOG(MODULE_FILEIO, QString(QV2RAY_TPROXY_VCORE_PATH) + ": File already exists.") LOG(MODULE_FILEIO, QString(QV2RAY_TPROXY_VCORE_PATH) + ": File already exists.")
LOG(MODULE_FILEIO, QString(QV2RAY_TPROXY_VCORE_PATH) + ": Deleting file.") LOG(MODULE_FILEIO, QString(QV2RAY_TPROXY_VCORE_PATH) + ": Deleting file.")
QFile(QV2RAY_TPROXY_VCORE_PATH).remove(); QFile(QV2RAY_TPROXY_VCORE_PATH).remove();
} }
if (QFile(QV2RAY_TPROXY_VCTL_PATH).exists()) { if (QFile(QV2RAY_TPROXY_VCTL_PATH).exists())
{
LOG(MODULE_FILEIO, QV2RAY_TPROXY_VCTL_PATH + ": File already exists.") LOG(MODULE_FILEIO, QV2RAY_TPROXY_VCTL_PATH + ": File already exists.")
LOG(MODULE_FILEIO, QV2RAY_TPROXY_VCTL_PATH + ": Deleting file.") LOG(MODULE_FILEIO, QV2RAY_TPROXY_VCTL_PATH + ": Deleting file.")
QFile(QV2RAY_TPROXY_VCTL_PATH).remove(); QFile(QV2RAY_TPROXY_VCTL_PATH).remove();
@ -456,18 +484,22 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
LOG(MODULE_FILEIO, " --> V2ray Ctl: " + vCtlresult) LOG(MODULE_FILEIO, " --> V2ray Ctl: " + vCtlresult)
// //
if (vCoreresult == "OK" && vCtlresult == "OK") { if (vCoreresult == "OK" && vCtlresult == "OK")
{
LOG(MODULE_VCORE, " --> Done copying files.") LOG(MODULE_VCORE, " --> Done copying files.")
on_vCorePathTxt_textEdited(QV2RAY_TPROXY_VCORE_PATH); on_vCorePathTxt_textEdited(QV2RAY_TPROXY_VCORE_PATH);
} else { }
else
{
LOG(MODULE_VCORE, "FAILED to copy V2ray files. Aborting.") LOG(MODULE_VCORE, "FAILED to copy V2ray files. Aborting.")
QvMessageBoxWarn(this, tr("Enable tProxy Support"), QvMessageBoxWarn(this, tr("Enable tProxy Support"),
tr("Qv2ray cannot copy one or both V2ray files from: ") + NEWLINE + NEWLINE + tr("Qv2ray cannot copy one or both V2ray files from: ") + NEWLINE + NEWLINE + CurrentConfig.v2CorePath +
CurrentConfig.v2CorePath + NEWLINE + v2ctlPath + NEWLINE + NEWLINE + NEWLINE + v2ctlPath + NEWLINE + NEWLINE + tr("to this path: ") + NEWLINE + newPath);
tr("to this path: ") + NEWLINE + newPath);
return; return;
} }
} else { }
else
{
LOG(MODULE_VCORE, "Skipped removing files since the current V2ray core is in the default path.") LOG(MODULE_VCORE, "Skipped removing files since the current V2ray core is in the default path.")
LOG(MODULE_VCORE, " --> Actually because we don't know where else to obtain the files.") LOG(MODULE_VCORE, " --> Actually because we don't know where else to obtain the files.")
} }
@ -475,7 +507,8 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
LOG(MODULE_UI, "Calling pkexec and setcap...") LOG(MODULE_UI, "Calling pkexec and setcap...")
int ret = QProcess::execute("pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip " + CurrentConfig.v2CorePath); int ret = QProcess::execute("pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip " + CurrentConfig.v2CorePath);
if (ret != 0) { if (ret != 0)
{
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret)) LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret))
QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually.")); QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually."));
} }
@ -483,10 +516,13 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
CurrentConfig.tProxySupport = true; CurrentConfig.tProxySupport = true;
NEEDRESTART NEEDRESTART
} }
} else { }
else
{
int ret = QProcess::execute("pkexec setcap -r " + CurrentConfig.v2CorePath); int ret = QProcess::execute("pkexec setcap -r " + CurrentConfig.v2CorePath);
if (ret != 0) { if (ret != 0)
{
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret)) LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret))
QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually.")); QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually."));
} }
@ -540,14 +576,15 @@ void PreferencesWindow::on_socksUDPIP_textEdited(const QString &arg1)
NEEDRESTART NEEDRESTART
CurrentConfig.inboundConfig.socksLocalIP = arg1; CurrentConfig.inboundConfig.socksLocalIP = arg1;
if (IsValidIPAddress(arg1)) { if (IsValidIPAddress(arg1)) { BLACK(socksUDPIP) }
BLACK(socksUDPIP) else
} else { {
RED(socksUDPIP) RED(socksUDPIP)
} }
} }
// ------------------- NET SPEED PLUGIN OPERATIONS ----------------------------------------------------------------- // ------------------- NET SPEED PLUGIN OPERATIONS
// -----------------------------------------------------------------
#define CurrentBarPage CurrentConfig.toolBarConfig.Pages[this->CurrentBarPageId] #define CurrentBarPage CurrentConfig.toolBarConfig.Pages[this->CurrentBarPageId]
#define CurrentBarLine CurrentBarPage.Lines[this->CurrentBarLineId] #define CurrentBarLine CurrentBarPage.Lines[this->CurrentBarLineId]
@ -557,7 +594,7 @@ void PreferencesWindow::on_nsBarPageAddBTN_clicked()
{ {
QvBarPage page; QvBarPage page;
CurrentConfig.toolBarConfig.Pages.push_back(page); CurrentConfig.toolBarConfig.Pages.push_back(page);
CurrentBarPageId = CurrentConfig.toolBarConfig.Pages.size() - 1 ; CurrentBarPageId = CurrentConfig.toolBarConfig.Pages.size() - 1;
// Add default line. // Add default line.
QvBarLine line; QvBarLine line;
CurrentBarPage.Lines.push_back(line); CurrentBarPage.Lines.push_back(line);
@ -576,11 +613,13 @@ void PreferencesWindow::on_nsBarPageAddBTN_clicked()
void PreferencesWindow::on_nsBarPageDelBTN_clicked() void PreferencesWindow::on_nsBarPageDelBTN_clicked()
{ {
if (nsBarPagesList->currentRow() >= 0) { if (nsBarPagesList->currentRow() >= 0)
{
CurrentConfig.toolBarConfig.Pages.removeAt(nsBarPagesList->currentRow()); CurrentConfig.toolBarConfig.Pages.removeAt(nsBarPagesList->currentRow());
nsBarPagesList->takeItem(nsBarPagesList->currentRow()); nsBarPagesList->takeItem(nsBarPagesList->currentRow());
if (nsBarPagesList->count() <= 0) { if (nsBarPagesList->count() <= 0)
{
nsBarPageDelBTN->setEnabled(false); nsBarPageDelBTN->setEnabled(false);
nsBarLineAddBTN->setEnabled(false); nsBarLineAddBTN->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
@ -613,12 +652,14 @@ void PreferencesWindow::on_nsBarLineAddBTN_clicked()
void PreferencesWindow::on_nsBarLineDelBTN_clicked() void PreferencesWindow::on_nsBarLineDelBTN_clicked()
{ {
if (nsBarLinesList->currentRow() >= 0) { if (nsBarLinesList->currentRow() >= 0)
{
CurrentBarPage.Lines.removeAt(nsBarLinesList->currentRow()); CurrentBarPage.Lines.removeAt(nsBarLinesList->currentRow());
nsBarLinesList->takeItem(nsBarLinesList->currentRow()); nsBarLinesList->takeItem(nsBarLinesList->currentRow());
CurrentBarLineId = 0; CurrentBarLineId = 0;
if (nsBarLinesList->count() <= 0) { if (nsBarLinesList->count() <= 0)
{
networkToolbarSettingsFrame->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
} }
@ -639,15 +680,19 @@ void PreferencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow)
nsBarPageYOffset->setValue(CurrentBarPage.OffsetYpx); nsBarPageYOffset->setValue(CurrentBarPage.OffsetYpx);
nsBarLinesList->clear(); nsBarLinesList->clear();
if (!CurrentBarPage.Lines.empty()) { if (!CurrentBarPage.Lines.empty())
for (auto line : CurrentBarPage.Lines) { {
for (auto line : CurrentBarPage.Lines)
{
auto description = GetBarLineDescription(line); auto description = GetBarLineDescription(line);
nsBarLinesList->addItem(description); nsBarLinesList->addItem(description);
} }
nsBarLinesList->setCurrentRow(0); nsBarLinesList->setCurrentRow(0);
ShowLineParameters(CurrentBarLine); ShowLineParameters(CurrentBarLine);
} else { }
else
{
networkToolbarSettingsFrame->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
} }
} }
@ -725,11 +770,9 @@ QString PreferencesWindow::GetBarLineDescription(QvBarLine barLine)
QString result = "Empty"; QString result = "Empty";
result = NetSpeedPluginMessages[barLine.ContentType]; result = NetSpeedPluginMessages[barLine.ContentType];
if (barLine.ContentType == 0) { if (barLine.ContentType == 0) { result += " (" + barLine.Message + ")"; }
result += " (" + barLine.Message + ")";
}
result = result.append(barLine.Bold ? ", " + tr("Bold") : ""); result = result.append(barLine.Bold ? ", " + tr("Bold") : "");
result = result.append(barLine.Italic ? ", " + tr("Italic") : ""); result = result.append(barLine.Italic ? ", " + tr("Italic") : "");
return result; return result;
} }
@ -738,9 +781,7 @@ void PreferencesWindow::ShowLineParameters(QvBarLine &barLine)
{ {
finishedLoading = false; finishedLoading = false;
if (!barLine.Family.isEmpty()) { if (!barLine.Family.isEmpty()) { fontComboBox->setCurrentFont(QFont(barLine.Family)); }
fontComboBox->setCurrentFont(QFont(barLine.Family));
}
// Colors // Colors
nsBarFontASB->setValue(barLine.ColorA); nsBarFontASB->setValue(barLine.ColorA);
@ -749,10 +790,9 @@ void PreferencesWindow::ShowLineParameters(QvBarLine &barLine)
nsBarFontRSB->setValue(barLine.ColorR); nsBarFontRSB->setValue(barLine.ColorR);
// //
QColor color = QColor::fromRgb(barLine.ColorR, barLine.ColorG, barLine.ColorB, barLine.ColorA); QColor color = QColor::fromRgb(barLine.ColorR, barLine.ColorG, barLine.ColorB, barLine.ColorA);
QString s(QStringLiteral("background: #") QString s(QStringLiteral("background: #") + ((color.red() < 16) ? "0" : "") + QString::number(color.red(), 16) +
+ ((color.red() < 16) ? "0" : "") + QString::number(color.red(), 16) ((color.green() < 16) ? "0" : "") + QString::number(color.green(), 16) + ((color.blue() < 16) ? "0" : "") +
+ ((color.green() < 16) ? "0" : "") + QString::number(color.green(), 16) QString::number(color.blue(), 16) + ";");
+ ((color.blue() < 16) ? "0" : "") + QString::number(color.blue(), 16) + ";");
chooseColorBtn->setStyleSheet(s); chooseColorBtn->setStyleSheet(s);
nsBarFontSizeSB->setValue(barLine.Size); nsBarFontSizeSB->setValue(barLine.Size);
nsBarFontBoldCB->setChecked(barLine.Bold); nsBarFontBoldCB->setChecked(barLine.Bold);
@ -769,7 +809,8 @@ void PreferencesWindow::on_chooseColorBtn_clicked()
QColorDialog d(QColor::fromRgb(CurrentBarLine.ColorR, CurrentBarLine.ColorG, CurrentBarLine.ColorB, CurrentBarLine.ColorA), this); QColorDialog d(QColor::fromRgb(CurrentBarLine.ColorR, CurrentBarLine.ColorG, CurrentBarLine.ColorB, CurrentBarLine.ColorA), this);
d.exec(); d.exec();
if (d.result() == QDialog::DialogCode::Accepted) { if (d.result() == QDialog::DialogCode::Accepted)
{
d.selectedColor().getRgb(&CurrentBarLine.ColorR, &CurrentBarLine.ColorG, &CurrentBarLine.ColorB, &CurrentBarLine.ColorA); d.selectedColor().getRgb(&CurrentBarLine.ColorR, &CurrentBarLine.ColorG, &CurrentBarLine.ColorB, &CurrentBarLine.ColorA);
ShowLineParameters(CurrentBarLine); ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT SET_LINE_LIST_TEXT
@ -792,9 +833,10 @@ void PreferencesWindow::on_nsBarContentCombo_currentIndexChanged(const QString &
void PreferencesWindow::on_applyNSBarSettingsBtn_clicked() void PreferencesWindow::on_applyNSBarSettingsBtn_clicked()
{ {
if (QvMessageBoxAsk(this, tr("Apply network toolbar settings"), tr("All other modified settings will be applied as well after this object.") + if (QvMessageBoxAsk(this, tr("Apply network toolbar settings"),
NEWLINE + tr("All other modified settings will be applied as well after this object.") + NEWLINE +
tr("Do you want to continue?")) == QMessageBox::Yes) { tr("Do you want to continue?")) == QMessageBox::Yes)
{
auto conf = GlobalConfig; auto conf = GlobalConfig;
conf.toolBarConfig = CurrentConfig.toolBarConfig; conf.toolBarConfig = CurrentConfig.toolBarConfig;
SaveGlobalConfig(conf); SaveGlobalConfig(conf);
@ -815,7 +857,8 @@ void PreferencesWindow::on_darkThemeCB_stateChanged(int arg1)
#ifdef QV2RAY_USE_BUILTIN_DARKTHEME #ifdef QV2RAY_USE_BUILTIN_DARKTHEME
themeCombo->setEnabled(arg1 != Qt::Checked); themeCombo->setEnabled(arg1 != Qt::Checked);
if (arg1 == Qt::Checked) { if (arg1 == Qt::Checked)
{
themeCombo->setCurrentIndex(QStyleFactory::keys().indexOf("Fusion")); themeCombo->setCurrentIndex(QStyleFactory::keys().indexOf("Fusion"));
CurrentConfig.uiConfig.theme = "Fusion"; CurrentConfig.uiConfig.theme = "Fusion";
} }
@ -840,7 +883,8 @@ void PreferencesWindow::on_pacGoBtn_clicked()
LOG(MODULE_PROXY, "Downloading GFWList file.") LOG(MODULE_PROXY, "Downloading GFWList file.")
bool withProxy = getGFWListWithProxyCB->isChecked(); bool withProxy = getGFWListWithProxyCB->isChecked();
switch (gfwListCB->currentIndex()) { switch (gfwListCB->currentIndex())
{
case 0: case 0:
gfwLocation = "https://gitlab.com/gfwlist/gfwlist/raw/master/gfwlist.txt"; gfwLocation = "https://gitlab.com/gfwlist/gfwlist/raw/master/gfwlist.txt";
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
@ -874,7 +918,8 @@ void PreferencesWindow::on_pacGoBtn_clicked()
case 6: case 6:
auto file = QFileDialog::getOpenFileName(this, tr("Select GFWList in base64")); auto file = QFileDialog::getOpenFileName(this, tr("Select GFWList in base64"));
if (file.isEmpty()) { if (file.isEmpty())
{
QvMessageBoxWarn(this, tr("Download GFWList"), tr("Operation is cancelled.")); QvMessageBoxWarn(this, tr("Download GFWList"), tr("Operation is cancelled."));
return; return;
} }
@ -888,9 +933,7 @@ void PreferencesWindow::on_pacGoBtn_clicked()
pacGoBtn->setEnabled(true); pacGoBtn->setEnabled(true);
gfwListCB->setEnabled(true); gfwListCB->setEnabled(true);
if (!QDir(QV2RAY_RULES_DIR).exists()) { if (!QDir(QV2RAY_RULES_DIR).exists()) { QDir(QV2RAY_RULES_DIR).mkpath(QV2RAY_RULES_DIR); }
QDir(QV2RAY_RULES_DIR).mkpath(QV2RAY_RULES_DIR);
}
StringToFile(fileContent, QV2RAY_RULES_GFWLIST_PATH); StringToFile(fileContent, QV2RAY_RULES_GFWLIST_PATH);
} }
@ -900,7 +943,8 @@ void PreferencesWindow::on_pacPortSB_valueChanged(int arg1)
LOADINGCHECK LOADINGCHECK
NEEDRESTART NEEDRESTART
CurrentConfig.inboundConfig.pacConfig.port = arg1; CurrentConfig.inboundConfig.pacConfig.port = arg1;
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac"); pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
QSTRN(pacPortSB->value()) + "/pac");
} }
void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1) void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
@ -939,9 +983,7 @@ void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString
auto list = ConnectionManager->Connections(groupId); auto list = ConnectionManager->Connections(groupId);
autoStartConnCombo->clear(); autoStartConnCombo->clear();
for (auto id : list) { for (auto id : list) { autoStartConnCombo->addItem(ConnectionManager->GetDisplayName(id)); }
autoStartConnCombo->addItem(ConnectionManager->GetDisplayName(id));
}
} }
void PreferencesWindow::on_autoStartConnCombo_currentIndexChanged(const QString &arg1) void PreferencesWindow::on_autoStartConnCombo_currentIndexChanged(const QString &arg1)
@ -955,9 +997,7 @@ void PreferencesWindow::on_startWithLoginCB_stateChanged(int arg1)
bool isEnabled = arg1 == Qt::Checked; bool isEnabled = arg1 == Qt::Checked;
SetLaunchAtLoginStatus(isEnabled); SetLaunchAtLoginStatus(isEnabled);
if (GetLaunchAtLoginStatus() != isEnabled) { if (GetLaunchAtLoginStatus() != isEnabled) { QvMessageBoxWarn(this, tr("Start with boot"), tr("Failed to set auto start option.")); }
QvMessageBoxWarn(this, tr("Start with boot"), tr("Failed to set auto start option."));
}
SetAutoStartButtonsState(GetLaunchAtLoginStatus()); SetAutoStartButtonsState(GetLaunchAtLoginStatus());
} }
@ -978,9 +1018,9 @@ void PreferencesWindow::on_fpAddressTx_textEdited(const QString &arg1)
LOADINGCHECK LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress = arg1;
if (IsValidIPAddress(arg1)) { if (IsValidIPAddress(arg1)) { BLACK(fpAddressTx) }
BLACK(fpAddressTx) else
} else { {
RED(fpAddressTx) RED(fpAddressTx)
} }
} }
@ -1021,13 +1061,14 @@ void PreferencesWindow::on_pacProxyTxt_textChanged(const QString &arg1)
{ {
Q_UNUSED(arg1) Q_UNUSED(arg1)
if (IsValidIPAddress(arg1)) { if (IsValidIPAddress(arg1)) { BLACK(pacProxyTxt) }
BLACK(pacProxyTxt) else
} else { {
RED(pacProxyTxt) RED(pacProxyTxt)
} }
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac"); pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
QSTRN(pacPortSB->value()) + "/pac");
} }
void PreferencesWindow::on_checkVCoreSettings_clicked() void PreferencesWindow::on_checkVCoreSettings_clicked()
@ -1036,11 +1077,12 @@ void PreferencesWindow::on_checkVCoreSettings_clicked()
auto vAssetsPath = vCoreAssetsPathTxt->text(); auto vAssetsPath = vCoreAssetsPathTxt->text();
QString result; QString result;
if (!V2rayKernelInstance::ValidateKernel(vcorePath, vAssetsPath, &result)) { if (!V2rayKernelInstance::ValidateKernel(vcorePath, vAssetsPath, &result)) { QvMessageBoxWarn(this, tr("V2ray Core Settings"), result); }
QvMessageBoxWarn(this, tr("V2ray Core Settings"), result); else
} else { {
QvMessageBoxInfo(this, tr("V2ray Core Settings"), tr("V2ray path configuration check passed.") + NEWLINE + NEWLINE + QvMessageBoxInfo(this, tr("V2ray Core Settings"),
tr("Current version of V2ray is: ") + NEWLINE + result); tr("V2ray path configuration check passed.") + NEWLINE + NEWLINE + tr("Current version of V2ray is: ") + NEWLINE +
result);
} }
} }

View File

@ -1,180 +1,183 @@
#pragma once #pragma once
#include <QDialog>
#include <ui_w_PreferencesWindow.h>
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "ui/messaging/QvMessageBus.hpp" #include "ui/messaging/QvMessageBus.hpp"
class PreferencesWindow : public QDialog, private Ui::PreferencesWindow #include <QDialog>
#include <ui_w_PreferencesWindow.h>
class PreferencesWindow
: public QDialog
, private Ui::PreferencesWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PreferencesWindow(QWidget *parent = nullptr); explicit PreferencesWindow(QWidget *parent = nullptr);
~PreferencesWindow(); ~PreferencesWindow();
signals: signals:
void s_reload_config(bool need_restart); void s_reload_config(bool need_restart);
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_httpAuthCB_stateChanged(int arg1); void on_httpAuthCB_stateChanged(int arg1);
void on_socksAuthCB_stateChanged(int arg1); void on_socksAuthCB_stateChanged(int arg1);
void on_languageComboBox_currentTextChanged(const QString &arg1); void on_languageComboBox_currentTextChanged(const QString &arg1);
void on_logLevelComboBox_currentIndexChanged(int index); void on_logLevelComboBox_currentIndexChanged(int index);
void on_vCoreAssetsPathTxt_textEdited(const QString &arg1); void on_vCoreAssetsPathTxt_textEdited(const QString &arg1);
void on_listenIPTxt_textEdited(const QString &arg1); void on_listenIPTxt_textEdited(const QString &arg1);
void on_socksPortLE_valueChanged(int arg1); void on_socksPortLE_valueChanged(int arg1);
void on_httpPortLE_valueChanged(int arg1); void on_httpPortLE_valueChanged(int arg1);
void on_httpAuthUsernameTxt_textEdited(const QString &arg1); void on_httpAuthUsernameTxt_textEdited(const QString &arg1);
void on_httpAuthPasswordTxt_textEdited(const QString &arg1); void on_httpAuthPasswordTxt_textEdited(const QString &arg1);
void on_socksAuthUsernameTxt_textEdited(const QString &arg1); void on_socksAuthUsernameTxt_textEdited(const QString &arg1);
void on_socksAuthPasswordTxt_textEdited(const QString &arg1); void on_socksAuthPasswordTxt_textEdited(const QString &arg1);
void on_proxyDefaultCb_stateChanged(int arg1); void on_proxyDefaultCb_stateChanged(int arg1);
void on_localDNSCb_stateChanged(int arg1); void on_localDNSCb_stateChanged(int arg1);
void on_selectVAssetBtn_clicked(); void on_selectVAssetBtn_clicked();
void on_DNSListTxt_textChanged(); void on_DNSListTxt_textChanged();
void on_aboutQt_clicked(); void on_aboutQt_clicked();
void on_cancelIgnoreVersionBtn_clicked(); void on_cancelIgnoreVersionBtn_clicked();
void on_tProxyCheckBox_stateChanged(int arg1); void on_tProxyCheckBox_stateChanged(int arg1);
void on_bypassCNCb_stateChanged(int arg1); void on_bypassCNCb_stateChanged(int arg1);
void on_statsPortBox_valueChanged(int arg1); void on_statsPortBox_valueChanged(int arg1);
void on_socksUDPCB_stateChanged(int arg1); void on_socksUDPCB_stateChanged(int arg1);
void on_socksUDPIP_textEdited(const QString &arg1); void on_socksUDPIP_textEdited(const QString &arg1);
void on_nsBarPageAddBTN_clicked(); void on_nsBarPageAddBTN_clicked();
void on_nsBarPageDelBTN_clicked(); void on_nsBarPageDelBTN_clicked();
void on_nsBarPageYOffset_valueChanged(int arg1); void on_nsBarPageYOffset_valueChanged(int arg1);
void on_nsBarLineAddBTN_clicked(); void on_nsBarLineAddBTN_clicked();
void on_nsBarLineDelBTN_clicked(); void on_nsBarLineDelBTN_clicked();
void on_nsBarPagesList_currentRowChanged(int currentRow); void on_nsBarPagesList_currentRowChanged(int currentRow);
void on_nsBarLinesList_currentRowChanged(int currentRow); void on_nsBarLinesList_currentRowChanged(int currentRow);
void on_fontComboBox_currentFontChanged(const QFont &f); void on_fontComboBox_currentFontChanged(const QFont &f);
void on_nsBarFontBoldCB_stateChanged(int arg1); void on_nsBarFontBoldCB_stateChanged(int arg1);
void on_nsBarFontItalicCB_stateChanged(int arg1); void on_nsBarFontItalicCB_stateChanged(int arg1);
void on_nsBarFontASB_valueChanged(int arg1); void on_nsBarFontASB_valueChanged(int arg1);
void on_nsBarFontRSB_valueChanged(int arg1); void on_nsBarFontRSB_valueChanged(int arg1);
void on_nsBarFontGSB_valueChanged(int arg1); void on_nsBarFontGSB_valueChanged(int arg1);
void on_nsBarFontBSB_valueChanged(int arg1); void on_nsBarFontBSB_valueChanged(int arg1);
void on_nsBarFontSizeSB_valueChanged(double arg1); void on_nsBarFontSizeSB_valueChanged(double arg1);
void on_chooseColorBtn_clicked(); void on_chooseColorBtn_clicked();
void on_nsBarTagTxt_textEdited(const QString &arg1); void on_nsBarTagTxt_textEdited(const QString &arg1);
void on_nsBarContentCombo_currentIndexChanged(const QString &arg1); void on_nsBarContentCombo_currentIndexChanged(const QString &arg1);
void on_applyNSBarSettingsBtn_clicked(); void on_applyNSBarSettingsBtn_clicked();
void on_selectVCoreBtn_clicked(); void on_selectVCoreBtn_clicked();
void on_vCorePathTxt_textEdited(const QString &arg1); void on_vCorePathTxt_textEdited(const QString &arg1);
void on_themeCombo_currentTextChanged(const QString &arg1); void on_themeCombo_currentTextChanged(const QString &arg1);
void on_darkThemeCB_stateChanged(int arg1); void on_darkThemeCB_stateChanged(int arg1);
void on_darkTrayCB_stateChanged(int arg1); void on_darkTrayCB_stateChanged(int arg1);
void on_pacGoBtn_clicked(); void on_pacGoBtn_clicked();
void on_pacPortSB_valueChanged(int arg1); void on_pacPortSB_valueChanged(int arg1);
void on_setSysProxyCB_stateChanged(int arg1); void on_setSysProxyCB_stateChanged(int arg1);
void on_pacProxyCB_currentIndexChanged(int index); void on_pacProxyCB_currentIndexChanged(int index);
void on_pushButton_clicked(); void on_pushButton_clicked();
void on_pacProxyTxt_textEdited(const QString &arg1); void on_pacProxyTxt_textEdited(const QString &arg1);
void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1); void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1);
void on_autoStartConnCombo_currentIndexChanged(const QString &arg1); void on_autoStartConnCombo_currentIndexChanged(const QString &arg1);
void on_fpTypeCombo_currentIndexChanged(const QString &arg1); void on_fpTypeCombo_currentIndexChanged(const QString &arg1);
void on_fpAddressTx_textEdited(const QString &arg1); void on_fpAddressTx_textEdited(const QString &arg1);
void on_spPortSB_valueChanged(int arg1); void on_spPortSB_valueChanged(int arg1);
void on_fpUseAuthCB_stateChanged(int arg1); void on_fpUseAuthCB_stateChanged(int arg1);
void on_fpUsernameTx_textEdited(const QString &arg1); void on_fpUsernameTx_textEdited(const QString &arg1);
void on_fpPasswordTx_textEdited(const QString &arg1); void on_fpPasswordTx_textEdited(const QString &arg1);
void on_fpPortSB_valueChanged(int arg1); void on_fpPortSB_valueChanged(int arg1);
void on_pacProxyTxt_textChanged(const QString &arg1); void on_pacProxyTxt_textChanged(const QString &arg1);
void on_checkVCoreSettings_clicked(); void on_checkVCoreSettings_clicked();
void on_httpGroupBox_clicked(bool checked); void on_httpGroupBox_clicked(bool checked);
void on_socksGroupBox_clicked(bool checked); void on_socksGroupBox_clicked(bool checked);
void on_pacGroupBox_clicked(bool checked); void on_pacGroupBox_clicked(bool checked);
void on_fpGroupBox_clicked(bool checked); void on_fpGroupBox_clicked(bool checked);
void on_maxLogLinesSB_valueChanged(int arg1); void on_maxLogLinesSB_valueChanged(int arg1);
void on_enableAPI_stateChanged(int arg1); void on_enableAPI_stateChanged(int arg1);
void on_startWithLoginCB_stateChanged(int arg1); void on_startWithLoginCB_stateChanged(int arg1);
void on_tProxyGroupBox_clicked(bool checked); void on_tProxyGroupBox_clicked(bool checked);
private: private:
void SetAutoStartButtonsState(bool isAutoStart); void SetAutoStartButtonsState(bool isAutoStart);
// Set ui parameters for a line; // Set ui parameters for a line;
void ShowLineParameters(QvBarLine &line); void ShowLineParameters(QvBarLine &line);
QString GetBarLineDescription(QvBarLine line); QString GetBarLineDescription(QvBarLine line);
// //
int CurrentBarLineId; int CurrentBarLineId;
int CurrentBarPageId; int CurrentBarPageId;
// //
bool IsConnectionPropertyChanged = false; bool IsConnectionPropertyChanged = false;
bool finishedLoading = false; bool finishedLoading = false;
Qv2rayConfig CurrentConfig; Qv2rayConfig CurrentConfig;
}; };

View File

@ -1,15 +1,18 @@
#include "w_ScreenShot_Core.hpp" #include "w_ScreenShot_Core.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include <QMessageBox> #include <QMessageBox>
#include <QThread>
#include <QStyleFactory> #include <QStyleFactory>
#include <QThread>
#define QV2RAY_SCREENSHOT_DIM_RATIO 0.6f #define QV2RAY_SCREENSHOT_DIM_RATIO 0.6f
ScreenShotWindow::ScreenShotWindow() : QDialog(), rubber(new QRubberBand(QRubberBand::Rectangle, this)) ScreenShotWindow::ScreenShotWindow() : QDialog(), rubber(new QRubberBand(QRubberBand::Rectangle, this))
{ {
setupUi(this); setupUi(this);
// Fusion prevents the KDE Plasma Breeze's "Move window when dragging in the empty area" issue // Fusion prevents the KDE Plasma Breeze's "Move window when dragging in the
// empty area" issue
this->setStyle(QStyleFactory::create("Fusion")); this->setStyle(QStyleFactory::create("Fusion"));
// //
label->setAttribute(Qt::WA_TranslucentBackground); label->setAttribute(Qt::WA_TranslucentBackground);
@ -30,8 +33,9 @@ ScreenShotWindow::ScreenShotWindow() : QDialog(), rubber(new QRubberBand(QRubber
QImage ScreenShotWindow::DoScreenShot() QImage ScreenShotWindow::DoScreenShot()
{ {
LOG(MODULE_IMPORT, "We currently only support the current screen.") LOG(MODULE_IMPORT, "We currently only support the current screen.")
// The msleep is the only solution which prevent capturing our windows again. // The msleep is the only solution which prevent capturing our windows
// It works on KDE, https://www.qtcentre.org/threads/55708-Get-Desktop-Screenshot-Without-Application-Window-Being-Shown?p=248993#post248993 // again. It works on KDE,
// https://www.qtcentre.org/threads/55708-Get-Desktop-Screenshot-Without-Application-Window-Being-Shown?p=248993#post248993
QThread::msleep(100); QThread::msleep(100);
QApplication::processEvents(); QApplication::processEvents();
// //
@ -45,8 +49,10 @@ QImage ScreenShotWindow::DoScreenShot()
int r, g, b; int r, g, b;
auto _xdesktopImg = desktopImage.toImage(); auto _xdesktopImg = desktopImage.toImage();
for (int i = 0; i < w; i++) { for (int i = 0; i < w; i++)
for (int j = 0; j < h; j++) { {
for (int j = 0; j < h; j++)
{
r = static_cast<int>(qRed(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO); r = static_cast<int>(qRed(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO);
g = static_cast<int>(qGreen(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO); g = static_cast<int>(qGreen(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO);
b = static_cast<int>(qBlue(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO); b = static_cast<int>(qBlue(_xdesktopImg.pixel(i, j)) * QV2RAY_SCREENSHOT_DIM_RATIO);
@ -72,16 +78,18 @@ void ScreenShotWindow::pSize()
imgH = abs(end.y() - origin.y()); imgH = abs(end.y() - origin.y());
imgX = origin.x() < end.x() ? origin.x() : end.x(); imgX = origin.x() < end.x() ? origin.x() : end.x();
imgY = origin.y() < end.y() ? origin.y() : end.y(); imgY = origin.y() < end.y() ? origin.y() : end.y();
DEBUG("Capture Mouse Position", QSTRN(imgW) + " " + QSTRN(imgH) + " " + QSTRN(imgX) + " " + QSTRN(imgY)) DEBUG("Capture Mouse Position", QSTRN(imgW) + " " + QSTRN(imgH) + " " + QSTRN(imgX) + " " + QSTRN(imgY))
rubber->setGeometry(imgX, imgY, imgW, imgH); rubber->setGeometry(imgX, imgY, imgW, imgH);
fg->setGeometry(rubber->geometry()); fg->setGeometry(rubber->geometry());
auto copied = desktopImage.copy(fg->x() * devicePixelRatio(), fg->y() * devicePixelRatio(), fg->width() * devicePixelRatio(), fg->height() * devicePixelRatio()); auto copied = desktopImage.copy(fg->x() * devicePixelRatio(), fg->y() * devicePixelRatio(), fg->width() * devicePixelRatio(),
fg->height() * devicePixelRatio());
fg->setPixmap(copied); fg->setPixmap(copied);
} }
bool ScreenShotWindow::event(QEvent *e) bool ScreenShotWindow::event(QEvent *e)
{ {
if (e->type() == QEvent::Move) { if (e->type() == QEvent::Move)
{
// //
} }
@ -90,9 +98,9 @@ bool ScreenShotWindow::event(QEvent *e)
void ScreenShotWindow::keyPressEvent(QKeyEvent *e) void ScreenShotWindow::keyPressEvent(QKeyEvent *e)
{ {
if (e->key() == Qt::Key_Escape) { if (e->key() == Qt::Key_Escape) { reject(); }
reject(); else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { {
on_startBtn_clicked(); on_startBtn_clicked();
} }
} }
@ -103,13 +111,14 @@ void ScreenShotWindow::mousePressEvent(QMouseEvent *e)
rubber->setGeometry(origin.x(), origin.y(), 0, 0); rubber->setGeometry(origin.x(), origin.y(), 0, 0);
rubber->show(); rubber->show();
rubber->raise(); rubber->raise();
//label->hide(); // label->hide();
//startBtn->hide(); // startBtn->hide();
} }
void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e) void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
{ {
if (e->buttons() & Qt::LeftButton) { if (e->buttons() & Qt::LeftButton)
{
end = e->pos(); end = e->pos();
pSize(); pSize();
// //
@ -120,15 +129,15 @@ void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
QRect labelRect(label->contentsRect()); QRect labelRect(label->contentsRect());
QRect btnRect(startBtn->contentsRect()); QRect btnRect(startBtn->contentsRect());
if (imgY > labelRect.height()) { if (imgY > labelRect.height()) { label->move(imgX, imgY - labelRect.height()); }
label->move(imgX, imgY - labelRect.height()); else
} else { {
label->move(imgX, imgY); label->move(imgX, imgY);
} }
if (height() - imgY - imgH > btnRect.height()) { if (height() - imgY - imgH > btnRect.height()) { startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH); }
startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH); else
} else { {
startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH - btnRect.height()); startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH - btnRect.height());
} }
@ -137,12 +146,9 @@ void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
} }
} }
void ScreenShotWindow::mouseReleaseEvent(QMouseEvent *e) void ScreenShotWindow::mouseReleaseEvent(QMouseEvent *e)
{ {
if (e->button() == Qt::RightButton) { if (e->button() == Qt::RightButton) { reject(); }
reject();
}
} }
ScreenShotWindow::~ScreenShotWindow() ScreenShotWindow::~ScreenShotWindow()

View File

@ -1,46 +1,48 @@
#pragma once #pragma once
#include <QDialog>
#include <QRubberBand>
#include <QImage>
#include <QLabel>
#include <QMouseEvent>
#include <QScreen>
#include <QKeyEvent>
#include <QPushButton>
#include <QPalette>
#include "ui_w_ScreenShot_Core.h" #include "ui_w_ScreenShot_Core.h"
class ScreenShotWindow : public QDialog, private Ui::ScreenShot #include <QDialog>
#include <QImage>
#include <QKeyEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QPalette>
#include <QPushButton>
#include <QRubberBand>
#include <QScreen>
class ScreenShotWindow
: public QDialog
, private Ui::ScreenShot
{ {
Q_OBJECT Q_OBJECT
public: public:
ScreenShotWindow(); ScreenShotWindow();
~ScreenShotWindow(); ~ScreenShotWindow();
QImage DoScreenShot(); QImage DoScreenShot();
// //
void mouseMoveEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override;
void keyPressEvent(QKeyEvent *e) override; void keyPressEvent(QKeyEvent *e) override;
protected: protected:
bool event(QEvent *e) override; bool event(QEvent *e) override;
private slots: private slots:
void on_startBtn_clicked(); void on_startBtn_clicked();
private: private:
QRubberBand *rubber; QRubberBand *rubber;
// Desktop Image // Desktop Image
QPixmap desktopImage; QPixmap desktopImage;
QImage windowBg; QImage windowBg;
QImage resultImage; QImage resultImage;
// //
QPoint origin; QPoint origin;
QPoint end; QPoint end;
int imgW, imgH, imgX, imgY; int imgW, imgH, imgX, imgY;
void pSize(); void pSize();
}; };

View File

@ -1,33 +1,31 @@
#include "w_SubscriptionManager.hpp" #include "w_SubscriptionManager.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "core/config/ConfigBackend.hpp" #include "core/config/ConfigBackend.hpp"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
SubscribeEditor::SubscribeEditor(QWidget *parent) : SubscribeEditor::SubscribeEditor(QWidget *parent) : QDialog(parent)
QDialog(parent)
{ {
setupUi(this); setupUi(this);
QvMessageBusConnect(SubscribeEditor); QvMessageBusConnect(SubscribeEditor);
addSubsButton->setIcon(QICON_R("add.png")); addSubsButton->setIcon(QICON_R("add.png"));
removeSubsButton->setIcon(QICON_R("delete.png")); removeSubsButton->setIcon(QICON_R("delete.png"));
for (auto subs : ConnectionManager->Subscriptions()) { for (auto subs : ConnectionManager->Subscriptions())
subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList() << ConnectionManager->GetDisplayName(subs) << subs.toString())); { subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList() << ConnectionManager->GetDisplayName(subs) << subs.toString())); }
}
} }
QvMessageBusSlotImpl(SubscribeEditor) QvMessageBusSlotImpl(SubscribeEditor)
{ {
switch (msg) { switch (msg)
MBShowDefaultImpl {
MBHideDefaultImpl MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
MBRetranslateDefaultImpl
} }
} }
QPair<QString, CONFIGROOT> SubscribeEditor::GetSelectedConfig() QPair<QString, CONFIGROOT> SubscribeEditor::GetSelectedConfig()
{ {
return QPair<QString, CONFIGROOT> (); return QPair<QString, CONFIGROOT>();
} }
SubscribeEditor::~SubscribeEditor() SubscribeEditor::~SubscribeEditor()
@ -46,22 +44,26 @@ void SubscribeEditor::on_addSubsButton_clicked()
void SubscribeEditor::on_updateButton_clicked() void SubscribeEditor::on_updateButton_clicked()
{ {
//auto newName = subNameTxt->text().trimmed(); ConnectionManager->UpdateSubscription(currentSubId, withProxyCB->isChecked());
//auto newAddress = subAddrTxt->text().trimmed(); // auto newName = subNameTxt->text().trimmed();
//auto newUpdateInterval = updateIntervalSB->value(); // auto newAddress = subAddrTxt->text().trimmed();
//if (currentSubId != newName) { // auto newUpdateInterval = updateIntervalSB->value();
// if (currentSubId != newName) {
// // Rename needed. // // Rename needed.
// LOG(MODULE_SUBSCRIPTION, "Renaming a subscription, from " + currentSubId + " to: " + newName) // LOG(MODULE_SUBSCRIPTION, "Renaming a subscription, from " +
// bool canGo = true; // currentSubId + " to: " + newName) bool canGo = true;
// //
// if (newName.isEmpty() || !IsValidFileName(newName)) { // if (newName.isEmpty() || !IsValidFileName(newName)) {
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("The subscription name is invalid, please try another.")); // QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("The
// canGo = false; // subscription name is invalid, please try another.")); canGo =
// false;
// } // }
// //
// if (subscriptionList->findItems(newName, Qt::MatchExactly).count() > 0) { // if (subscriptionList->findItems(newName, Qt::MatchExactly).count() >
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("New name of this subscription has been used already, please suggest another one")); // 0) {
// canGo = false; // QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("New name
// of this subscription has been used already, please suggest another
// one")); canGo = false;
// } // }
// //
// if (!canGo) { // if (!canGo) {
@ -72,7 +74,8 @@ void SubscribeEditor::on_updateButton_clicked()
// ////bool result = RenameSubscription(currentSubName, newName); // ////bool result = RenameSubscription(currentSubName, newName);
// // // //
// //if (!result) { // //if (!result) {
// // QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("Failed to rename a subscription, this is an unknown error.")); // // QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("Failed
// to rename a subscription, this is an unknown error."));
// // return; // // return;
// //} // //}
// subscriptions[newName] = subscriptions[currentSubId]; // subscriptions[newName] = subscriptions[currentSubId];
@ -93,68 +96,45 @@ void SubscribeEditor::on_updateButton_clicked()
// // Update thing still down // // Update thing still down
// subAddrTxt->setText(newAddress); // subAddrTxt->setText(newAddress);
// updateIntervalSB->setValue(newUpdateInterval); // updateIntervalSB->setValue(newUpdateInterval);
// QvMessageBoxInfo(this, tr("Renaming a subscription"), tr("Successfully renamed a subscription")); // QvMessageBoxInfo(this, tr("Renaming a subscription"), tr("Successfully
// renamed a subscription"));
//} //}
// //
//subscriptions[currentSubId].updateInterval = newUpdateInterval; // subscriptions[currentSubId].updateInterval = newUpdateInterval;
// //
//if (subscriptions[currentSubId].address != newAddress) { // if (subscriptions[currentSubId].address != newAddress) {
// LOG(MODULE_SUBSCRIPTION, "Setting new address, from " + subscriptions[currentSubId].address + " to: " + newAddress) // LOG(MODULE_SUBSCRIPTION, "Setting new address, from " +
// subscriptions[currentSubId].address + " to: " + newAddress)
// subscriptions[currentSubId].address = newAddress; // subscriptions[currentSubId].address = newAddress;
//} //}
// //
////SaveConfig(); ////SaveConfig();
// //
////if (QvMessageBoxAsk(this, tr("Update Subscription"), tr("Would you like to reload this subscription from the Url?")) == QMessageBox::Yes) { ////if (QvMessageBoxAsk(this, tr("Update Subscription"), tr("Would you like
//// StartUpdateSubscription(currentSubId); /// to reload this subscription from the Url?")) == QMessageBox::Yes) { /
/// StartUpdateSubscription(currentSubId);
////} ////}
} }
void SubscribeEditor::StartUpdateSubscription(const QString &subscriptionName) void SubscribeEditor::StartUpdateSubscription(const QString &subscriptionName)
{ {
this->setEnabled(false); this->setEnabled(false);
// auto data = helper.syncget(subscriptions[subscriptionName].address, withProxyCB->isChecked()); // auto data = helper.syncget(subscriptions[subscriptionName].address,
// auto content = DecodeSubscriptionString(data).trimmed(); // withProxyCB->isChecked()); auto content =
//if (!content.isEmpty()) {
// connectionsList->clear();
// auto vmessList = SplitLines(content);
// QDir(QV2RAY_SUBSCRIPTION_DIR + subscriptionName).removeRecursively();
// QDir().mkpath(QV2RAY_SUBSCRIPTION_DIR + subscriptionName);
//
// for (auto vmess : vmessList) {
// QString errMessage;
// QString _alias;
// auto config = ConvertConfigFromString(vmess.trimmed(), &_alias, &errMessage);
//
// if (!errMessage.isEmpty()) {
// LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage)
// } else {
// //SaveSubscriptionConfig(config, subscriptionName, &_alias);
// connectionsList->addItem(_alias);
// }
// }
//
// subscriptions[subscriptionName].lastUpdated = system_clock::to_time_t(system_clock::now());
// lastUpdatedLabel->setText(timeToString(subscriptions[subscriptionName].lastUpdated));
// isUpdateInProgress = false;
//} else {
// LOG(MODULE_NETWORK, "We have received an empty string from the URL.")
// QvMessageBoxWarn(this, tr("Updating subscriptions"), tr("Failed to process the result from the upstream, please check your Url."));
//}
//
this->setEnabled(true); this->setEnabled(true);
} }
void SubscribeEditor::on_removeSubsButton_clicked() void SubscribeEditor::on_removeSubsButton_clicked()
{ {
//if (subscriptionList->currentRow() < 0) // if (subscriptionList->currentRow() < 0)
// return; // return;
// //
//auto name = subscriptionList->currentItem()->text(); // auto name = subscriptionList->currentItem()->text();
//subscriptionList->takeItem(subscriptionList->currentRow()); // subscriptionList->takeItem(subscriptionList->currentRow());
//subscriptions.remove(name); // subscriptions.remove(name);
// //
//if (!name.isEmpty()) { // if (!name.isEmpty()) {
// QDir(QV2RAY_SUBSCRIPTION_DIR + name).removeRecursively(); // QDir(QV2RAY_SUBSCRIPTION_DIR + name).removeRecursively();
//} //}
// //
@ -163,22 +143,22 @@ void SubscribeEditor::on_removeSubsButton_clicked()
//// GlobalConfig.autoStartConfig = QvConnectionObject(); //// GlobalConfig.autoStartConfig = QvConnectionObject();
//// SaveGlobalConfig(GlobalConfig); //// SaveGlobalConfig(GlobalConfig);
////} ////}
//groupBox_2->setEnabled(subscriptionList->count() > 0); // groupBox_2->setEnabled(subscriptionList->count() > 0);
//SaveConfig(); // SaveConfig();
} }
void SubscribeEditor::SaveConfig() void SubscribeEditor::SaveConfig()
{ {
//QMap<QString, SubscriptionObject_Config> newConf; // QMap<QString, SubscriptionObject_Config> newConf;
// //
//for (auto _ : subscriptions.toStdMap()) { // for (auto _ : subscriptions.toStdMap()) {
// if (!_.second.address.isEmpty()) { // if (!_.second.address.isEmpty()) {
// newConf[_.first] = _.second; // newConf[_.first] = _.second;
// } // }
//} //}
// //
//GlobalConfig.subscriptions = newConf; // GlobalConfig.subscriptions = newConf;
//SaveGlobalConfig(GlobalConfig); // SaveGlobalConfig(GlobalConfig);
} }
void SubscribeEditor::on_buttonBox_accepted() void SubscribeEditor::on_buttonBox_accepted()
@ -195,9 +175,7 @@ void SubscribeEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, int
{ {
Q_UNUSED(column) Q_UNUSED(column)
if (item == nullptr) { if (item == nullptr) { return; }
return;
}
currentSubId = GroupId(item->text(1)); currentSubId = GroupId(item->text(1));
// //
@ -209,7 +187,5 @@ void SubscribeEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, int
// //
connectionsList->clear(); connectionsList->clear();
for (auto conn : ConnectionManager->Connections(currentSubId)) { for (auto conn : ConnectionManager->Connections(currentSubId)) { connectionsList->addItem(ConnectionManager->GetDisplayName(conn)); }
connectionsList->addItem(ConnectionManager->GetDisplayName(conn));
}
} }

View File

@ -1,40 +1,43 @@
#pragma once #pragma once
#include <QDialog>
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "ui_w_SubscriptionManager.h"
#include "ui/messaging/QvMessageBus.hpp"
#include "core/CoreSafeTypes.hpp" #include "core/CoreSafeTypes.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_SubscriptionManager.h"
class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor #include <QDialog>
class SubscribeEditor
: public QDialog
, private Ui::w_SubscribeEditor
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SubscribeEditor(QWidget *parent = nullptr); explicit SubscribeEditor(QWidget *parent = nullptr);
~SubscribeEditor(); ~SubscribeEditor();
QPair<QString, CONFIGROOT> GetSelectedConfig(); QPair<QString, CONFIGROOT> GetSelectedConfig();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_addSubsButton_clicked(); void on_addSubsButton_clicked();
void on_updateButton_clicked(); void on_updateButton_clicked();
void on_removeSubsButton_clicked(); void on_removeSubsButton_clicked();
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_subscriptionList_itemSelectionChanged(); void on_subscriptionList_itemSelectionChanged();
void on_subscriptionList_itemClicked(QTreeWidgetItem *item, int column); void on_subscriptionList_itemClicked(QTreeWidgetItem *item, int column);
private: private:
void StartUpdateSubscription(const QString &subscriptionName); void StartUpdateSubscription(const QString &subscriptionName);
void SaveConfig(); void SaveConfig();
bool isUpdateInProgress = false; bool isUpdateInProgress = false;
GroupId currentSubId = NullGroupId; GroupId currentSubId = NullGroupId;
}; };

View File

@ -1,9 +1,10 @@
#include "ConnectionInfoWidget.hpp" #include "ConnectionInfoWidget.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
#include "core/CoreUtils.hpp" #include "core/CoreUtils.hpp"
#include "core/connection/Serialization.hpp" #include "core/connection/Serialization.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent): QWidget(parent) ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent)
{ {
setupUi(this); setupUi(this);
duplicateBtn->setIcon(QICON_R("duplicate.png")); duplicateBtn->setIcon(QICON_R("duplicate.png"));
@ -12,7 +13,8 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent): QWidget(parent)
editJsonBtn->setIcon(QICON_R("json.png")); editJsonBtn->setIcon(QICON_R("json.png"));
// //
shareLinkTxt->setAutoFillBackground(true); shareLinkTxt->setAutoFillBackground(true);
shareLinkTxt->setStyleSheet("border-bottom: 1px solid gray; border-radius: 0px; padding: 2px; background-color: " + this->palette().color(this->backgroundRole()).name(QColor::HexRgb)); shareLinkTxt->setStyleSheet("border-bottom: 1px solid gray; border-radius: 0px; padding: 2px; background-color: " +
this->palette().color(this->backgroundRole()).name(QColor::HexRgb));
shareLinkTxt->setCursor(QCursor(Qt::CursorShape::IBeamCursor)); shareLinkTxt->setCursor(QCursor(Qt::CursorShape::IBeamCursor));
shareLinkTxt->installEventFilter(this); shareLinkTxt->installEventFilter(this);
// //
@ -30,10 +32,12 @@ void ConnectionInfoWidget::ShowDetails(const tuple<GroupId, ConnectionId> &_iden
duplicateBtn->setEnabled(isConnection); duplicateBtn->setEnabled(isConnection);
editBtn->setEnabled(isConnection); editBtn->setEnabled(isConnection);
if (isConnection) { if (isConnection)
{
groupLabel->setText(ConnectionManager->GetDisplayName(groupId, 175)); groupLabel->setText(ConnectionManager->GetDisplayName(groupId, 175));
protocolLabel->setText(ConnectionManager->GetConnectionProtocolString(connectionId)); protocolLabel->setText(ConnectionManager->GetConnectionProtocolString(connectionId));
auto [host, port] = ConnectionManager->GetConnectionInfo(connectionId); auto [protocol, host, port] = ConnectionManager->GetConnectionData(connectionId);
Q_UNUSED(protocol)
addressLabel->setText(host); addressLabel->setText(host);
portLabel->setNum(port); portLabel->setNum(port);
// //
@ -48,7 +52,9 @@ void ConnectionInfoWidget::ShowDetails(const tuple<GroupId, ConnectionId> &_iden
qrLabel->setPixmap(QPixmap::fromImage(img)); qrLabel->setPixmap(QPixmap::fromImage(img));
// //
connectBtn->setIcon(ConnectionManager->IsConnected(connectionId) ? QICON_R("stop.png") : QICON_R("connect.png")); connectBtn->setIcon(ConnectionManager->IsConnected(connectionId) ? QICON_R("stop.png") : QICON_R("connect.png"));
} else { }
else
{
connectBtn->setIcon(QICON_R("connect.png")); connectBtn->setIcon(QICON_R("connect.png"));
groupLabel->setText(tr("N/A")); groupLabel->setText(tr("N/A"));
protocolLabel->setText(tr("N/A")); protocolLabel->setText(tr("N/A"));
@ -66,9 +72,9 @@ ConnectionInfoWidget::~ConnectionInfoWidget()
void ConnectionInfoWidget::on_connectBtn_clicked() void ConnectionInfoWidget::on_connectBtn_clicked()
{ {
if (ConnectionManager->IsConnected(connectionId)) { if (ConnectionManager->IsConnected(connectionId)) { ConnectionManager->StopConnection(); }
ConnectionManager->StopConnection(); else
} else { {
ConnectionManager->StartConnection(connectionId); ConnectionManager->StartConnection(connectionId);
} }
} }
@ -90,11 +96,11 @@ void ConnectionInfoWidget::on_deleteBtn_clicked()
bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event) bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event)
{ {
if (event->type() == QEvent::MouseButtonRelease) { if (event->type() == QEvent::MouseButtonRelease)
if (shareLinkTxt->underMouse()) { {
if (!shareLinkTxt->hasSelectedText()) { if (shareLinkTxt->underMouse())
shareLinkTxt->selectAll(); {
} if (!shareLinkTxt->hasSelectedText()) { shareLinkTxt->selectAll(); }
} }
} }
@ -103,51 +109,50 @@ bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event)
void ConnectionInfoWidget::OnConnected(const ConnectionId &id) void ConnectionInfoWidget::OnConnected(const ConnectionId &id)
{ {
if (connectionId == id) { if (connectionId == id) { connectBtn->setIcon(QICON_R("stop.png")); }
connectBtn->setIcon(QICON_R("stop.png"));
}
} }
void ConnectionInfoWidget::OnDisConnected(const ConnectionId &id) void ConnectionInfoWidget::OnDisConnected(const ConnectionId &id)
{ {
if (connectionId == id) { if (connectionId == id) { connectBtn->setIcon(QICON_R("connect.png")); }
connectBtn->setIcon(QICON_R("connect.png"));
}
} }
//MWTryPingConnection(CurrentConnectionIdentifier); // MWTryPingConnection(CurrentConnectionIdentifier);
void ConnectionInfoWidget::on_duplicateBtn_clicked() void ConnectionInfoWidget::on_duplicateBtn_clicked()
{ {
//QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP"); // QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP");
//if (!IsSelectionConnectable) { // if (!IsSelectionConnectable) {
// return; // return;
//} //}
// //
//auto selectedFirst = connectionListWidget->currentItem(); // auto selectedFirst = connectionListWidget->currentItem();
//auto _identifier = ItemConnectionIdentifier(selectedFirst); // auto _identifier = ItemConnectionIdentifier(selectedFirst);
//SUBSCRIPTION_CONFIG_MODIFY_ASK(selectedFirst) // SUBSCRIPTION_CONFIG_MODIFY_ASK(selectedFirst)
//CONFIGROOT conf; // CONFIGROOT conf;
//// Alias may change. //// Alias may change.
//QString alias = _identifier.connectionName; // QString alias = _identifier.connectionName;
//bool isComplex = IsComplexConfig(connections[_identifier].config); // bool isComplex = IsComplexConfig(connections[_identifier].config);
// //
//if (connections[_identifier].configType == CONNECTION_REGULAR) { // if (connections[_identifier].configType == CONNECTION_REGULAR) {
// conf = ConvertConfigFromFile(QV2RAY_CONFIG_DIR + _identifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION, isComplex); // conf = ConvertConfigFromFile(QV2RAY_CONFIG_DIR +
// _identifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION, isComplex);
//} else { //} else {
// conf = ConvertConfigFromFile(QV2RAY_SUBSCRIPTION_DIR + _identifier.subscriptionName + "/" + _identifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION, isComplex); // conf = ConvertConfigFromFile(QV2RAY_SUBSCRIPTION_DIR +
// alias = _identifier.subscriptionName + "_" + _identifier.connectionName; // _identifier.subscriptionName + "/" + _identifier.connectionName +
// QV2RAY_CONFIG_FILE_EXTENSION, isComplex); alias =
// _identifier.subscriptionName + "_" + _identifier.connectionName;
//} //}
// //
//SaveConnectionConfig(conf, &alias, false); // SaveConnectionConfig(conf, &alias, false);
//GlobalConfig.configs.push_back(alias); // GlobalConfig.configs.push_back(alias);
//SaveGlobalConfig(GlobalConfig); // SaveGlobalConfig(GlobalConfig);
//this->OnConfigListChanged(false);} // this->OnConfigListChanged(false);}
} }
void ConnectionInfoWidget::on_latencyBtn_clicked() void ConnectionInfoWidget::on_latencyBtn_clicked()
{ {
if (connectionId != NullConnectionId) { if (connectionId != NullConnectionId) { ConnectionManager->StartLatencyTest(connectionId); }
ConnectionManager->StartLatencyTest(connectionId); else
} else { {
ConnectionManager->StartLatencyTest(groupId); ConnectionManager->StartLatencyTest(groupId);
} }
} }

View File

@ -1,39 +1,41 @@
#pragma once #pragma once
#include <QWidget>
#include "ui_ConnectionInfoWidget.h"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
#include "ui_ConnectionInfoWidget.h"
class ConnectionInfoWidget : public QWidget, private Ui::ConnectionInfoWidget #include <QWidget>
class ConnectionInfoWidget
: public QWidget
, private Ui::ConnectionInfoWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ConnectionInfoWidget(QWidget *parent = nullptr); explicit ConnectionInfoWidget(QWidget *parent = nullptr);
void ShowDetails(const tuple<GroupId, ConnectionId> &_identifier); void ShowDetails(const tuple<GroupId, ConnectionId> &_identifier);
~ConnectionInfoWidget(); ~ConnectionInfoWidget();
signals: signals:
void OnEditRequested(const ConnectionId &id); void OnEditRequested(const ConnectionId &id);
void OnJsonEditRequested(const ConnectionId &id); void OnJsonEditRequested(const ConnectionId &id);
private slots: private slots:
void on_connectBtn_clicked(); void on_connectBtn_clicked();
void on_editBtn_clicked(); void on_editBtn_clicked();
void on_editJsonBtn_clicked(); void on_editJsonBtn_clicked();
void on_deleteBtn_clicked(); void on_deleteBtn_clicked();
protected: protected:
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
private slots: private slots:
void OnConnected(const ConnectionId &id); void OnConnected(const ConnectionId &id);
void OnDisConnected(const ConnectionId &id); void OnDisConnected(const ConnectionId &id);
void on_duplicateBtn_clicked(); void on_duplicateBtn_clicked();
void on_latencyBtn_clicked(); void on_latencyBtn_clicked();
private: private:
ConnectionId connectionId = NullConnectionId; ConnectionId connectionId = NullConnectionId;
GroupId groupId = NullGroupId; GroupId groupId = NullGroupId;
}; };

Some files were not shown because too many files have changed in this diff Show More