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
#include "base/models/QvConfigModel.hpp"
#include "base/models/QvRuntimeConfig.hpp"
#include "base/models/QvStartupConfig.hpp"
#include "base/models/QvConfigModel.hpp"
#include <QTranslator>
@ -18,4 +18,4 @@ namespace Qv2ray
inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
//
inline std::unique_ptr<QTranslator> Qv2rayTranslator;
}
} // namespace Qv2ray

View File

@ -4,27 +4,27 @@
#define XTOSTRUCT_QT
#endif
//
#include <QtCore>
#include <QtGui>
#include <QApplication>
#include <QMap>
#include <vector>
#include <QtCore>
#include <QtGui>
#include <algorithm>
#include <iostream>
#include <ctime>
#include <iostream>
#include <optional>
#include <vector>
// Base support.
#include "base/Qv2rayLog.hpp"
#include "base/Qv2rayFeatures.hpp"
#include "base/JsonHelpers.hpp"
#include "base/GlobalInstances.hpp"
#include "base/JsonHelpers.hpp"
#include "base/Qv2rayFeatures.hpp"
#include "base/Qv2rayLog.hpp"
// Code Models
#include "base/models/QvSafeType.hpp"
#include "base/models/CoreObjectModels.hpp"
#include "base/models/QvConfigModel.hpp"
#include "base/models/QvConfigIdentifier.hpp"
#include "base/models/QvStartupConfig.hpp"
#include "base/models/QvConfigModel.hpp"
#include "base/models/QvRuntimeConfig.hpp"
#include "base/models/QvSafeType.hpp"
#include "base/models/QvStartupConfig.hpp"
using namespace std;
using namespace std::chrono;
@ -100,10 +100,10 @@ using namespace Qv2ray::base::objects::transfer;
_temp.setColor(QPalette::Text, Qt::red); \
obj->setPalette(_temp);
#define BLACK(obj) \
obj->setPalette(QWidget::palette());
#define BLACK(obj) 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 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 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
{
@ -129,4 +130,4 @@ namespace Qv2ray
isExiting = true;
QApplication::quit();
}
}
} // namespace Qv2ray

View File

@ -1,5 +1,7 @@
#include "Qv2rayLog.hpp"
#include "base/GlobalInstances.hpp"
#include <iostream>
namespace Qv2ray::base
@ -15,18 +17,22 @@ namespace Qv2ray::base
auto logString = "[" + module + "]: " + log;
auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " ");
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) {
logString = logString.prepend(funcPrepend);
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) { logString = logString.prepend(funcPrepend); }
}
} else {
else
{
// We only process DEBUG log in Release mode
if (type == QV2RAY_LOG_DEBUG) {
if (StartupOption.debugLog) {
logString = logString.prepend(funcPrepend);
} else {
// Discard debug log in non-debug Qv2ray version with no-debugLog mode.
if (type == QV2RAY_LOG_DEBUG)
{
if (StartupOption.debugLog) { logString = logString.prepend(funcPrepend); }
else
{
// Discard debug log in non-debug Qv2ray version with
// no-debugLog mode.
return;
}
}
@ -50,4 +56,4 @@ namespace Qv2ray::base
__purgerBuffer->clear();
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);
const QString readLastLog();
}
} // namespace Qv2ray::base
#define NEWLINE "\r\n"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,8 @@
namespace Qv2ray::common
{
enum CommandLineParseResult {
enum CommandLineParseResult
{
CommandLineOk,
CommandLineError,
CommandLineVersionRequested,
@ -30,6 +31,6 @@ namespace Qv2ray::common
QCommandLineOption helpOption;
QCommandLineOption versionOption;
};
}
} // namespace Qv2ray::common
using namespace Qv2ray::common;

View File

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

View File

@ -18,10 +18,10 @@
#pragma once
#include <QObject>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
namespace Qv2ray::common
{
@ -50,6 +50,7 @@ namespace Qv2ray::common
void onRequestFinished_p();
private slots:
void onReadyRead();
private:
QByteArray data;
QUrl url;
@ -57,6 +58,6 @@ namespace Qv2ray::common
QNetworkRequest request;
QNetworkAccessManager accessManager;
};
}
} // namespace Qv2ray::common
using namespace Qv2ray::common;

View File

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

View File

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

View File

@ -25,11 +25,11 @@
#pragma once
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
class QJsonModel;
class QJsonItem;
@ -51,20 +51,15 @@ class QJsonTreeItem
QString value() const;
QJsonValue::Type type() const;
static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0);
protected:
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<QJsonTreeItem *> mChilds;
QJsonTreeItem *mParent;
};
//---------------------------------------------------
@ -96,6 +91,4 @@ class QJsonModel : public QAbstractItemModel
QJsonTreeItem *mRootItem;
QStringList mHeaders;
};

View File

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

View File

@ -1,10 +1,13 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#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_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_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_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])*)"
namespace Qv2ray::common
@ -19,7 +22,8 @@ namespace Qv2ray::common
//
void QvMessageBoxWarn(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(QFile *source);
@ -68,9 +72,7 @@ namespace Qv2ray::common
{
QString t = str;
t.truncate(limit);
return (limit == -1 || str.length() < limit)
? str
: (t + suffix);
return (limit == -1 || str.length() < limit) ? str : (t + suffix);
}
namespace validation
@ -92,7 +94,7 @@ namespace Qv2ray::common
{
return IsIPv4Address(addr) || IsIPv6Address(addr);
}
}
} // namespace validation
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);
return QString(MY_TIME);
}
}
} // namespace Qv2ray::common
using namespace Qv2ray::common;

View File

@ -1,13 +1,14 @@
#include "QvAutoLaunch.hpp"
#include <QSettings>
#include <QApplication>
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
#include <QTextStream>
// macOS headers (possibly OBJ-c)
#if defined(Q_OS_MAC)
#include <CoreServices/CoreServices.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#endif
namespace Qv2ray::components::autolaunch
{
@ -34,8 +35,10 @@ namespace Qv2ray::components::autolaunch
}
#elif defined Q_OS_MAC
// From 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.
// From
// 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;
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
@ -49,16 +52,16 @@ namespace Qv2ray::components::autolaunch
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
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);
CFURLRef itemUrlRef = NULL;
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef)
{
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
returnValue = true;
}
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { returnValue = true; }
CFRelease(itemUrlRef);
}
@ -86,16 +89,20 @@ namespace Qv2ray::components::autolaunch
QString appName = QApplication::applicationName();
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
if (enable) {
if (enable)
{
QString strAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
reg.setValue(appName, strAppPath);
} else {
}
else
{
reg.remove(appName);
}
}
#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();
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
@ -104,29 +111,30 @@ namespace Qv2ray::components::autolaunch
if (loginItems && enable)
{
// Insert an item to the list.
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems,
kLSSharedFileListItemLast, 0, 0,
urlRef, 0, 0);
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, 0, 0, urlRef, 0, 0);
if (item)
CFRelease(item);
if (item) CFRelease(item);
CFRelease(loginItems);
} else if (loginItems && !enable)
}
else if (loginItems && !enable)
{
// We need to iterate over the items and check which one is "ours".
UInt32 seedValue;
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
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);
CFURLRef itemUrlRef = NULL;
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef)
{
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo)
{
LSSharedFileListItemRemove(loginItems, item); // remove it!
}
@ -143,22 +151,27 @@ namespace Qv2ray::components::autolaunch
}
#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 userAutoStartPath = getUserAutostartDir_private();
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
if (enable)
{
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) {
// qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath;
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath))
{
// qCWarning(lcUtility) << "Could not create autostart folder"
// << userAutoStartPath;
return;
}
QFile iniFile(desktopFileLocation);
if (!iniFile.open(QIODevice::WriteOnly)) {
// qCWarning(lcUtility) << "Could not write auto start entry" << desktopFileLocation;
if (!iniFile.open(QIODevice::WriteOnly))
{
// qCWarning(lcUtility) << "Could not write auto start entry" <<
// desktopFileLocation;
return;
}
@ -174,13 +187,15 @@ namespace Qv2ray::components::autolaunch
<< QLatin1String("Type=") << "Application" << endl
<< QLatin1String("StartupNotify=") << "false" << endl
<< QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl;
} else
}
else
{
if (!QFile::remove(desktopFileLocation)) {
// qCWarning(lcUtility) << "Could not remove autostart desktop file";
if (!QFile::remove(desktopFileLocation))
{
// qCWarning(lcUtility) << "Could not remove autostart desktop
// file";
}
}
}
#endif
}
} // namespace Qv2ray::components::autolaunch

View File

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

View File

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

View File

@ -1,26 +1,25 @@
#include "ICMPPinger.hpp"
ICMPPinger::ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT) {
ICMPPinger::ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT)
{
// create icmp handle
if ((this->hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE) {
throw "IcmpCreateFile failed";
}
if ((this->hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE) { throw "IcmpCreateFile failed"; }
// remember the timeout
this->timeout = timeout;
}
ICMPPinger::~ICMPPinger() {
ICMPPinger::~ICMPPinger()
{
// release the handle on destruction
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
const auto addr = inet_addr(ipAddr.c_str());
if (addr == INADDR_NONE) {
return std::pair(std::nullopt, "invalid ip address: " + ipAddr);
}
if (addr == INADDR_NONE) { return std::pair(std::nullopt, "invalid ip address: " + ipAddr); }
// request buffer
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);
// ret == 0: failed
if (ret == 0) {
return std::pair(std::nullopt, "IcmpSendEcho returned error");
}
if (ret == 0) { return std::pair(std::nullopt, "IcmpSendEcho returned error"); }
// read round-trip time
PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY) bufRecv.get();

View File

@ -6,16 +6,15 @@
* License: WTFPL
*/
#include<winsock2.h>
#include<iphlpapi.h>
#include <icmpapi.h>
#include<utility>
#include<optional>
#include <iphlpapi.h>
#include <memory>
#include <optional>
#include <utility>
#include <winsock2.h>
class ICMPPinger {
class ICMPPinger
{
public:
ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
~ICMPPinger();

View File

@ -21,29 +21,23 @@ namespace Qv2ray::components::pac
bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines
bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines
if (originLine[endPosition] == '\n') {
endPosition -= 1;
}
if (originLine[endPosition] == '\n') { endPosition -= 1; }
if (originLine.find("http://") != string::npos) {
startPosition += 8;
} else if (originLine.find("https://") != string::npos) {
if (originLine.find("http://") != string::npos) { startPosition += 8; }
else if (originLine.find("https://") != string::npos)
{
startPosition += 9;
}
// Skip unrelated lines
if (skipRule1 || skipRule2 || skipRule3 || skipRule4) {
return "";
} else if (passRule2) {
if (passRule1) {
startPosition += originLine.find_last_of("|") + 1;
}
if (skipRule1 || skipRule2 || skipRule3 || skipRule4) { return ""; }
else if (passRule2)
{
if (passRule1) { startPosition += originLine.find_last_of("|") + 1; }
if (originLine[startPosition] == '\n') startPosition += 1;
for (size_t i = startPosition; i < endPosition; ++i) {
returnBuffer += originLine[i];
}
for (size_t i = startPosition; i < endPosition; ++i) { returnBuffer += originLine[i]; }
}
return returnBuffer;
@ -56,15 +50,15 @@ namespace Qv2ray::components::pac
string writeBuffer;
string domainListCache = "";
for (size_t i = 0; i < rawFileContent.size(); ++i) {
for (size_t i = 0; i < rawFileContent.size(); ++i)
{
readBuffer += rawFileContent[i];
if (rawFileContent[i + 1] == '\n') {
if (rawFileContent[i + 1] == '\n')
{
writeBuffer = getRawDomain(readBuffer);
if (writeBuffer != "") {
domainListCache += writeBuffer + "\n";
}
if (writeBuffer != "") { domainListCache += writeBuffer + "\n"; }
readBuffer = "";
i += 1;
@ -79,12 +73,15 @@ namespace Qv2ray::components::pac
outputContent += "var domains = {\n";
// Read and process output content line by line
while (rotatorTwo < domainListCache.size()) {
while (true) {
while (rotatorTwo < domainListCache.size())
{
while (true)
{
// Get Domain
readDomainBuffer += domainListCache[rotatorTwo];
if (domainListCache[rotatorTwo + 1] == '\n') {
if (domainListCache[rotatorTwo + 1] == '\n')
{
rotatorTwo += 2;
break;
}
@ -94,7 +91,8 @@ namespace Qv2ray::components::pac
// Format
if (!isFirstLine) outputContent += ",\n";
else isFirstLine = false;
else
isFirstLine = false;
outputContent += "\t\"";
outputContent += readDomainBuffer;
@ -103,31 +101,17 @@ namespace Qv2ray::components::pac
}
// End Message
outputContent +=
NEWLINE "};"
NEWLINE ""
NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
NEWLINE " var direct = 'DIRECT;';"
NEWLINE " function FindProxyForURL(url, host) {"
NEWLINE " var suffix;"
NEWLINE " var pos = host.lastIndexOf('.');"
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
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 " }";
outputContent += NEWLINE "};" NEWLINE "" NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
NEWLINE " var direct = 'DIRECT;';" NEWLINE " function FindProxyForURL(url, host) {" NEWLINE
" var suffix;" NEWLINE " var pos = host.lastIndexOf('.');" NEWLINE
" pos = host.lastIndexOf('.', pos - 1);" 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);
}
}
} // namespace Qv2ray::components::pac

View File

@ -1,8 +1,9 @@
#include "QvPACHandler.hpp"
#include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
#include "qhttprequest.h"
#include "qhttpresponse.h"
#include "core/CoreUtils.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::components::pac
{
@ -13,9 +14,7 @@ namespace Qv2ray::components::pac
}
PACServer::~PACServer()
{
if (isStarted) {
pacServer.close();
}
if (isStarted) { pacServer.close(); }
}
void PACServer::SetProxyString(const QString &proxyString)
{
@ -36,10 +35,13 @@ namespace Qv2ray::components::pac
//
auto result = pacServer.listen(QHostAddress(address), static_cast<ushort>(port));
if (result) {
if (result)
{
isStarted = true;
DEBUG(MODULE_PROXY, "Started PAC handler")
} else {
}
else
{
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"));
}
@ -47,7 +49,8 @@ namespace Qv2ray::components::pac
void PACServer::StopServer()
{
if (isStarted) {
if (isStarted)
{
pacServer.close();
DEBUG(MODULE_PROXY, "PAC Handler stopped.")
isStarted = false;
@ -58,22 +61,28 @@ namespace Qv2ray::components::pac
{
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.")
//
rsp->setHeader("Content-Type", "application/javascript; charset=utf-8");
rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK);
rsp->end(pacContent.toUtf8());
DEBUG(MODULE_PROXY, "Serving a pac file...")
} else {
}
else
{
rsp->writeHead(QHttpResponse::StatusCode::STATUS_NOT_FOUND);
rsp->end("NOT FOUND");
}
} else {
}
else
{
rsp->writeHead(QHttpResponse::StatusCode::STATUS_METHOD_NOT_ALLOWED);
rsp->end("PAC ONLY SUPPORT GET");
}
}
}
} // namespace Qv2ray::components::pac

View File

@ -1,5 +1,6 @@
#pragma once
#include "qhttpserver.h"
#include <QObject>
#include <memory>
@ -27,7 +28,7 @@ namespace Qv2ray::components::pac
QString pacContent;
QString proxyString;
};
}
} // namespace Qv2ray::components::pac
using namespace Qv2ray::components;
using namespace Qv2ray::components::pac;

View File

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

View File

@ -9,8 +9,7 @@ namespace Qv2ray::components::plugins
namespace Toolbar
{
/// NO NOT CHANGE THE ORDER
static const QMap<int, QString> NetSpeedPluginMessages {
{ 0, QObject::tr("Custom Text")},
static const QMap<int, QString> NetSpeedPluginMessages{ { 0, QObject::tr("Custom Text") },
// Current Status
{ 101, QObject::tr("Current Time") },
{ 102, QObject::tr("Current Date") },
@ -26,8 +25,7 @@ namespace Qv2ray::components::plugins
{ 301, QObject::tr("Total Uploaded Data") },
{ 302, QObject::tr("Total Downloaded Data") },
{ 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 StopProcessingPlugins();
#ifdef Q_OS_WIN
@ -35,7 +33,7 @@ namespace Qv2ray::components::plugins
{
void StartNamedPipeThread();
void KillNamedPipeThread();
}
} // namespace _win
#endif
#ifdef Q_OS_LINUX
namespace _linux
@ -45,13 +43,12 @@ namespace Qv2ray::components::plugins
void StartMessageQThread();
void StopMessageQThread();
}
} // namespace _linux
#endif
QString GetAnswerToRequest(const QString &pchRequest);
}
}
} // namespace Toolbar
} // namespace Qv2ray::components::plugins
using namespace Qv2ray::components;
using namespace Qv2ray::components::plugins::Toolbar;

View File

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

View File

@ -1,7 +1,8 @@
#include <QtCore>
#ifdef Q_OS_WIN
#include "components/plugins/toolbar/QvToolbar.hpp"
#include "common/QvHelpers.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp"
#include <windows.h>
namespace Qv2ray::components::plugins::Toolbar
{
@ -25,10 +26,13 @@ namespace Qv2ray::components::plugins::Toolbar
{
auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr);
if (hThread == nullptr) {
if (hThread == nullptr)
{
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
return;
} else CloseHandle(hThread);
}
else
CloseHandle(hThread);
}
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam)
@ -39,26 +43,36 @@ namespace Qv2ray::components::plugins::Toolbar
HANDLE hPipe = INVALID_HANDLE_VALUE;
auto lpszPipename = QString(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN).toStdWString();
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);
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);
if (hPipe == INVALID_HANDLE_VALUE) {
if (hPipe == INVALID_HANDLE_VALUE)
{
LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError()))
return static_cast<DWORD>(-1);
}
fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected) {
if (fConnected)
{
LOG(MODULE_PLUGIN, "Client connected, creating a processing thread")
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
if (ThreadHandle == nullptr) {
if (ThreadHandle == nullptr)
{
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
return static_cast<DWORD>(-1);
} else CloseHandle(ThreadHandle);
} else CloseHandle(hPipe);
}
else
CloseHandle(ThreadHandle);
}
else
CloseHandle(hPipe);
}
return 0;
@ -71,13 +85,16 @@ namespace Qv2ray::components::plugins::Toolbar
HANDLE hPipe = static_cast<HANDLE>(lpvParam);
TCHAR pchRequest[BUFSIZE] = { 0 };
while (!isExiting) {
while (!isExiting)
{
fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr);
if (!fSuccess || cbBytesRead == 0) {
if (GetLastError() == ERROR_BROKEN_PIPE) {
LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
} else {
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{ LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) }
else
{
LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
}
@ -87,17 +104,20 @@ namespace Qv2ray::components::plugins::Toolbar
auto req = QString::fromStdWString(pchRequest);
QString replyQString = "{}";
if (!isExiting) {
if (!isExiting)
{
replyQString = GetAnswerToRequest(req);
//
// REPLY as std::string
std::string pchReply = replyQString.toUtf8().constData();
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);
if (!fSuccess || cbReplyBytes != cbWritten) {
if (!fSuccess || cbReplyBytes != cbWritten)
{
LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
break;
}
@ -109,6 +129,6 @@ namespace Qv2ray::components::plugins::Toolbar
CloseHandle(hPipe);
return 1;
}
}
}
} // namespace _win
} // namespace Qv2ray::components::plugins::Toolbar
#endif

View File

