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

@ -4,27 +4,27 @@
#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;
@ -100,10 +100,10 @@ using namespace Qv2ray::base::objects::transfer;
_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

@ -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,7 +17,7 @@ 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"

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,
tProxySupport,
logLevel, uiConfig,
v2CorePath, v2AssetsPath,
groups, connections, subscriptions,
autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
};
} }
XTOSTRUCT(O(config_version, ignoredVersion, tProxySupport, 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: \ public: \
template<class... Args> \ template<class... Args> \
explicit name (Args... args) : Base(args...) {} \ explicit name(Args... args) : Base(args...) \
const Base& raw() const { return *this; } \ { \
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,10 +1,11 @@
#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")),
@ -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,
@ -30,6 +31,6 @@ namespace Qv2ray::common
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,10 +18,10 @@
#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
{ {
@ -50,6 +50,7 @@ namespace Qv2ray::common
void onRequestFinished_p(); void onRequestFinished_p();
private slots: private slots:
void onReadyRead(); void onReadyRead();
private: private:
QByteArray data; QByteArray data;
QUrl url; QUrl url;
@ -57,6 +58,6 @@ namespace Qv2ray::common
QNetworkRequest request; QNetworkRequest request;
QNetworkAccessManager accessManager; 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);
@ -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,9 +49,9 @@
****************************************************************************/ ****************************************************************************/
#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
@ -67,7 +67,8 @@ namespace Qv2ray::common
void highlightBlock(const QString &text) override; void highlightBlock(const QString &text) override;
private: private:
struct HighlightingRule { struct HighlightingRule
{
QRegularExpression pattern; QRegularExpression pattern;
QTextCharFormat format; QTextCharFormat format;
}; };
@ -88,6 +89,6 @@ namespace Qv2ray::common
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 // Get all QJsonValue childs
for (QString key : value.toObject().keys()) { 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()) { }
else if (value.isArray())
{
// Get all QJsonValue childs // 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) {
if (index.column() == 1) {
return QString("%1").arg(item->value());
} }
else if (Qt::EditRole == role)
{
if (index.column() == 1) { return QString("%1").arg(item->value()); }
} }
return QVariant(); return QVariant();
@ -240,8 +242,10 @@ 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 });
@ -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());
@ -327,9 +320,9 @@ Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const
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,9 +332,9 @@ 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());
} }
@ -353,26 +346,33 @@ 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

@ -25,11 +25,11 @@
#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;
@ -51,20 +51,15 @@ class QJsonTreeItem
QString value() const; QString value() const;
QJsonValue::Type type() const; QJsonValue::Type type() const;
static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0); static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0);
protected: protected:
private: private:
QString mKey; QString mKey;
QString mValue; QString mValue;
QJsonValue::Type mType; QJsonValue::Type mType;
QList<QJsonTreeItem *> mChilds; QList<QJsonTreeItem *> mChilds;
QJsonTreeItem *mParent; QJsonTreeItem *mParent;
}; };
//--------------------------------------------------- //---------------------------------------------------
@ -96,6 +91,4 @@ class QJsonModel : public QAbstractItemModel
QJsonTreeItem *mRootItem; QJsonTreeItem *mRootItem;
QStringList mHeaders; 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);
@ -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

