Merge branch 'dev' into dev-group-routing

This commit is contained in:
Qv2ray-dev 2020-05-15 00:06:57 +08:00 committed by GitHub
commit f10b35f34f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1289 additions and 215 deletions

View File

@ -10,7 +10,7 @@ env:
addons:
snaps:
- name: snapcraft
channel: stable
channel: edge
confinement: classic
- name: lxd
channel: stable

View File

@ -29,10 +29,12 @@ add_definitions(-DQV2RAY_VERSION_BUGFIX=${CPACK_PACKAGE_VERSION_PATCH})
add_definitions(-DQV2RAY_VERSION_BUILD=${QV2RAY_BUILD_VERSION})
add_definitions(-DQV2RAY_VERSION_STRING="${QV2RAY_VERSION_STRING}")
add_definitions(-DXTOSTRUCT_QT)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
find_package(Qt5 5.11 COMPONENTS Core Gui Widgets Network REQUIRED)
@ -145,6 +147,10 @@ if(QV2RAY_DISABLE_AUTO_UPDATE)
add_definitions(-DDISABLE_AUTO_UPDATE)
endif()
if(FALL_BACK_TO_XDG_OPEN)
add_definitions(-DFALL_BACK_TO_XDG_OPEN)
endif()
set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface")
include(src/components/plugins/interface/QvPluginInterface.cmake)
@ -168,11 +174,13 @@ set(QV2RAY_SOURCES
src/components/plugins/toolbar/QvToolbar_linux.cpp
src/components/plugins/toolbar/QvToolbar_win.cpp
src/components/plugins/QvPluginHost.cpp
src/components/port/QvPortDetector.cpp
src/components/proxy/QvProxyConfigurator.cpp
src/components/route/RouteSchemeIO.cpp
src/components/speedchart/speedplotview.cpp
src/components/speedchart/speedwidget.cpp
src/components/darkmode/DarkmodeDetector.cpp
src/components/ntp/QvNTPClient.cpp
src/components/update/UpdateChecker.cpp
src/core/connection/ConnectionIO.cpp
src/core/connection/Generation.cpp
@ -257,7 +265,9 @@ set(QV2RAY_SOURCES
src/components/latency/QvTCPing.hpp
src/components/plugins/toolbar/QvToolbar.hpp
src/components/plugins/QvPluginHost.hpp
src/components/port/QvPortDetector.hpp
src/components/proxy/QvProxyConfigurator.hpp
src/components/ntp/QvNTPClient.hpp
src/components/route/RouteSchemeIO.hpp
src/components/route/presets/RouteScheme_V2rayN.hpp
src/components/speedchart/speedplotview.hpp

View File

@ -1 +1 @@
5391
5400

View File

@ -1,5 +1,5 @@
name: qv2ray
base: core18
base: core20
adopt-info: qv2ray
icon: assets/icons/qv2ray.png
@ -57,7 +57,6 @@ parts:
- libgrpc++-dev
- libprotobuf-dev
- protobuf-compiler-grpc
- ninja-build
- pkg-config
stage-packages:
- libgcc1
@ -70,11 +69,11 @@ parts:
- libqt5network5
- libqt5widgets5
- libglib2.0-bin
configflags:
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -GNinja
- -DEMBED_TRANSLATIONS=ON
- -DFALL_BACK_TO_XDG_OPEN=ON
override-pull: |
snapcraftctl pull
build_number=$(cat makespec/BUILDVERSION)
@ -84,7 +83,6 @@ parts:
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop
after:
- desktop-qt5
- ppa
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
@ -110,21 +108,8 @@ parts:
- locales-all
- xdg-user-dirs
- fcitx-frontend-qt5
after:
- ppa
qt5-gtk-platform:
plugin: nil
stage-packages:
- qt5-gtk-platformtheme
after:
- ppa
ppa:
plugin: nil
build-packages:
- software-properties-common
- dirmngr
override-build: |
sudo add-apt-repository -y ppa:ymshenyu/qv2ray-deps
sudo apt-get dist-upgrade -y

View File

@ -8,6 +8,7 @@ namespace Qv2ray::base
struct Qv2rayRuntimeConfig
{
bool screenShotHideQv2ray = false;
bool deepinHorribleProxyHint = false;
};
inline base::Qv2rayRuntimeConfig RuntimeConfig = base::Qv2rayRuntimeConfig();
} // namespace Qv2ray::base

View File

@ -127,7 +127,7 @@ namespace Qv2ray::base::config
Qv2rayRouteConfig_Impl(){};
friend bool operator==(const Qv2rayRouteConfig_Impl &left, const Qv2rayRouteConfig_Impl &right)
{
return left.direct == right.direct && left.block == right.block && left.proxy == left.proxy;
return left.direct == right.direct && left.block == right.block && left.proxy == right.proxy;
}
Qv2rayRouteConfig_Impl(const QList<QString> &_direct, const QList<QString> &_block, const QList<QString> &_proxy)
: direct(_direct), block(_block), proxy(_proxy){};

View File

@ -111,6 +111,31 @@ namespace Qv2ray::common
return doc.object();
}
// backported from QvPlugin-SSR.
QString SafeBase64Decode(QString string) {
QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8();
return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals);
}
// backported from QvPlugin-SSR.
QString SafeBase64Encode(const QString &string, bool trim)
{
QString base64 = string.toUtf8().toBase64();
if (trim)
{
auto tmp = base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_'));
auto crbedin = tmp.crbegin();
auto idx = tmp.length();
while (crbedin != tmp.crend() && (*crbedin) == '=') idx -= 1, crbedin++;
return idx != tmp.length() ? tmp.remove(idx, tmp.length() - idx) : tmp;
}
else
{
return base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_'));
}
}
QString Base64Encode(const QString &string)
{
QByteArray ba = string.toUtf8();

View File

@ -18,6 +18,10 @@
namespace Qv2ray::common
{
QStringList GetFileList(const QDir &dir);
QString SafeBase64Decode(QString string);
QString SafeBase64Encode(const QString &string, bool trim);
QString Base64Encode(const QString &string);
QString Base64Decode(const QString &string);
QStringList SplitLines(const QString &str);

View File

@ -2,7 +2,9 @@
#include <WS2tcpip.h>
#include <WinSock2.h>
#else
#include <fcntl.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
@ -11,6 +13,110 @@
#include "QvTCPing.hpp"
#include "core/handler/ConfigHandler.hpp"
#ifdef _WIN32
using qv_socket_t = SOCKET;
#else
using qv_socket_t = int;
#endif
namespace
{
inline int setnonblocking(qv_socket_t sockno, int &opt)
{
#ifdef _WIN32
ULONG block = 1;
if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR)
{
return -1;
}
#else
if ((opt = fcntl(sockno, F_GETFL, NULL)) < 0)
{
// get socket flags
return -1;
}
if (fcntl(sockno, F_SETFL, opt | O_NONBLOCK) < 0)
{
// set socket non-blocking
return -1;
}
#endif
return 0;
}
inline int setblocking(qv_socket_t sockno, int &opt)
{
#ifdef _WIN32
ULONG block = 0;
if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR)
{
return -1;
}
#else
if (fcntl(sockno, F_SETFL, opt) < 0)
{
// reset socket flags
return -1;
}
#endif
return 0;
}
int connect_wait(qv_socket_t sockno, struct sockaddr *addr, size_t addrlen, int timeout_sec = 5)
{
int res;
int opt;
timeval tv = { 0 };
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
if ((res = setnonblocking(sockno, opt)) != 0)
return -1;
if ((res = ::connect(sockno, addr, addrlen)) < 0)
{
#ifdef _WIN32
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
#else
if (errno == EINPROGRESS)
{
#endif
// connecting
fd_set wait_set;
FD_ZERO(&wait_set);
FD_SET(sockno, &wait_set);
res = select(sockno + 1, NULL, &wait_set, NULL, &tv);
}
}
else
{
// connect immediately
res = 1;
}
if (setblocking(sockno, opt) != 0)
{
return -1;
}
if (res < 0)
{
// an error occured
return -1;
}
else if (res == 0)
{
// timeout
return 1;
}
else
{
socklen_t len = sizeof(opt);
if (getsockopt(sockno, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&opt), &len) < 0)
{
return -1;
}
}
return 0;
}
} // namespace
namespace Qv2ray::components::tcping
{
static int resolveHost(const std::string &host, int portnr, struct addrinfo **res);
@ -159,11 +265,8 @@ namespace Qv2ray::components::tcping
int testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end)
{
#ifdef _WIN32
SOCKET fd;
#else
int fd;
#endif
qv_socket_t fd;
const int on = 1;
/* int flags; */
int rv = 0;
@ -198,7 +301,7 @@ namespace Qv2ray::components::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_wait(fd, addr->ai_addr, addr->ai_addrlen) == 0)
{
*end = system_clock::now();
#ifdef Q_OS_WIN

View File

@ -0,0 +1,216 @@
/* This file from part of QNtp, a library that implements NTP protocol.
*
* Copyright (C) 2011 Alexander Fokin <apfokin@gmail.com>
*
* QNtp is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* QNtp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with QNtp. If not, see <http://www.gnu.org/licenses/>. */
#include "QvNTPClient.hpp"
#include <cmath>
namespace Qv2ray::components::ntp
{
NtpTimestamp NtpTimestamp::fromDateTime(const QDateTime &dateTime)
{
qint64 ntpMSecs = dateTime.toMSecsSinceEpoch() - january_1_1900;
quint32 seconds = ntpMSecs / 1000;
quint32 fraction = 0x100000000ll * (ntpMSecs % 1000) / 1000;
NtpTimestamp result;
result.seconds = qToBigEndian(seconds);
result.fraction = qToBigEndian(fraction);
return result;
}
QDateTime NtpTimestamp::toDateTime(const NtpTimestamp &ntpTime)
{
/* Convert to local-endian. */
quint32 seconds = qFromBigEndian(ntpTime.seconds);
quint32 fraction = qFromBigEndian(ntpTime.fraction);
/* Convert NTP timestamp to number of milliseconds passed since Jan 1 1900. */
qint64 ntpMSecs = seconds * 1000ll + fraction * 1000ll / 0x100000000ll;
/* Construct Qt date time. */
return QDateTime::fromMSecsSinceEpoch(ntpMSecs + january_1_1900);
}
NtpReply::NtpReply() : d(new NtpReplyPrivate())
{
/* We don't use shared null because NtpReplyPrivate isn't a POD type and
* allocation overhead is negligible here. */
memset(&d->packet, 0, sizeof(d->packet));
}
NtpReply::NtpReply(NtpReplyPrivate *dd) : d(dd)
{
Q_ASSERT(dd != NULL);
}
NtpReply::NtpReply(const NtpReply &other) : d(other.d)
{
}
NtpReply::~NtpReply()
{
}
NtpReply &NtpReply::operator=(const NtpReply &other)
{
d = other.d;
return *this;
}
NtpLeapIndicator NtpReply::leapIndicator() const
{
return static_cast<NtpLeapIndicator>(d->packet.basic.flags.leapIndicator);
}
quint8 NtpReply::versionNumber() const
{
return d->packet.basic.flags.versionNumber;
}
NtpMode NtpReply::mode() const
{
return static_cast<NtpMode>(d->packet.basic.flags.mode);
}
quint8 NtpReply::stratum() const
{
return d->packet.basic.stratum;
}
qreal NtpReply::pollInterval() const
{
return std::pow(static_cast<qreal>(2), static_cast<qreal>(d->packet.basic.poll));
}
qreal NtpReply::precision() const
{
return std::pow(static_cast<qreal>(2), static_cast<qreal>(d->packet.basic.precision));
}
QDateTime NtpReply::referenceTime() const
{
return NtpTimestamp::toDateTime(d->packet.basic.referenceTimestamp);
}
QDateTime NtpReply::originTime() const
{
return NtpTimestamp::toDateTime(d->packet.basic.originateTimestamp);
}
QDateTime NtpReply::receiveTime() const
{
return NtpTimestamp::toDateTime(d->packet.basic.receiveTimestamp);
}
QDateTime NtpReply::transmitTime() const
{
return NtpTimestamp::toDateTime(d->packet.basic.transmitTimestamp);
}
QDateTime NtpReply::destinationTime() const
{
return d->destinationTime;
}
qint64 NtpReply::roundTripDelay() const
{
return originTime().msecsTo(destinationTime()) - receiveTime().msecsTo(transmitTime());
}
qint64 NtpReply::localClockOffset() const
{
return (originTime().msecsTo(receiveTime()) + destinationTime().msecsTo(transmitTime())) / 2;
}
bool NtpReply::isNull() const
{
return d->destinationTime.isNull();
}
NtpClient::NtpClient(QObject *parent) : QObject(parent)
{
init(QHostAddress::Any, 0);
}
NtpClient::NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent) : QObject(parent)
{
init(bindAddress, bindPort);
}
void NtpClient::init(const QHostAddress &bindAddress, quint16 bindPort)
{
mSocket = new QUdpSocket(this);
mSocket->bind(bindAddress, bindPort);
connect(mSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
NtpClient::~NtpClient()
{
return;
}
bool NtpClient::sendRequest(const QHostAddress &address, quint16 port)
{
if (mSocket->state() != QAbstractSocket::BoundState)
return false;
/* Initialize the NTP packet. */
NtpPacket packet;
memset(&packet, 0, sizeof(packet));
packet.flags.mode = ClientMode;
packet.flags.versionNumber = 4;
packet.transmitTimestamp = NtpTimestamp::fromDateTime(QDateTime::currentDateTimeUtc());
/* Send it. */
if (mSocket->writeDatagram(reinterpret_cast<const char *>(&packet), sizeof(packet), address, port) < 0)
return false;
return true;
}
void NtpClient::readPendingDatagrams()
{
while (mSocket->hasPendingDatagrams())
{
NtpFullPacket packet;
memset(&packet, 0, sizeof(packet));
QHostAddress address;
quint16 port;
if (mSocket->readDatagram(reinterpret_cast<char *>(&packet), sizeof(packet), &address, &port) < (qint64) sizeof(NtpPacket))
continue;
QDateTime now = QDateTime::currentDateTime();
/* Prepare reply. */
NtpReplyPrivate *replyPrivate = new NtpReplyPrivate();
replyPrivate->packet = packet;
replyPrivate->destinationTime = now;
NtpReply reply(replyPrivate);
/* Notify. */
Q_EMIT replyReceived(address, port, reply);
}
}
} // namespace Qv2ray::components::ntp

View File

@ -0,0 +1,165 @@
/* This file from part of QNtp, a library that implements NTP protocol.
*
* Copyright (C) 2011 Alexander Fokin <apfokin@gmail.com>
*
* QNtp is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* QNtp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with QNtp. If not, see <http://www.gnu.org/licenses/>. */
#pragma once
#include <QDateTime>
#include <QHostAddress>
#include <QObject>
#include <QUdpSocket>
#include <QtEndian>
#include <QtGlobal>
namespace Qv2ray::components::ntp
{
const qint64 january_1_1900 = -2208988800000ll;
enum NtpLeapIndicator
{
NoWarning = 0, /**< No warning. */
LastMinute61Warning = 1, /**< Last minute has 61 seconds. */
LastMinute59Warning = 2, /**< Last minute has 59 seconds. */
UnsynchronizedWarning = 3, /**< Alarm condition (clock not synchronized). */
};
enum NtpMode
{
ReservedMode = 0, /**< Reserved. */
SymmetricActiveMode = 1, /**< Symmetric active. */
SymmetricPassiveMode = 2, /**< Symmetric passive. */
ClientMode = 3, /**< Client. */
ServerMode = 4, /**< Server. */
BroadcastMode = 5, /**< Broadcast. */
ControlMode = 6, /**< NTP control message. */
PrivateMode = 7, /**< Reserved for private use. */
};
enum NtpStratum
{
UnspecifiedStratum = 0, /**< Unspecified or unavailable. */
PrimaryStratum = 1, /**< Primary reference (e.g. radio-clock). */
SecondaryStratumFirst = 2, /**< Secondary reference (via NTP or SNTP). */
SecondaryStratumLast = 15,
UnsynchronizedStratum = 16, /**< Unsynchronized. */
/* 17-255 are reserved. */
};
struct NtpPacketFlags
{
unsigned char mode : 3;
unsigned char versionNumber : 3;
unsigned char leapIndicator : 2;
};
#pragma pack(push, 1)
struct NtpTimestamp
{
quint32 seconds;
quint32 fraction;
static inline NtpTimestamp fromDateTime(const QDateTime &dateTime);
static inline QDateTime toDateTime(const NtpTimestamp &ntpTime);
};
struct NtpPacket
{
NtpPacketFlags flags;
quint8 stratum;
qint8 poll;
qint8 precision;
qint32 rootDelay;
qint32 rootDispersion;
qint8 referenceID[4];
NtpTimestamp referenceTimestamp;
NtpTimestamp originateTimestamp;
NtpTimestamp receiveTimestamp;
NtpTimestamp transmitTimestamp;
};
struct NtpAuthenticationInfo
{
quint32 keyId;
quint8 messageDigest[16];
};
struct NtpFullPacket
{
NtpPacket basic;
NtpAuthenticationInfo auth;
};
#pragma pack(pop)
class NtpReplyPrivate : public QSharedData
{
public:
NtpFullPacket packet;
QDateTime destinationTime;
};
class NtpReply
{
public:
NtpReply();
NtpReply(const NtpReply &other);
~NtpReply();
NtpReply &operator=(const NtpReply &other);
NtpLeapIndicator leapIndicator() const;
quint8 versionNumber() const;
NtpMode mode() const;
quint8 stratum() const;
qreal pollInterval() const;
qreal precision() const;
QDateTime referenceTime() const;
QDateTime originTime() const;
QDateTime receiveTime() const;
QDateTime transmitTime() const;
QDateTime destinationTime() const;
qint64 roundTripDelay() const;
qint64 localClockOffset() const;
bool isNull() const;
protected:
friend class NtpClient;
NtpReply(NtpReplyPrivate *dd);
private:
QSharedDataPointer<NtpReplyPrivate> d;
};
class NtpClient : public QObject
{
Q_OBJECT;
public:
NtpClient(QObject *parent = NULL);
NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent = NULL);
virtual ~NtpClient();
bool sendRequest(const QHostAddress &address, quint16 port);
Q_SIGNALS:
void replyReceived(const QHostAddress &address, quint16 port, const NtpReply &reply);
private Q_SLOTS:
void readPendingDatagrams();
private:
void init(const QHostAddress &bindAddress, quint16 bindPort);
QUdpSocket *mSocket;
};
} // namespace Qv2ray::components::ntp

View File

@ -0,0 +1,13 @@
#include "QvPortDetector.hpp"
#include <QTcpServer>
namespace Qv2ray::components::port
{
bool detectPortTCP(quint16 port)
{
QTcpServer server;
return server.listen(QHostAddress::LocalHost, port);
}
} // namespace Qv2ray::components::port

View File

@ -0,0 +1,7 @@
#pragma once
#include <QtGlobal>
namespace Qv2ray::components::port
{
bool detectPortTCP(quint16 port);
}

View File

@ -232,61 +232,101 @@ namespace Qv2ray::components::proxy
QStringList actions;
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg("manual");
bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE";
bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_CURRENT_DESKTOP").toLower() == "deepin";
const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
if (isKDE)
{
LOG(MODULE_PROXY, "KDE detected")
actions << QString("kwriteconfig5 --file " + configPath + "/kioslaverc --group \"Proxy Settings\" --key ProxyType 1");
}
// Configure HTTP Proxies for HTTP, FTP and HTTPS
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);
if (isKDE)
// iterate over protocols...
for (const auto protocol : { "http", "ftp", "https" })
{
// FTP here should be scheme: ftp://
for (auto protocol : { "http", "ftp", "https" })
// for GNOME:
{
auto str =
QString("kwriteconfig5 --file " + configPath + "/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"")
.arg(protocol)
.arg(address)
.arg(QSTRN(httpPort));
actions << str;
actions << QString("gsettings set org.gnome.system.proxy.%1 host '%2'").arg(protocol, address);
actions << QString("gsettings set org.gnome.system.proxy.%1 port %2").arg(protocol, QSTRN(httpPort));
}
// for KDE:
if (isKDE)
{
actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key %2Proxy "http://%3 %4")")
.arg(configPath, protocol, address, QSTRN(httpPort));
}
}
}
// Configure SOCKS5 Proxies
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);
// for GNOME:
{
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);
}
// for KDE:
if (isKDE)
{
actions << QString("kwriteconfig5 --file " + configPath +
"/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"")
.arg(address)
.arg(QSTRN(socksPort));
actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key socksProxy "socks://%2 %3")")
.arg(configPath, address, QSTRN(socksPort));
}
}
// Setting Proxy Mode to Manual
{
// for GNOME:
{
actions << "gsettings set org.gnome.system.proxy mode 'manual'";
}
// for KDE:
if (isKDE)
{
LOG(MODULE_PROXY, "KDE detected")
actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 1)").arg(configPath);
}
}
// Execute them all!
//
// note: do not use std::all_of / any_of / none_of,
// because those are short-circuit and cannot guarantee atomicity.
auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString &action) {
DEBUG(MODULE_PROXY, action)
return QProcess::execute(action) == QProcess::NormalExit;
// execute and get the code
const auto returnCode = QProcess::execute(action);
// print out the commands and result codes
DEBUG(MODULE_PROXY, QString("[%1] %2").arg(QSTRN(returnCode), action))
// give the code back
return returnCode == QProcess::NormalExit;
}) == actions.size();
if (!result)
{
LOG(MODULE_PROXY, "There was something wrong when setting proxies.")
LOG(MODULE_PROXY, "It may happen if you are using KDE with no gsettings support.")
LOG(MODULE_PROXY, "Something wrong when setting proxies.")
}
// Post-Actions for HTTP on Deepin Desktop Environment.
if (isDDE && hasHTTP)
{
if (!RuntimeConfig.deepinHorribleProxyHint)
{
RuntimeConfig.deepinHorribleProxyHint = true;
const auto deepinWarnTitle = QObject::tr("Deepin Detected");
const auto deepinWarnMessage =
QObject::tr("Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.") + NEWLINE + //
QObject::tr("The origin scheme http is wrongly replaced by https and ftp, causing the problem.") + NEWLINE + //
QObject::tr("Qv2ray cannot help you change them back. Please don't blame us if things go wrong."); //
QvMessageBoxWarn(nullptr, deepinWarnTitle, deepinWarnMessage);
}
// set them back! - NOPE. setenv only works within your little program.
// const auto httpProxyURL = QString("http://%1:%2").arg(address, QSTRN(httpPort)).toStdString();
// setenv("https_proxy", httpProxyURL.c_str(), true);
// setenv("ftp_proxy", httpProxyURL.c_str(), true);
}
Q_UNUSED(result);
#else
for (auto service : macOSgetNetworkServices())
@ -323,6 +363,7 @@ namespace Qv2ray::components::proxy
void ClearSystemProxy()
{
LOG(MODULE_PROXY, "Clearing System Proxy")
#ifdef Q_OS_WIN
LOG(MODULE_PROXY, "Cleaning system proxy settings.")
INTERNET_PER_CONN_OPTION_LIST list;
@ -353,21 +394,33 @@ namespace Qv2ray::components::proxy
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
#elif defined(Q_OS_LINUX)
if (qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE")
QStringList actions;
const bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE";
const auto configRoot = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
// Setting System Proxy Mode to: None
{
QProcess::execute("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
"/kioslaverc --group \"Proxy Settings\" --key ProxyType 0");
for (auto protocol : { "http", "ftp", "https" })
// for GNOME:
{
auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
"/kioslaverc --group \"Proxy Settings\" --key %1Proxy ''")
.arg(protocol);
QProcess::execute(str);
actions << QString("gsettings set org.gnome.system.proxy mode 'none'");
}
// for KDE:
if (isKDE)
{
actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 0)").arg(configRoot);
}
QProcess::execute("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
"/kioslaverc --group \"Proxy Settings\" --key socksProxy ''");
}
QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'");
// Execute the Actions
for (const auto &action : actions)
{
// execute and get the code
const auto returnCode = QProcess::execute(action);
// print out the commands and result codes
DEBUG(MODULE_PROXY, QString("[%1] %2").arg(QSTRN(returnCode), action))
}
#else
for (auto service : macOSgetNetworkServices())
{

View File

@ -122,7 +122,7 @@ namespace Qv2ray::core::connection
// 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.contains("://") ? result : Base64Decode(result);
return result.contains("://") ? result : SafeBase64Decode(result);
}
} // namespace Serialization
} // namespace Qv2ray::core::connection