@ -1,7 +1,9 @@
#include "QvProxyConfigurator.hpp"
#include "common/QvHelpers.hpp"
#ifdef Q_OS_WIN
#include "wininet.h"
#include <windows.h>
#endif
@ -23,11 +25,10 @@ namespace Qv2ray::components::proxy
QStringList result;
// 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.
if (!lines[i].contains("*")) {
result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]);
}
if (!lines[i].contains("*")) { result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]); }
}
LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
@ -36,7 +37,8 @@ namespace Qv2ray::components::proxy
#endif
#ifdef Q_OS_WIN
#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
bool __QueryProxyOptions()
@ -57,55 +59,36 @@ namespace Qv2ray::components::proxy
List.dwOptionError = 0;
List.pOptions = Option;
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
LOG(MODULE_PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError()))
}
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize))
{ LOG(MODULE_PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError())) }
LOG(MODULE_PROXY, "System default proxy info:")
if (Option[0].Value.pszValue != nullptr) {
LOG(MODULE_PROXY, QString::fromWCharArray(Option[0].Value.pszValue))
}
if (Option[0].Value.pszValue != nullptr) { LOG(MODULE_PROXY, QString::fromWCharArray(Option[0].Value.pszValue)) }
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) {
LOG(MODULE_PROXY, "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") }
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) {
LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_DETECT")
}
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) { LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_DETECT") }
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) {
LOG(MODULE_PROXY, "PROXY_TYPE_DIRECT")
}
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) { LOG(MODULE_PROXY, "PROXY_TYPE_DIRECT") }
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) {
LOG(MODULE_PROXY, "PROXY_TYPE_PROXY")
}
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) { LOG(MODULE_PROXY, "PROXY_TYPE_PROXY") }
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
LOG(MODULE_PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError()))
}
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize))
{ LOG(MODULE_PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError())) }
if (Option[4].Value.pszValue != nullptr) {
LOG(MODULE_PROXY, QString::fromStdWString(Option[4].Value.pszValue))
}
if (Option[4].Value.pszValue != nullptr) { LOG(MODULE_PROXY, QString::fromStdWString(Option[4].Value.pszValue)) }
INTERNET_VERSION_INFO Version;
nSize = sizeof(INTERNET_VERSION_INFO);
InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize);
if (Option[0].Value.pszValue != nullptr) {
GlobalFree(Option[0].Value.pszValue);
}
if (Option[0].Value.pszValue != nullptr) { GlobalFree(Option[0].Value.pszValue); }
if (Option[3].Value.pszValue != nullptr) {
GlobalFree(Option[3].Value.pszValue);
}
if (Option[3].Value.pszValue != nullptr) { GlobalFree(Option[3].Value.pszValue); }
if (Option[4].Value.pszValue != nullptr) {
GlobalFree(Option[4].Value.pszValue);
}
if (Option[4].Value.pszValue != nullptr) { GlobalFree(Option[4].Value.pszValue); }
return false;
}
@ -119,14 +102,16 @@ namespace Qv2ray::components::proxy
// NULL == LAN, otherwise connectoid name.
list.pszConnection = nullptr;
if (isPAC) {
if (isPAC)
{
LOG(MODULE_PROXY, "Setting system proxy for PAC")
//
list.dwOptionCount = 2;
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
// Ensure that the memory was allocated.
if (nullptr == list.pOptions) {
if (nullptr == list.pOptions)
{
// Return FALSE if the memory wasn't allocated.
return FALSE;
}
@ -137,15 +122,15 @@ namespace Qv2ray::components::proxy
// Set proxy name.
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
list.pOptions[1].Value.pszValue = proxy_full_addr;
} else {
}
else
{
LOG(MODULE_PROXY, "Setting system proxy for Global Proxy")
//
list.dwOptionCount = 3;
list.pOptions = new INTERNET_PER_CONN_OPTION[3];
if (nullptr == list.pOptions) {
return false;
}
if (nullptr == list.pOptions) { return false; }
// Set flags.
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
@ -173,29 +158,26 @@ namespace Qv2ray::components::proxy
bool hasHTTP = (httpPort != 0);
bool hasSOCKS = (socksPort != 0);
if (!(hasHTTP || hasSOCKS || usePAC)) {
if (!(hasHTTP || hasSOCKS || usePAC))
{
LOG(MODULE_PROXY, "Nothing?")
return;
}
if (usePAC) {
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file")
} else {
if (hasHTTP) {
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
}
if (usePAC) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file") }
else
{
if (hasHTTP) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP") }
if (hasSOCKS) {
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
}
if (hasSOCKS) { LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS") }
}
#ifdef Q_OS_WIN
QString __a;
if (usePAC) {
__a = address;
} else {
if (usePAC) { __a = address; }
else
{
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
}
@ -205,9 +187,7 @@ namespace Qv2ray::components::proxy
//
__QueryProxyOptions();
if (!__SetProxyOptions(proxyStrW, usePAC)) {
LOG(MODULE_PROXY, "Failed to set proxy.")
}
if (!__SetProxyOptions(proxyStrW, usePAC)) { LOG(MODULE_PROXY, "Failed to set proxy.") }
__QueryProxyOptions();
#elif defined(Q_OS_LINUX)
@ -215,18 +195,21 @@ namespace Qv2ray::components::proxy
auto proxyMode = usePAC ? "auto" : "manual";
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
if (usePAC) {
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address);
} else {
if (hasHTTP) {
if (usePAC) { actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); }
else
{
if (hasHTTP)
{
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.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 port %1").arg(socksPort);
}
@ -239,7 +222,8 @@ namespace Qv2ray::components::proxy
return QProcess::execute(action) == QProcess::NormalExit;
}) == actions.size();
if (!result) {
if (!result)
{
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.")
}
@ -247,21 +231,27 @@ namespace Qv2ray::components::proxy
Q_UNUSED(result);
#else
for (auto service : macOSgetNetworkServices()) {
for (auto service : macOSgetNetworkServices())
{
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 -setautoproxyurl " + service + " " + address);
} else {
if (hasHTTP) {
}
else
{
if (hasHTTP)
{
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + 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 -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
}
if (hasSOCKS) {
if (hasSOCKS)
{
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
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];
// Make sure the memory was allocated.
if (nullptr == list.pOptions) {
if (nullptr == list.pOptions)
{
// Return FALSE if the memory wasn't allocated.
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'");
#else
for (auto service : macOSgetNetworkServices()) {
for (auto service : macOSgetNetworkServices())
{
LOG(MODULE_PROXY, "Clearing proxy for interface: " + service)
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off");
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off");
@ -315,4 +307,4 @@ namespace Qv2ray::components::proxy
#endif
}
}
} // namespace Qv2ray::components::proxy

View File

@ -1,12 +1,12 @@
#pragma once
#include <QString>
#include <QObject>
#include <QString>
//
namespace Qv2ray::components::proxy
{
void ClearSystemProxy();
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::proxy;

View File

@ -14,32 +14,33 @@
*
* You should have received a copy of the GNU General Public License
* 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
* 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),
* 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
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
* License in all respects for all of the code used other than "OpenSSL". If
* you modify file(s), you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version.
*/
#include "speedplotview.hpp"
#include <QCoreApplication>
#include <QLocale>
#include <QPainter>
#include <QPen>
#include <list>
#include <QCoreApplication>
#define VIEWABLE 120
// use binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
enum class SizeUnit {
enum class SizeUnit
{
Byte, // 1024^0,
KibiByte, // 1024^1,
MebiByte, // 1024^2,
@ -54,27 +55,25 @@ enum class SizeUnit {
namespace
{
const struct {
const struct
{
const char *source;
const char *comment;
} units[] = {
QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
} units[] = { QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
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)
{
const auto &unitString = units[static_cast<int>(unit)];
QString ret = QCoreApplication::translate("misc", unitString.source, unitString.comment);
if (isSpeed)
ret += QCoreApplication::translate("misc", "/s", "per second");
if (isSpeed) ret += QCoreApplication::translate("misc", "/s", "per second");
return ret;
}
@ -82,36 +81,34 @@ QString unitString(const SizeUnit unit, const bool isSpeed)
int friendlyUnitPrecision(const SizeUnit unit)
{
// friendlyUnit's number of digits after the decimal point
switch (unit) {
case SizeUnit::Byte:
return 0;
switch (unit)
{
case SizeUnit::Byte: return 0;
case SizeUnit::KibiByte:
case SizeUnit::MebiByte:
return 1;
case SizeUnit::MebiByte: return 1;
case SizeUnit::GibiByte:
return 2;
case SizeUnit::GibiByte: return 2;
default:
return 3;
default: return 3;
}
}
qlonglong sizeInBytes(qreal size, const SizeUnit unit)
{
for (int i = 0; i < static_cast<int>(unit); ++i)
size *= 1024;
for (int i = 0; i < static_cast<int>(unit); ++i) size *= 1024;
return size;
}
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 };
struct SplittedValue {
struct SplittedValue
{
double arg;
SizeUnit unit;
qint64 sizeInBytes() const
@ -128,32 +125,33 @@ namespace
SizeUnit calculatedUnit = SizeUnit::Byte;
while (value > 1024) {
while (value > 1024)
{
value /= 1024;
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;
while (roundedValue < value)
roundedValue += 40;
while (roundedValue < value) roundedValue += 40;
return { static_cast<double>(roundedValue), calculatedUnit };
}
if (value > 10.0) {
if (value > 10.0)
{
int roundedValue = static_cast<int>(value / 4) * 4;
while (roundedValue < value)
roundedValue += 4;
while (roundedValue < value) roundedValue += 4;
return { static_cast<double>(roundedValue), calculatedUnit };
}
for (const auto &roundedValue : roundingTable) {
if (value <= roundedValue)
return {roundedValue, calculatedUnit};
for (const auto &roundedValue : roundingTable)
{
if (value <= roundedValue) return { roundedValue, calculatedUnit };
}
return { 10.0, calculatedUnit };
@ -163,15 +161,11 @@ namespace
{
// check is there need for digits after decimal separator
const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0;
return QLocale::system().toString(argValue, 'f', precision)
+ QString::fromUtf8(" ")
+ unitString(unit, true);
}
return QLocale::system().toString(argValue, 'f', precision) + QString::fromUtf8(" ") + unitString(unit, true);
}
} // namespace
SpeedPlotView::SpeedPlotView(QWidget *parent)
: QGraphicsView(parent)
, m_currentData(&m_datahalfMin)
SpeedPlotView::SpeedPlotView(QWidget *parent) : QGraphicsView(parent), m_currentData(&m_datahalfMin)
{
QPen greenPen;
greenPen.setWidthF(1.5);
@ -193,9 +187,7 @@ void SpeedPlotView::pushPoint(const SpeedPlotView::PointData &point)
{
m_datahalfMin.push_back(point);
while (m_datahalfMin.length() > VIEWABLE) {
m_datahalfMin.removeFirst();
}
while (m_datahalfMin.length() > VIEWABLE) { m_datahalfMin.removeFirst(); }
}
void SpeedPlotView::replot()
@ -213,11 +205,12 @@ quint64 SpeedPlotView::maxYValue()
auto &queue = getCurrentData();
quint64 maxYValue = 0;
for (int id = UP; id < NB_GRAPHS; ++id) {
for (int id = UP; id < NB_GRAPHS; ++id)
{
// 30 is half min
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];
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];
}
}
@ -244,12 +237,12 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
int yAxisWidth = 0;
for (const QString &label : speedLabels)
if (fontMetrics.horizontalAdvance(label) > yAxisWidth)
yAxisWidth = fontMetrics.horizontalAdvance(label);
if (fontMetrics.horizontalAdvance(label) > yAxisWidth) yAxisWidth = fontMetrics.horizontalAdvance(label);
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()),
QSizeF(2 * yAxisWidth, fontMetrics.height()));
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());
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;
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;
auto &queue = getCurrentData();
for (int id = UP; id < NB_GRAPHS; ++id) {
for (int id = UP; id < NB_GRAPHS; ++id)
{
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 newY = rect.bottom() - queue[i].y[id] * yMultiplier;
points.push_back(QPoint(newX, newY));
@ -300,9 +296,9 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
double legendHeight = 0;
int legendWidth = 0;
for (const auto &property : m_properties) {
if (fontMetrics.horizontalAdvance(property.name) > legendWidth)
legendWidth = fontMetrics.horizontalAdvance(property.name);
for (const auto &property : m_properties)
{
if (fontMetrics.horizontalAdvance(property.name) > legendWidth) legendWidth = fontMetrics.horizontalAdvance(property.name);
legendHeight += 1.5 * fontMetrics.height();
}
@ -313,14 +309,15 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
painter.fillRect(legendBackgroundRect, legendBackgroundColor);
i = 0;
for (const auto &property : m_properties) {
for (const auto &property : m_properties)
{
int nameSize = fontMetrics.horizontalAdvance(property.name);
double indent = 1.5 * (i++) * fontMetrics.height();
painter.setPen(property.pen);
painter.drawLine(legendTopLeft + QPointF(0, indent + fontMetrics.height()),
legendTopLeft + QPointF(nameSize, indent + fontMetrics.height()));
painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())),
property.name, QTextOption(Qt::AlignVCenter));
painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())), property.name,
QTextOption(Qt::AlignVCenter));
}
}
@ -328,8 +325,6 @@ SpeedPlotView::GraphProperties::GraphProperties()
{
}
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen)
: name(name)
, pen(pen)
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen) : name(name), pen(pen)
{
}

View File

@ -39,13 +39,15 @@ class SpeedPlotView : public QGraphicsView
Q_OBJECT
public:
enum GraphID {
enum GraphID
{
UP = 0,
DOWN,
NB_GRAPHS
};
struct PointData {
struct PointData
{
qint64 x;
quint64 y[NB_GRAPHS];
};
@ -59,7 +61,8 @@ class SpeedPlotView : public QGraphicsView
void paintEvent(QPaintEvent *event) override;
private:
struct GraphProperties {
struct GraphProperties
{
GraphProperties();
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
* 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
* 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),
* 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
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
* License in all respects for all of the code used other than "OpenSSL". If
* you modify file(s), you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version.
*/
#include "speedwidget.hpp"
#include "speedplotview.hpp"
#include <QDateTime>
#include <QHBoxLayout>
#include <QLabel>
@ -35,10 +38,7 @@
#include <QTimer>
#include <QVBoxLayout>
#include "speedplotview.hpp"
SpeedWidget::SpeedWidget(QWidget *parent)
: QWidget(parent)
SpeedWidget::SpeedWidget(QWidget *parent) : QWidget(parent)
{
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
@ -52,7 +52,9 @@ SpeedWidget::SpeedWidget(QWidget *parent)
m_plot->show();
}
SpeedWidget::~SpeedWidget() {}
SpeedWidget::~SpeedWidget()
{
}
void SpeedWidget::AddPointData(long up, long down)
{

View File

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

View File

@ -1,10 +1,10 @@
#pragma once
#include <QString>
#include "base/models/QvConfigIdentifier.hpp"
#include <QHash>
#include <QHashFunctions>
#include "base/models/QvConfigIdentifier.hpp"
#include <QString>
namespace Qv2ray::core
{
@ -12,7 +12,12 @@ namespace Qv2ray::core
class IDType
{
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)
{
return lhs.m_id == rhs.m_id;
@ -29,11 +34,11 @@ namespace Qv2ray::core
{
return ::qHash(m_id, seed);
}
private:
QString m_id;
};
// Define several safetypes to prevent misuse of QString.
class __QvGroup;
class __QvConnection;
@ -48,9 +53,7 @@ namespace Qv2ray::core
{
QList<IDType> list;
for (auto str : strings) {
list << IDType(str);
}
for (auto str : strings) { list << IDType(str); }
return list;
}
@ -60,21 +63,23 @@ namespace Qv2ray::core
{
QList<QString> list;
for (auto id : ids) {
list << id.toString();
}
for (auto id : ids) { list << id.toString(); }
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);
}
//
/// Metadata object representing a connection.
struct ConnectionMetaObject : ConnectionObject_Config {
struct ConnectionMetaObject : ConnectionObject_Config
{
GroupId groupId = NullGroupId;
ConnectionMetaObject(): ConnectionObject_Config() { }
ConnectionMetaObject() : ConnectionObject_Config()
{
}
// Suger for down casting.
ConnectionMetaObject(const ConnectionObject_Config &base) : ConnectionMetaObject()
{
@ -88,12 +93,15 @@ namespace Qv2ray::core
};
/// 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.
bool isSubscription;
QList<ConnectionId> connections;
// Suger for down casting.
GroupMetaObject(): connections() {}
GroupMetaObject() : connections()
{
}
GroupMetaObject(const GroupObject_Config &base) : GroupMetaObject()
{
this->isSubscription = false;
@ -110,6 +118,6 @@ namespace Qv2ray::core
this->isSubscription = true;
}
};
}
} // namespace Qv2ray::core
using namespace Qv2ray::core;

View File

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

View File

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

View File

@ -1,4 +1,5 @@
#include "ConfigBackend.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::core::config
@ -14,10 +15,8 @@ namespace Qv2ray::core::config
{
Qv2rayConfigPath = path;
if (!path.endsWith("/")) {
Qv2rayConfigPath += "/";
}
}
if (!path.endsWith("/")) { Qv2rayConfigPath += "/"; }
}
} // 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 SetConfigDirPath(const QString &path);
}
} // namespace Qv2ray::core::config
using namespace Qv2ray::core;
using namespace Qv2ray::core::config;

View File

@ -13,11 +13,14 @@ namespace Qv2ray
// Private member
QJsonObject UpgradeConfig_Inc(int fromVersion, QJsonObject root)
{
switch (fromVersion) {
switch (fromVersion)
{
// --------------------------------------------------------------------------------------
// Below is for Qv2ray version 2
case 4: {
// We changed the "proxyCN" to "bypassCN" as it's easier to understand....
case 4:
{
// We changed the "proxyCN" to "bypassCN" as it's easier to
// understand....
auto v2_oldProxyCN = root["proxyCN"].toBool();
//
// From 3 to 4, we changed 'runAsRoot' to 'tProxySupport'
@ -66,13 +69,15 @@ namespace Qv2ray
}
// Qv2ray version 2, RC 2
case 5: {
case 5:
{
// Added subscription auto update
auto subs = root["subscribes"].toObject();
root.remove("subscribes");
QJsonObject newSubscriptions;
for (auto item = subs.begin(); item != subs.end(); item++) {
for (auto item = subs.begin(); item != subs.end(); item++)
{
auto key = item.key();
SubscriptionObject_Config _conf;
_conf.address = item.value().toString();
@ -88,7 +93,8 @@ namespace Qv2ray
}
// Qv2ray version 2, RC 4
case 6: {
case 6:
{
// Moved API Stats port from connectionConfig to apiConfig
QJsonObject apiConfig;
apiConfig["enableAPI"] = true;
@ -97,7 +103,8 @@ namespace Qv2ray
break;
}
case 7: {
case 7:
{
auto lang = root["uiConfig"].toObject()["language"].toString().replace("-", "_");
auto uiConfig = root["uiConfig"].toObject();
uiConfig["language"] = lang;
@ -106,23 +113,24 @@ namespace Qv2ray
break;
}
// From version 8 to 9, we introduced a lot of new connection metainfo(s)
case 8: {
// From version 8 to 9, we introduced a lot of new connection
// metainfo(s)
case 8:
{
// Generate a default group
QJsonObject defaultGroup;
QStringList defaultGroupConnectionId;
defaultGroup["displayName"] = QObject::tr("Default Group");
QString defaultGroupId = "000000000000";
if (!QDir(QV2RAY_CONNECTIONS_DIR + defaultGroupId).exists()) {
QDir().mkpath(QV2RAY_CONNECTIONS_DIR + defaultGroupId);
}
if (!QDir(QV2RAY_CONNECTIONS_DIR + defaultGroupId).exists()) { QDir().mkpath(QV2RAY_CONNECTIONS_DIR + defaultGroupId); }
QString autoStartId;
UPGRADELOG("Upgrading connections...")
QJsonObject rootConnections;
for (auto config : root["configs"].toArray()) {
for (auto config : root["configs"].toArray())
{
UPGRADELOG("Migrating: " + config.toString())
//
// MOVE FILES.
@ -133,17 +141,19 @@ namespace Qv2ray
DEBUG(MODULE_SETTINGS, "Generated new UUID: " + newUuid);
// Check Autostart Id
if (root["autoStartConfig"].toObject()["subscriptionName"].toString().isEmpty()) {
if (root["autoStartConfig"].toObject()["connectionName"].toString() == config.toString()) {
autoStartId = newUuid;
}
if (root["autoStartConfig"].toObject()["subscriptionName"].toString().isEmpty())
{
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;
configFile.rename(newPath);
UPGRADELOG("Moved: " + filePath + " to " + newPath);
} else {
}
else
{
UPGRADELOG("WARNING! This file is not found, possible loss of data!")
continue;
}
@ -159,7 +169,8 @@ namespace Qv2ray
QJsonObject rootSubscriptions = root.take("subscriptions").toObject();
QJsonObject newSubscriptions;
for (auto i = 0; i < rootSubscriptions.count(); i++) {
for (auto i = 0; i < rootSubscriptions.count(); i++)
{
auto key = rootSubscriptions.keys()[i];
auto value = rootSubscriptions.value(key);
//
@ -176,15 +187,14 @@ namespace Qv2ray
auto newDirPath = QV2RAY_SUBSCRIPTION_DIR + subsUuid;
QDir newDir(newDirPath);
if (!newDir.exists()) {
newDir.mkpath(newDirPath);
}
if (!newDir.exists()) { newDir.mkpath(newDirPath); }
// With extensions
auto fileList = GetFileList(baseDirPath);
// Copy every file within a subscription.
for (auto fileName : fileList) {
for (auto fileName : fileList)
{
auto subsConnectionId = GenerateUuid();
auto baseFilePath = baseDirPath + "/" + fileName;
auto newFilePath = newDirPath + "/" + subsConnectionId + QV2RAY_CONFIG_FILE_EXTENSION;
@ -199,10 +209,10 @@ namespace Qv2ray
//
// Check Autostart Id
if (root["autoStartConfig"].toObject()["subscriptionName"].toString() == key) {
if (root["autoStartConfig"].toObject()["connectionName"].toString() == subsConnection["displayName"].toString()) {
autoStartId = subsConnectionId;
}
if (root["autoStartConfig"].toObject()["subscriptionName"].toString() == key)
{
if (root["autoStartConfig"].toObject()["connectionName"].toString() == subsConnection["displayName"].toString())
{ autoStartId = subsConnectionId; }
}
}
@ -222,9 +232,12 @@ namespace Qv2ray
break;
}
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
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
QvMessageBoxWarn(nullptr, QObject::tr("Configuration Upgrade Failed"),
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."));
@ -242,10 +255,8 @@ namespace Qv2ray
{
LOG(MODULE_SETTINGS, "Migrating config from version " + QSTRN(fromVersion) + " to " + QSTRN(toVersion))
for (int i = fromVersion; i < toVersion; i++) {
root = UpgradeConfig_Inc(i, root);
}
for (int i = fromVersion; i < toVersion; i++) { root = UpgradeConfig_Inc(i, root); }
return root;
}
}
} // namespace Qv2ray

View File

@ -1,4 +1,5 @@
#include "ConnectionIO.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::core::connection
@ -8,16 +9,19 @@ namespace Qv2ray::core::connection
////
//// Save Connection to a place, with checking if there's existing file.
//// 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 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 (QFile::exists(fullPath) && !canOverrideExisting) {
// // Alias is a pointer to a QString.
// DeducePossibleFileName(QV2RAY_CONFIG_DIR, alias, QV2RAY_CONFIG_FILE_EXTENSION);
// fullPath = QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION;
// DeducePossibleFileName(QV2RAY_CONFIG_DIR, alias,
// QV2RAY_CONFIG_FILE_EXTENSION); fullPath = QV2RAY_CONFIG_DIR +
// *alias + QV2RAY_CONFIG_FILE_EXTENSION;
// }
//
// LOG(MODULE_SETTINGS, "Saving a config named: " + *alias)
@ -25,7 +29,8 @@ namespace Qv2ray::core::connection
// 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 fName = *name;
@ -34,18 +39,21 @@ namespace Qv2ray::core::connection
// 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 (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)
// bool result = StringToFile(&str, &config);
//
// 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;
@ -54,7 +62,8 @@ namespace Qv2ray::core::connection
//
// 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()) {
// 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()) {
// 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)
// return QFile::rename(QV2RAY_CONFIG_DIR + originalName + QV2RAY_CONFIG_FILE_EXTENSION, QV2RAY_CONFIG_DIR + newName + QV2RAY_CONFIG_FILE_EXTENSION);
// LOG(MODULE_CONNECTION, "[RENAME] --> ORIGINAL: " + originalName +
// ", 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)
// return QDir().rename(QV2RAY_SUBSCRIPTION_DIR + originalName, QV2RAY_SUBSCRIPTION_DIR + newName);
// LOG(MODULE_SUBSCRIPTION, "[RENAME] --> ORIGINAL: " + originalName
// + ", 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);
//
// if (!source.exists()) {
// LOG(MODULE_FILEIO, "Trying to import from an non-existing file.")
// return CONFIGROOT();
// LOG(MODULE_FILEIO, "Trying to import from an non-existing
// file.") return CONFIGROOT();
// }
//
// auto root = CONFIGROOT(JsonFromString(StringFromFile(&source)));
@ -111,4 +129,4 @@ namespace Qv2ray::core::connection
// return root;
//}
}
}
} // namespace Qv2ray::core::connection

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
#include "Serialization.hpp"
#include "Generation.hpp"
#include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
@ -12,11 +13,13 @@ namespace Qv2ray::core::connection
{
CONFIGROOT config;
if (link.startsWith("vmess://")) {
config = ConvertConfigFromVMessString(link, alias, errMessage);
} else if (link.startsWith("ss://")) {
if (link.startsWith("vmess://")) { config = ConvertConfigFromVMessString(link, alias, errMessage); }
else if (link.startsWith("ss://"))
{
config = ConvertConfigFromSSString(link, alias, errMessage);
} else {
}
else
{
*errMessage = QObject::tr("Unsupported share link format.");
}
@ -31,21 +34,29 @@ namespace Qv2ray::core::connection
auto type = outbound["protocol"].toString();
QString sharelink = "";
if (type == "vmess") {
auto vmessServer = StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
if (type == "vmess")
{
auto vmessServer =
StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
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);
} else {
}
else
{
LOG(MODULE_CONNECTION, "Unsupported outbound type: " + type)
}
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)
{
QJsonObject vmessUriRoot;
@ -59,15 +70,19 @@ namespace Qv2ray::core::connection
vmessUriRoot["net"] = transfer.network;
vmessUriRoot["tls"] = transfer.security;
if (transfer.network == "tcp") {
vmessUriRoot["type"] = transfer.tcpSettings.header.type;
} else if (transfer.network == "kcp") {
if (transfer.network == "tcp") { vmessUriRoot["type"] = transfer.tcpSettings.header.type; }
else if (transfer.network == "kcp")
{
vmessUriRoot["type"] = transfer.kcpSettings.header.type;
} else if (transfer.network == "quic") {
}
else if (transfer.network == "quic")
{
vmessUriRoot["type"] = transfer.quicSettings.header.type;
vmessUriRoot["host"] = transfer.quicSettings.security;
vmessUriRoot["path"] = transfer.quicSettings.key;
} else if (transfer.network == "ws") {
}
else if (transfer.network == "ws")
{
auto x = transfer.wsSettings.headers;
auto host = x.contains("host");
auto CapHost = x.contains("Host");
@ -75,7 +90,9 @@ namespace Qv2ray::core::connection
//
vmessUriRoot["host"] = realHost;
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["path"] = transfer.httpSettings.path;
}
@ -89,8 +106,8 @@ namespace Qv2ray::core::connection
{
// String may start with: vmess:// and ss://
// We only process vmess:// here
// Some subscription providers may use plain vmess:// saperated by lines
// But others may use base64 of above.
// Some subscription providers may use plain vmess:// saperated by
// lines But others may use base64 of above.
auto result = QString::fromUtf8(arr).trimmed();
return result.startsWith("vmess://") ? result : Base64Decode(result);
}
@ -101,7 +118,8 @@ namespace Qv2ray::core::connection
QString d_name;
// auto ssUri = _ssUri.toStdString();
if (ssUri.length() < 5) {
if (ssUri.length() < 5)
{
LOG(MODULE_CONNECTION, "ss:// string too short")
*errMessage = QObject::tr("SS URI is too short");
}
@ -110,7 +128,8 @@ namespace Qv2ray::core::connection
auto hashPos = uri.lastIndexOf("#");
DEBUG(MODULE_CONNECTION, "Hash sign position: " + QSTRN(hashPos))
if (hashPos >= 0) {
if (hashPos >= 0)
{
// Get the name/remark
d_name = uri.mid(uri.lastIndexOf("#") + 1);
uri.truncate(hashPos);
@ -126,37 +145,34 @@ namespace Qv2ray::core::connection
auto atPos = uri.indexOf('@');
DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos))
if (atPos < 0) {
if (atPos < 0)
{
// Old URI scheme
QString decoded = QByteArray::fromBase64(uri.toUtf8(), QByteArray::Base64Option::OmitTrailingEquals);
auto colonPos = decoded.indexOf(':');
DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos))
if (colonPos < 0) {
*errMessage = QObject::tr("Can't find the colon separator between method and password");
}
if (colonPos < 0) { *errMessage = QObject::tr("Can't find the colon separator between method and password"); }
server.method = decoded.left(colonPos);
decoded.remove(0, colonPos + 1);
atPos = decoded.lastIndexOf('@');
DEBUG(MODULE_CONNECTION, "At sign position: " + QSTRN(atPos))
if (atPos < 0) {
*errMessage = QObject::tr("Can't find the at separator between password and hostname");
}
if (atPos < 0) { *errMessage = QObject::tr("Can't find the at separator between password and hostname"); }
server.password = decoded.mid(0, atPos);
decoded.remove(0, atPos + 1);
colonPos = decoded.lastIndexOf(':');
DEBUG(MODULE_CONNECTION, "Colon position: " + QSTRN(colonPos))
if (colonPos < 0) {
*errMessage = QObject::tr("Can't find the colon separator between hostname and port");
}
if (colonPos < 0) { *errMessage = QObject::tr("Can't find the colon separator between hostname and port"); }
server.address = decoded.mid(0, colonPos);
server.port = decoded.mid(colonPos + 1).toInt();
} else {
}
else
{
// SIP002 URI scheme
auto x = QUrl::fromUserInput(uri);
server.address = x.host();
@ -166,9 +182,7 @@ namespace Qv2ray::core::connection
//
DEBUG(MODULE_CONNECTION, "Userinfo splitter position: " + QSTRN(userInfoSp))
if (userInfoSp < 0) {
*errMessage = QObject::tr("Can't find the colon separator between method and password");
}
if (userInfoSp < 0) { *errMessage = QObject::tr("Can't find the colon separator between method and password"); }
QString method = userInfo.mid(0, userInfoSp);
server.method = method;
@ -178,7 +192,8 @@ namespace Qv2ray::core::connection
d_name = QUrl::fromPercentEncoding(d_name.toUtf8());
CONFIGROOT root;
OUTBOUNDS outbounds;
outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>() << server), QJsonObject()));
outbounds.append(
GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>() << server), QJsonObject()));
JADD(outbounds)
*alias = alias->isEmpty() ? d_name : *alias + "_" + d_name;
LOG(MODULE_CONNECTION, "Deduced alias: " + *alias)
@ -189,12 +204,15 @@ namespace Qv2ray::core::connection
{
auto myAlias = QUrl::toPercentEncoding(alias);
if (isSip002) {
if (isSip002)
{
LOG(MODULE_CONNECTION, "Converting an ss-server config to Sip002 ss:// format")
QString plainUserInfo = server.method + ":" + server.password;
QString userinfo(plainUserInfo.toUtf8().toBase64(QByteArray::Base64Option::Base64UrlEncoding).data());
return "ss://" + userinfo + "@" + server.address + ":" + QSTRN(server.port) + "#" + myAlias;
} else {
}
else
{
LOG(MODULE_CONNECTION, "Converting an ss-server config to old ss:// string format")
QString ssUri = server.method + ":" + server.password + "@" + server.address + ":" + QSTRN(server.port);
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.")
QString vmess = vmessStr;
if (vmess.trimmed() != vmess) {
if (vmess.trimmed() != vmess)
{
LOG(MODULE_SETTINGS, "VMess string has some prefix/postfix spaces, trimming.")
vmess = vmessStr.trimmed();
}
@ -217,16 +236,19 @@ namespace Qv2ray::core::connection
// Reset errMessage
*errMessage = "";
if (!vmess.toLower().startsWith("vmess://")) {
if (!vmess.toLower().startsWith("vmess://"))
{
*errMessage = QObject::tr("VMess string should start with 'vmess://'");
return default;
}
try {
try
{
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
auto b64Str = vmessJsonB64.toString();
if (b64Str.isEmpty()) {
if (b64Str.isEmpty())
{
*errMessage = QObject::tr("VMess string should be a valid base64 string");
return default;
}
@ -234,14 +256,16 @@ namespace Qv2ray::core::connection
auto vmessString = Base64Decode(b64Str);
auto jsonErr = VerifyJsonString(vmessString);
if (!jsonErr.isEmpty()) {
if (!jsonErr.isEmpty())
{
*errMessage = jsonErr;
return default;
}
auto vmessConf = JsonFromString(vmessString);
if (vmessConf.isEmpty()) {
if (vmessConf.isEmpty())
{
*errMessage = QObject::tr("JSON should not be empty");
return default;
}
@ -249,13 +273,13 @@ namespace Qv2ray::core::connection
bool flag = true;
// C is a quick hack...
#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");
// Stream Settings
auto net = C("net") ? vmessConf["net"].toString() : "tcp";
if (net == "http" || net == "ws")
flag = flag && C("host") && C("path");
if (net == "http" || net == "ws") flag = flag && C("host") && C("path");
else if (net == "domainsocket")
flag = flag && C("path");
else if (net == "quic")
@ -263,7 +287,9 @@ namespace Qv2ray::core::connection
#undef C
// return flag ? 0 : 1;
} catch (exception *e) {
}
catch (exception *e)
{
*errMessage = e->what();
LOG(MODULE_IMPORT, "Failed to decode vmess string: " + *errMessage)
delete e;
@ -279,41 +305,59 @@ namespace Qv2ray::core::connection
int port, aid;
//
// key = key in JSON and the variable name.
// values = Candidate variable list, if not match, the first one is used as default.
// [[val.size() <= 1]] is used when only the default value exists.
// - 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
// values = Candidate variable list, if not match, the first one is
// used as default.
// [[val.size() <= 1]] is used when only the default value
// exists.
// - 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.
//
#define empty_arg
#define __vmess_checker__func(key, values) \
{ \
auto val = QStringList() values; \
if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() \
&& (val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) {\
if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() && \
(val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) \
{ \
key = vmessConf[#key].toVariant().toString(); \
DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.") \
} else if (!val.isEmpty()) {\
} \
else if (!val.isEmpty()) \
{ \
key = val.first(); \
DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key) \
} else{\
} \
else \
{ \
*errMessage = QObject::tr(#key " does not exist."); \
LOG(MODULE_IMPORT, "Cannot process \"" #key "\" since it's not included in the json object.") \
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \
LOG(MODULE_IMPORT, " --> PS: " + ps) \
} \
}
// Strict check of VMess protocol, to check if the specified value is in the correct range.
// Strict check of VMess protocol, to check if the specified value
// is in the correct range.
//
// Get Alias (AKA ps) from address and port.
__vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString());
__vmess_checker__func(add, empty_arg)
__vmess_checker__func(id, empty_arg)
__vmess_checker__func(net, << "tcp" << "http" << "h2" << "ws" << "kcp" << "domainsocket" << "quic")
__vmess_checker__func(type, << "none" << "http" << "srtp" << "utp" << "wechat-video")
__vmess_checker__func(path, << "")
__vmess_checker__func(host, << "")
__vmess_checker__func(add, empty_arg) __vmess_checker__func(id, empty_arg) __vmess_checker__func(net, << "tcp"
<< "http"
<< "h2"
<< "ws"
<< "kcp"
<< "domainsocket"
<< "quic")
__vmess_checker__func(type, << "none"
<< "http"
<< "srtp"
<< "utp"
<< "wechat-video") __vmess_checker__func(path, << "") __vmess_checker__func(host, << "")
__vmess_checker__func(tls, << "")
//
port = vmessConf["port"].toVariant().toInt();
@ -340,23 +384,29 @@ namespace Qv2ray::core::connection
// Stream Settings
StreamSettingsObject streaming;
if (net == "tcp") {
streaming.tcpSettings.header.type = type;
} else if (net == "http" || net == "h2") {
if (net == "tcp") { streaming.tcpSettings.header.type = type; }
else if (net == "http" || net == "h2")
{
// Fill hosts for HTTP
for (auto _host : host.split(',')) {
streaming.httpSettings.host.push_back(_host.trimmed());
}
for (auto _host : host.split(',')) { streaming.httpSettings.host.push_back(_host.trimmed()); }
streaming.httpSettings.path = path;
} else if (net == "ws") {
}
else if (net == "ws")
{
streaming.wsSettings.headers["Host"] = host;
streaming.wsSettings.path = path;
} else if (net == "kcp") {
}
else if (net == "kcp")
{
streaming.kcpSettings.header.type = type;
} else if (net == "domainsocket") {
}
else if (net == "domainsocket")
{
streaming.dsSettings.path = path;
} else if (net == "quic") {
}
else if (net == "quic")
{
streaming.quicSettings.security = host;
streaming.quicSettings.header.type = type;
streaming.quicSettings.key = path;
@ -364,7 +414,8 @@ namespace Qv2ray::core::connection
// FIXME: makeshift patch for #290.
// 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.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);
//
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;
#undef default
return root;
}
}
}
} // namespace Serialization
} // namespace Qv2ray::core::connection

View File

@ -19,8 +19,8 @@ namespace Qv2ray::core::connection
// SS URI Protocol
CONFIGROOT ConvertConfigFromSSString(const QString &ss, QString *alias, QString *errMessage);
QString ConvertConfigToSSString(const ShadowSocksServerObject &server, const QString &alias, bool isSip002);
}
}
} // namespace Serialization
} // namespace Qv2ray::core::connection
using namespace Qv2ray::core;
using namespace Qv2ray::core::connection;

View File

@ -1,6 +1,8 @@
#include "ConnectionHandler.hpp"
#include "common/QvHelpers.hpp"
#include "core/config/ConfigBackend.hpp"
#include "core/connection/Serialization.hpp"
namespace Qv2ray::core::handlers
{
@ -10,26 +12,23 @@ namespace Qv2ray::core::handlers
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
// Do we need to check how many of them are loaded?
for (auto i = 0; i < GlobalConfig.connections.count(); i++) {
connections[ConnectionId(GlobalConfig.connections.keys()[i])] = GlobalConfig.connections.values()[i];
}
for (auto i = 0; i < GlobalConfig.connections.count(); 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];
groups[GroupId(key)] = val;
for (auto conn : val.connections) {
connections[ConnectionId(conn)].groupId = GroupId(key);
}
for (auto conn : val.connections) { connections[ConnectionId(conn)].groupId = GroupId(key); }
}
for (auto key : GlobalConfig.groups.keys()) {
for (auto key : GlobalConfig.groups.keys())
{
auto val = GlobalConfig.groups[key];
groups[GroupId(key)] = val;
for (auto conn : val.connections) {
connections[ConnectionId(conn)].groupId = GroupId(key);
}
for (auto conn : val.connections) { connections[ConnectionId(conn)].groupId = GroupId(key); }
}
vCoreInstance = new V2rayKernelInstance();
@ -47,7 +46,7 @@ namespace Qv2ray::core::handlers
pingConnectionTimerId = startTimer(60 * 1000);
}
void QvConnectionHandler::CHSaveConnectionData_p()
void QvConnectionHandler::CHSaveConfigData_p()
{
// Copy
auto newGlobalConfig = GlobalConfig;
@ -55,18 +54,21 @@ namespace Qv2ray::core::handlers
newGlobalConfig.groups.clear();
newGlobalConfig.subscriptions.clear();
for (auto i = 0; i < connections.count(); i++) {
newGlobalConfig.connections[connections.keys()[i].toString()] = connections.values()[i];
}
for (auto i = 0; i < connections.count(); 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);
if (groups.values()[i].isSubscription) {
if (groups.values()[i].isSubscription)
{
SubscriptionObject_Config o = groups.values()[i];
o.connections = connections;
newGlobalConfig.subscriptions[groups.keys()[i].toString()] = o;
} else {
}
else
{
GroupObject_Config o = groups.values()[i];
o.connections = connections;
newGlobalConfig.groups[groups.keys()[i].toString()] = o;
@ -78,29 +80,25 @@ namespace Qv2ray::core::handlers
void QvConnectionHandler::timerEvent(QTimerEvent *event)
{
if (event->timerId() == saveTimerId) {
CHSaveConnectionData_p();
} else if (event->timerId() == pingAllTimerId) {
if (event->timerId() == saveTimerId) { CHSaveConfigData_p(); }
else if (event->timerId() == pingAllTimerId)
{
StartLatencyTest();
} else if (event->timerId() == pingConnectionTimerId) {
if (currentConnectionId != NullConnectionId) {
StartLatencyTest(currentConnectionId);
}
else if (event->timerId() == pingConnectionTimerId)
{
if (currentConnectionId != NullConnectionId) { StartLatencyTest(currentConnectionId); }
}
}
void QvConnectionHandler::StartLatencyTest()
{
for (auto connection : connections.keys()) {
StartLatencyTest(connection);
}
for (auto connection : connections.keys()) { StartLatencyTest(connection); }
}
void QvConnectionHandler::StartLatencyTest(const GroupId &id)
{
for (auto connection : groups[id].connections) {
StartLatencyTest(connection);
}
for (auto connection : groups[id].connections) { StartLatencyTest(connection); }
}
void QvConnectionHandler::StartLatencyTest(const ConnectionId &id)
@ -123,10 +121,9 @@ namespace Qv2ray::core::handlers
{
QList<GroupId> subsList;
for (auto group : groups.keys()) {
if (groups[group].isSubscription) {
subsList.push_back(group);
}
for (auto group : groups.keys())
{
if (groups[group].isSubscription) { subsList.push_back(group); }
}
return subsList;
@ -149,10 +146,9 @@ namespace Qv2ray::core::handlers
const ConnectionId QvConnectionHandler::GetConnectionIdByDisplayName(const QString &displayName) const
{
for (auto conn : connections.keys()) {
if (connections[conn].displayName == displayName) {
return conn;
}
for (auto conn : connections.keys())
{
if (connections[conn].displayName == displayName) { return conn; }
}
return NullConnectionId;
@ -160,10 +156,9 @@ namespace Qv2ray::core::handlers
const GroupId QvConnectionHandler::GetGroupIdByDisplayName(const QString &displayName) const
{
for (auto group : groups.keys()) {
if (groups[group].displayName == displayName) {
return group;
}
for (auto group : groups.keys())
{
if (groups[group].displayName == displayName) { return group; }
}
return NullGroupId;
@ -171,38 +166,47 @@ namespace Qv2ray::core::handlers
const GroupId QvConnectionHandler::GetConnectionGroupId(const ConnectionId &id) const
{
if (!connections.contains(id)) {
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
}
if (!connections.contains(id)) { LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString()); }
return connections[id].groupId;
}
double QvConnectionHandler::GetConnectionLatency(const ConnectionId &id) const
{
if (!connections.contains(id)) {
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
}
if (!connections.contains(id)) { LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString()); }
return connections[id].latency;
}
const optional<QString> QvConnectionHandler::DeleteConnection(const ConnectionId &id)
{
// TODO
Q_UNUSED(id)
return "";
//
auto groupId = connections[id].groupId;
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)
{
if (!connections.contains(identifier)) {
return tr("No connection selected!") + NEWLINE + tr("Please select a config from the list.");
}
if (!connections.contains(identifier)) { return tr("No connection selected!") + NEWLINE + tr("Please select a config from the list."); }
if (currentConnectionId != NullConnectionId) {
StopConnection();
}
if (currentConnectionId != NullConnectionId) { StopConnection(); }
CONFIGROOT root = GetConnectionRoot(connections[identifier].groupId, identifier);
return CHStartConnection_p(identifier, root);
@ -226,9 +230,7 @@ namespace Qv2ray::core::handlers
{
QString result;
if (!connections.contains(id)) {
result = tr("N/A");
}
if (!connections.contains(id)) { result = tr("N/A"); }
CONFIGROOT root = GetConnectionRoot(connections[id].groupId, id);
QStringList protocols;
@ -236,12 +238,12 @@ namespace Qv2ray::core::handlers
auto outbound = root["outbounds"].toArray().first().toObject();
result.append(outbound["protocol"].toString());
if (outbound.contains("streamSettings")) {
result.append(" + " + outbound["streamSettings"].toObject()["network"].toString());
if (outbound.contains("streamSettings"))
{
result.append(" / " + outbound["streamSettings"].toObject()["network"].toString());
if (outbound["streamSettings"].toObject().contains("tls")) {
result.append(outbound["streamSettings"].toObject()["tls"].toBool() ? " + tls" : "");
}
if (outbound["streamSettings"].toObject().contains("tls"))
{ result.append(outbound["streamSettings"].toObject()["tls"].toBool() ? " / tls" : ""); }
}
return result;
@ -249,23 +251,23 @@ namespace Qv2ray::core::handlers
const tuple<quint64, quint64> QvConnectionHandler::GetConnectionUsageAmount(const ConnectionId &id) const
{
if (!connections.contains(id)) {
return make_tuple(0, 0);
}
if (!connections.contains(id)) { return make_tuple(0, 0); }
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];
//}
QvConnectionHandler::~QvConnectionHandler()
{
CHSaveConnectionData_p();
CHSaveConfigData_p();
if (vCoreInstance->KernelStarted) {
if (vCoreInstance->KernelStarted)
{
vCoreInstance->StopConnection();
LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.")
}
@ -285,36 +287,49 @@ namespace Qv2ray::core::handlers
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);
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;
int port;
for (auto item : root["outbounds"].toArray()) {
OUTBOUND outBoundRoot = OUTBOUND(item.toObject());
QString outboundType = "";
validOutboundFound = GetOutboundData(outBoundRoot, &host, &port, &outboundType);
if (validOutboundFound) {
return make_tuple(host, port);
} else {
LOG(MODULE_CORE_HANDLER, "Unknown outbound entry: " + outboundType + ", cannot deduce host and port.")
if (GetOutboundData(outBoundRoot, &host, &port, &outboundType))
{
*ok = true;
return make_tuple(outboundType, host, port);
}
else
{
LOG(MODULE_CORE_HANDLER, "Unknown outbound type: " + outboundType + ", cannot deduce host and port.")
}
}
return make_tuple(QObject::tr("N/A"), 0);
return make_tuple(tr("N/A"), tr("N/A"), 0);
}
void QvConnectionHandler::OnLatencyDataArrived(const QvTCPingResultObject &result)
{
if (connections.contains(result.connectionId)) {
if (connections.contains(result.connectionId))
{
connections[result.connectionId].latency = result.avg;
emit OnLatencyTestFinished(result.connectionId, result.avg);
} else {
}
else
{
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)
{
auto groupId = connections[id].groupId;
auto path = (groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR)
+ groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION;
auto path = (groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
id.toString() + QV2RAY_CONFIG_FILE_EXTENSION;
auto content = JsonToString(root);
emit OnConnectionChanged(id);
return StringToFile(content, path);
@ -333,10 +348,101 @@ namespace Qv2ray::core::handlers
{
tuple<QString, int64_t, float> result;
if (!groups[id].isSubscription) {
return result;
}
if (!groups[id].isSubscription) { return result; }
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
#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/tcping/QvTCPing.hpp"
#include "core/CoreSafeTypes.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "core/CoreUtils.hpp"
#include "common/HTTPRequestHelper.hpp"
namespace Qv2ray::core::handlers
{
@ -49,7 +49,7 @@ namespace Qv2ray::core::handlers
//
// Get Conncetion Property
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;
//
// Misc Connection Operations
@ -64,8 +64,8 @@ namespace Qv2ray::core::handlers
const optional<QString> RenameGroup(const GroupId &id, const QString &newName);
//
// Subscriptions
const optional<QString> UpdateSubscription(const GroupId &id, bool useSystemProxy);
const optional<QString> UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy);
bool UpdateSubscription(const GroupId &id, bool useSystemProxy);
bool UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy);
const tuple<QString, int64_t, float> GetSubscriptionData(const GroupId &id);
signals:
@ -73,9 +73,8 @@ namespace Qv2ray::core::handlers
void OnConnected(const ConnectionId &id);
void OnDisConnected(const ConnectionId &id);
void OnVCoreLogAvailable(const ConnectionId &id, const QString &log);
void OnStatsAvailable(const ConnectionId &id,
const quint64 uploadSpeed, const quint64 downloadSpeed,
const quint64 totalUpload, const quint64 totalDownload);
void OnStatsAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed, const quint64 totalUpload,
const quint64 totalDownload);
//
void OnConnectionCreated(const ConnectionId &id, const QString &displayName);
void OnConnectionRenamed(const ConnectionId &id, const QString &originalName, const QString &newName);
@ -102,11 +101,13 @@ namespace Qv2ray::core::handlers
void timerEvent(QTimerEvent *event) override;
private:
void CHSaveConnectionData_p();
void CHSaveConfigData_p();
//
optional<QString> CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root);
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:
int saveTimerId;
@ -118,6 +119,7 @@ namespace Qv2ray::core::handlers
private:
QvHttpRequestHelper *httpHelper;
bool isHttpRequestInProgress = false;
QvTCPingHelper *tcpingHelper;
// We only support one cuncurrent connection currently.
#ifdef QV2RAY_MULTIPlE_ONNECTION
@ -129,6 +131,6 @@ namespace Qv2ray::core::handlers
};
inline ::Qv2ray::core::handlers::QvConnectionHandler *ConnectionManager = nullptr;
}
} // 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 result = vCoreInstance->StartConnection(id, fullConfig);
if (!result.has_value()) {
if (!result.has_value())
{
currentConnectionId = id;
emit OnConnected(currentConnectionId);
}
@ -18,13 +19,16 @@ optional<QString> QvConnectionHandler::CHStartConnection_p(const ConnectionId &i
void QvConnectionHandler::CHStopConnection_p()
{
if (vCoreInstance->KernelStarted) {
if (vCoreInstance->KernelStarted)
{
vCoreInstance->StopConnection();
// Copy
ConnectionId id = currentConnectionId;
currentConnectionId = NullConnectionId;
emit OnDisConnected(id);
} else {
}
else
{
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.")
connect(this, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), this, SLOT(process()));
connect(thread, &QThread::finished, []() {
LOG(MODULE_VCORE, "API thread stopped")
});
connect(thread, &QThread::finished, []() { LOG(MODULE_VCORE, "API thread stopped") });
started = true;
thread->start();
DEBUG(MODULE_VCORE, "API Worker started.")
@ -52,21 +50,22 @@ namespace Qv2ray::core::kernel
thread->wait();
// Although thread shouldnot be null, we'll add this check to be safe.
if (thread) {
delete thread;
}
if (thread) { delete thread; }
}
// API Core Operations
// Start processing data.
void APIWorker::process()
{
while (started) {
while (started)
{
QThread::msleep(1000);
bool dialed = false;
while (running) {
if (!dialed) {
while (running)
{
if (!dialed)
{
auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.apiConfig.statsPort);
#ifdef WITH_LIB_GRPCPP
Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials());
@ -85,36 +84,44 @@ namespace Qv2ray::core::kernel
qint64 value_up = 0;
qint64 value_down = 0;
for (auto tag : inboundTags) {
for (auto tag : inboundTags)
{
value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
}
if (value_up < 0 || value_down < 0) {
if (value_up < 0 || value_down < 0)
{
dialed = false;
break;
}
if (running) {
if (running)
{
apiFailedCounter = 0;
emit OnDataReady(value_up, value_down);
}
#else
for (auto tag : inboundTags) {
for (auto tag : inboundTags)
{
auto valup = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
auto valdown = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
if (valup < 0 || valdown < 0) {
if (valup < 0 || valdown < 0)
{
dialed = false;
break;
}
if (running) {
if (running)
{
apiFailedCounter = 0;
emit OnDataReady(tag, valup, valdown);
} else {
}
else
{
// If the connection has stopped, just quit.
break;
}
@ -130,12 +137,15 @@ namespace Qv2ray::core::kernel
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.")
emit error("Failed to get statistics data, please check if V2ray is running properly");
apiFailedCounter++;
return 0;
} else if (apiFailedCounter > QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) {
}
else if (apiFailedCounter > QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD)
{
return 0;
}
@ -147,7 +157,8 @@ namespace Qv2ray::core::kernel
ClientContext context;
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()) + ")")
apiFailedCounter++;
}
@ -157,7 +168,8 @@ namespace Qv2ray::core::kernel
qint64 data = GetStats(const_cast<char *>(name.toStdString().c_str()), 1000);
#endif
if (data < 0) {
if (data < 0)
{
LOG(MODULE_VCORE, "API call returns: " + QSTRN(data))
apiFailedCounter++;
return 0;
@ -165,4 +177,4 @@ namespace Qv2ray::core::kernel
return data;
}
}
} // namespace Qv2ray::core::kernel

View File

@ -1,9 +1,10 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#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.pb.h"
#include <grpc++/grpc++.h>
#endif
// 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;
#endif
};
}
} // 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 <QWidget>
#include <QDesktopServices>
#include "common/QvHelpers.hpp"
#include "KernelInteractions.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "APIBackend.hpp"
namespace Qv2ray::core::kernel
{
@ -12,14 +14,17 @@ namespace Qv2ray::core::kernel
{
QFile coreFile(vCorePath);
if (!coreFile.exists()) {
if (!coreFile.exists())
{
DEBUG(MODULE_VCORE, "V2ray core file cannot be found.")
*message = tr("V2ray core executable not found.");
return false;
}
// Use open() here to prevent `executing` a folder, which may have the same name as the V2ray core.
if (!coreFile.open(QFile::ReadOnly)) {
// Use open() here to prevent `executing` a folder, which may have the
// same name as the V2ray core.
if (!coreFile.open(QFile::ReadOnly))
{
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.");
return false;
@ -33,19 +38,22 @@ namespace Qv2ray::core::kernel
bool hasGeoIP = FileExistsIn(QDir(vAssetsPath), "geoip.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.")
*message = tr("V2ray assets path is not valid.");
return false;
}
if (!hasGeoIP) {
if (!hasGeoIP)
{
DEBUG(MODULE_VCORE, "No geoip.dat in assets path, aborting.")
*message = tr("No geoip.dat in assets path.");
return false;
}
if (!hasGeoSite) {
if (!hasGeoSite)
{
DEBUG(MODULE_VCORE, "No geosite.dat in assets path, aborting.")
*message = tr("No geosite.dat in assets path.");
return false;
@ -54,7 +62,8 @@ namespace Qv2ray::core::kernel
// Check if V2ray core returns a version number correctly.
QProcess proc;
#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.setProgram(vCorePath);
proc.setNativeArguments("--version");
@ -66,7 +75,8 @@ namespace Qv2ray::core::kernel
proc.waitForFinished();
auto exitCode = proc.exitCode();
if (exitCode != 0) {
if (exitCode != 0)
{
DEBUG(MODULE_VCORE, "VCore failed with an exit code: " + QSTRN(exitCode))
*message = tr("V2ray core failed with an exit code: ") + QSTRN(exitCode);
return false;
@ -75,7 +85,8 @@ namespace Qv2ray::core::kernel
QString output = proc.readAllStandardOutput();
LOG(MODULE_VCORE, "V2ray output: " + SplitLines(output).join(";"))
if (SplitLines(output).isEmpty()) {
if (SplitLines(output).isEmpty())
{
*message = tr("V2ray core returns empty string.");
return false;
}
@ -84,12 +95,12 @@ namespace Qv2ray::core::kernel
return true;
}
bool V2rayKernelInstance::ValidateConfig(const QString &path)
{
QString v2rayCheckResult;
if (ValidateKernel(GlobalConfig.v2CorePath, GlobalConfig.v2AssetsPath, &v2rayCheckResult)) {
if (ValidateKernel(GlobalConfig.v2CorePath, GlobalConfig.v2AssetsPath, &v2rayCheckResult))
{
DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult)
// Append assets location env.
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@ -98,21 +109,28 @@ namespace Qv2ray::core::kernel
QProcess process;
process.setProcessEnvironment(env);
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();
if (process.exitCode() != 0) {
if (process.exitCode() != 0)
{
QString output = QString(process.readAllStandardOutput());
QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
return false;
} else {
}
else
{
DEBUG(MODULE_VCORE, "Config file check passed.")
return true;
}
} else {
}
else
{
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE +
tr("The error is: ") + NEWLINE + v2rayCheckResult);
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("The error is: ") + NEWLINE + v2rayCheckResult);
return false;
}
}
@ -120,14 +138,14 @@ namespace Qv2ray::core::kernel
V2rayKernelInstance::V2rayKernelInstance()
{
vProcess = new QProcess();
connect(vProcess, &QProcess::readyReadStandardOutput, this, [&]() {
emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed());
});
connect(vProcess, &QProcess::readyReadStandardOutput, this,
[&]() { emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); });
connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
// If V2ray crashed AFTER we start it.
if (KernelStarted && state == QProcess::NotRunning) {
if (KernelStarted && state == QProcess::NotRunning)
{
LOG(MODULE_VCORE, "V2ray kernel crashed.")
StopConnection();
emit OnProcessErrored(id);
@ -140,7 +158,8 @@ namespace Qv2ray::core::kernel
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")
return tr("Invalid V2ray Instance Status.");
}
@ -151,7 +170,8 @@ namespace Qv2ray::core::kernel
//
auto filePath = QV2RAY_GENERATED_FILE_PATH;
if (ValidateConfig(filePath)) {
if (ValidateConfig(filePath))
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", GlobalConfig.v2AssetsPath);
vProcess->setProcessEnvironment(env);
@ -163,10 +183,12 @@ namespace Qv2ray::core::kernel
this->id = id;
QStringList inboundTags;
for (auto item : root["inbounds"].toArray()) {
for (auto item : root["inbounds"].toArray())
{
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.
continue;
}
@ -178,20 +200,26 @@ namespace Qv2ray::core::kernel
apiEnabled = false;
//
if (StartupOption.noAPI) {
LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"")
} else if (!GlobalConfig.apiConfig.enableAPI) {
if (StartupOption.noAPI) { LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"") }
else if (!GlobalConfig.apiConfig.enableAPI)
{
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.")
} else {
}
else
{
apiWorker->StartAPI(inboundTags);
apiEnabled = true;
DEBUG(MODULE_VCORE, "Qv2ray API started")
}
return {};
} else {
}
else
{
KernelStarted = false;
return tr("V2ray kernel failed to start.");
}
@ -199,12 +227,14 @@ namespace Qv2ray::core::kernel
void V2rayKernelInstance::StopConnection()
{
if (apiEnabled) {
if (apiEnabled)
{
apiWorker->StopAPI();
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;
vProcess->close();
// Block until V2ray core exits
@ -214,9 +244,7 @@ namespace Qv2ray::core::kernel
V2rayKernelInstance::~V2rayKernelInstance()
{
if (KernelStarted) {
StopConnection();
}
if (KernelStarted) { StopConnection(); }
delete apiWorker;
delete vProcess;
@ -226,4 +254,4 @@ namespace Qv2ray::core::kernel
{
emit OnNewStatsDataArrived(id, _totalUp, _totalDown);
}
}
} // namespace Qv2ray::core::kernel

View File

@ -1,8 +1,9 @@
#pragma once
#include <QProcess>
#include "base/Qv2rayBase.hpp"
#include "core/CoreSafeTypes.hpp"
#include <QProcess>
namespace Qv2ray::core::kernel
{
class APIWorker;
@ -41,6 +42,6 @@ namespace Qv2ray::core::kernel
//
ConnectionId id = NullConnectionId;
};
}
} // namespace Qv2ray::core::kernel
using namespace Qv2ray::core::kernel;

View File

@ -1,15 +1,15 @@
#ifdef _WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <WinSock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include "QtConcurrent/QtConcurrent"
#include "QvTCPing.hpp"
#include "core/handler/ConnectionHandler.hpp"
#include "QtConcurrent/QtConcurrent"
namespace Qv2ray::core::tcping
{
@ -23,7 +23,8 @@ namespace Qv2ray::core::tcping
void QvTCPingHelper::StopAllLatenceTest()
{
while (!pingWorkingThreads.isEmpty()) {
while (!pingWorkingThreads.isEmpty())
{
auto worker = pingWorkingThreads.dequeue();
worker->future().cancel();
worker->cancel();
@ -39,9 +40,7 @@ namespace Qv2ray::core::tcping
auto result = watcher->result();
this->pingWorkingThreads.removeOne(watcher);
if (!result.errorMessage.isEmpty()) {
LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage)
}
if (!result.errorMessage.isEmpty()) { LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage) }
emit this->OnLatencyTestCompleted(result);
});
@ -54,12 +53,14 @@ namespace Qv2ray::core::tcping
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;
addrinfo *resolved;
int errcode;
if ((errcode = resolveHost(host.toStdString(), port, &resolved)) != 0) {
if ((errcode = resolveHost(host.toStdString(), port, &resolved)) != 0)
{
#ifdef _WIN32
data.errorMessage = QString::fromStdWString(gai_strerror(errcode));
#else
@ -71,26 +72,35 @@ namespace Qv2ray::core::tcping
bool noAddress = false;
int currentCount = 0;
while (currentCount < count) {
while (currentCount < count)
{
if (isExiting) return QvTCPingResultObject();
system_clock::time_point start;
system_clock::time_point end;
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 = testLatency(resolved, &start, &end)) != 0)
{
if (errcode != -EADDRNOTAVAIL)
{
// LOG(MODULE_NETWORK, "Error connecting to host: " +
// data.hostName + ":" + QSTRN(data.port) + " " +
// strerror(-errcode))
errorCount++;
} else {
if (noAddress) {
LOG(MODULE_NETWORK, ".")
} else {
}
else
{
if (noAddress) { LOG(MODULE_NETWORK, ".") }
else
{
LOG(MODULE_NETWORK, "error connecting to host: " + QSTRN(-errcode) + " " + strerror(-errcode))
}
noAddress = true;
}
} else {
}
else
{
noAddress = false;
successCount++;
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.best = max(data.best, ms);
if (ms > 1000) {
if (ms > 1000)
{
LOG(MODULE_NETWORK, "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;
/* try to connect for each of the entries: */
while (addr != nullptr) {
while (addr != nullptr)
{
if (isExiting) return 0;
/* create socket */
fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (!fd) {
goto next_addr0;
}
if (!fd) { goto next_addr0; }
#ifdef _WIN32
@ -172,7 +182,8 @@ namespace Qv2ray::core::tcping
/* connect to peer */
// Qt has its own connect() function in QObject....
// 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();
#ifdef _WIN32
closesocket(fd);
@ -195,4 +206,4 @@ next_addr0:
rv = rv ? rv : -errno;
return rv;
}
}
} // namespace Qv2ray::core::tcping

View File

@ -4,7 +4,8 @@
namespace Qv2ray::core::tcping
{
struct QvTCPingResultObject {
struct QvTCPingResultObject
{
ConnectionId connectionId = NullConnectionId;
QString errorMessage;
int total, succeed, failed;
@ -21,11 +22,12 @@ namespace Qv2ray::core::tcping
void StopAllLatenceTest();
signals:
void OnLatencyTestCompleted(const QvTCPingResultObject &data);
private:
static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count);
int count;
QQueue<QFutureWatcher<QvTCPingResultObject> *> pingWorkingThreads;
};
}
} // namespace Qv2ray::core::tcping
using namespace Qv2ray::core::tcping;

View File

@ -1,20 +1,20 @@
#include <QFileInfo>
#include <QStandardPaths>
#include <QTranslator>
#include <QStyle>
#include <QLocale>
#include <QObject>
#include <QStyleFactory>
#include <QApplication>
#include <singleapplication.h>
#include <csignal>
#include "ui/w_MainWindow.hpp"
#include "common/CommandArgs.hpp"
#include "common/QvHelpers.hpp"
#include "common/QvTranslator.hpp"
#include "core/config/ConfigBackend.hpp"
#include "core/handler/ConnectionHandler.hpp"
#include "common/QvHelpers.hpp"
#include "common/CommandArgs.hpp"
#include "common/QvTranslator.hpp"
#include "ui/w_MainWindow.hpp"
#include <QApplication>
#include <QLocale>
#include <QObject>
#include <QStandardPaths>
#include <QStyle>
#include <QStyleFactory>
#include <QTranslator>
#include <csignal>
#include <singleapplication.h>
#ifdef Q_OS_UNIX
// For unix root user check
@ -31,7 +31,6 @@ void signalHandler(int signum)
qApp->exit(-99);
}
bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{
// 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()));
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_INIT, "---> Cannot create a new file or openwrite a file.")
return false;
} else {
}
else
{
testFile.write("Qv2ray test file, feel free to remove.");
testFile.flush();
testFile.close();
bool removed = testFile.remove();
if (!removed) {
if (!removed)
{
// 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_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.
QFile configFile(path + "Qv2ray.conf");
@ -68,37 +72,52 @@ bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
bool opened2 = configFile.open(QIODevice::ReadWrite);
try {
if (opened2) {
try
{
if (opened2)
{
// Verify if the config can be loaded.
// Failed to parse if we changed the file structure...
// 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));
if (!err.isEmpty()) {
if (!err.isEmpty())
{
LOG(MODULE_INIT, "Json parse returns: " + err)
return false;
} else {
}
else
{
// If the file format is valid.
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();
return true;
}
} else {
}
else
{
LOG(MODULE_SETTINGS, "File: " + configFile.fileName() + " cannot be opened!")
return false;
}
} catch (...) {
}
catch (...)
{
LOG(MODULE_SETTINGS, "Exception raised when checking config: " + configFile.fileName())
// 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;
}
} else return true;
}
else
return true;
}
bool initialiseQv2ray()
@ -109,12 +128,14 @@ bool initialiseQv2ray()
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;
configFilePaths << currentPathConfig;
#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;
#endif
configFilePaths << configQv2ray;
@ -123,31 +144,40 @@ bool initialiseQv2ray()
QString configPath = "";
bool hasExistingConfig = false;
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 |
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 |
bool isValidConfigPath = verifyConfigAvaliability(path, true);
// If we already found a valid config file. just simply load it...
if (hasExistingConfig) break;
if (isValidConfigPath) {
if (isValidConfigPath)
{
DEBUG(MODULE_INIT, "Path: " + path + " is valid.")
configPath = path;
hasExistingConfig = true;
} else {
}
else
{
LOG(MODULE_INIT, "Path: " + path + " does not contain a valid config file.")
}
}
// If there's no existing config.
if (hasExistingConfig) {
if (hasExistingConfig)
{
// Use the config path found by the checks above
SetConfigDirPath(configPath);
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
configPath = currentPathConfig;
#else
@ -156,23 +186,26 @@ bool initialiseQv2ray()
bool mkpathResult = QDir().mkpath(configPath);
// Check if the dirs are write-able
if (mkpathResult && verifyConfigAvaliability(configPath, false)) {
// Found a valid config dir, with write permission, but assume no config is located in it.
if (mkpathResult && verifyConfigAvaliability(configPath, false))
{
// 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.")
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.
// This condition branch (!hasExistingConfig check) holds the fact that
// current config dir, should NOT contain any valid file (at least in the same name)
// It usually means that QV2RAY_CONFIG_FILE here is corrupted, in JSON format.
// Otherwise Qv2ray would have loaded this config already instead of notifying to
// create a new config in this folder.
// This condition branch (!hasExistingConfig check) holds the
// fact that current config dir, should NOT contain any valid
// file (at least in the same name) It usually means that
// QV2RAY_CONFIG_FILE here is corrupted, in JSON format.
// 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.")
QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"),
QObject::tr("Failed to determine the location of config file.") + NEWLINE +
QObject::tr("Qv2ray will now exit.") + NEWLINE +
QObject::tr("Please report if you think it's a bug."));
QObject::tr("Qv2ray will now exit.") + NEWLINE + QObject::tr("Please report if you think it's a bug."));
return false;
}
@ -184,7 +217,9 @@ bool initialiseQv2ray()
// Save initial config.
SaveGlobalConfig(conf);
LOG(MODULE_INIT, "Created initial config file.")
} else {
}
else
{
// None of the path above can be used as a dir for storing config.
// Even the last folder failed to pass the check.
LOG(MODULE_INIT, "FATAL")
@ -192,14 +227,14 @@ bool initialiseQv2ray()
QString searchPath = configFilePaths.join(NEWLINE);
QvMessageBoxWarn(nullptr, QObject::tr("Cannot Start Qv2ray"),
QObject::tr("Cannot find a place to store config files.") + NEWLINE +
QObject::tr("Qv2ray has searched these paths below:") +
NEWLINE + NEWLINE + searchPath + NEWLINE +
QObject::tr("Qv2ray has searched these paths below:") + NEWLINE + NEWLINE + searchPath + NEWLINE +
QObject::tr("Qv2ray will now exit."));
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.
QDir().mkdir(QV2RAY_GENERATED_DIR);
LOG(MODULE_INIT, "Created config generation dir at: " + QV2RAY_GENERATED_DIR)
@ -208,7 +243,6 @@ bool initialiseQv2ray()
return true;
}
int main(int argc, char *argv[])
{
#ifndef Q_OS_WIN
@ -228,9 +262,9 @@ int main(int argc, char *argv[])
QvCommandArgParser parser;
QString errorMessage;
switch (parser.ParseCommandLine(&errorMessage)) {
case CommandLineOk:
break;
switch (parser.ParseCommandLine(&errorMessage))
{
case CommandLineOk: break;
case CommandLineError:
cout << errorMessage.toStdString() << endl;
@ -243,16 +277,16 @@ int main(int argc, char *argv[])
LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO)
return 0;
case CommandLineHelpRequested:
cout << parser.Parser()->helpText().toStdString() << endl;
return 0;
case CommandLineHelpRequested: cout << parser.Parser()->helpText().toStdString() << endl; return 0;
}
}
#ifdef Q_OS_UNIX
// 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.
if (!StartupOption.forceRunAsRootUser && geteuid() == 0) {
// Do not use getuid() here since it's installed as owned by the root,
// 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(" --> USE IT AT YOUR OWN RISK!"))
return 1;
@ -263,9 +297,11 @@ int main(int argc, char *argv[])
// finished: command line parsing
LOG("QV2RAY_BUILD_INFO", QV2RAY_BUILD_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::setApplicationVersion(QV2RAY_VERSION_STRING);
SingleApplication::setApplicationDisplayName("Qv2ray");
@ -275,7 +311,8 @@ int main(int argc, char *argv[])
// ----------------------------> For debug build...
SingleApplication::setApplicationName("Qv2ray - DEBUG");
#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);
// Early initialisation
//
@ -288,14 +325,11 @@ int main(int argc, char *argv[])
bool _result_ = _qApp.installTranslator(Qv2rayTranslator.get());
LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed"))
//
LOG("LICENCE", 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
"Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE
NEWLINE NEWLINE
LOG("LICENCE", 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 "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
"Copyright (c) 2020 dridk (@dridk): X2Struct (Apache)" NEWLINE
"Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE
"Copyright (c) 2020 dridk (@dridk): X2Struct (Apache)" NEWLINE "Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE
"Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE
"Copyright (c) 2016 Singein (@Singein): ScreenShot (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) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE
"Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE
"Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE
NEWLINE)
"Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE NEWLINE)
//
LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay()))
//
#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
//
// Load the language translation list.
auto langs = GetFileList(QDir(":/translations"));
if (langs.empty()) {
if (langs.empty())
{
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."));
} else {
for (auto lang : langs) {
LOG(MODULE_INIT, "Found Translator: " + lang)
QvMessageBoxWarn(nullptr, QObject::tr("Cannot load languages"),
QObject::tr("Qv2ray will continue running, but you cannot change the UI language."));
}
else
{
for (auto lang : langs) { LOG(MODULE_INIT, "Found Translator: " + lang) }
}
// Qv2ray Initialize, find possible config paths and verify them.
if (!initialiseQv2ray()) {
return -1;
}
if (!initialiseQv2ray()) { return -1; }
// Load the config for upgrade, but do not parse it to the struct.
auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE));
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...
// This is rare but it may happen....
QvMessageBoxWarn(nullptr, QObject::tr("Qv2ray Cannot Continue"),
@ -345,7 +380,8 @@ int main(int argc, char *argv[])
return -2;
}
if (confVersion < QV2RAY_CONFIG_VERSION) {
if (confVersion < QV2RAY_CONFIG_VERSION)
{
// That is, config file needs to be upgraded.
conf = Qv2ray::UpgradeConfig(confVersion, QV2RAY_CONFIG_VERSION, conf);
}
@ -356,7 +392,8 @@ int main(int argc, char *argv[])
qApp->removeTranslator(Qv2rayTranslator.get());
LOG(MODULE_INIT, "Removed system translations")
if (confObject.uiConfig.language.isEmpty()) {
if (confObject.uiConfig.language.isEmpty())
{
// Prevent empty.
LOG(MODULE_UI, "Setting default UI language to 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);
if (qApp->installTranslator(Qv2rayTranslator.get())) {
LOG(MODULE_INIT, "Successfully installed a translator for " + confObject.uiConfig.language)
} else {
if (qApp->installTranslator(Qv2rayTranslator.get()))
{ LOG(MODULE_INIT, "Successfully installed a translator for " + confObject.uiConfig.language) }
else
{
// Do not translate these.....
// If a translator fails to load, pop up a message.
QvMessageBoxWarn(
nullptr, "Translation Failed",
QvMessageBoxWarn(nullptr, "Translation Failed",
"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");
}
@ -383,15 +420,15 @@ int main(int argc, char *argv[])
auto osslCurVersion = QSslSocket::sslLibraryVersionString();
LOG(MODULE_NETWORK, "Current OpenSSL version: " + osslCurVersion)
if (!QSslSocket::supportsSsl()) {
if (!QSslSocket::supportsSsl())
{
LOG(MODULE_NETWORK, "Required OpenSSL version: " + osslReqVersion)
LOG(MODULE_NETWORK, "OpenSSL library MISSING, Quitting.")
QvMessageBoxWarn(nullptr, QObject::tr("Dependency Missing"),
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("If you are using an AppImage from Github Action, please report a bug.") + NEWLINE + NEWLINE +
QObject::tr("Technical Details") + NEWLINE +
"OSsl.Rq.V=" + osslReqVersion + NEWLINE +
QObject::tr("Technical Details") + NEWLINE + "OSsl.Rq.V=" + osslReqVersion + NEWLINE +
"OSsl.Cr.V=" + osslCurVersion);
return -3;
}
@ -406,7 +443,8 @@ int main(int argc, char *argv[])
#ifdef QV2RAY_USE_BUILTIN_DARKTHEME
LOG(MODULE_UI, "Using built-in theme.")
if (confObject.uiConfig.useDarkTheme) {
if (confObject.uiConfig.useDarkTheme)
{
LOG(MODULE_UI, " --> Using built-in dark theme.")
// From https://forum.qt.io/topic/101391/windows-10-dark-theme/4
_qApp.setStyle("Fusion");
@ -441,7 +479,8 @@ int main(int argc, char *argv[])
QStringList themes = QStyleFactory::keys();
//_qApp.setDesktopFileName("qv2ray.desktop");
if (themes.contains(confObject.uiConfig.theme)) {
if (themes.contains(confObject.uiConfig.theme))
{
_qApp.setStyle(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
#ifndef QT_DEBUG
try {
try
{
#endif
//_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar);
// Initialise Connection Handler
@ -465,23 +505,19 @@ int main(int argc, char *argv[])
// Handler for session logout, shutdown, etc.
// Will not block.
QGuiApplication::setFallbackSessionManagementEnabled(false);
QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, []() {
LOG(MODULE_INIT, "Quit triggered by session manager.")
});
QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, []() { LOG(MODULE_INIT, "Quit triggered by session manager.") });
#ifndef Q_OS_WIN
signal(SIGUSR1, [](int) {
emit MainWindow::mwInstance->Connect();
});
signal(SIGUSR2, [](int) {
emit MainWindow::mwInstance->DisConnect();
});
signal(SIGUSR1, [](int) { emit MainWindow::mwInstance->Connect(); });
signal(SIGUSR2, [](int) { emit MainWindow::mwInstance->DisConnect(); });
#endif
auto rcode = _qApp.exec();
delete ConnectionManager;
LOG(MODULE_INIT, "Quitting normally")
return rcode;
#ifndef QT_DEBUG
} catch (...) {
}
catch (...)
{
QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
LOG(MODULE_INIT, "EXCEPTION THROWN: " __FILE__)
return -99;

View File

@ -1,14 +1,13 @@
#include "w_InboundEditor.hpp"
#include "core/CoreUtils.hpp"
#include "common/QvHelpers.hpp"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp"
static bool isLoading = false;
#define CHECKLOADING if(isLoading) return;
#define CHECKLOADING \
if (isLoading) return;
InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
QDialog(parent),
original(root)
InboundEditor::InboundEditor(INBOUND root, QWidget *parent) : QDialog(parent), original(root)
{
QvMessageBusConnect(InboundEditor);
setupUi(this);
@ -17,20 +16,30 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
allocate = root["allocate"].toObject();
sniffing = root["sniffing"].toObject();
if (inboundType == "http") {
httpSettings = root["settings"].toObject();
} else if (inboundType == "socks") {
if (inboundType == "http") { httpSettings = root["settings"].toObject(); }
else if (inboundType == "socks")
{
socksSettings = root["settings"].toObject();
} else if (inboundType == "dokodemo-door") {
}
else if (inboundType == "dokodemo-door")
{
dokoSettings = root["settings"].toObject();
} else if (inboundType == "mtproto") {
}
else if (inboundType == "mtproto")
{
mtSettings = root["settings"].toObject();
} else {
if (!root["protocol"].toString().isEmpty()) {
}
else
{
if (!root["protocol"].toString().isEmpty())
{
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);
} else {
}
else
{
LOG(MODULE_UI, "Creating new inbound config")
root["protocol"] = inboundType = "http";
}
@ -41,10 +50,9 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
QvMessageBusSlotImpl(InboundEditor)
{
switch (msg) {
MBShowDefaultImpl\
MBHideDefaultImpl\
MBRetranslateDefaultImpl\
switch (msg)
{
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
}
}
@ -59,27 +67,28 @@ INBOUND InboundEditor::GenerateNewRoot()
INBOUND _newRoot = root;
auto inboundType = root["protocol"].toString();
if (inboundType.isNull() || inboundType.isEmpty()) {
inboundType = "http";
}
if (inboundType.isNull() || inboundType.isEmpty()) { inboundType = "http"; }
if (inboundType == "http") {
if (inboundType == "http")
{
// Remove useless, misleading 'accounts' array.
if (httpAccountListBox->count() == 0) {
httpSettings.remove("accounts");
}
if (httpAccountListBox->count() == 0) { httpSettings.remove("accounts"); }
_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;
} else if (inboundType == "dokodemo-door") {
}
else if (inboundType == "dokodemo-door")
{
_newRoot["settings"] = dokoSettings;
} else if (inboundType == "mtproto") {
}
else if (inboundType == "mtproto")
{
_newRoot["settings"] = mtSettings;
}
@ -102,7 +111,8 @@ void InboundEditor::LoadUIData()
//
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() == "tls") destOverrideList->item(1)->setCheckState(Qt::Checked);
@ -118,9 +128,8 @@ void InboundEditor::LoadUIData()
httpUserLevelSB->setValue(httpSettings["userLevel"].toInt());
httpAccountListBox->clear();
for (auto user : httpSettings["accounts"].toArray()) {
httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString());
}
for (auto user : httpSettings["accounts"].toArray())
{ httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); }
// SOCKS
socksAuthCombo->setCurrentText(socksSettings["auth"].toString());
@ -128,9 +137,8 @@ void InboundEditor::LoadUIData()
socksUDPIPAddrTxt->setText(socksSettings["ip"].toString());
socksUserLevelSB->setValue(socksSettings["userLevel"].toInt());
for (auto user : socksSettings["accounts"].toArray()) {
socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString());
}
for (auto user : socksSettings["accounts"].toArray())
{ socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); }
// Dokodemo-Door
dokoFollowRedirectCB->setChecked(dokoSettings["followRedirect"].toBool());
@ -190,15 +198,18 @@ void InboundEditor::on_httpRemoveUserBtn_clicked()
{
CHECKLOADING
if (httpAccountListBox->currentRow() != -1) {
if (httpAccountListBox->currentRow() != -1)
{
auto item = httpAccountListBox->currentItem();
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 entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) {
if (entry == item->text().trimmed())
{
list.removeAt(i);
httpSettings["accounts"] = list;
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?"));
} else {
// QvMessageBox(this, tr("Removing a user"), tr("No user has been
// removed. Why?"));
}
else
{
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();
for (int i = 0 ; i < list.count(); i++) {
for (int i = 0; i < list.count(); i++)
{
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."));
return;
}
@ -243,15 +259,18 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
{
CHECKLOADING
if (socksAccountListBox->currentRow() != -1) {
if (socksAccountListBox->currentRow() != -1)
{
auto item = socksAccountListBox->currentItem();
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 entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) {
if (entry == item->text().trimmed())
{
list.removeAt(i);
socksSettings["accounts"] = list;
LOG(MODULE_UI, "Removed http inbound user " + entry)
@ -259,7 +278,9 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
return;
}
}
} else {
}
else
{
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();
for (int i = 0 ; i < list.count(); i++) {
for (int i = 0; i < list.count(); i++)
{
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."));
return;
}
@ -322,12 +345,11 @@ void InboundEditor::on_destOverrideList_itemChanged(QListWidgetItem *item)
Q_UNUSED(item)
QJsonArray list;
for (int i = 0; i < destOverrideList->count(); i++) {
for (int i = 0; i < destOverrideList->count(); i++)
{
auto _item = destOverrideList->item(i);
if (_item->checkState() == Qt::Checked) {
list.append(_item->text().toLower());
}
if (_item->checkState() == Qt::Checked) { list.append(_item->text().toLower()); }
}
sniffing["destOverride"] = list;

View File

@ -1,13 +1,16 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_InboundEditor.h"
#include <QDialog>
#include <QJsonObject>
#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
@ -16,7 +19,7 @@ class InboundEditor : public QDialog, private Ui::InboundEditor
~InboundEditor();
INBOUND OpenEditor();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_inboundProtocolCombo_currentIndexChanged(const QString &arg1);

View File

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

View File

@ -1,13 +1,16 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include "common/QJsonModel.hpp"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_JsonEditor.h"
#include <QDialog>
#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
@ -16,7 +19,7 @@ class JsonEditor : public QDialog, private Ui::JsonEditor
~JsonEditor();
QJsonObject OpenEditor();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_jsonEditor_textChanged();

View File

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

View File

@ -1,12 +1,15 @@
#pragma once
#include <QtCore>
#include <QDialog>
#include "base/Qv2rayBase.hpp"
#include "ui_w_OutboundEditor.h"
#include "ui/widgets/StreamSettingsWidget.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
public:
@ -16,7 +19,7 @@ class OutboundEditor : public QDialog, private Ui::OutboundEditor
OUTBOUND OpenEditor();
QString GetFriendlyName();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
signals:
void s_reload_config(bool need_restart);
private slots:

View File

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

View File

@ -1,31 +1,34 @@
#pragma once
#include <list>
#include "ConnectionStyle.hpp"
#include "FlowScene.hpp"
#include "Node.hpp"
#include "NodeData.hpp"
#include "common/QvHelpers.hpp"
#include <QDialog>
#include <QJsonArray>
#include <QJsonObject>
#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::FlowScene;
using QtNodes::Node;
#include "ui_w_RoutesEditor.h"
#include "ui/messaging/QvMessageBus.hpp"
#include "ui_w_RoutesEditor.h"
enum ROUTE_EDIT_MODE {
enum ROUTE_EDIT_MODE
{
RENAME_INBOUND,
RENAME_OUTBOUND,
RENAME_RULE,
};
class RouteEditor : public QDialog, private Ui::RouteEditor
class RouteEditor
: public QDialog
, private Ui::RouteEditor
{
Q_OBJECT
@ -34,7 +37,7 @@ class RouteEditor : public QDialog, private Ui::RouteEditor
~RouteEditor();
CONFIGROOT OpenEditor();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_buttonBox_accepted();

View File

@ -1,18 +1,16 @@
// WARNING
// Since it's required for this file to know the content of the macros defined in another CPP file.
// We included an CPP file instead of the proper HPP file.
// Since it's required for this file to know the content of the macros defined
// in another CPP file. We included an CPP file instead of the proper HPP file.
#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)
{
QString tag = getTag(in);
if (inbounds.contains(tag)) {
tag = tag + "_" + GenerateRandomString(5);
}
if (inbounds.contains(tag)) { tag = tag + "_" + GenerateRandomString(5); }
in["tag"] = tag;
auto _nodeData = make_unique<QvInboundNodeModel>(make_shared<InboundNodeData>(tag));
@ -29,9 +27,7 @@ void RouteEditor::AddOutbound(OUTBOUND out)
{
QString tag = getTag(out);
if (outbounds.contains(tag)) {
tag = tag + "_" + GenerateRandomString(5);
}
if (outbounds.contains(tag)) { tag = tag + "_" + GenerateRandomString(5); }
out["tag"] = tag;
auto _nodeData = make_unique<QvOutboundNodeModel>(make_shared<OutboundNodeData>(tag));
@ -64,9 +60,7 @@ QString RouteEditor::AddNewRule()
void RouteEditor::AddRule(RuleObject rule)
{
// Prevent duplicate
if (ruleNodes.contains(rule.QV2RAY_RULE_TAG)) {
rule.QV2RAY_RULE_TAG += "-" + GenerateRandomString(5);
}
if (ruleNodes.contains(rule.QV2RAY_RULE_TAG)) { rule.QV2RAY_RULE_TAG += "-" + GenerateRandomString(5); }
rules[rule.QV2RAY_RULE_TAG] = rule;
auto pos = nodeGraphWidget->pos();
@ -76,25 +70,34 @@ void RouteEditor::AddRule(RuleObject rule)
auto &node = nodeScene->createNode(std::move(_nodeData));
nodeScene->setNodePosition(node, pos);
for (auto inTag : rule.inboundTag) {
if (!inboundNodes.contains(inTag)) {
for (auto inTag : rule.inboundTag)
{
if (!inboundNodes.contains(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);
rule.inboundTag.removeAll(inTag);
} else {
}
else
{
auto inboundNode = inboundNodes[inTag];
nodeScene->createConnection(node, 0, *inboundNode, 0);
}
}
// If not using balancers (use outbound tag)
if (!rule.QV2RAY_RULE_USE_BALANCER) {
if (outboundNodes.contains(rule.outboundTag)) {
if (!rule.QV2RAY_RULE_USE_BALANCER)
{
if (outboundNodes.contains(rule.outboundTag))
{
DEBUG(MODULE_GRAPH, "Found outbound tag: " + rule.outboundTag + ", for rule: " + rule.QV2RAY_RULE_TAG)
nodeScene->createConnection(*outboundNodes[rule.outboundTag], 0, node, 0);
} else {
}
else
{
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
void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag, QString newTag)
{
switch (mode) {
switch (mode)
{
case RENAME_RULE:
if (rules.contains(originalTag) && ruleNodes.contains(originalTag)) {
if (rules.contains(newTag) && rules.contains(newTag)) {
if (rules.contains(originalTag) && ruleNodes.contains(originalTag))
{
if (rules.contains(newTag) && rules.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5);
}
auto node = static_cast<QvRuleNodeDataModel *>(ruleNodes[originalTag]->nodeDataModel());
if (node == nullptr) {
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
node->setData(newTag);
//
@ -125,27 +129,30 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
rules[newTag].QV2RAY_RULE_TAG = newTag;
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);
if (items.isEmpty()) {
LOG(MODULE_UI, "Cannot find a node: " + originalTag)
} else {
if (items.isEmpty()) { LOG(MODULE_UI, "Cannot find a node: " + originalTag) }
else
{
items.first()->setText(newTag);
}
if (currentRuleTag == originalTag) {
currentRuleTag = newTag;
if (currentRuleTag == originalTag) { currentRuleTag = newTag; }
}
} else {
else
{
LOG(MODULE_UI, "There's nothing match " + originalTag + " in the containers.")
}
break;
case RENAME_OUTBOUND:
if (outbounds.contains(originalTag) && outboundNodes.contains(originalTag)) {
if (outbounds.contains(newTag) && outboundNodes.contains(newTag)) {
if (outbounds.contains(originalTag) && outboundNodes.contains(originalTag))
{
if (outbounds.contains(newTag) && outboundNodes.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5);
}
@ -154,34 +161,39 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
outboundNodes[newTag] = outboundNodes.take(originalTag);
auto node = static_cast<QvOutboundNodeModel *>(outboundNodes[newTag]->nodeDataModel());
if (node == nullptr) {
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
node->setData(newTag);
// Change outbound tag in rules accordingly.
for (auto k : rules.keys()) {
for (auto k : rules.keys())
{
auto v = rules[k];
if (v.outboundTag == originalTag) {
if (v.outboundTag == originalTag)
{
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;
}
}
// Resolve default outbound.
ResolveDefaultOutboundTag(originalTag, newTag);
} else {
}
else
{
LOG(MODULE_UI, "Failed to rename an outbound --> Item not found.")
}
break;
case RENAME_INBOUND:
if (inbounds.contains(originalTag) && inboundNodes.contains(originalTag)) {
if (inbounds.contains(newTag) && inboundNodes.contains(newTag)) {
if (inbounds.contains(originalTag) && inboundNodes.contains(originalTag))
{
if (inbounds.contains(newTag) && inboundNodes.contains(newTag))
{
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, we appended a postfix."));
newTag += "_" + GenerateRandomString(5);
}
@ -190,26 +202,29 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString originalTag,
inboundNodes[newTag] = inboundNodes.take(originalTag);
auto node = static_cast<QvInboundNodeModel *>(inboundNodes[newTag]->nodeDataModel());
if (node == nullptr) {
LOG(MODULE_GRAPH, "EMPTY NODE WARN")
}
if (node == nullptr) { LOG(MODULE_GRAPH, "EMPTY NODE WARN") }
node->setData(newTag);
// Change inbound tag in rules accordingly.
// k -> rule tag
// v -> rule object
for (auto k : rules.keys()) {
for (auto k : rules.keys())
{
auto v = rules[k];
if (v.inboundTag.contains(originalTag)) {
if (v.inboundTag.contains(originalTag))
{
v.inboundTag.append(newTag);
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;
}
}
} else {
}
else
{
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->addItems(outbounds.keys());
if (!isDefaultChanged) {
if (!isDefaultChanged)
{
LOG(MODULE_UI, "Default outbound is not changed: retaining: " + defaultOutbound)
// Just simply restore the default one.
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.")
// Removed the default one, so set the first one as the default.
if (outbounds.isEmpty()) {
defaultOutbound.clear();
} else {
if (outbounds.isEmpty()) { defaultOutbound.clear(); }
else
{
defaultOutbound = getTag(outbounds.first());
defaultOutboundCombo->addItem(outbounds.firstKey());
}
} else {
}
else
{
LOG(MODULE_UI, "Default outbound is renamed, ")
defaultOutboundCombo->setCurrentText(newTag);
defaultOutbound = newTag;

View File

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

View File

@ -6,7 +6,7 @@
#define QvMessageBusSlotSig const QvMBMessage &msg
#define QvMessageBusSlotIdentifier on_QvMessageReceived
#define QvMessageBusSlotDecl void QvMessageBusSlotIdentifier(QvMessageBusSlotSig);
#define QvMessageBusSlotDecl void QvMessageBusSlotIdentifier(QvMessageBusSlotSig)
#define QvMessageBusSlotImpl(CLASSNAME) void CLASSNAME::QvMessageBusSlotIdentifier(QvMessageBusSlotSig)
#define MBShowDefaultImpl \
@ -20,14 +20,13 @@
break;
#define MBRetranslateDefaultImpl \
case RETRANSLATE:\
this->retranslateUi(this);\
break;
case RETRANSLATE: this->retranslateUi(this); break;
namespace Qv2ray::ui::messaging
{
Q_NAMESPACE
enum QvMBMessage {
enum QvMBMessage
{
/// Show all windows.
SHOW_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)
inline QvMessageBusObject messageBus = QvMessageBusObject();
}
} // namespace Qv2ray::ui::messaging
using namespace Qv2ray::ui::messaging;

View File

@ -1,8 +1,9 @@
#pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvInboundNodeModel : public NodeDataModel
{
Q_OBJECT
@ -47,7 +48,9 @@ class QvInboundNodeModel : public NodeDataModel
return _in;
}
void setInData(std::shared_ptr<NodeData>, int) override {}
void setInData(std::shared_ptr<NodeData>, int) override
{
}
void setData(const QString &data)
{
_in = make_shared<InboundNodeData>(data);
@ -59,6 +62,7 @@ class QvInboundNodeModel : public NodeDataModel
{
return _label;
}
private:
NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs");

View File

@ -1,22 +1,20 @@
#pragma once
#include <QLabel>
#include "NodeDataModel.hpp"
#include "PortType.hpp"
#include "common/QvHelpers.hpp"
using QtNodes::PortType;
using QtNodes::PortIndex;
#include <QLabel>
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
using QtNodes::NodeDataType;
using QtNodes::NodeValidationState;
using QtNodes::PortIndex;
using QtNodes::PortType;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
const int GRAPH_NODE_LABEL_FONTSIZE_INCREMENT = 3;
@ -29,11 +27,10 @@ namespace Qv2ray::ui::nodemodels
class InboundNodeData : public NodeData
{
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
{
@ -44,6 +41,7 @@ namespace Qv2ray::ui::nodemodels
{
return _inboundTag;
}
private:
QString _inboundTag;
};
@ -53,11 +51,11 @@ namespace Qv2ray::ui::nodemodels
class OutboundNodeData : public NodeData
{
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
{
@ -68,6 +66,7 @@ namespace Qv2ray::ui::nodemodels
{
return _outboundTag;
}
private:
QString _outboundTag;
};
@ -77,11 +76,10 @@ namespace Qv2ray::ui::nodemodels
class RuleNodeData : public NodeData
{
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
{
@ -92,10 +90,10 @@ namespace Qv2ray::ui::nodemodels
{
return _ruleTag;
}
private:
QString _ruleTag;
};
}
} // namespace Qv2ray::ui::nodemodels
using namespace Qv2ray::ui::nodemodels;

View File

@ -1,7 +1,8 @@
#pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvOutboundNodeModel : public NodeDataModel
{
Q_OBJECT
@ -46,9 +47,13 @@ class QvOutboundNodeModel : public NodeDataModel
return _out;
}
void setInData(shared_ptr<NodeData>, int) override {}
void setInData(shared_ptr<NodeData>, int) override
{
}
void setInData(vector<shared_ptr<NodeData>>, int) override {}
void setInData(vector<shared_ptr<NodeData>>, int) override
{
}
void setData(const QString &data)
{
_out = make_shared<OutboundNodeData>(data);
@ -65,6 +70,7 @@ class QvOutboundNodeModel : public NodeDataModel
{
return ConnectionPolicy::Many;
}
private:
NodeValidationState modelValidationState = NodeValidationState::Warning;
QString modelValidationError = tr("Missing or incorrect inputs");

View File

@ -1,8 +1,9 @@
#pragma once
#include <QtCore/qglobal.h>
#include "ui/models/NodeModelsBase.hpp"
#include <QtCore/qglobal.h>
class QvRuleNodeDataModel : public NodeDataModel
{
Q_OBJECT
@ -27,11 +28,13 @@ class QvRuleNodeDataModel : public NodeDataModel
unsigned int nPorts(PortType portType) const override
{
if (portType == PortType::In) {
if (portType == PortType::In) { return 1; }
else if (portType == PortType::Out)
{
return 1;
} else if (portType == PortType::Out) {
return 1;
} else {
}
else
{
return 0;
}
}
@ -45,15 +48,13 @@ class QvRuleNodeDataModel : public NodeDataModel
{
Q_UNUSED(portIndex)
switch (portType) {
case PortType::In:
return inboundType;
switch (portType)
{
case PortType::In: return inboundType;
case PortType::Out:
return outboundType;
case PortType::Out: return outboundType;
case PortType::None:
break;
case PortType::None: break;
}
return NodeDataType();
@ -65,8 +66,12 @@ class QvRuleNodeDataModel : public NodeDataModel
return _ruleTag;
}
void setInData(std::shared_ptr<NodeData>, int) override {}
void setInData(vector<shared_ptr<NodeData>>, int) override {}
void setInData(std::shared_ptr<NodeData>, int) override
{
}
void setInData(vector<shared_ptr<NodeData>>, int) override
{
}
void setData(const QString &data)
{
_ruleTag = make_shared<RuleNodeData>(data);

View File

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

View File

@ -1,11 +1,13 @@
#pragma once
#include "ui_w_ExportConfig.h"
#include "base/Qv2rayBase.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
#include "base/Qv2rayBase.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
@ -14,7 +16,8 @@ class ConfigExporter : public QDialog, private Ui::ExportConfigWindow
~ConfigExporter();
void OpenExport();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
protected:
void changeEvent(QEvent *e);
private slots:
@ -25,6 +28,7 @@ class ConfigExporter : public QDialog, private Ui::ExportConfigWindow
void on_copyImageBtn_clicked();
void on_copyVMessBtn_clicked();
private:
QZXing qzxing;
QImage image;

View File

@ -1,26 +1,25 @@
#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 <QFileDialog>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QThread>
#include "3rdparty/qzxing/src/QZXing.h"
#include "core/CoreUtils.hpp"
#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)
ImportConfigWindow::ImportConfigWindow(QWidget *parent) : QDialog(parent)
{
setupUi(this);
nameTxt->setText(QDateTime::currentDateTime().toString("MMdd_hhmm"));
@ -30,10 +29,9 @@ ImportConfigWindow::ImportConfigWindow(QWidget *parent)
QvMessageBusSlotImpl(ImportConfigWindow)
{
switch (msg) {
MBShowDefaultImpl
MBHideDefaultImpl
MBRetranslateDefaultImpl
switch (msg)
{
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
}
}
@ -43,7 +41,8 @@ ImportConfigWindow::~ImportConfigWindow()
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->setEnabled(!partialImport);
routeEditBtn->setEnabled(!partialImport);
@ -61,9 +60,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
{
bool hideQv2ray = hideQv2rayCB->isChecked();
if (hideQv2ray) {
messageBus.EmitGlobalSignal(QvMBMessage::HIDE_WINDOWS);
}
if (hideQv2ray) { messageBus.EmitGlobalSignal(QvMBMessage::HIDE_WINDOWS); }
QApplication::processEvents();
QThread::msleep(static_cast<ulong>(doubleSpinBox->value() * 1000));
@ -72,24 +69,29 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
auto _r = w.result();
// Explicitly delete w to call UNREGISTER_WINDOW
if (hideQv2ray) {
if (hideQv2ray)
{
messageBus.EmitGlobalSignal(QvMBMessage::SHOW_WINDOWS);
// ShowAllGlobalWindow();
}
if (_r == QDialog::Accepted) {
if (_r == QDialog::Accepted)
{
QZXing decoder;
decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13);
auto str = decoder.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()))
QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region."));
} else {
}
else
{
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
//QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Successfully imported a QR code form the screen."));
//this->show();
// QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Successfully
// imported a QR code form the screen.")); this->show();
}
}
}
@ -99,24 +101,28 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
QString aliasPrefix = nameTxt->text();
// auto conf = GetGlobalConfig();
switch (tabWidget->currentIndex()) {
case 0: {
switch (tabWidget->currentIndex())
{
case 0:
{
//// From File...
// bool ImportAsComplex = keepImportedInboundCheckBox->isChecked();
// QString path = fileLineTxt->text();
//
// if (!V2rayKernelInstance::ValidateConfig(path)) {
// QvMessageBoxWarn(this, tr("Import config file"), tr("Failed to check the validity of the config file."));
// return;
// QvMessageBoxWarn(this, tr("Import config file"), tr("Failed to
// check the validity of the config file.")); return;
//}
//
// aliasPrefix += "_" + QFileInfo(path).fileName();
////CONFIGROOT config = ConvertConfigFromFile(path, ImportAsComplex);
////CONFIGROOT config = ConvertConfigFromFile(path,
/// ImportAsComplex);
// connections[aliasPrefix] = config;
// break;
}
case 1: {
case 1:
{
QStringList linkList = SplitLines(vmessConnectionStringTxt->toPlainText());
//
// 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.")
while (!linkList.isEmpty()) {
while (!linkList.isEmpty())
{
aliasPrefix = nameTxt->text();
auto link = linkList.takeFirst();
QString errMessage;
CONFIGROOT config = ConvertConfigFromString(link, &aliasPrefix, &errMessage);
// 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.
linkErrors[link] = QSTRN(linkErrors.count() + 1) + ": " + errMessage;
continue;
} else {
}
else
{
connections[aliasPrefix] = config;
}
}
if (!linkErrors.isEmpty()) {
for (auto item : linkErrors) {
if (!linkErrors.isEmpty())
{
for (auto item : linkErrors)
{
vmessConnectionStringTxt->appendPlainText(linkErrors.key(item));
errorsList->addItem(item);
}
@ -173,10 +185,13 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
decoder.setDecoder(QZXing::DecoderFormat_QR_CODE | QZXing::DecoderFormat_EAN_13);
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."));
return;
} else {
}
else
{
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
}
}
@ -184,9 +199,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
{
Q_UNUSED(previous)
if (current == nullptr) {
return;
}
if (current == nullptr) { return; }
auto currentErrorText = current->text();
auto vmessEntry = linkErrors.key(currentErrorText);
@ -194,9 +207,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
auto startPos = vmessConnectionStringTxt->toPlainText().indexOf(vmessEntry);
auto endPos = startPos + vmessEntry.length();
if (startPos < 0) {
return;
}
if (startPos < 0) { return; }
// Select vmess string that is invalid.
QTextCursor c = vmessConnectionStringTxt->textCursor();
@ -208,7 +219,8 @@ void ImportConfigWindow::on_editFileBtn_clicked()
{
QFile file(fileLineTxt->text());
if (!file.exists()) {
if (!file.exists())
{
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Provided file not found: ") + fileLineTxt->text());
return;
}
@ -216,12 +228,17 @@ void ImportConfigWindow::on_editFileBtn_clicked()
auto jsonString = StringFromFile(&file);
auto jsonCheckingError = VerifyJsonString(jsonString);
if (!jsonCheckingError.isEmpty()) {
if (!jsonCheckingError.isEmpty())
{
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) {
return;
} else {
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)
{ return; }
else
{
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);
json = editor.OpenEditor();
if (editor.result() == QDialog::Accepted) {
if (editor.result() == QDialog::Accepted)
{
auto str = JsonToString(json);
bool result = StringToFile(str, file);
if (!result) {
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Failed to save file, please check if you have proper permissions"));
if (!result) { 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.")
}
}
@ -249,7 +267,8 @@ void ImportConfigWindow::on_connectionEditBtn_clicked()
bool isChanged = w.result() == QDialog::Accepted;
QString alias = w.GetFriendlyName();
if (isChanged) {
if (isChanged)
{
OUTBOUNDS outboundsList;
outboundsList.push_back(outboundEntry);
CONFIGROOT root;
@ -257,7 +276,9 @@ void ImportConfigWindow::on_connectionEditBtn_clicked()
//
connections[alias] = root;
accept();
} else {
}
else
{
return;
}
}
@ -275,7 +296,8 @@ void ImportConfigWindow::on_subscriptionButton_clicked()
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
connections.clear();
if (importToComplex) {
if (importToComplex)
{
auto _result = w.GetSelectedConfig();
connections[_result.first] = _result.second;
}
@ -290,10 +312,13 @@ void ImportConfigWindow::on_routeEditBtn_clicked()
bool isChanged = w.result() == QDialog::Accepted;
QString alias = nameTxt->text();
if (isChanged) {
if (isChanged)
{
connections[alias] = result;
accept();
} else {
}
else
{
return;
}
}

View File

@ -1,13 +1,16 @@
#pragma once
#include <QDialog>
#include <QString>
#include <QJsonObject>
#include "base/Qv2rayBase.hpp"
#include "ui_w_ImportConfig.h"
#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
@ -16,7 +19,7 @@ class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow
~ImportConfigWindow();
QMap<QString, CONFIGROOT> OpenImport(bool outboundsOnly = false);
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_selectFileBtn_clicked();

View File

@ -1,49 +1,56 @@
#pragma once
#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_PreferencesWindow.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 "components/pac/QvPACHandler.hpp"
#include "components/plugins/toolbar/QvToolbar.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "ui/widgets/ConnectionInfoWidget.hpp"
#include <QCloseEvent>
#include <QDebug>
#include <QDesktopServices>
#include <QFile>
#include <QFileInfo>
#include <QHeaderView>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
#include <QStandardItemModel>
#include <QDesktopServices>
#include <QUrl>
#include <QVersionNumber>
#include <QKeyEvent>
#include "components/plugins/toolbar/QvToolbar.hpp"
#include "components/pac/QvPACHandler.hpp"
#include "core/connection/ConnectionIO.hpp"
#include "ui/widgets/ConnectionInfoWidget.hpp"
// MainWindow.cpp --> Main MainWindow source file, handles mostly UI-related operations.
// MainWindow.cpp --> Main MainWindow source file, handles mostly UI-related
// operations.
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
//
#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_) \
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.") + \
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) { \
if (QvMessageBoxAsk(this, QObject::tr("Editing a subscription config"),
QObject::tr("You are trying to edit a config loaded from subscription.") + \
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; \
} \
} \
@ -51,22 +58,25 @@
#define SUBSCRIPTION_CONFIG_MODIFY_DENY(_item_) \
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; \
} \
#define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 && (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION)))
#define IsSelectionConnectable (!connectionListWidget->selectedItems().empty() && IsConnectableItem(connectionListWidget->selectedItems().first()))
#define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 &&
(CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION))) #define
IsSelectionConnectable (!connectionListWidget->selectedItems().empty() &&
IsConnectableItem(connectionListWidget->selectedItems().first()))
*/
MainWindow *MainWindow::mwInstance = nullptr;
QvMessageBusSlotImpl(MainWindow)
{
switch (msg) {
MBShowDefaultImpl
MBHideDefaultImpl
MBRetranslateDefaultImpl
switch (msg)
{
MBShowDefaultImpl MBHideDefaultImpl 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_ShowPreferencesWindow, &QAction::triggered, this, &MainWindow::on_preferencesBtn_clicked);
//connect(action_Tray_Start, &QAction::triggered, this, &MainWindow::on_startButton_clicked);
//connect(action_Tray_Stop, &QAction::triggered, this, &MainWindow::on_stopButton_clicked);
//connect(action_Tray_Reconnect, &QAction::triggered, this, &MainWindow::on_reconnectButton_clicked);
// connect(action_Tray_Start, &QAction::triggered, this,
// &MainWindow::on_startButton_clicked); connect(action_Tray_Stop,
// &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_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy);
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);
//
// Globally invokable signals.
//connect(this, &MainWindow::Connect, this, &MainWindow::on_startButton_clicked);
//connect(this, &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked);
//connect(this, &MainWindow::ReConnect, this, &MainWindow::on_reconnectButton_clicked);
// connect(this, &MainWindow::Connect, this,
// &MainWindow::on_startButton_clicked); connect(this,
// &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked);
// connect(this, &MainWindow::ReConnect, this,
// &MainWindow::on_reconnectButton_clicked);
//
hTray.setContextMenu(tray_RootMenu);
hTray.show();
@ -183,13 +197,15 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
LOG(MODULE_UI, "Loading data...")
auto groups = ConnectionManager->AllGroups();
for (auto group : groups) {
for (auto group : groups)
{
auto groupItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(group));
connectionListWidget->addTopLevelItem(groupItem);
connectionListWidget->setItemWidget(groupItem, 0, new ConnectionItemWidget(group, connectionListWidget));
auto connections = ConnectionManager->Connections(group);
for (auto connection : connections) {
for (auto connection : connections)
{
auto connectionItem = new QTreeWidgetItem(QStringList() << "" << ConnectionManager->GetDisplayName(connection));
groupItem->addChild(connectionItem);
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
auto needShowWindow = true;
if (!GlobalConfig.autoStartId.isEmpty()) {
if (!GlobalConfig.autoStartId.isEmpty())
{
auto id = ConnectionId(GlobalConfig.autoStartId);
needShowWindow = ConnectionManager->StartConnection(id).has_value();
}
if (needShowWindow) {
this->show();
}
if (needShowWindow) { this->show(); }
//// If we are not connected to anything, show the MainWindow.
if (needShowWindow) {
}
if (needShowWindow) {}
#ifndef DISABLE_AUTO_UPDATE
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");
#endif
if (StartupOption.enableToolbarPlguin) {
if (StartupOption.enableToolbarPlguin)
{
LOG(MODULE_UI, "Plugin daemon is enabled.")
StartProcessingPlugins();
}
@ -231,10 +246,10 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
splitter->setSizes(QList<int>() << 100 << 300);
}
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.
// Try to connect to the selected connection.
// if (focusWidget() == connectionListWidget) {
@ -243,7 +258,8 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
// auto selections = connectionListWidget->selectedItems();
// QVariant v;
// 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()
{
// if (!IsSelectionConnectable) {
// QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please select a config from the list."));
// return;
// QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please
// select a config from the list.")); return;
//}
//
// CurrentSelectedItemPtr = connectionListWidget->selectedItems().first();
//CurrentConnectionIdentifier = ItemConnectionIdentifier(CurrentSelectedItemPtr);
// CurrentConnectionIdentifier =
// ItemConnectionIdentifier(CurrentSelectedItemPtr);
// 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 currentVersion = QVersionNumber::fromString(QString(QV2RAY_VERSION_STRING).remove(0, 1));
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.
// 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.")
auto link = root["html_url"].toString("");
auto result = QvMessageBoxAsk(this, tr("Update"),
tr("Found a new version: ") + root["tag_name"].toString("") +
"\r\n" +
root["name"].toString("") +
"\r\n------------\r\n" +
root["body"].toString("") +
"\r\n------------\r\n" +
tr("Download Link: ") + link, QMessageBox::Ignore);
auto result =
QvMessageBoxAsk(this, tr("Update"),
tr("Found a new version: ") + root["tag_name"].toString("") + "\r\n" + root["name"].toString("") +
"\r\n------------\r\n" + root["body"].toString("") + "\r\n------------\r\n" + tr("Download Link: ") + link,
QMessageBox::Ignore);
if (result == QMessageBox::Yes) {
QDesktopServices::openUrl(QUrl::fromUserInput(link));
} else if (result == QMessageBox::Ignore) {
if (result == QMessageBox::Yes) { QDesktopServices::openUrl(QUrl::fromUserInput(link)); }
else if (result == QMessageBox::Ignore)
{
// Set and save ingored version.
GlobalConfig.ignoredVersion = newVersion.toString();
// SaveGlobalConfig(GlobalConfig);
@ -309,12 +325,14 @@ void MainWindow::closeEvent(QCloseEvent *event)
}
void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) {
switch (reason)
{
case QSystemTrayIcon::Trigger:
// Toggle Show/Hide
#ifndef __APPLE__
// 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();
#endif
break;
@ -325,13 +343,13 @@ void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
#endif
break;
default:
break;
default: break;
}
}
void MainWindow::ToggleVisibility()
{
if (this->isHidden()) {
if (this->isHidden())
{
this->show();
#ifdef Q_OS_WIN
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);
#endif
tray_RootMenu->actions()[0]->setText(tr("Hide"));
} else {
}
else
{
this->hide();
tray_RootMenu->actions()[0]->setText(tr("Show"));
}
@ -348,9 +368,7 @@ void MainWindow::ToggleVisibility()
void MainWindow::on_actionExit_triggered()
{
if (StartupOption.enableToolbarPlguin) {
StopProcessingPlugins();
}
if (StartupOption.enableToolbarPlguin) { StopProcessingPlugins(); }
ExitQv2ray();
}
@ -372,7 +390,8 @@ void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *cur
// if (!IsConnectableItem(current)) return;
//
//// 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);
}
void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos)
@ -381,9 +400,7 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint
auto _pos = QCursor::pos();
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
if (GetItemWidget(item)->IsConnection()) {
connectionListMenu->popup(_pos);
}
if (GetItemWidget(item)->IsConnection()) { connectionListMenu->popup(_pos); }
}
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)
{
//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) {
// return;
@ -407,26 +425,30 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
//// and tell user you should not rename a config from subscription.
// auto newIdentifier = renameOriginalIdentifier;
// 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 (renameOriginalIdentifier != newIdentifier) {
// bool canContinueRename = true;
//
// if (newIdentifier.connectionName.trimmed().isEmpty()) {
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name cannot be empty"));
// canContinueRename = false;
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name
// cannot be empty")); canContinueRename = false;
// }
//
// QvMessageBoxInfo(this, "NOT SUPPORTED", "WIP");
// //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;
// //}
//
// if (!IsValidFileName(newIdentifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION)) {
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name you suggested is not valid, please try another."));
// canContinueRename = false;
// if (!IsValidFileName(newIdentifier.connectionName +
// QV2RAY_CONFIG_FILE_EXTENSION)) {
// QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name you
// suggested is not valid, please try another.")); canContinueRename
// = false;
// }
//
// if (!canContinueRename) {
@ -448,8 +470,10 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
// //GlobalConfig.configs.removeOne(renameOriginalIdentifier.connectionName);
// //GlobalConfig.configs.push_back(newIdentifier.connectionName);
// //
// //connections[newIdentifier] = connections.take(renameOriginalIdentifier);
// //RenameConnection(renameOriginalIdentifier.connectionName, newIdentifier.connectionName);
// //connections[newIdentifier] =
// connections.take(renameOriginalIdentifier);
// //RenameConnection(renameOriginalIdentifier.connectionName,
// newIdentifier.connectionName);
// //LOG(UI, "Saving a global config")
// //SaveGlobalConfig(GlobalConfig);
// ////
@ -462,7 +486,8 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
// // on_reconnectButton_clicked();
// // }
// //}
// //OnConfigListChanged(CurrentConnectionIdentifier.connectionName == renameOriginalName);
// //OnConfigListChanged(CurrentConnectionIdentifier.connectionName ==
// renameOriginalName);
//}
}
void MainWindow::on_removeConfigButton_clicked()
@ -483,11 +508,13 @@ void MainWindow::on_removeConfigButton_clicked()
// 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;
//}
//
//// 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;
//
// for (auto conn : connlist) {
@ -499,8 +526,10 @@ void MainWindow::on_removeConfigButton_clicked()
// auto connData = connections[conn];
//
// // Remove auto start config.
// if (GlobalConfig.autoStartConfig.subscriptionName == connData.subscriptionName &&
// GlobalConfig.autoStartConfig.connectionName == connData.connectionName)
// if (GlobalConfig.autoStartConfig.subscriptionName ==
// connData.subscriptionName &&
// GlobalConfig.autoStartConfig.connectionName ==
// connData.connectionName)
// // If all those settings match.
// {
// GlobalConfig.autoStartConfig.subscriptionName.clear();
@ -510,29 +539,38 @@ void MainWindow::on_removeConfigButton_clicked()
// if (connData.configType == CONNECTION_REGULAR) {
// // Just remove the regular configs.
// if (!connData.subscriptionName.isEmpty()) {
// LOG(UI, "Unexpected subscription name in a single regular config.")
// connData.subscriptionName.clear();
// LOG(UI, "Unexpected subscription name in a single regular
// config.") connData.subscriptionName.clear();
// }
//
// GlobalConfig.configs.removeOne(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) {
// 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
// : 0; // No please keep
// }
//
// if (subscriptionRemovalCheckStatus == 1) {
// if (!RemoveSubscriptionConnection(connData.subscriptionName, connData.connectionName)) {
// QvMessageBoxWarn(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
// if (!RemoveSubscriptionConnection(connData.subscriptionName,
// connData.connectionName)) {
// QvMessageBoxWarn(this, tr("Removing this Connection"),
// tr("Failed to delete connection file, please delete
// manually."));
// }
// }
// } 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...
// if (!IsSelectionConnectable) {
// QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a Config"));
// return;
// QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a
// Config")); return;
//}
//
// auto selectedFirst = connectionListWidget->currentItem();
@ -616,9 +654,7 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item
Q_UNUSED(column)
auto widget = GetItemWidget(item);
if (widget->IsConnection()) {
widget->BeginConnection();
}
if (widget->IsConnection()) { widget->BeginConnection(); }
}
void MainWindow::OnDisConnected(const ConnectionId &id)
@ -628,13 +664,12 @@ void MainWindow::OnDisConnected(const ConnectionId &id)
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE);
connetionStatusLabel->setText(tr("Disconnected"));
if (systemProxyEnabled) {
MWClearSystemProxy(false);
}
if (systemProxyEnabled) { MWClearSystemProxy(false); }
// QFile(QV2RAY_GENERATED_FILE_PATH).remove();
if (GlobalConfig.inboundConfig.pacConfig.enablePAC) {
if (GlobalConfig.inboundConfig.pacConfig.enablePAC)
{
// pacServer.StopServer();
LOG(MODULE_UI, "Stopping PAC server")
}
@ -655,56 +690,64 @@ void MainWindow::OnConnected(const ConnectionId &id)
bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
if (usePAC) {
if (usePAC)
{
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;
if (pacIP.isEmpty()) {
if (pacIP.isEmpty())
{
LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1")
pacIP = "127.0.0.1";
}
if (pacUseSocks) {
if (socksEnabled) {
pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port);
} else {
if (pacUseSocks)
{
if (socksEnabled) { pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port); }
else
{
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;
}
} else {
if (httpEnabled) {
pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port);
} else {
}
else
{
if (httpEnabled) { pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port); }
else
{
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;
}
}
if (canStartPAC) {
if (canStartPAC)
{
// pacServer.SetProxyString(pacProxyString);
// pacServer.StartListen();
} else {
}
else
{
LOG(MODULE_PROXY, "Not starting PAC due to previous error.")
}
}
if (GlobalConfig.inboundConfig.setSystemProxy) {
MWSetSystemProxy();
if (GlobalConfig.inboundConfig.setSystemProxy) { MWSetSystemProxy(); }
}
}
void MainWindow::onConnectionWidgetFocusRequested(const ConnectionItemWidget *_widget)
{
if (_widget == nullptr) {
return;
}
if (_widget == nullptr) { return; }
for (auto _item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive)) {
if (GetItemWidget(_item_) == _widget) {
for (auto _item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive))
{
if (GetItemWidget(_item_) == _widget)
{
LOG(MODULE_UI, "Setting current item.")
connectionListWidget->setCurrentItem(_item_);
connectionListWidget->scrollToItem(_item_);
@ -717,28 +760,31 @@ void MainWindow::onConnectionWidgetFocusRequested(const ConnectionItemWidget *_w
void MainWindow::on_connectionFilterTxt_textEdited(const QString &arg1)
{
// 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_);
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);
if (GetItemWidget(_child_)->NameMatched(arg1)) {
if (GetItemWidget(_child_)->NameMatched(arg1))
{
LOG(MODULE_UI, "Setting current item.")
// Show the child
_child_->setHidden(false);
isTotallyHide = false;
} else {
}
else
{
_child_->setHidden(true);
}
}
_top_item_->setHidden(isTotallyHide);
if (!isTotallyHide) {
connectionListWidget->expandItem(_top_item_);
}
if (!isTotallyHide) { connectionListWidget->expandItem(_top_item_); }
}
}
@ -748,11 +794,12 @@ void MainWindow::on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int
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);
// This may not be, or may not precisely be, speed per second if low-level has "any" latency.
// (Hope not...)
// This may not be, or may not precisely be, speed per second if low-level
// has "any" latency. (Hope not...)
speedChartWidget->AddPointData(upSpeed, downSpeed);
//
auto totalSpeedUp = FormatBytes(upSpeed) + "/s";
@ -763,7 +810,8 @@ void MainWindow::onConnectionStatsArrived(const ConnectionId &id, const quint64
netspeedLabel->setText(totalSpeedUp + NEWLINE + totalSpeedDown);
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)
@ -777,35 +825,40 @@ void MainWindow::onVCoreLogArrived(const ConnectionId &id, const QString &log)
auto maxLines = GlobalConfig.uiConfig.maximumLogLines;
QTextBlock block = masterLogBrowser->document()->begin();
while (block.isValid()) {
if (masterLogBrowser->document()->blockCount() > maxLines) {
while (block.isValid())
{
if (masterLogBrowser->document()->blockCount() > maxLines)
{
QTextCursor cursor(block);
block = block.next();
cursor.select(QTextCursor::BlockUnderCursor);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
} else {
}
else
{
break;
}
}
if (val >= max * 0.8 || val >= max - 20)
bar->setValue(max);
if (val >= max * 0.8 || val >= max - 20) bar->setValue(max);
}
void MainWindow::OnEditRequested(const ConnectionId &id)
{
auto outBoundRoot = ConnectionManager->GetConnectionRoot(id);
CONFIGROOT root;
bool isChanged = false;
if (IsComplexConfig(outBoundRoot)) {
if (IsComplexConfig(outBoundRoot))
{
LOG(MODULE_UI, "INFO: Opening route editor.")
RouteEditor routeWindow(outBoundRoot, this);
root = routeWindow.OpenEditor();
isChanged = routeWindow.result() == QDialog::Accepted;
} else {
}
else
{
LOG(MODULE_UI, "INFO: Opening single connection edit window.")
auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject());
OutboundEditor w(out, this);
@ -816,18 +869,21 @@ void MainWindow::OnEditRequested(const ConnectionId &id)
root.insert("outbounds", outboundsList);
}
if (isChanged) {
if (isChanged)
{
// if (CheckConfigType(firstSelected, SUBSCRIPTION)) {
// auto name = connections[_identifier].connectionName;
// // Assume name will not change.
// SaveSubscriptionConfig(root, connections[_identifier].subscriptionName, &name);
// SaveSubscriptionConfig(root,
// connections[_identifier].subscriptionName, &name);
//} else {
// connections[_identifier].config = root;
// // true indicates the alias will NOT change
// SaveConnectionConfig(root, &alias, true);
//}
ConnectionManager->UpdateConnection(id, root);
//OnConfigListChanged(alias == CurrentConnectionIdentifier.connectionName);
// OnConfigListChanged(alias ==
// CurrentConnectionIdentifier.connectionName);
}
}
void MainWindow::OnJsonEditRequested(const ConnectionId &id)
@ -835,7 +891,5 @@ void MainWindow::OnJsonEditRequested(const ConnectionId &id)
JsonEditor w(ConnectionManager->GetConnectionRoot(id), this);
auto root = CONFIGROOT(w.OpenEditor());
if (w.result() == QDialog::Accepted) {
ConnectionManager->UpdateConnection(id, root);
}
if (w.result() == QDialog::Accepted) { ConnectionManager->UpdateConnection(id, root); }
}

View File

@ -1,23 +1,24 @@
#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 <QMenu>
#include <QScrollBar>
#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/ConnectionItemWidget.hpp"
class MainWindow : public QMainWindow, Ui::MainWindow
class MainWindow
: public QMainWindow
, Ui::MainWindow
{
Q_OBJECT
public:
@ -28,7 +29,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow
void DisConnect() const;
void ReConnect() const;
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_action_RCM_ShareQR_triggered();
void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
@ -66,7 +67,8 @@ class MainWindow : public QMainWindow, Ui::MainWindow
void onConnectionWidgetFocusRequested(const ConnectionItemWidget *widget);
// void onConnectionConnected(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 on_action_StartThis_triggered();

View File

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

View File

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

View File

@ -1,11 +1,14 @@
#pragma once
#include <QDialog>
#include <ui_w_PreferencesWindow.h>
#include "base/Qv2rayBase.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
@ -16,7 +19,7 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
void s_reload_config(bool need_restart);
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_buttonBox_accepted();

View File

@ -1,15 +1,18 @@
#include "w_ScreenShot_Core.hpp"
#include "common/QvHelpers.hpp"
#include <QMessageBox>
#include <QThread>
#include <QStyleFactory>
#include <QThread>
#define QV2RAY_SCREENSHOT_DIM_RATIO 0.6f
ScreenShotWindow::ScreenShotWindow() : QDialog(), rubber(new QRubberBand(QRubberBand::Rectangle, 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"));
//
label->setAttribute(Qt::WA_TranslucentBackground);
@ -30,8 +33,9 @@ ScreenShotWindow::ScreenShotWindow() : QDialog(), rubber(new QRubberBand(QRubber
QImage ScreenShotWindow::DoScreenShot()
{
LOG(MODULE_IMPORT, "We currently only support the current screen.")
// The msleep is the only solution which prevent capturing our windows again.
// It works on KDE, https://www.qtcentre.org/threads/55708-Get-Desktop-Screenshot-Without-Application-Window-Being-Shown?p=248993#post248993
// The msleep is the only solution which prevent capturing our windows
// 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);
QApplication::processEvents();
//
@ -45,8 +49,10 @@ QImage ScreenShotWindow::DoScreenShot()
int r, g, b;
auto _xdesktopImg = desktopImage.toImage();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
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);
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))
rubber->setGeometry(imgX, imgY, imgW, imgH);
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);
}
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)
{
if (e->key() == Qt::Key_Escape) {
reject();
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (e->key() == Qt::Key_Escape) { reject(); }
else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
{
on_startBtn_clicked();
}
}
@ -109,7 +117,8 @@ void ScreenShotWindow::mousePressEvent(QMouseEvent *e)
void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
{
if (e->buttons() & Qt::LeftButton) {
if (e->buttons() & Qt::LeftButton)
{
end = e->pos();
pSize();
//
@ -120,15 +129,15 @@ void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
QRect labelRect(label->contentsRect());
QRect btnRect(startBtn->contentsRect());
if (imgY > labelRect.height()) {
label->move(imgX, imgY - labelRect.height());
} else {
if (imgY > labelRect.height()) { label->move(imgX, imgY - labelRect.height()); }
else
{
label->move(imgX, imgY);
}
if (height() - imgY - imgH > btnRect.height()) {
startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH);
} else {
if (height() - imgY - imgH > btnRect.height()) { startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH); }
else
{
startBtn->move(imgX + imgW - btnRect.width(), imgY + imgH - btnRect.height());
}
@ -137,12 +146,9 @@ void ScreenShotWindow::mouseMoveEvent(QMouseEvent *e)
}
}
void ScreenShotWindow::mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::RightButton) {
reject();
}
if (e->button() == Qt::RightButton) { reject(); }
}
ScreenShotWindow::~ScreenShotWindow()

View File

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

View File

@ -1,27 +1,25 @@
#include "w_SubscriptionManager.hpp"
#include "common/QvHelpers.hpp"
#include "core/config/ConfigBackend.hpp"
#include "core/handler/ConnectionHandler.hpp"
SubscribeEditor::SubscribeEditor(QWidget *parent) :
QDialog(parent)
SubscribeEditor::SubscribeEditor(QWidget *parent) : QDialog(parent)
{
setupUi(this);
QvMessageBusConnect(SubscribeEditor);
addSubsButton->setIcon(QICON_R("add.png"));
removeSubsButton->setIcon(QICON_R("delete.png"));
for (auto subs : ConnectionManager->Subscriptions()) {
subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList() << ConnectionManager->GetDisplayName(subs) << subs.toString()));
}
for (auto subs : ConnectionManager->Subscriptions())
{ subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList() << ConnectionManager->GetDisplayName(subs) << subs.toString())); }
}
QvMessageBusSlotImpl(SubscribeEditor)
{
switch (msg) {
MBShowDefaultImpl
MBHideDefaultImpl
MBRetranslateDefaultImpl
switch (msg)
{
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
}
}
@ -46,22 +44,26 @@ void SubscribeEditor::on_addSubsButton_clicked()
void SubscribeEditor::on_updateButton_clicked()
{
ConnectionManager->UpdateSubscription(currentSubId, withProxyCB->isChecked());
// auto newName = subNameTxt->text().trimmed();
// auto newAddress = subAddrTxt->text().trimmed();
// auto newUpdateInterval = updateIntervalSB->value();
// if (currentSubId != newName) {
// // Rename needed.
// LOG(MODULE_SUBSCRIPTION, "Renaming a subscription, from " + currentSubId + " to: " + newName)
// bool canGo = true;
// LOG(MODULE_SUBSCRIPTION, "Renaming a subscription, from " +
// currentSubId + " to: " + newName) bool canGo = true;
//
// if (newName.isEmpty() || !IsValidFileName(newName)) {
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("The subscription name is invalid, please try another."));
// canGo = false;
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("The
// subscription name is invalid, please try another.")); canGo =
// false;
// }
//
// if (subscriptionList->findItems(newName, Qt::MatchExactly).count() > 0) {
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("New name of this subscription has been used already, please suggest another one"));
// canGo = false;
// if (subscriptionList->findItems(newName, Qt::MatchExactly).count() >
// 0) {
// QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("New name
// of this subscription has been used already, please suggest another
// one")); canGo = false;
// }
//
// if (!canGo) {
@ -72,7 +74,8 @@ void SubscribeEditor::on_updateButton_clicked()
// ////bool result = RenameSubscription(currentSubName, newName);
// //
// //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;
// //}
// subscriptions[newName] = subscriptions[currentSubId];
@ -93,55 +96,32 @@ void SubscribeEditor::on_updateButton_clicked()
// // Update thing still down
// subAddrTxt->setText(newAddress);
// 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;
//
// 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;
//}
//
////SaveConfig();
//
////if (QvMessageBoxAsk(this, tr("Update Subscription"), tr("Would you like to reload this subscription from the Url?")) == QMessageBox::Yes) {
//// StartUpdateSubscription(currentSubId);
////if (QvMessageBoxAsk(this, tr("Update Subscription"), tr("Would you like
/// to reload this subscription from the Url?")) == QMessageBox::Yes) { /
/// StartUpdateSubscription(currentSubId);
////}
}
void SubscribeEditor::StartUpdateSubscription(const QString &subscriptionName)
{
this->setEnabled(false);
// auto data = helper.syncget(subscriptions[subscriptionName].address, withProxyCB->isChecked());
// auto content = DecodeSubscriptionString(data).trimmed();
//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."));
//}
//
// auto data = helper.syncget(subscriptions[subscriptionName].address,
// withProxyCB->isChecked()); auto content =
this->setEnabled(true);
}
@ -195,9 +175,7 @@ void SubscribeEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, int
{
Q_UNUSED(column)
if (item == nullptr) {
return;
}
if (item == nullptr) { return; }
currentSubId = GroupId(item->text(1));
//
@ -209,7 +187,5 @@ void SubscribeEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, int
//
connectionsList->clear();
for (auto conn : ConnectionManager->Connections(currentSubId)) {
connectionsList->addItem(ConnectionManager->GetDisplayName(conn));
}
for (auto conn : ConnectionManager->Connections(currentSubId)) { connectionsList->addItem(ConnectionManager->GetDisplayName(conn)); }
}

View File

@ -1,12 +1,15 @@
#pragma once
#include <QDialog>
#include "base/Qv2rayBase.hpp"
#include "ui_w_SubscriptionManager.h"
#include "ui/messaging/QvMessageBus.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
@ -16,7 +19,7 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
QPair<QString, CONFIGROOT> GetSelectedConfig();
public slots:
QvMessageBusSlotDecl
QvMessageBusSlotDecl;
private slots:
void on_addSubsButton_clicked();

View File

@ -1,7 +1,8 @@
#include "ConnectionInfoWidget.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
#include "core/CoreUtils.hpp"
#include "core/connection/Serialization.hpp"
#include "3rdparty/qzxing/src/QZXing.h"
ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent)
{
@ -12,7 +13,8 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent): QWidget(parent)
editJsonBtn->setIcon(QICON_R("json.png"));
//
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->installEventFilter(this);
//
@ -30,10 +32,12 @@ void ConnectionInfoWidget::ShowDetails(const tuple<GroupId, ConnectionId> &_iden
duplicateBtn->setEnabled(isConnection);
editBtn->setEnabled(isConnection);
if (isConnection) {
if (isConnection)
{
groupLabel->setText(ConnectionManager->GetDisplayName(groupId, 175));
protocolLabel->setText(ConnectionManager->GetConnectionProtocolString(connectionId));
auto [host, port] = ConnectionManager->GetConnectionInfo(connectionId);
auto [protocol, host, port] = ConnectionManager->GetConnectionData(connectionId);
Q_UNUSED(protocol)
addressLabel->setText(host);
portLabel->setNum(port);
//
@ -48,7 +52,9 @@ void ConnectionInfoWidget::ShowDetails(const tuple<GroupId, ConnectionId> &_iden
qrLabel->setPixmap(QPixmap::fromImage(img));
//
connectBtn->setIcon(ConnectionManager->IsConnected(connectionId) ? QICON_R("stop.png") : QICON_R("connect.png"));
} else {
}
else
{
connectBtn->setIcon(QICON_R("connect.png"));
groupLabel->setText(tr("N/A"));
protocolLabel->setText(tr("N/A"));
@ -66,9 +72,9 @@ ConnectionInfoWidget::~ConnectionInfoWidget()
void ConnectionInfoWidget::on_connectBtn_clicked()
{
if (ConnectionManager->IsConnected(connectionId)) {
ConnectionManager->StopConnection();
} else {
if (ConnectionManager->IsConnected(connectionId)) { ConnectionManager->StopConnection(); }
else
{
ConnectionManager->StartConnection(connectionId);
}
}
@ -90,11 +96,11 @@ void ConnectionInfoWidget::on_deleteBtn_clicked()
bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease) {
if (shareLinkTxt->underMouse()) {
if (!shareLinkTxt->hasSelectedText()) {
shareLinkTxt->selectAll();
}
if (event->type() == QEvent::MouseButtonRelease)
{
if (shareLinkTxt->underMouse())
{
if (!shareLinkTxt->hasSelectedText()) { shareLinkTxt->selectAll(); }
}
}
@ -103,16 +109,12 @@ bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event)
void ConnectionInfoWidget::OnConnected(const ConnectionId &id)
{
if (connectionId == id) {
connectBtn->setIcon(QICON_R("stop.png"));
}
if (connectionId == id) { connectBtn->setIcon(QICON_R("stop.png")); }
}
void ConnectionInfoWidget::OnDisConnected(const ConnectionId &id)
{
if (connectionId == id) {
connectBtn->setIcon(QICON_R("connect.png"));
}
if (connectionId == id) { connectBtn->setIcon(QICON_R("connect.png")); }
}
// MWTryPingConnection(CurrentConnectionIdentifier);
void ConnectionInfoWidget::on_duplicateBtn_clicked()
@ -131,10 +133,13 @@ void ConnectionInfoWidget::on_duplicateBtn_clicked()
// bool isComplex = IsComplexConfig(connections[_identifier].config);
//
// 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 {
// conf = ConvertConfigFromFile(QV2RAY_SUBSCRIPTION_DIR + _identifier.subscriptionName + "/" + _identifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION, isComplex);
// alias = _identifier.subscriptionName + "_" + _identifier.connectionName;
// conf = ConvertConfigFromFile(QV2RAY_SUBSCRIPTION_DIR +
// _identifier.subscriptionName + "/" + _identifier.connectionName +
// QV2RAY_CONFIG_FILE_EXTENSION, isComplex); alias =
// _identifier.subscriptionName + "_" + _identifier.connectionName;
//}
//
// SaveConnectionConfig(conf, &alias, false);
@ -145,9 +150,9 @@ void ConnectionInfoWidget::on_duplicateBtn_clicked()
void ConnectionInfoWidget::on_latencyBtn_clicked()
{
if (connectionId != NullConnectionId) {
ConnectionManager->StartLatencyTest(connectionId);
} else {
if (connectionId != NullConnectionId) { ConnectionManager->StartLatencyTest(connectionId); }
else
{
ConnectionManager->StartLatencyTest(groupId);
}
}

View File

@ -1,10 +1,13 @@
#pragma once
#include <QWidget>
#include "ui_ConnectionInfoWidget.h"
#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
@ -36,4 +39,3 @@ class ConnectionInfoWidget : public QWidget, private Ui::ConnectionInfoWidget
ConnectionId connectionId = NullConnectionId;
GroupId groupId = NullGroupId;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#include "StreamSettingsWidget.hpp"
#include "common/QvHelpers.hpp"
#include "ui/editors/w_JsonEditor.hpp"
StreamSettingsWidget::StreamSettingsWidget(QWidget *parent) :
QWidget(parent)
StreamSettingsWidget::StreamSettingsWidget(QWidget *parent) : QWidget(parent)
{
setupUi(this);
}
@ -34,9 +34,8 @@ void StreamSettingsWidget::SetStreamObject(StreamSettingsObject sso)
wsPathTxt->setText(stream.wsSettings.path);
QString wsHeaders;
for (auto item = stream.wsSettings.headers.begin(); item != stream.wsSettings.headers.end(); item++) {
wsHeaders += item.key() + "|" + item.value() + NEWLINE;
}
for (auto item = stream.wsSettings.headers.begin(); item != stream.wsSettings.headers.end(); item++)
{ wsHeaders += item.key() + "|" + item.value() + NEWLINE; }
wsHeadersTxt->setPlainText(wsHeaders);
// mKCP
@ -60,7 +59,6 @@ void StreamSettingsWidget::SetStreamObject(StreamSettingsObject sso)
soMarkSpinBox->setValue(stream.sockopt.mark);
}
void StreamSettingsWidget::on_transportCombo_currentIndexChanged(int index)
{
v2rayStackView->setCurrentIndex(index);
@ -73,29 +71,33 @@ void StreamSettingsWidget::on_httpPathTxt_textEdited(const QString &arg1)
void StreamSettingsWidget::on_httpHostTxt_textChanged()
{
try {
try
{
QStringList hosts = httpHostTxt->toPlainText().replace("\r", "").split("\n");
stream.httpSettings.host.clear();
for (auto host : hosts) {
if (!host.trimmed().isEmpty()) {
stream.httpSettings.host.push_back(host.trimmed());
}
for (auto host : hosts)
{
if (!host.trimmed().isEmpty()) { stream.httpSettings.host.push_back(host.trimmed()); }
}
BLACK(httpHostTxt)
} catch (...) {
}
catch (...)
{
RED(httpHostTxt)
}
}
void StreamSettingsWidget::on_wsHeadersTxt_textChanged()
{
try {
try
{
QStringList headers = SplitLines(wsHeadersTxt->toPlainText());
stream.wsSettings.headers.clear();
for (auto header : headers) {
for (auto header : headers)
{
if (header.isEmpty()) continue;
auto index = header.indexOf("|");
@ -108,12 +110,13 @@ void StreamSettingsWidget::on_wsHeadersTxt_textChanged()
}
BLACK(wsHeadersTxt)
} catch (...) {
}
catch (...)
{
RED(wsHeadersTxt)
}
}
void StreamSettingsWidget::on_tcpRequestDefBtn_clicked()
{
tcpRequestTxt->clear();
@ -130,7 +133,8 @@ void StreamSettingsWidget::on_tcpRequestDefBtn_clicked()
void StreamSettingsWidget::on_tcpRespDefBtn_clicked()
{
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)

View File

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