@ -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);
@ -104,29 +111,30 @@ 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,26 +1,25 @@
#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";
@ -33,9 +32,7 @@ std::pair<std::optional<UINT64>, std::optional<std::string>> ICMPPinger::ping(co
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,16 +6,15 @@
* License: WTFPL * License: WTFPL
*/ */
#include<winsock2.h>
#include<iphlpapi.h>
#include <icmpapi.h> #include <icmpapi.h>
#include <iphlpapi.h>
#include<utility>
#include<optional>
#include <memory> #include <memory>
#include <optional>
#include <utility>
#include <winsock2.h>
class ICMPPinger
class ICMPPinger { {
public: public:
ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT); ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
~ICMPPinger(); ~ICMPPinger();

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;
@ -56,15 +50,15 @@ namespace Qv2ray::components::pac
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;
@ -79,12 +73,15 @@ namespace Qv2ray::components::pac
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) { {
while (true)
{
// Get Domain // Get Domain
readDomainBuffer += domainListCache[rotatorTwo]; readDomainBuffer += domainListCache[rotatorTwo];
if (domainListCache[rotatorTwo + 1] == '\n') { if (domainListCache[rotatorTwo + 1] == '\n')
{
rotatorTwo += 2; rotatorTwo += 2;
break; break;
} }
@ -94,7 +91,8 @@ namespace Qv2ray::components::pac
// Format // Format
if (!isFirstLine) outputContent += ",\n"; if (!isFirstLine) outputContent += ",\n";
else isFirstLine = false; else
isFirstLine = false;
outputContent += "\t\""; outputContent += "\t\"";
outputContent += readDomainBuffer; outputContent += readDomainBuffer;
@ -103,31 +101,17 @@ namespace Qv2ray::components::pac
} }
// 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>
@ -27,7 +28,7 @@ namespace Qv2ray::components::pac
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
{ {
@ -41,40 +42,49 @@ namespace Qv2ray::components::plugins
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;
@ -82,13 +92,15 @@ namespace Qv2ray::components::plugins
// case 104: { // case 104: {
// // Current Connection Name // // Current Connection Name
// CL.Message = instance->GetCurrentConnectedConfigName(); // CL.Message =
// instance->GetCurrentConnectedConfigName();
// break; // break;
//} //}
// //
// case 105: { // case 105: {
// // Current Connection Status // // Current Connection Status
// CL.Message = instance->vinstance->KernelStarted // CL.Message =
// instance->vinstance->KernelStarted
// ? QObject::tr("Connected") // ? QObject::tr("Connected")
// : QObject::tr("Disconnected"); // : QObject::tr("Disconnected");
// break; // break;
@ -96,53 +108,61 @@ namespace Qv2ray::components::plugins
// //
// case 201: { // case 201: {
// // Total upload speed; // // Total upload speed;
// CL.Message = FormatBytes(vinstance->getAllSpeedUp()) + "/s"; // CL.Message =
// break; // FormatBytes(vinstance->getAllSpeedUp()) +
// "/s"; break;
//} //}
// //
// case 202: { // case 202: {
// // Total download speed; // // Total download speed;
// CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s"; // CL.Message =
// break; // FormatBytes(vinstance->getAllSpeedDown()) +
// "/s"; break;
//} //}
// //
// case 203: { // case 203: {
// // Upload speed for tag // // Upload speed for tag
// CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s"; // CL.Message =
// break; // FormatBytes(vinstance->getTagSpeedUp(CL.Message))
// + "/s"; break;
//} //}
// //
// case 204: { // case 204: {
// // Download speed for tag // // Download speed for tag
// CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s"; // CL.Message =
// break; // FormatBytes(vinstance->getTagSpeedDown(CL.Message))
// + "/s"; break;
//} //}
// //
// case 301: { // case 301: {
// // Total Upload // // Total Upload
// CL.Message = FormatBytes(vinstance->getAllDataUp()); // CL.Message =
// break; // FormatBytes(vinstance->getAllDataUp()); break;
//} //}
// //
// case 302: { // case 302: {
// // Total download // // Total download
// CL.Message = FormatBytes(vinstance->getAllDataDown()); // CL.Message =
// FormatBytes(vinstance->getAllDataDown());
// break; // break;
//} //}
// //
// case 303: { // case 303: {
// // Upload for tag // // Upload for tag
// CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message)); // CL.Message =
// FormatBytes(vinstance->getTagDataUp(CL.Message));
// break; // break;
//} //}
// //
// case 304: { // case 304: {
// // Download for tag // // Download for tag
// CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message)); // CL.Message =
// FormatBytes(vinstance->getTagDataDown(CL.Message));
// break; // 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,8 +9,7 @@ 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") },
@ -26,8 +25,7 @@ namespace Qv2ray::components::plugins
{ 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 <QLocalSocket> #include "components/plugins/toolbar/QvToolbar.hpp"
#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,7 +1,8 @@
#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
{ {
@ -25,10 +26,13 @@ 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)
@ -39,26 +43,36 @@ namespace Qv2ray::components::plugins::Toolbar
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,7 +1,9 @@
#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
@ -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(";"))
@ -36,7 +37,8 @@ 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()
@ -57,55 +59,36 @@ namespace Qv2ray::components::proxy
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;
} }
@ -119,14 +102,16 @@ namespace Qv2ray::components::proxy
// 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;
@ -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);
} }
@ -239,7 +222,8 @@ namespace Qv2ray::components::proxy
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()")
} }
@ -305,7 +296,8 @@ namespace Qv2ray::components::proxy
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,32 +14,33 @@
* *
* 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, Byte, // 1024^0,
KibiByte, // 1024^1, KibiByte, // 1024^1,
MebiByte, // 1024^2, MebiByte, // 1024^2,
@ -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
// of scale
const double roundingTable[] = { 1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8 }; 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
@ -128,32 +125,33 @@ namespace
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 };
@ -163,15 +161,11 @@ namespace
{ {
// 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,9 +296,9 @@ 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();
} }
@ -313,14 +309,15 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
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