View File

@ -81,7 +81,7 @@ namespace Qv2ray::core::connection
auto x = QUrl::fromUserInput(uri);
server.address = x.host();
server.port = x.port();
QString userInfo = Base64Decode(x.userName());
QString userInfo = SafeBase64Decode(x.userName());
auto userInfoSp = userInfo.indexOf(':');
//
DEBUG(MODULE_CONNECTION, "Userinfo splitter position: " + QSTRN(userInfoSp))

View File

@ -84,7 +84,7 @@ namespace Qv2ray::core::connection::Serialization
// decode base64
const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 6);
const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8());
const auto decodedJSON = SafeBase64Decode(ssdURIBody.toString()).toUtf8();
if (decodedJSON.length() == 0)
{

View File

@ -53,6 +53,11 @@ namespace Qv2ray::core::connection
vmessUriRoot["path"] = transfer.httpSettings.path;
}
if(!vmessUriRoot.contains("type") || vmessUriRoot["type"].toString().isEmpty())
{
vmessUriRoot["type"] = "none";
}
//
auto vmessPart = Base64Encode(JsonToString(vmessUriRoot, QJsonDocument::JsonFormat::Compact));
return "vmess://" + vmessPart;
@ -88,7 +93,7 @@ namespace Qv2ray::core::connection
return default;
}
auto vmessString = Base64Decode(b64Str);
auto vmessString = SafeBase64Decode(b64Str);
auto jsonErr = VerifyJsonString(vmessString);
if (!jsonErr.isEmpty())
@ -111,28 +116,10 @@ namespace Qv2ray::core::connection
*errMessage = QObject::tr("seems like a v1 vmess, we don't support it");
return default;
}
bool flag = true;
// C is a quick hack...
#define C(k) (flag = (vmessConf.contains(k) ? (errMessage->clear(), true) : (*errMessage += (k " does not exist"), false)))
// id, aid, port and add are mandatory fields of a vmess://
// link.
flag = flag && C("id") && (C("aid") || C("alterId")) && C("port") && C("add");
// Stream Settings
auto net = vmessConf["net"].toString();
if (net == "http" || net == "ws")
flag = flag && C("host") && C("path");
else if (net == "domainsocket")
flag = flag && C("path");
else if (net == "quic")
flag = flag && C("host") && C("type") && C("path");
#undef C
// return flag ? 0 : 1;
// --------------------------------------------------------------------------------------
CONFIGROOT root;
QString ps, add, id, /*net,*/ type, host, path, tls;
QString ps, add, id, net, type, host, path, tls;
int port, aid;
//
// __vmess_checker__func(key, values)
@ -194,9 +181,10 @@ namespace Qv2ray::core::connection
<< "domainsocket" //
<< "quic"); //
//
__vmess_checker__func(path, << ""); //
__vmess_checker__func(host, << ""); //
__vmess_checker__func(tls, << ""); //
__vmess_checker__func(tls, << "none" //
<< "tls"); //
path = vmessConf.contains("path") ? vmessConf["path"].toVariant().toString() : (net == "quic" ? "" : "/");
host = vmessConf.contains("host") ? vmessConf["host"].toVariant().toString() : (net == "quic" ? "none" : "");
}
// Repect connection type rather than obfs type //
if (QStringList{ "srtp", "utp", "wechat-video" }.contains(type)) //
@ -250,7 +238,8 @@ namespace Qv2ray::core::connection
}
else if (net == "ws")
{
streaming.wsSettings.headers["Host"] = host;
if (!host.isEmpty())
streaming.wsSettings.headers["Host"] = host;
streaming.wsSettings.path = path;
}
else if (net == "kcp")