@ -39,13 +39,15 @@ class SpeedPlotView : public QGraphicsView
Q_OBJECT Q_OBJECT
public: public:
enum GraphID { enum GraphID
{
UP = 0, UP = 0,
DOWN, DOWN,
NB_GRAPHS NB_GRAPHS
}; };
struct PointData { struct PointData
{
qint64 x; qint64 x;
quint64 y[NB_GRAPHS]; quint64 y[NB_GRAPHS];
}; };
@ -59,7 +61,8 @@ class SpeedPlotView : public QGraphicsView
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
private: private:
struct GraphProperties { struct GraphProperties
{
GraphProperties(); GraphProperties();
GraphProperties(const QString &name, const QPen &pen); GraphProperties(const QString &name, const QPen &pen);

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

@ -48,6 +48,7 @@ class SpeedWidget : public QWidget
~SpeedWidget(); ~SpeedWidget();
void AddPointData(long up, long down); void AddPointData(long up, long down);
void Clear(); void Clear();
private: private:
QVBoxLayout *m_layout; QVBoxLayout *m_layout;
QHBoxLayout *m_hlayout; QHBoxLayout *m_hlayout;

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <QString> #include "base/models/QvConfigIdentifier.hpp"
#include <QHash> #include <QHash>
#include <QHashFunctions> #include <QHashFunctions>
#include <QString>
#include "base/models/QvConfigIdentifier.hpp"
namespace Qv2ray::core namespace Qv2ray::core
{ {
@ -12,7 +12,12 @@ namespace Qv2ray::core
class IDType class IDType
{ {
public: public:
explicit IDType(const QString &id): m_id(id) {} explicit IDType() : m_id("null")
{
}
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.m_id == rhs.m_id; return lhs.m_id == rhs.m_id;
@ -29,11 +34,11 @@ namespace Qv2ray::core
{ {
return ::qHash(m_id, seed); return ::qHash(m_id, seed);
} }
private: private:
QString m_id; QString m_id;
}; };
// Define several safetypes to prevent misuse of QString. // Define several safetypes to prevent misuse of QString.
class __QvGroup; class __QvGroup;
class __QvConnection; class __QvConnection;
@ -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,12 +93,15 @@ 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;
@ -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'
@ -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,9 +232,12 @@ 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."));
@ -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,18 +39,21 @@ 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;
@ -54,7 +62,8 @@ namespace Qv2ray::core::connection
// //
// 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.
// //
@ -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,19 +232,23 @@ 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

@ -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); \ logList << QObject::tr("invalid ssd link: json: field %1 must exist").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \ 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); \
if (int value = obj[(fieldName)].toInt(-1); value < 0 || value > 65535) \
{ \
logList << QObject::tr("invalid ssd link: json: field %1 must be valid port number"); \ logList << QObject::tr("invalid ssd link: json: field %1 must be valid port number"); \
return std::make_pair(std::nullopt, logList); \ 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); \
if (!obj[(fieldName)].isString()) \
{ \
logList << QObject::tr("invalid ssd link: json: field %1 must be of type 'string'").arg(fieldName); \ logList << QObject::tr("invalid ssd link: json: field %1 must be of type 'string'").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \ 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); \
if (!obj[(fieldName)].isArray()) \
{ \
logList << QObject::tr("invalid ssd link: json: field %1 must be an array").arg(fieldName); \ logList << QObject::tr("invalid ssd link: json: field %1 must be an array").arg(fieldName); \
return std::make_pair(std::nullopt, logList); \ 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"); \ logList << QObject::tr("skipping invalid ssd server: server must be an object"); \
continue; \ 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); \ logList << QObject::tr("skipping invalid ssd server: missing required field %1").arg(fieldName); \
continue; \ continue; \
} }
#define SHOULD_STRING(fieldName) SHOULD_EXIST(fieldName); \ #define SHOULD_STRING(fieldName) \
if (!serverObject[(fieldName)].isString()) { \ SHOULD_EXIST(fieldName); \
if (!serverObject[(fieldName)].isString()) \
{ \
logList << QObject::tr("skipping invalid ssd server: field %1 should be of type 'string'").arg(fieldName); \ logList << QObject::tr("skipping invalid ssd server: field %1 should be of type 'string'").arg(fieldName); \
continue; \ 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);
} }
@ -101,7 +118,8 @@ namespace Qv2ray::core::connection
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,7 +128,8 @@ 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);
@ -126,37 +145,34 @@ namespace Qv2ray::core::connection
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,13 +273,13 @@ 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")
@ -263,7 +287,9 @@ namespace Qv2ray::core::connection
#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,41 +305,59 @@ 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(); \ key = vmessConf[#key].toVariant().toString(); \
DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.") \ DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.") \
} else if (!val.isEmpty()) {\ } \
else if (!val.isEmpty()) \
{ \
key = val.first(); \ key = val.first(); \
DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key) \ DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key) \
} else{\ } \
else \
{ \
*errMessage = QObject::tr(#key " does not exist."); \ *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, "Cannot process \"" #key "\" since it's not included in the json object.") \
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \ LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \
LOG(MODULE_IMPORT, " --> PS: " + ps) \ 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"
<< "quic")
__vmess_checker__func(type, << "none"
<< "http"
<< "srtp"
<< "utp"
<< "wechat-video") __vmess_checker__func(path, << "") __vmess_checker__func(host, << "")
__vmess_checker__func(tls, << "") __vmess_checker__func(tls, << "")
// //
port = vmessConf["port"].toVariant().toInt(); port = vmessConf["port"].toVariant().toInt();
@ -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

@ -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) {
StartLatencyTest(currentConnectionId);
} }
else if (event->timerId() == pingConnectionTimerId)
{
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,38 +166,47 @@ 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);
@ -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;
bool isSucceed = false;
auto result = CHGetOutboundData_p(root, &isSucceed);
return result;
}
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; QString host;
int port; int port;
for (auto item : root["outbounds"].toArray()) {
OUTBOUND outBoundRoot = OUTBOUND(item.toObject());
QString outboundType = ""; QString outboundType = "";
validOutboundFound = GetOutboundData(outBoundRoot, &host, &port, &outboundType);
if (validOutboundFound) { if (GetOutboundData(outBoundRoot, &host, &port, &outboundType))
return make_tuple(host, port); {
} else { *ok = true;
LOG(MODULE_CORE_HANDLER, "Unknown outbound entry: " + outboundType + ", cannot deduce host and port.") 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);
return make_tuple(QObject::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,12 +1,12 @@
#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
{ {
@ -49,7 +49,7 @@ namespace Qv2ray::core::handlers
// //
// 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
@ -64,8 +64,8 @@ namespace Qv2ray::core::handlers
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:
@ -73,9 +73,8 @@ namespace Qv2ray::core::handlers
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);
@ -102,11 +101,13 @@ namespace Qv2ray::core::handlers
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;
@ -118,6 +119,7 @@ namespace Qv2ray::core::handlers
private: private:
QvHttpRequestHelper *httpHelper; QvHttpRequestHelper *httpHelper;
bool isHttpRequestInProgress = false;
QvTCPingHelper *tcpingHelper; QvTCPingHelper *tcpingHelper;
// We only support one cuncurrent connection currently. // We only support one cuncurrent connection currently.
#ifdef QV2RAY_MULTIPlE_ONNECTION #ifdef QV2RAY_MULTIPlE_ONNECTION
@ -129,6 +131,6 @@ namespace Qv2ray::core::handlers
}; };
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

@ -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;
} }
@ -130,12 +137,15 @@ namespace Qv2ray::core::kernel
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.pb.h"
#include "libs/gen/v2ray_api.grpc.pb.h" #include "libs/gen/v2ray_api.grpc.pb.h"
#include "libs/gen/v2ray_api.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.
@ -41,6 +42,6 @@ namespace Qv2ray::core::kernel
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,19 +38,22 @@ 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;
@ -54,7 +62,8 @@ namespace Qv2ray::core::kernel
// 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,8 +1,9 @@
#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;
@ -41,6 +42,6 @@ namespace Qv2ray::core::kernel
// //
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();
@ -39,9 +40,7 @@ namespace Qv2ray::core::tcping
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,15 +157,14 @@ 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
@ -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);
@ -195,4 +206,4 @@ next_addr0:
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;
@ -21,11 +22,12 @@ namespace Qv2ray::core::tcping
void StopAllLatenceTest(); void StopAllLatenceTest();
signals: signals:
void OnLatencyTestCompleted(const QvTCPingResultObject &data); void OnLatencyTestCompleted(const QvTCPingResultObject &data);
private: private:
static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count); static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count);
int count; int count;
QQueue<QFutureWatcher<QvTCPingResultObject> *> pingWorkingThreads; QQueue<QFutureWatcher<QvTCPingResultObject> *> pingWorkingThreads;
}; };
} } // namespace Qv2ray::core::tcping
using namespace Qv2ray::core::tcping; using namespace Qv2ray::core::tcping;

View File

@ -1,20 +1,20 @@
#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
@ -31,7 +31,6 @@ void signalHandler(int signum)
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 { }
else
{
LOG(MODULE_SETTINGS, "File: " + configFile.fileName() + " cannot be opened!") 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,37 +338,38 @@ 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"),
@ -345,7 +380,8 @@ int main(int argc, char *argv[])
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,13 +401,13 @@ 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");
} }
@ -383,15 +420,15 @@ 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,14 +1,13 @@
#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);
@ -17,20 +16,30 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
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("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" +
tr("Inbound: ") + inboundType); tr("Inbound: ") + inboundType);
} else { }
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") {
// See above
if (socksAccountListBox->count() == 0) {
socksSettings.remove("accounts");
} }
else if (inboundType == "socks")
{
// See above
if (socksAccountListBox->count() == 0) { 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;

View File

@ -1,13 +1,16 @@
#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
@ -16,7 +19,7 @@ class InboundEditor : public QDialog, private Ui::InboundEditor
~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);

View File

@ -2,8 +2,7 @@
#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);
@ -11,11 +10,14 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) :
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,13 +1,16 @@
#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
@ -16,7 +19,7 @@ class JsonEditor : public QDialog, private Ui::JsonEditor
~JsonEditor(); ~JsonEditor();
QJsonObject OpenEditor(); QJsonObject OpenEditor();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_jsonEditor_textChanged(); void on_jsonEditor_textChanged();

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,12 +1,15 @@
#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:
@ -16,7 +19,7 @@ class OutboundEditor : public QDialog, private Ui::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:

View File

@ -1,34 +1,38 @@
// 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 \
if (this->rules.isEmpty()) \
{ \
LOG(MODULE_UI, "No rules currently, we add one.") \ LOG(MODULE_UI, "No rules currently, we add one.") \
AddNewRule(); \ AddNewRule(); \
} }
@ -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);
@ -721,25 +767,28 @@ void RouteEditor::on_delBtn_clicked()
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" + {
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.")); 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,7 +845,8 @@ 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."));
@ -798,17 +854,21 @@ void RouteEditor::on_editBtn_clicked()
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,31 +1,34 @@
#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
@ -34,7 +37,7 @@ class RouteEditor : public QDialog, private Ui::RouteEditor
~RouteEditor(); ~RouteEditor();
CONFIGROOT OpenEditor(); CONFIGROOT OpenEditor();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
private slots: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();

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.")
} }
@ -224,21 +239,26 @@ void RouteEditor::ResolveDefaultOutboundTag(QString original, QString newTag)
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,7 +6,7 @@
#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 \
@ -20,14 +20,13 @@
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.
@ -57,6 +56,6 @@ namespace Qv2ray::ui::messaging
// 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,8 +1,9 @@
#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
@ -47,7 +48,9 @@ class QvInboundNodeModel : public NodeDataModel
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) void setData(const QString &data)
{ {
_in = make_shared<InboundNodeData>(data); _in = make_shared<InboundNodeData>(data);
@ -59,6 +62,7 @@ class QvInboundNodeModel : public NodeDataModel
{ {
return _label; return _label;
} }
private: private:
NodeValidationState modelValidationState = NodeValidationState::Warning; NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs"); QString modelValidationError = tr("Missing or incorrect inputs");

View File

@ -1,22 +1,20 @@
#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;
@ -29,11 +27,10 @@ namespace Qv2ray::ui::nodemodels
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
{ {
@ -44,6 +41,7 @@ namespace Qv2ray::ui::nodemodels
{ {
return _inboundTag; return _inboundTag;
} }
private: private:
QString _inboundTag; QString _inboundTag;
}; };
@ -53,11 +51,11 @@ namespace Qv2ray::ui::nodemodels
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)
: _outboundTag(out)
{ {
DEBUG(MODULE_GRAPH, "DANGER: Initialising a data model without value.")
} }
OutboundNodeData(QString out) : _outboundTag(out) { }
NodeDataType type() const override NodeDataType type() const override
{ {
@ -68,6 +66,7 @@ namespace Qv2ray::ui::nodemodels
{ {
return _outboundTag; return _outboundTag;
} }
private: private:
QString _outboundTag; QString _outboundTag;
}; };
@ -77,11 +76,10 @@ namespace Qv2ray::ui::nodemodels
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
{ {
@ -92,10 +90,10 @@ namespace Qv2ray::ui::nodemodels
{ {
return _ruleTag; return _ruleTag;
} }
private: private:
QString _ruleTag; QString _ruleTag;
}; };
} } // namespace Qv2ray::ui::nodemodels
using namespace Qv2ray::ui::nodemodels; using namespace Qv2ray::ui::nodemodels;

View File