View File

@ -9,7 +9,7 @@
#endif
// Check 10 times before telling user that API has failed.
constexpr auto QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD = 10;
constexpr auto QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD = 30;
namespace Qv2ray::core::kernel
{

View File

@ -6,6 +6,7 @@ namespace Qv2ray::core::kernel::abi
{
QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType)
{
#ifndef QV2RAY_TRUSTED_ABI
switch (hostType)
{
case ABI_WIN32:
@ -15,12 +16,19 @@ namespace Qv2ray::core::kernel::abi
case ABI_ELF_X86: return targetType == hostType ? ABI_PERFECT : ABI_NOPE;
case ABI_ELF_X86_64: return targetType == hostType ? ABI_PERFECT : targetType == ABI_ELF_X86 ? ABI_MAYBE : ABI_NOPE;
case ABI_ELF_OTHER: return targetType == hostType ? ABI_PERFECT : ABI_MAYBE;
case ABI_TRUSTED: return ABI_PERFECT;
default: return ABI_MAYBE;
}
#else
return ABI_PERFECT;
#endif
}
std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable)
{
#ifdef QV2RAY_TRUSTED_ABI
return { QvKernelABIType::ABI_TRUSTED, std::nullopt };
#else
QFile file(pathCoreExecutable);
if (!file.exists())
return { std::nullopt, QObject::tr("core executable file %1 does not exist").arg(pathCoreExecutable) };
@ -55,6 +63,7 @@ namespace Qv2ray::core::kernel::abi
return { QvKernelABIType::ABI_MACH_O, std::nullopt };
else
return { std::nullopt, QObject::tr("cannot deduce the type of core executable file %1").arg(pathCoreExecutable) };
#endif
}
QString abiToString(QvKernelABIType abi)
@ -68,6 +77,7 @@ namespace Qv2ray::core::kernel::abi
case ABI_ELF_AARCH64: return QObject::tr("ELF arm64 executable");
case ABI_ELF_ARM: return QObject::tr("ELF arm executable");
case ABI_ELF_OTHER: return QObject::tr("other ELF executable");
case ABI_TRUSTED: return QObject::tr("trusted abi");
default: return QObject::tr("unknown abi");
}
}