@ -1,7 +1,8 @@
#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
@ -46,9 +47,13 @@ class QvOutboundNodeModel : public NodeDataModel
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) void setData(const QString &data)
{ {
_out = make_shared<OutboundNodeData>(data); _out = make_shared<OutboundNodeData>(data);
@ -65,6 +70,7 @@ class QvOutboundNodeModel : public NodeDataModel
{ {
return ConnectionPolicy::Many; return ConnectionPolicy::Many;
} }
private: private:
NodeValidationState modelValidationState = NodeValidationState::Warning; NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs"); QString modelValidationError = tr("Missing or incorrect inputs");

View File

@ -1,8 +1,9 @@
#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
@ -27,11 +28,13 @@ class QvRuleNodeDataModel : public NodeDataModel
unsigned int nPorts(PortType portType) const override unsigned int nPorts(PortType portType) const override
{ {
if (portType == PortType::In) { if (portType == PortType::In) { return 1; }
else if (portType == PortType::Out)
{
return 1; return 1;
} else if (portType == PortType::Out) { }
return 1; else
} else { {
return 0; return 0;
} }
} }
@ -45,15 +48,13 @@ class QvRuleNodeDataModel : public NodeDataModel
{ {
Q_UNUSED(portIndex) Q_UNUSED(portIndex)
switch (portType) { switch (portType)
case PortType::In: {
return inboundType; case PortType::In: return inboundType;
case PortType::Out: case PortType::Out: return outboundType;
return outboundType;
case PortType::None: case PortType::None: break;
break;
} }
return NodeDataType(); return NodeDataType();
@ -65,8 +66,12 @@ class QvRuleNodeDataModel : public NodeDataModel
return _ruleTag; return _ruleTag;
} }
void setInData(std::shared_ptr<NodeData>, int) override {} void setInData(std::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) void setData(const QString &data)
{ {
_ruleTag = make_shared<RuleNodeData>(data); _ruleTag = make_shared<RuleNodeData>(data);

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
} }
} }
@ -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,11 +1,13 @@
#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
@ -14,7 +16,8 @@ class ConfigExporter : public QDialog, private Ui::ExportConfigWindow
~ConfigExporter(); ~ConfigExporter();
void OpenExport(); void OpenExport();
public slots: public slots:
QvMessageBusSlotDecl QvMessageBusSlotDecl;
protected: protected:
void changeEvent(QEvent *e); void changeEvent(QEvent *e);
private slots: private slots:
@ -25,6 +28,7 @@ class ConfigExporter : public QDialog, private Ui::ExportConfigWindow
void on_copyImageBtn_clicked(); void on_copyImageBtn_clicked();
void on_copyVMessBtn_clicked(); void on_copyVMessBtn_clicked();
private: private:
QZXing qzxing; QZXing qzxing;
QImage image; QImage image;

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();
} }
} }
} }
@ -99,24 +101,28 @@ 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,
/// ImportAsComplex);
// connections[aliasPrefix] = config; // connections[aliasPrefix] = config;
// break; // 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,7 +219,8 @@ 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;
} }
@ -216,12 +228,17 @@ void ImportConfigWindow::on_editFileBtn_clicked()
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.")
} }
} }
@ -230,14 +247,15 @@ void ImportConfigWindow::on_editFileBtn_clicked()
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,13 +1,16 @@
#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
@ -16,7 +19,7 @@ class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow
~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();

View File

@ -1,49 +1,56 @@
#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
overwritten when the subscriptions are updated next time.") + \
NEWLINE + QObject::tr("Are you still going to do
so?")) != QMessageBox::Yes) { \
return; \ return; \
} \ } \
} \ } \
@ -51,22 +58,25 @@
#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"),
QObject::tr("You should not modity this property of a config from a
subscription")); \
return; \ 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
} }
} }
@ -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,10 +246,10 @@ 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) {
@ -243,7 +258,8 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
// 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);
//} //}
} }
} }
@ -251,12 +267,13 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
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 =
// ItemConnectionIdentifier(CurrentSelectedItemPtr);
// on_reconnectButton_clicked(); // on_reconnectButton_clicked();
} }
@ -269,25 +286,24 @@ 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();
} }
@ -372,7 +390,8 @@ void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *cur
// 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,9 +400,7 @@ 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()
{ {
@ -396,7 +413,8 @@ void MainWindow::on_action_RCM_RenameConnection_triggered()
} }
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;
@ -407,26 +425,30 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
//// 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,7 +486,8 @@ 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()
@ -483,11 +508,13 @@ void MainWindow::on_removeConfigButton_clicked()
// 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
/// configs loaded from a subscription.
// int subscriptionRemovalCheckStatus = -1; // int subscriptionRemovalCheckStatus = -1;
// //
// for (auto conn : connlist) { // for (auto conn : connlist) {
@ -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,29 +539,38 @@ 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...")
// } // }
//} //}
// //
@ -570,8 +608,8 @@ 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();
@ -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,13 +664,12 @@ 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.SetProxyString(pacProxyString);
// pacServer.StartListen(); // pacServer.StartListen();
} else { }
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,23 +1,24 @@
#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:
@ -28,7 +29,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow
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);
@ -66,7 +67,8 @@ class MainWindow : public QMainWindow, Ui::MainWindow
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,
const quint64 totalDown);
void onVCoreLogArrived(const ConnectionId &id, const QString &log); void onVCoreLogArrived(const ConnectionId &id, const QString &log);
// //
void on_action_StartThis_triggered(); void on_action_StartThis_triggered();

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]
@ -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,9 +770,7 @@ 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") : "");
@ -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,11 +1,14 @@
#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
@ -16,7 +19,7 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
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();

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);
@ -75,13 +81,15 @@ void ScreenShotWindow::pSize()
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();
} }
} }
@ -109,7 +117,8 @@ void ScreenShotWindow::mousePressEvent(QMouseEvent *e)
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,17 +1,19 @@
#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

View File

@ -1,27 +1,25 @@
#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
} }
} }
@ -46,22 +44,26 @@ void SubscribeEditor::on_addSubsButton_clicked()
void SubscribeEditor::on_updateButton_clicked() void SubscribeEditor::on_updateButton_clicked()
{ {
ConnectionManager->UpdateSubscription(currentSubId, withProxyCB->isChecked());
// auto newName = subNameTxt->text().trimmed(); // auto newName = subNameTxt->text().trimmed();
// auto newAddress = subAddrTxt->text().trimmed(); // auto newAddress = subAddrTxt->text().trimmed();
// auto newUpdateInterval = updateIntervalSB->value(); // auto newUpdateInterval = updateIntervalSB->value();
// if (currentSubId != newName) { // 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,55 +96,32 @@ 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);
} }
@ -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,12 +1,15 @@
#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
@ -16,7 +19,7 @@ class SubscribeEditor : public QDialog, private Ui::w_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();

View File

@ -1,7 +1,8 @@
#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)
{ {
@ -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,16 +109,12 @@ 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()
@ -131,10 +133,13 @@ void ConnectionInfoWidget::on_duplicateBtn_clicked()
// 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);
@ -145,9 +150,9 @@ void ConnectionInfoWidget::on_duplicateBtn_clicked()
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,10 +1,13 @@
#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
@ -36,4 +39,3 @@ class ConnectionInfoWidget : public QWidget, private Ui::ConnectionInfoWidget
ConnectionId connectionId = NullConnectionId; ConnectionId connectionId = NullConnectionId;
GroupId groupId = NullGroupId; GroupId groupId = NullGroupId;
}; };

View File

@ -1,4 +1,5 @@
#include "ConnectionItemWidget.hpp" #include "ConnectionItemWidget.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
ConnectionItemWidget::ConnectionItemWidget(QWidget *parent) : QWidget(parent), connectionId("null"), groupId("null") ConnectionItemWidget::ConnectionItemWidget(QWidget *parent) : QWidget(parent), connectionId("null"), groupId("null")
@ -21,9 +22,9 @@ ConnectionItemWidget::ConnectionItemWidget(const ConnectionId &id, QWidget *pare
// TODO // TODO
auto latency = ConnectionManager->GetConnectionLatency(id); auto latency = ConnectionManager->GetConnectionLatency(id);
if (latency == 0) { if (latency == 0) { latencyLabel->setText(tr("Not Tested")); }
latencyLabel->setText(tr("Not Tested")); else
} else { {
latencyLabel->setText(QSTRN(latency) + " " + tr("ms")); latencyLabel->setText(QSTRN(latency) + " " + tr("ms"));
} }
@ -52,16 +53,17 @@ ConnectionItemWidget::ConnectionItemWidget(const GroupId &id, QWidget *parent) :
void ConnectionItemWidget::BeginConnection() void ConnectionItemWidget::BeginConnection()
{ {
if (itemType == NODE_ITEM) { if (itemType == NODE_ITEM) { ConnectionManager->StartConnection(connectionId); }
ConnectionManager->StartConnection(connectionId); else
} else { {
LOG(MODULE_UI, "Trying to start a non-connection entry, this call is illegal.") LOG(MODULE_UI, "Trying to start a non-connection entry, this call is illegal.")
} }
} }
void ConnectionItemWidget::OnConnected(const ConnectionId &id) void ConnectionItemWidget::OnConnected(const ConnectionId &id)
{ {
if (id == connectionId) { if (id == connectionId)
{
connNameLabel->setText("" + originalConnectionName); connNameLabel->setText("" + originalConnectionName);
LOG(MODULE_UI, "OnConnected signal received for: " + id.toString()) LOG(MODULE_UI, "OnConnected signal received for: " + id.toString())
emit RequestWidgetFocus(this); emit RequestWidgetFocus(this);
@ -70,34 +72,33 @@ void ConnectionItemWidget::OnConnected(const ConnectionId &id)
void ConnectionItemWidget::OnDisConnected(const ConnectionId &id) void ConnectionItemWidget::OnDisConnected(const ConnectionId &id)
{ {
if (id == connectionId) { if (id == connectionId) { connNameLabel->setText(originalConnectionName); }
connNameLabel->setText(originalConnectionName);
}
} }
void ConnectionItemWidget::OnConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp, const quint64 totalDown) void ConnectionItemWidget::OnConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed,
const quint64 totalUp, const quint64 totalDown)
{ {
Q_UNUSED(upSpeed) Q_UNUSED(upSpeed)
Q_UNUSED(downSpeed) Q_UNUSED(downSpeed)
if (id == connectionId) { if (id == connectionId) { dataLabel->setText(FormatBytes(totalUp) + " / " + FormatBytes(totalDown)); }
dataLabel->setText(FormatBytes(totalUp) + " / " + FormatBytes(totalDown));
}
} }
void ConnectionItemWidget::OnLatencyTestStart(const ConnectionId &id) void ConnectionItemWidget::OnLatencyTestStart(const ConnectionId &id)
{ {
if (id == connectionId) { if (id == connectionId) { latencyLabel->setText(tr("Testing...")); }
latencyLabel->setText(tr("Testing..."));
}
} }
void ConnectionItemWidget::OnLatencyTestFinished(const ConnectionId &id, const uint average) void ConnectionItemWidget::OnLatencyTestFinished(const ConnectionId &id, const uint average)
{ {
if (id == connectionId) { if (id == connectionId)
if (average == 0) { {
if (average == 0)
{
latencyLabel->setText(tr("Error")); latencyLabel->setText(tr("Error"));
RED(latencyLabel) RED(latencyLabel)
} else { }
else
{
latencyLabel->setText(QSTRN(average) + tr("ms")); latencyLabel->setText(QSTRN(average) + tr("ms"));
BLACK(latencyLabel) BLACK(latencyLabel)
} }

View File

@ -1,15 +1,19 @@
#pragma once #pragma once
#include <QWidget>
#include "ui_ConnectionItemWidget.h"
#include "core/handler/ConnectionHandler.hpp" #include "core/handler/ConnectionHandler.hpp"
#include "ui_ConnectionItemWidget.h"
enum ITEM_TYPE { #include <QWidget>
enum ITEM_TYPE
{
GROUP_HEADER_ITEM, GROUP_HEADER_ITEM,
NODE_ITEM NODE_ITEM
}; };
class ConnectionItemWidget : public QWidget, private Ui::ConnectionWidget class ConnectionItemWidget
: public QWidget
, private Ui::ConnectionWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -24,9 +28,9 @@ class ConnectionItemWidget : public QWidget, private Ui::ConnectionWidget
auto searchString = arg.toLower(); auto searchString = arg.toLower();
auto headerMatched = ConnectionManager->GetDisplayName(groupId).toLower().contains(arg); auto headerMatched = ConnectionManager->GetDisplayName(groupId).toLower().contains(arg);
if (itemType != NODE_ITEM) { if (itemType != NODE_ITEM) { return headerMatched; }
return headerMatched; else
} else { {
return headerMatched || ConnectionManager->GetDisplayName(connectionId).toLower().contains(searchString); return headerMatched || ConnectionManager->GetDisplayName(connectionId).toLower().contains(searchString);
} }
} }
@ -41,11 +45,13 @@ class ConnectionItemWidget : public QWidget, private Ui::ConnectionWidget
signals: signals:
void RequestWidgetFocus(const ConnectionItemWidget *me); void RequestWidgetFocus(const ConnectionItemWidget *me);
private slots: private slots:
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,
const quint64 totalDown);
void OnConnected(const ConnectionId &id); void OnConnected(const ConnectionId &id);
void OnDisConnected(const ConnectionId &id); void OnDisConnected(const ConnectionId &id);
void OnLatencyTestStart(const ConnectionId &id); void OnLatencyTestStart(const ConnectionId &id);
void OnLatencyTestFinished(const ConnectionId &id, const uint average); void OnLatencyTestFinished(const ConnectionId &id, const uint average);
private: private:
QString originalConnectionName; QString originalConnectionName;
explicit ConnectionItemWidget(QWidget *parent = nullptr); explicit ConnectionItemWidget(QWidget *parent = nullptr);

View File

@ -49,17 +49,17 @@
****************************************************************************/ ****************************************************************************/
#include "textedit.h" #include "textedit.h"
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QApplication>
#include <QCompleter> #include <QCompleter>
#include <QKeyEvent> #include <QKeyEvent>
#include <QAbstractItemView>
#include <QtDebug>
#include <QApplication>
#include <QModelIndex> #include <QModelIndex>
#include <QAbstractItemModel>
#include <QScrollBar> #include <QScrollBar>
#include <QtDebug>
TextEdit::TextEdit(QWidget *parent) TextEdit::TextEdit(QWidget *parent) : QTextEdit(parent)
: QTextEdit(parent)
{ {
setPlainText(tr("This TextEdit provides autocompletions for words that have more than" setPlainText(tr("This TextEdit provides autocompletions for words that have more than"
" 3 characters. You can trigger autocompletion using ") + " 3 characters. You can trigger autocompletion using ") +
@ -72,14 +72,11 @@ TextEdit::~TextEdit()
void TextEdit::setCompleter(QCompleter *completer) void TextEdit::setCompleter(QCompleter *completer)
{ {
if (c) if (c) c->disconnect(this);
c->disconnect(this);
c = completer; c = completer;
if (!c) { if (!c) { return; }
return;
}
c->setWidget(this); c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion); c->setCompletionMode(QCompleter::PopupCompletion);
@ -94,8 +91,7 @@ QCompleter *TextEdit::completer() const
void TextEdit::insertCompletion(const QString &completion) void TextEdit::insertCompletion(const QString &completion)
{ {
if (c->widget() != this) if (c->widget() != this) return;
return;
QTextCursor tc = textCursor(); QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length(); int extra = completion.length() - c->completionPrefix().length();
@ -121,43 +117,43 @@ void TextEdit::focusInEvent(QFocusEvent *e)
void TextEdit::keyPressEvent(QKeyEvent *e) void TextEdit::keyPressEvent(QKeyEvent *e)
{ {
if (c && c->popup()->isVisible()) { if (c && c->popup()->isVisible())
{
// The following keys are forwarded by the completer to the widget // The following keys are forwarded by the completer to the widget
switch (e->key()) { switch (e->key())
{
case Qt::Key_Enter: case Qt::Key_Enter:
case Qt::Key_Return: case Qt::Key_Return:
case Qt::Key_Escape: case Qt::Key_Escape:
case Qt::Key_Tab: case Qt::Key_Tab:
case Qt::Key_Backtab: case Qt::Key_Backtab: e->ignore(); return; // let the completer do default behavior
e->ignore();
return; // let the completer do default behavior
default: default: break;
break;
} }
} }
const bool isShortcut = (e->modifiers().testFlag(Qt::ControlModifier) && e->key() == Qt::Key_Space); // CTRL+Space const bool isShortcut = (e->modifiers().testFlag(Qt::ControlModifier) && e->key() == Qt::Key_Space); // CTRL+Space
if (!c || !isShortcut) // do not process the shortcut when we have a completer if (!c || !isShortcut) // do not process the shortcut when we have a
// completer
QTextEdit::keyPressEvent(e); QTextEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers().testFlag(Qt::ControlModifier) || const bool ctrlOrShift = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::ShiftModifier);
e->modifiers().testFlag(Qt::ShiftModifier);
if (!c || (ctrlOrShift && e->text().isEmpty())) if (!c || (ctrlOrShift && e->text().isEmpty())) return;
return;
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
const bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; const bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor(); QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length() < 2 || eow.contains(e->text().right(1)))) { if (!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length() < 2 || eow.contains(e->text().right(1))))
{
c->popup()->hide(); c->popup()->hide();
return; return;
} }
if (completionPrefix != c->completionPrefix()) { if (completionPrefix != c->completionPrefix())
{
c->setCompletionPrefix(completionPrefix); c->setCompletionPrefix(completionPrefix);
c->popup()->setCurrentIndex(c->completionModel()->index(0, 0)); c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
} }

View File

@ -85,4 +85,3 @@ private:
//! [0] //! [0]
#endif // TEXTEDIT_H #endif // TEXTEDIT_H

View File

@ -1,9 +1,9 @@
#include "StreamSettingsWidget.hpp" #include "StreamSettingsWidget.hpp"
#include "common/QvHelpers.hpp" #include "common/QvHelpers.hpp"
#include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_JsonEditor.hpp"
StreamSettingsWidget::StreamSettingsWidget(QWidget *parent) : StreamSettingsWidget::StreamSettingsWidget(QWidget *parent) : QWidget(parent)
QWidget(parent)
{ {
setupUi(this); setupUi(this);
} }
@ -34,9 +34,8 @@ void StreamSettingsWidget::SetStreamObject(StreamSettingsObject sso)
wsPathTxt->setText(stream.wsSettings.path); wsPathTxt->setText(stream.wsSettings.path);
QString wsHeaders; QString wsHeaders;
for (auto item = stream.wsSettings.headers.begin(); item != stream.wsSettings.headers.end(); item++) { for (auto item = stream.wsSettings.headers.begin(); item != stream.wsSettings.headers.end(); item++)
wsHeaders += item.key() + "|" + item.value() + NEWLINE; { wsHeaders += item.key() + "|" + item.value() + NEWLINE; }
}
wsHeadersTxt->setPlainText(wsHeaders); wsHeadersTxt->setPlainText(wsHeaders);
// mKCP // mKCP
@ -60,7 +59,6 @@ void StreamSettingsWidget::SetStreamObject(StreamSettingsObject sso)
soMarkSpinBox->setValue(stream.sockopt.mark); soMarkSpinBox->setValue(stream.sockopt.mark);
} }
void StreamSettingsWidget::on_transportCombo_currentIndexChanged(int index) void StreamSettingsWidget::on_transportCombo_currentIndexChanged(int index)
{ {
v2rayStackView->setCurrentIndex(index); v2rayStackView->setCurrentIndex(index);
@ -73,29 +71,33 @@ void StreamSettingsWidget::on_httpPathTxt_textEdited(const QString &arg1)
void StreamSettingsWidget::on_httpHostTxt_textChanged() void StreamSettingsWidget::on_httpHostTxt_textChanged()
{ {
try { try
{
QStringList hosts = httpHostTxt->toPlainText().replace("\r", "").split("\n"); QStringList hosts = httpHostTxt->toPlainText().replace("\r", "").split("\n");
stream.httpSettings.host.clear(); stream.httpSettings.host.clear();
for (auto host : hosts) { for (auto host : hosts)
if (!host.trimmed().isEmpty()) { {
stream.httpSettings.host.push_back(host.trimmed()); if (!host.trimmed().isEmpty()) { stream.httpSettings.host.push_back(host.trimmed()); }
}
} }
BLACK(httpHostTxt) BLACK(httpHostTxt)
} catch (...) { }
catch (...)
{
RED(httpHostTxt) RED(httpHostTxt)
} }
} }
void StreamSettingsWidget::on_wsHeadersTxt_textChanged() void StreamSettingsWidget::on_wsHeadersTxt_textChanged()
{ {
try { try
{
QStringList headers = SplitLines(wsHeadersTxt->toPlainText()); QStringList headers = SplitLines(wsHeadersTxt->toPlainText());
stream.wsSettings.headers.clear(); stream.wsSettings.headers.clear();
for (auto header : headers) { for (auto header : headers)
{
if (header.isEmpty()) continue; if (header.isEmpty()) continue;
auto index = header.indexOf("|"); auto index = header.indexOf("|");
@ -108,12 +110,13 @@ void StreamSettingsWidget::on_wsHeadersTxt_textChanged()
} }
BLACK(wsHeadersTxt) BLACK(wsHeadersTxt)
} catch (...) { }
catch (...)
{
RED(wsHeadersTxt) RED(wsHeadersTxt)
} }
} }
void StreamSettingsWidget::on_tcpRequestDefBtn_clicked() void StreamSettingsWidget::on_tcpRequestDefBtn_clicked()
{ {
tcpRequestTxt->clear(); tcpRequestTxt->clear();
@ -130,7 +133,8 @@ void StreamSettingsWidget::on_tcpRequestDefBtn_clicked()
void StreamSettingsWidget::on_tcpRespDefBtn_clicked() void StreamSettingsWidget::on_tcpRespDefBtn_clicked()
{ {
tcpRespTxt->clear(); tcpRespTxt->clear();
tcpRespTxt->setPlainText("{\"version\":\"1.1\",\"status\":\"200\",\"reason\":\"OK\",\"headers\":{\"Content-Type\":[\"application/octet-stream\",\"video/mpeg\"],\"Transfer-Encoding\":[\"chunked\"],\"Connection\":[\"keep-alive\"],\"Pragma\":\"no-cache\"}}"); tcpRespTxt->setPlainText(
"{\"version\":\"1.1\",\"status\":\"200\",\"reason\":\"OK\",\"headers\":{\"Content-Type\":[\"application/octet-stream\",\"video/mpeg\"],\"Transfer-Encoding\":[\"chunked\"],\"Connection\":[\"keep-alive\"],\"Pragma\":\"no-cache\"}}");
} }
void StreamSettingsWidget::on_tlsCB_stateChanged(int arg1) void StreamSettingsWidget::on_tlsCB_stateChanged(int arg1)

View File

@ -4,7 +4,9 @@
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "ui_StreamSettingsWidget.h" #include "ui_StreamSettingsWidget.h"
class StreamSettingsWidget : public QWidget, private Ui::StreamSettingsWidget class StreamSettingsWidget
: public QWidget
, private Ui::StreamSettingsWidget
{ {
Q_OBJECT Q_OBJECT
@ -77,4 +79,3 @@ class StreamSettingsWidget : public QWidget, private Ui::StreamSettingsWidget
private: private:
StreamSettingsObject stream; StreamSettingsObject stream;
}; };