View File

@ -19,6 +19,7 @@ namespace Qv2ray::core::kernel
ABI_ELF_AARCH64,
ABI_ELF_ARM,
ABI_ELF_OTHER,
ABI_TRUSTED,
};
enum QvKernelABICompatibility
@ -42,7 +43,8 @@ namespace Qv2ray::core::kernel
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7)
QvKernelABIType::ABI_ELF_ARM;
#else
#error "unknown architecture"
QvKernelABIType::ABI_TRUSTED;
#define QV2RAY_TRUSTED_ABI
#endif
std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable);

View File

@ -196,6 +196,7 @@ int main(int argc, char *argv[])
case CommandLineHelpRequested: std::cout << parser.Parser()->helpText().toStdString() << std::endl; return 0;
}
}
#ifdef Q_OS_UNIX
// Unix OS root user check.
@ -419,6 +420,7 @@ int main(int argc, char *argv[])
// Initialise Connection Handler
PluginHost = new QvPluginHost();
ConnectionManager = new QvConfigHandler();
// Show MainWindow
MainWindow w;
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() {

View File

@ -122,8 +122,8 @@ void PluginManageWindow::on_openPluginFolder_clicked()
{
pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/");
}
#ifdef Q_OS_LINUX
QProcess::execute("xdg-open", { "\"" + pluginPath.absolutePath() + "\"" });
#ifdef FALL_BACK_TO_XDG_OPEN
QProcess::execute("xdg-open", { pluginPath.absolutePath() });
#else
QDesktopServices::openUrl(QUrl::fromLocalFile(pluginPath.absolutePath()));
#endif

View File

@ -1153,7 +1153,15 @@ void PreferencesWindow::on_enableAPI_stateChanged(int arg1)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.kernelConfig.enableAPI = arg1 == Qt::Checked;
CurrentConfig.apiConfig.enableAPI = arg1 == Qt::Checked;
if (arg1 == Qt::Unchecked)
{
const auto msgAPIDisableTitle = tr("Disabling API Subsystem");
const auto msgAPIDisableMsg = tr("Disabling API subsystem will also disable the statistics function of Qv2ray.") + NEWLINE + //
tr("Speed chart and traffic statistics will be disabled.");
QvMessageBoxWarn(this, msgAPIDisableTitle, msgAPIDisableMsg);
}
}
void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index)

View File

@ -33,7 +33,7 @@
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>3</number>
<number>0</number>
</property>
<widget class="QWidget" name="tabWidgetPage1">
<attribute name="title">
@ -274,28 +274,28 @@
<string>Network Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_13">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_67">
<property name="text">
<string>User-Agent</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLineEdit" name="qvNetworkUATxt">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_71">
<property name="text">
<string>Qv2ray Proxy</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QRadioButton" name="qvProxyNoProxy">
@ -320,14 +320,14 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_64">
<property name="text">
<string>Curtom Proxy</string>
<string>Custom Proxy</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QComboBox" name="qvProxyTypeCombo">
<item>
<property name="text">
@ -341,14 +341,7 @@
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_66">
<property name="text">
<string>Curtom Proxy</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="qvProxyAddressTxt"/>
@ -375,6 +368,22 @@
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_89">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>These settings are for Qv2ray itself.
For example, for updating subscriptions.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -725,8 +734,8 @@ Custom DNS Settings</string>
<rect>
<x>0</x>
<y>0</y>
<width>818</width>
<height>558</height>
<width>824</width>
<height>516</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">

View File

@ -115,6 +115,13 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConnectionSettingsWidget</name>
<message>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConnectionWidget</name>
<message>
@ -142,6 +149,45 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>GroupManager</name>
<message>
<source>Connection Management</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload Subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export Connection(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Connection(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Copy to...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Move to...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ImportConfigWindow</name>
<message>
@ -516,6 +562,13 @@
<translation></translation>
</message>
</context>
<context>
<name>InboundSettingsWidget</name>
<message>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>JsonEditor</name>
<message>
@ -569,10 +622,6 @@
<source>Qv2ray</source>
<translation></translation>
</message>
<message>
<source>Add</source>
<translation></translation>
</message>
<message>
<source>Preferences</source>
<translation type="unfinished"></translation>
@ -851,6 +900,10 @@
<source>Groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add Connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OutboundEditor</name>
@ -1628,10 +1681,6 @@ But could damage your server if improperly used.</source>
<source>Custom Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Curtom Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Network Options</source>
<translation type="unfinished"></translation>
@ -1680,6 +1729,23 @@ But could damage your server if improperly used.</source>
<source>tproxy inbound&apos;s sniffing is enabled by default.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>These settings are for Qv2ray itself.
For example, for updating subscriptions.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disabling API Subsystem</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disabling API subsystem will also disable the statistics function of Qv2ray.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Speed chart and traffic statistics will be disabled.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -1903,14 +1969,6 @@ But could damage your server if improperly used.</source>
<source>Invalid ssd link: rc4-md5 encryption is not supported by v2ray-core</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not connected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Text</source>
<translation type="unfinished"></translation>
@ -2063,6 +2121,26 @@ But could damage your server if improperly used.</source>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deepin Detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The origin scheme http is wrongly replaced by https and ftp, causing the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Qv2ray cannot help you change them back. Please don&apos;t blame us if things go wrong.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>trusted abi</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Qv2ray::common::QvCommandArgParser</name>
@ -2871,25 +2949,6 @@ Maybe you have downloaded the wrong core?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SubscriptionEditor</name>
<message>
<source>Reload Subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>misc</name>
<message>
@ -2951,10 +3010,6 @@ Maybe you have downloaded the wrong core?</source>
<source>Remove Subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group Details</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group Name</source>
<translation type="unfinished"></translation>
@ -2976,11 +3031,43 @@ Maybe you have downloaded the wrong core?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connection List</source>
<source>Update Subscription Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Update Subscription Data</source>
<source>Group Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Created At</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connections</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Subscription Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This group is a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connection Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Route Settings</source>
<translation type="unfinished"></translation>
</message>
</context>

View File

@ -150,6 +150,13 @@
<translation></translation>
</message>
</context>
<context>
<name>ConnectionSettingsWidget</name>
<message>
<source>Form</source>
<translation>Form</translation>
</message>
</context>
<context>
<name>ConnectionWidget</name>
<message>
@ -219,6 +226,45 @@
<translation type="vanished"> (*.flow)</translation>
</message>
</context>
<context>
<name>GroupManager</name>
<message>
<source>Connection Management</source>
<translation></translation>
</message>
<message>
<source>Reload Subscription</source>
<translation></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation></translation>
</message>
<message>
<source>Export Connection(s)</source>
<translation></translation>
</message>
<message>
<source>Delete Connection(s)</source>
<translation></translation>
</message>
<message>
<source>Copy to...</source>
<translation>...</translation>
</message>
<message>
<source>Move to...</source>
<translation>...</translation>
</message>
</context>
<context>
<name>ImportConfigWindow</name>
<message>
@ -645,6 +691,13 @@
<translation>.</translation>
</message>
</context>
<context>
<name>InboundSettingsWidget</name>
<message>
<source>Form</source>
<translation>Form</translation>
</message>
</context>
<context>
<name>JsonEditor</name>
<message>
@ -792,7 +845,7 @@
</message>
<message>
<source>Add</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Preferences</source>
@ -1314,6 +1367,10 @@
<source>Groups</source>
<translation></translation>
</message>
<message>
<source>Add Connection</source>
<translation></translation>
</message>
</context>
<context>
<name>OutboundEditor</name>
@ -2337,7 +2394,7 @@ But could damage your server if improperly used.</source>
</message>
<message>
<source>Curtom Proxy</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Network Options</source>
@ -2387,6 +2444,24 @@ But could damage your server if improperly used.</source>
<source>tproxy inbound&apos;s sniffing is enabled by default.</source>
<translation>tProxyインバウンドのスニッフィングはデフォルトで有効になっています</translation>
</message>
<message>
<source>These settings are for Qv2ray itself.
For example, for updating subscriptions.</source>
<translation>Qv2ray自体のためのものです
</translation>
</message>
<message>
<source>Disabling API Subsystem</source>
<translation>APIサブシステムを無効</translation>
</message>
<message>
<source>Disabling API subsystem will also disable the statistics function of Qv2ray.</source>
<translation>APIサブシステムを無効にするとQv2rayの統計機能も無効になります</translation>
</message>
<message>
<source>Speed chart and traffic statistics will be disabled.</source>
<translation></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -2536,11 +2611,11 @@ But could damage your server if improperly used.</source>
</message>
<message>
<source>Not connected</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Connected</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Disconnected</source>
@ -2850,6 +2925,38 @@ But could damage your server if improperly used.</source>
<source>Unknown</source>
<translation></translation>
</message>
<message>
<source>Deepin Detected</source>
<translation>Deepin検出</translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. </source>
<translation type="vanished">DeepinのバカHTTPS_PROXY環境変数を設定します</translation>
</message>
<message>
<source>The original scheme should be http://, but he will replace with https://, causing the problem. </source>
<translation type="vanished"> http:// であるべきなのですが、彼は https:// に置き換えて問題を引き起こします。</translation>
</message>
<message>
<source>Qv2ray will help you change it back and make things work again. </source>
<translation type="vanished">Qv2rayはそれを元に戻して</translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.</source>
<translation>DeepinのバカHTTPS_PROXYとFTP_PROXYの環境変数を設定します</translation>
</message>
<message>
<source>The origin scheme http is wrongly replaced by https and ftp, causing the problem.</source>
<translation>httpがhttpsとftpに間違って置き換えられてしまい</translation>
</message>
<message>
<source>Qv2ray cannot help you change them back. Please don&apos;t blame us if things go wrong.</source>
<translation>Qv2rayでは元に戻すことはできません</translation>
</message>
<message>
<source>trusted abi</source>
<translation>ABI</translation>
</message>
</context>
<context>
<name>Qv2ray::common::QvCommandArgParser</name>
@ -3775,19 +3882,19 @@ Maybe you have downloaded the wrong core?</source>
<name>SubscriptionEditor</name>
<message>
<source>Reload Subscription</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
</context>
<context>
@ -3853,7 +3960,7 @@ Maybe you have downloaded the wrong core?</source>
</message>
<message>
<source>Group Details</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Group Name</source>
@ -3877,12 +3984,48 @@ Maybe you have downloaded the wrong core?</source>
</message>
<message>
<source>Connection List</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Update Subscription Data</source>
<translation></translation>
</message>
<message>
<source>Group Info</source>
<translation></translation>
</message>
<message>
<source>Created At</source>
<translation></translation>
</message>
<message>
<source>Connections</source>
<translation></translation>
</message>
<message>
<source>Delete Selection</source>
<translation></translation>
</message>
<message>
<source>Export Selection</source>
<translation></translation>
</message>
<message>
<source>Subscription Settings</source>
<translation></translation>
</message>
<message>
<source>This group is a subscription</source>
<translation></translation>
</message>
<message>
<source>Connection Settings</source>
<translation></translation>
</message>
<message>
<source>Route Settings</source>
<translation></translation>
</message>
</context>
<context>
<name>w_PluginManager</name>

View File

@ -134,6 +134,13 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConnectionSettingsWidget</name>
<message>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConnectionWidget</name>
<message>
@ -203,6 +210,45 @@
<translation type="vanished">Файл сцены потока (*.flow)</translation>
</message>
</context>
<context>
<name>GroupManager</name>
<message>
<source>Connection Management</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload Subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export Connection(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Connection(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Copy to...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Move to...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ImportConfigWindow</name>
<message>
@ -629,6 +675,13 @@
<translation>Этот пользователь уже существует.</translation>
</message>
</context>
<context>
<name>InboundSettingsWidget</name>
<message>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>JsonEditor</name>
<message>
@ -776,7 +829,7 @@
</message>
<message>
<source>Add</source>
<translation>Добавить</translation>
<translation type="vanished">Добавить</translation>
</message>
<message>
<source>Preferences</source>
@ -1294,6 +1347,10 @@
<source>Groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add Connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OutboundEditor</name>
@ -2239,10 +2296,6 @@ But could damage your server if improperly used.</source>
<source>Custom Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Curtom Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Network Options</source>
<translation type="unfinished"></translation>
@ -2291,6 +2344,23 @@ But could damage your server if improperly used.</source>
<source>tproxy inbound&apos;s sniffing is enabled by default.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>These settings are for Qv2ray itself.
For example, for updating subscriptions.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disabling API Subsystem</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disabling API subsystem will also disable the statistics function of Qv2ray.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Speed chart and traffic statistics will be disabled.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -2438,13 +2508,9 @@ But could damage your server if improperly used.</source>
<source>Technical Details</source>
<translation>Технические детали</translation>
</message>
<message>
<source>Not connected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connected</source>
<translation>Подключено</translation>
<translation type="vanished">Подключено</translation>
</message>
<message>
<source>Disconnected</source>
@ -2742,6 +2808,26 @@ But could damage your server if improperly used.</source>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deepin Detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The origin scheme http is wrongly replaced by https and ftp, causing the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Qv2ray cannot help you change them back. Please don&apos;t blame us if things go wrong.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>trusted abi</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Qv2ray::common::QvCommandArgParser</name>
@ -3612,25 +3698,6 @@ Maybe you have downloaded the wrong core?</source>
<translation type="vanished">Не удалось обработать результат из апстрима, проверьте ваш URL.</translation>
</message>
</context>
<context>
<name>SubscriptionEditor</name>
<message>
<source>Reload Subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>misc</name>
<message>
@ -3692,10 +3759,6 @@ Maybe you have downloaded the wrong core?</source>
<source>Remove Subscription</source>
<translation type="unfinished">Удалить подписку</translation>
</message>
<message>
<source>Group Details</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group Name</source>
<translation type="unfinished"></translation>
@ -3718,12 +3781,48 @@ Maybe you have downloaded the wrong core?</source>
</message>
<message>
<source>Connection List</source>
<translation type="unfinished">Список подключений</translation>
<translation type="obsolete">Список подключений</translation>
</message>
<message>
<source>Update Subscription Data</source>
<translation type="unfinished">Обновить данные подписки</translation>
</message>
<message>
<source>Group Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Created At</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connections</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Selection</source>
<translation type="unfinished">Удалить выделение</translation>
</message>
<message>
<source>Export Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Subscription Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This group is a subscription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Connection Settings</source>
<translation type="unfinished">Настройки соединения</translation>
</message>
<message>
<source>Route Settings</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>w_PluginManager</name>

View File

@ -122,6 +122,13 @@
<translation></translation>
</message>
</context>
<context>
<name>ConnectionSettingsWidget</name>
<message>
<source>Form</source>
<translation></translation>
</message>
</context>
<context>
<name>ConnectionWidget</name>
<message>
@ -149,6 +156,45 @@
<translation></translation>
</message>
</context>
<context>
<name>GroupManager</name>
<message>
<source>Connection Management</source>
<translation></translation>
</message>
<message>
<source>Reload Subscription</source>
<translation></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation></translation>
</message>
<message>
<source>Export Connection(s)</source>
<translation></translation>
</message>
<message>
<source>Delete Connection(s)</source>
<translation></translation>
</message>
<message>
<source>Copy to...</source>
<translation>...</translation>
</message>
<message>
<source>Move to...</source>
<translation>...</translation>
</message>
</context>
<context>
<name>ImportConfigWindow</name>
<message>
@ -555,6 +601,13 @@
<translation></translation>
</message>
</context>
<context>
<name>InboundSettingsWidget</name>
<message>
<source>Form</source>
<translation></translation>
</message>
</context>
<context>
<name>JsonEditor</name>
<message>
@ -618,7 +671,7 @@
</message>
<message>
<source>Add</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Preferences</source>
@ -952,6 +1005,10 @@
<source>Groups</source>
<translation></translation>
</message>
<message>
<source>Add Connection</source>
<translation></translation>
</message>
</context>
<context>
<name>OutboundEditor</name>
@ -1967,7 +2024,7 @@ But could damage your server if improperly used.</source>
</message>
<message>
<source>Curtom Proxy</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Network Options</source>
@ -2017,6 +2074,24 @@ But could damage your server if improperly used.</source>
<source>tproxy inbound&apos;s sniffing is enabled by default.</source>
<translation>tProxy </translation>
</message>
<message>
<source>These settings are for Qv2ray itself.
For example, for updating subscriptions.</source>
<translation> Qv2ray
</translation>
</message>
<message>
<source>Disabling API Subsystem</source>
<translation> API </translation>
</message>
<message>
<source>Disabling API subsystem will also disable the statistics function of Qv2ray.</source>
<translation> API Qv2ray </translation>
</message>
<message>
<source>Speed chart and traffic statistics will be disabled.</source>
<translation></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -2270,11 +2345,11 @@ But could damage your server if improperly used.</source>
</message>
<message>
<source>Not connected</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Connected</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Custom Text</source>
@ -2428,6 +2503,38 @@ But could damage your server if improperly used.</source>
<source>Unknown</source>
<translation></translation>
</message>
<message>
<source>Deepin Detected</source>
<translation> Deepin</translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. </source>
<translation type="vanished">Deepin HTTPS_PROXY </translation>
</message>
<message>
<source>The original scheme should be http://, but he will replace with https://, causing the problem. </source>
<translation type="vanished"> scheme http://,但 Deepin 将其替换成了 https://,导致 HTTPS 代理无法正常使用。</translation>
</message>
<message>
<source>Qv2ray will help you change it back and make things work again. </source>
<translation type="vanished">Qv2ray </translation>
</message>
<message>
<source>Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.</source>
<translation>Deepin可能自作聪明 HTTPS_PROXY FTP_PROXY </translation>
</message>
<message>
<source>The origin scheme http is wrongly replaced by https and ftp, causing the problem.</source>
<translation> http:// 可能被错误地替换为 https:// 和 ftp://,导致这个问题。</translation>
</message>
<message>
<source>Qv2ray cannot help you change them back. Please don&apos;t blame us if things go wrong.</source>
<translation>Qv2ray </translation>
</message>
<message>
<source>trusted abi</source>
<translation> ABI</translation>
</message>
</context>
<context>
<name>Qv2ray::common::QvCommandArgParser</name>
@ -3317,19 +3424,19 @@ Maybe you have downloaded the wrong core?</source>
<name>SubscriptionEditor</name>
<message>
<source>Reload Subscription</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Would you like to reload the subscription?</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Deleting a subscription</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>All connections will be moved to default group, do you want to continue?</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
</context>
<context>
@ -3395,7 +3502,7 @@ Maybe you have downloaded the wrong core?</source>
</message>
<message>
<source>Group Details</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Group Name</source>
@ -3419,12 +3526,48 @@ Maybe you have downloaded the wrong core?</source>
</message>
<message>
<source>Connection List</source>
<translation></translation>
<translation type="vanished"></translation>
</message>
<message>
<source>Update Subscription Data</source>
<translation></translation>
</message>
<message>
<source>Group Info</source>
<translation></translation>
</message>
<message>
<source>Created At</source>
<translation></translation>
</message>
<message>
<source>Connections</source>
<translation></translation>
</message>
<message>
<source>Delete Selection</source>
<translation></translation>
</message>
<message>
<source>Export Selection</source>
<translation></translation>
</message>
<message>
<source>Subscription Settings</source>
<translation></translation>
</message>
<message>
<source>This group is a subscription</source>
<translation></translation>
</message>
<message>
<source>Connection Settings</source>
<translation></translation>
</message>
<message>
<source>Route Settings</source>
<translation></translation>
</message>
</context>
<context>
<name>w_PluginManager</name>