feat: Add geo Asset management and Remove pointless features

This commit is contained in:
unknown 2024-08-01 15:20:38 +03:30
parent a85dc6fc41
commit 205830fcc3
No known key found for this signature in database
GPG Key ID: C2CA486E4F771093
20 changed files with 324 additions and 1638 deletions

View File

@ -1,440 +0,0 @@
#include "QvProxyConfigurator.hpp"
#ifdef Q_OS_WIN
//
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
//
#include <wininet.h>
#include <ras.h>
#include <raserror.h>
#include <vector>
#endif
#include <QStandardPaths>
#include <QProcess>
#include "3rdparty/fix_old_qt.h"
#include "3rdparty/qv2ray/wrapper.hpp"
#include "fmt/Preset.hpp"
#include "main/NekoGui.hpp"
#define QV_MODULE_NAME "SystemProxy"
#define QSTRN(num) QString::number(num)
namespace Qv2ray::components::proxy {
using ProcessArgument = QPair<QString, QStringList>;
#ifdef Q_OS_MACOS
QStringList macOSgetNetworkServices() {
QProcess p;
p.setProgram("/usr/sbin/networksetup");
p.setArguments(QStringList{"-listallnetworkservices"});
p.start();
p.waitForStarted();
p.waitForFinished();
LOG(p.errorString());
auto str = p.readAllStandardOutput();
auto lines = SplitLines(str);
QStringList result;
// Start from 1 since first line is unneeded.
for (auto i = 1; i < lines.count(); i++) {
// * means disabled.
if (!lines[i].contains("*")) {
result << lines[i];
}
}
LOG("Found " + QSTRN(result.size()) + " network services: " + result.join(";"));
return result;
}
#endif
#ifdef Q_OS_WIN
#define NO_CONST(expr) const_cast<wchar_t *>(expr)
// static auto DEFAULT_CONNECTION_NAME =
// NO_CONST(L"DefaultConnectionSettings");
///
/// INTERNAL FUNCTION
bool __QueryProxyOptions() {
INTERNET_PER_CONN_OPTION_LIST List;
INTERNET_PER_CONN_OPTION Option[5];
//
unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
Option[2].dwOption = INTERNET_PER_CONN_FLAGS;
Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
//
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
List.pszConnection = nullptr; // NO_CONST(DEFAULT_CONNECTION_NAME);
List.dwOptionCount = 5;
List.dwOptionError = 0;
List.pOptions = Option;
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
LOG("InternetQueryOption failed, GLE=" + QSTRN(GetLastError()));
}
LOG("System default proxy info:");
if (Option[0].Value.pszValue != nullptr) {
LOG(QString::fromWCharArray(Option[0].Value.pszValue));
}
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) {
LOG("PROXY_TYPE_AUTO_PROXY_URL");
}
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) {
LOG("PROXY_TYPE_AUTO_DETECT");
}
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) {
LOG("PROXY_TYPE_DIRECT");
}
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) {
LOG("PROXY_TYPE_PROXY");
}
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
LOG("InternetQueryOption failed,GLE=" + QSTRN(GetLastError()));
}
if (Option[4].Value.pszValue != nullptr) {
LOG(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[3].Value.pszValue != nullptr) {
GlobalFree(Option[3].Value.pszValue);
}
if (Option[4].Value.pszValue != nullptr) {
GlobalFree(Option[4].Value.pszValue);
}
return false;
}
bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC) {
INTERNET_PER_CONN_OPTION_LIST list;
DWORD dwBufSize = sizeof(list);
// Fill the list structure.
list.dwSize = sizeof(list);
// NULL == LAN, otherwise connectoid name.
list.pszConnection = nullptr;
if (nullptr == proxy_full_addr) {
LOG("Clearing system proxy");
//
list.dwOptionCount = 1;
list.pOptions = new INTERNET_PER_CONN_OPTION[1];
// Ensure that the memory was allocated.
if (nullptr == list.pOptions) {
// Return if the memory wasn't allocated.
return false;
}
// Set flags.
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
} else if (isPAC) {
LOG("Setting system proxy for PAC");
//
list.dwOptionCount = 2;
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
if (nullptr == list.pOptions) {
return false;
}
// Set flags.
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL;
// Set proxy name.
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
list.pOptions[1].Value.pszValue = proxy_full_addr;
} else {
LOG("Setting system proxy for Global Proxy");
//
list.dwOptionCount = 2;
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
if (nullptr == list.pOptions) {
return false;
}
// Set flags.
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
// Set proxy name.
list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
list.pOptions[1].Value.pszValue = proxy_full_addr;
// Set proxy override.
// list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
// auto localhost = L"localhost";
// list.pOptions[2].Value.pszValue = NO_CONST(localhost);
}
// Set proxy for LAN.
if (!InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize)) {
LOG("InternetSetOption failed for LAN, GLE=" + QSTRN(GetLastError()));
}
RASENTRYNAME entry;
entry.dwSize = sizeof(entry);
std::vector<RASENTRYNAME> entries;
DWORD size = sizeof(entry), count;
LPRASENTRYNAME entryAddr = &entry;
auto ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);
if (ERROR_BUFFER_TOO_SMALL == ret) {
entries.resize(count);
entries[0].dwSize = sizeof(RASENTRYNAME);
entryAddr = entries.data();
ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);
}
if (ERROR_SUCCESS != ret) {
LOG("Failed to list entry names");
return false;
}
// Set proxy for each connectoid.
for (DWORD i = 0; i < count; ++i) {
list.pszConnection = entryAddr[i].szEntryName;
if (!InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize)) {
LOG("InternetSetOption failed for connectoid " + QString::fromWCharArray(list.pszConnection) + ", GLE=" + QSTRN(GetLastError()));
}
}
delete[] list.pOptions;
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
return true;
}
#endif
void SetSystemProxy(int httpPort, int socksPort) {
const QString &address = "127.0.0.1";
bool hasHTTP = (httpPort > 0 && httpPort < 65536);
bool hasSOCKS = (socksPort > 0 && socksPort < 65536);
#ifdef Q_OS_WIN
if (!hasHTTP) {
LOG("Nothing?");
return;
} else {
LOG("Qv2ray will set system proxy to use HTTP");
}
#else
if (!hasHTTP && !hasSOCKS) {
LOG("Nothing?");
return;
}
if (hasHTTP) {
LOG("Qv2ray will set system proxy to use HTTP");
}
if (hasSOCKS) {
LOG("Qv2ray will set system proxy to use SOCKS");
}
#endif
#ifdef Q_OS_WIN
QString str = NekoGui::dataStore->system_proxy_format;
if (str.isEmpty()) str = Preset::Windows::system_proxy_format[0];
str = str.replace("{ip}", address)
.replace("{http_port}", Int2String(httpPort))
.replace("{socks_port}", Int2String(socksPort));
//
LOG("Windows proxy string: " + str);
auto proxyStrW = new WCHAR[str.length() + 1];
wcscpy(proxyStrW, str.toStdWString().c_str());
//
__QueryProxyOptions();
if (!__SetProxyOptions(proxyStrW, false)) {
LOG("Failed to set proxy.");
}
__QueryProxyOptions();
#elif defined(Q_OS_LINUX)
QList<ProcessArgument> actions;
actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "manual"}};
//
bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE" ||
qEnvironmentVariable("XDG_SESSION_DESKTOP") == "plasma";
const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
//
// Configure HTTP Proxies for HTTP, FTP and HTTPS
if (hasHTTP) {
// iterate over protocols...
for (const auto &protocol: QStringList{"http", "ftp", "https"}) {
// for GNOME:
{
actions << ProcessArgument{"gsettings",
{"set", "org.gnome.system.proxy." + protocol, "host", address}};
actions << ProcessArgument{"gsettings",
{"set", "org.gnome.system.proxy." + protocol, "port", QSTRN(httpPort)}};
}
// for KDE:
if (isKDE) {
actions << ProcessArgument{"kwriteconfig5",
{"--file", configPath + "/kioslaverc", //
"--group", "Proxy Settings", //
"--key", protocol + "Proxy", //
"http://" + address + " " + QSTRN(httpPort)}};
}
}
}
// Configure SOCKS5 Proxies
if (hasSOCKS) {
// for GNOME:
{
actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy.socks", "host", address}};
actions << ProcessArgument{"gsettings",
{"set", "org.gnome.system.proxy.socks", "port", QSTRN(socksPort)}};
// for KDE:
if (isKDE) {
actions << ProcessArgument{"kwriteconfig5",
{"--file", configPath + "/kioslaverc", //
"--group", "Proxy Settings", //
"--key", "socksProxy", //
"socks://" + address + " " + QSTRN(socksPort)}};
}
}
}
// Setting Proxy Mode to Manual
{
// for GNOME:
{
actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "manual"}};
}
// for KDE:
if (isKDE) {
actions << ProcessArgument{"kwriteconfig5",
{"--file", configPath + "/kioslaverc", //
"--group", "Proxy Settings", //
"--key", "ProxyType", "1"}};
}
}
// Notify kioslaves to reload system proxy configuration.
if (isKDE) {
actions << ProcessArgument{"dbus-send",
{"--type=signal", "/KIO/Scheduler", //
"org.kde.KIO.Scheduler.reparseSlaveConfiguration", //
"string:''"}};
}
// Execute them all!
//
// note: do not use std::all_of / any_of / none_of,
// because those are short-circuit and cannot guarantee atomicity.
QList<bool> results;
for (const auto &action: actions) {
// execute and get the code
const auto returnCode = QProcess::execute(action.first, action.second);
// print out the commands and result codes
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
// give the code back
results << (returnCode == QProcess::NormalExit);
}
if (results.count(true) != actions.size()) {
LOG("Something wrong when setting proxies.");
}
#else
for (const auto &service: macOSgetNetworkServices()) {
LOG("Setting proxy for interface: " + service);
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) {
QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxystate", service, "on"});
QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxy", service, address, QSTRN(socksPort)});
}
}
#endif
}
void ClearSystemProxy() {
LOG("Clearing System Proxy");
#ifdef Q_OS_WIN
if (!__SetProxyOptions(nullptr, false)) {
LOG("Failed to clear proxy.");
}
#elif defined(Q_OS_LINUX)
QList<ProcessArgument> actions;
const bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE" ||
qEnvironmentVariable("XDG_SESSION_DESKTOP") == "plasma";
const auto configRoot = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
// Setting System Proxy Mode to: None
{
// for GNOME:
{
actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "none"}};
}
// for KDE:
if (isKDE) {
actions << ProcessArgument{"kwriteconfig5",
{"--file", configRoot + "/kioslaverc", //
"--group", "Proxy Settings", //
"--key", "ProxyType", "0"}};
}
}
// Notify kioslaves to reload system proxy configuration.
if (isKDE) {
actions << ProcessArgument{"dbus-send",
{"--type=signal", "/KIO/Scheduler", //
"org.kde.KIO.Scheduler.reparseSlaveConfiguration", //
"string:''"}};
}
// Execute the Actions
for (const auto &action: actions) {
// execute and get the code
const auto returnCode = QProcess::execute(action.first, action.second);
// print out the commands and result codes
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
}
#else
for (const auto &service: macOSgetNetworkServices()) {
LOG("Clearing proxy for interface: " + service);
QProcess::execute("/usr/sbin/networksetup", {"-setautoproxystate", service, "off"});
QProcess::execute("/usr/sbin/networksetup", {"-setwebproxystate", service, "off"});
QProcess::execute("/usr/sbin/networksetup", {"-setsecurewebproxystate", service, "off"});
QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxystate", service, "off"});
}
#endif
}
} // namespace Qv2ray::components::proxy

View File

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

View File

@ -1,42 +0,0 @@
#include "GeositeReader.hpp"
#include "3rdparty/qv2ray/wrapper.hpp"
#include "picoproto.hpp"
#include <QFile>
#include <QMap>
namespace Qv2ray::components::GeositeReader {
QMap<QString, QStringList> GeositeEntries;
QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache) {
if (GeositeEntries.contains(filepath) && allowCache)
return GeositeEntries.value(filepath);
QStringList list;
qInfo() << "Reading geosites from:" << filepath;
QFile f(filepath);
bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
if (!opened) {
qInfo() << "File cannot be opened:" << filepath;
return list;
}
const auto content = f.readAll();
f.close();
{
picoproto::Message root;
root.ParseFromBytes((unsigned char *) content.data(), content.size());
list.reserve(root.GetMessageArray(1).size());
for (const auto &geosite: root.GetMessageArray(1))
list << QString::fromStdString(geosite->GetString(1));
}
qInfo() << "Loaded" << list.count() << "geosite entries from data file.";
list.sort();
GeositeEntries[filepath] = list;
return list;
}
} // namespace Qv2ray::components::GeositeReader

View File

@ -1,7 +0,0 @@
#pragma once
#include <QString>
namespace Qv2ray::components::GeositeReader {
QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache = true);
} // namespace Qv2ray::components::GeositeReader

View File

@ -1,551 +0,0 @@
/* Copyright 2016 Pete Warden. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "picoproto.hpp"
namespace picoproto {
namespace {
// To keep the dependencies down, here's a local copy of the widespread bit_cast
// operator. This is necessary because in practice weird things can happen if
// you just try to use reinterpret_cast.
template<class Dest, class Source>
inline Dest bit_cast(const Source &source) {
static_assert(sizeof(Dest) == sizeof(Source), "Sizes do not match");
Dest dest;
memcpy(&dest, &source, sizeof(dest));
return dest;
}
// These are defined in:
// https://developers.google.com/protocol-buffers/docs/encoding
enum WireType {
WIRETYPE_VARINT = 0,
WIRETYPE_64BIT = 1,
WIRETYPE_LENGTH_DELIMITED = 2,
WIRETYPE_GROUP_START = 3,
WIRETYPE_GROUP_END = 4,
WIRETYPE_32BIT = 5,
};
// Pull bytes from the stream, updating the state.
bool ConsumeBytes(uint8_t **current, size_t how_many, size_t *remaining) {
if (how_many > *remaining) {
PP_LOG(ERROR) << "ReadBytes overrun!";
return false;
}
*current += how_many;
*remaining -= how_many;
return true;
}
// Grabs a particular type from the byte stream.
template<class T>
T ReadFromBytes(uint8_t **current, size_t *remaining) {
PP_CHECK(ConsumeBytes(current, sizeof(T), remaining));
const T result = *(bit_cast<T *>(*current - sizeof(T)));
return result;
}
uint64_t ReadVarInt(uint8_t **current, size_t *remaining) {
uint64_t result = 0;
bool keep_going;
int shift = 0;
do {
const uint8_t next_number = ReadFromBytes<uint8_t>(current, remaining);
keep_going = (next_number >= 128);
result += (uint64_t) (next_number & 0x7f) << shift;
shift += 7;
} while (keep_going);
return result;
}
void ReadWireTypeAndFieldNumber(uint8_t **current, size_t *remaining, uint8_t *wire_type, uint32_t *field_number) {
uint64_t wire_type_and_field_number = ReadVarInt(current, remaining);
*wire_type = wire_type_and_field_number & 0x07;
*field_number = wire_type_and_field_number >> 3;
}
} // namespace
std::string FieldTypeDebugString(enum FieldType type) {
switch (type) {
case FIELD_UNSET:
return "UNSET";
break;
case FIELD_UINT32:
return "UINT32";
break;
case FIELD_UINT64:
return "UINT64";
break;
case FIELD_BYTES:
return "BYTES";
break;
default:
return "Unknown field type";
break;
}
return "Should never get here";
}
Field::Field(FieldType type, bool owns_data) : type(type), owns_data(owns_data) {
cached_messages = nullptr;
switch (type) {
case FIELD_UINT32: {
value.v_uint32 = new std::vector<uint32_t>();
} break;
case FIELD_UINT64: {
value.v_uint64 = new std::vector<uint64_t>();
} break;
case FIELD_BYTES: {
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>();
cached_messages = new std::vector<Message *>();
} break;
default: {
PP_LOG(ERROR) << "Bad field type when constructing field: " << type;
} break;
}
}
Field::Field(const Field &other) : type(other.type), owns_data(other.owns_data) {
switch (type) {
case FIELD_UINT32: {
value.v_uint32 = new std::vector<uint32_t>(*other.value.v_uint32);
} break;
case FIELD_UINT64: {
value.v_uint64 = new std::vector<uint64_t>(*other.value.v_uint64);
} break;
case FIELD_BYTES: {
if (owns_data) {
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>();
for (std::pair<uint8_t *, size_t> data_info: *other.value.v_bytes) {
uint8_t *new_data = new uint8_t[data_info.second];
std::copy_n(data_info.first, data_info.second, new_data);
value.v_bytes->push_back({new_data, data_info.second});
}
} else {
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>(*other.value.v_bytes);
}
cached_messages = new std::vector<Message *>();
cached_messages->reserve(other.cached_messages->size());
for (Message *other_cached_message: *other.cached_messages) {
Message *cached_message;
if (other_cached_message) {
cached_message = new Message(*other_cached_message);
} else {
cached_message = nullptr;
}
cached_messages->push_back(cached_message);
}
} break;
default: {
PP_LOG(ERROR) << "Bad field type when constructing field: " << type;
} break;
}
}
Field::~Field() {
switch (type) {
case FIELD_UINT32:
delete value.v_uint32;
break;
case FIELD_UINT64:
delete value.v_uint64;
break;
case FIELD_BYTES: {
if (owns_data)
for (std::pair<uint8_t *, size_t> data_info: *value.v_bytes)
delete[] data_info.first;
delete value.v_bytes;
for (Message *cached_message: *cached_messages)
if (cached_message)
delete cached_message;
delete cached_messages;
break;
}
default:
PP_LOG(ERROR) << "Bad field type when destroying field: " << type;
break;
}
}
Message::Message() : Message(true){};
Message::Message(bool copy_arrays) : copy_arrays(copy_arrays){};
Message::Message(const Message &other) : field_map(other.field_map), fields(other.fields), copy_arrays(other.copy_arrays){};
Message::~Message(){};
bool Message::ParseFromBytes(uint8_t *bytes, size_t bytes_size) {
uint8_t *current = bytes;
size_t remaining = bytes_size;
while (remaining > 0) {
uint8_t wire_type;
uint32_t field_number;
ReadWireTypeAndFieldNumber(&current, &remaining, &wire_type, &field_number);
switch (wire_type) {
case WIRETYPE_VARINT: {
Field *field = AddField(field_number, FIELD_UINT64);
const uint64_t varint = ReadVarInt(&current, &remaining);
field->value.v_uint64->push_back(varint);
break;
}
case WIRETYPE_64BIT: {
Field *field = AddField(field_number, FIELD_UINT64);
const uint64_t value = ReadFromBytes<uint64_t>(&current, &remaining);
field->value.v_uint64->push_back(value);
break;
}
case WIRETYPE_LENGTH_DELIMITED: {
Field *field = AddField(field_number, FIELD_BYTES);
const uint64_t size = ReadVarInt(&current, &remaining);
uint8_t *data;
if (copy_arrays) {
data = new uint8_t[size];
std::copy_n(current, size, data);
field->owns_data = true;
} else {
data = current;
field->owns_data = false;
}
field->value.v_bytes->push_back({data, size});
field->cached_messages->push_back(nullptr);
current += size;
remaining -= size;
break;
}
case WIRETYPE_GROUP_START: {
PP_LOG(INFO) << field_number << ": GROUPSTART" << std::endl;
PP_LOG(ERROR) << "Unhandled wire type encountered";
break;
}
case WIRETYPE_GROUP_END: {
PP_LOG(INFO) << field_number << ": GROUPEND" << std::endl;
PP_LOG(ERROR) << "Unhandled wire type encountered";
break;
}
case WIRETYPE_32BIT: {
Field *field = AddField(field_number, FIELD_UINT32);
const uint32_t value = ReadFromBytes<uint32_t>(&current, &remaining);
field->value.v_uint32->push_back(value);
break;
}
default: {
PP_LOG(ERROR) << "Unknown wire type encountered: " << static_cast<int>(wire_type) << " at offset" << (bytes_size - remaining);
return false;
break;
}
}
}
return true;
}
Field *Message::AddField(int32_t number, enum FieldType type) {
Field *field = GetField(number);
if (!field) {
fields.push_back(Field(type, copy_arrays));
field = &fields.back();
field_map.insert({number, fields.size() - 1});
}
return field;
}
Field *Message::GetField(int32_t number) {
if (field_map.count(number) == 0)
return nullptr;
return &(fields[field_map[number]]);
}
Field *Message::GetFieldAndCheckType(int32_t number, enum FieldType type) {
Field *field = GetField(number);
PP_CHECK(field) << "No field for " << number;
PP_CHECK(field->type == type) << "For field " << number << " wanted type " << FieldTypeDebugString(type) << " but found " << FieldTypeDebugString(field->type);
return field;
}
int32_t Message::GetInt32(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_UINT32);
uint32_t first_value = (*(field->value.v_uint32))[0];
int32_t zig_zag_decoded = static_cast<int32_t>((first_value >> 1) ^ (-(first_value & 1)));
return zig_zag_decoded;
}
int64_t Message::GetInt64(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_UINT64);
uint64_t first_value = (*(field->value.v_uint64))[0];
int64_t zig_zag_decoded = static_cast<int64_t>((first_value >> 1) ^ (-(first_value & 1)));
return zig_zag_decoded;
}
uint32_t Message::GetUInt32(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_UINT32);
uint32_t first_value = (*(field->value.v_uint32))[0];
return first_value;
}
uint64_t Message::GetUInt64(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_UINT64);
uint64_t first_value = (*(field->value.v_uint64))[0];
return first_value;
}
int64_t Message::GetInt(int32_t number) {
Field *field = GetField(number);
PP_CHECK(field) << "No field for " << number;
PP_CHECK((field->type == FIELD_UINT32) || (field->type == FIELD_UINT64))
<< "For field " << number << " wanted integer type but found " << FieldTypeDebugString(field->type);
switch (field->type) {
case FIELD_UINT32:
return GetInt32(number);
break;
case FIELD_UINT64:
return GetInt64(number);
break;
default: {
// Should never get here.
} break;
}
// Should never get here.
return 0;
}
bool Message::GetBool(int32_t number) {
return (GetInt(number) != 0);
}
float Message::GetFloat(int32_t number) {
uint32_t int_value = GetUInt32(number);
float float_value = *(bit_cast<float *>(&int_value));
return float_value;
}
double Message::GetDouble(int32_t number) {
uint64_t int_value = GetUInt64(number);
return *(bit_cast<double *>(&int_value));
}
std::pair<uint8_t *, size_t> Message::GetBytes(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
return first_value;
}
std::string Message::GetString(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
std::string result(first_value.first, first_value.first + first_value.second);
return result;
}
Message *Message::GetMessage(int32_t number) {
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
Message *cached_message = field->cached_messages->at(0);
if (!cached_message) {
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
cached_message = new Message(copy_arrays);
cached_message->ParseFromBytes(first_value.first, first_value.second);
field->cached_messages->at(0) = cached_message;
}
return cached_message;
}
std::vector<int32_t> Message::GetInt32Array(int32_t number) {
std::vector<uint64_t> raw_array = GetUInt64Array(number);
std::vector<int32_t> result;
result.reserve(raw_array.size());
for (uint64_t raw_value: raw_array) {
int32_t zig_zag_decoded = static_cast<int32_t>((raw_value >> 1) ^ (-(raw_value & 1)));
result.push_back(zig_zag_decoded);
}
return result;
}
std::vector<int64_t> Message::GetInt64Array(int32_t number) {
std::vector<uint64_t> raw_array = GetUInt64Array(number);
std::vector<int64_t> result;
result.reserve(raw_array.size());
for (uint64_t raw_value: raw_array) {
int64_t zig_zag_decoded = static_cast<int64_t>((raw_value >> 1) ^ (-(raw_value & 1)));
result.push_back(zig_zag_decoded);
}
return result;
}
std::vector<uint32_t> Message::GetUInt32Array(int32_t number) {
std::vector<uint64_t> raw_array = GetUInt64Array(number);
std::vector<uint32_t> result;
result.reserve(raw_array.size());
for (uint64_t raw_value: raw_array) {
result.push_back(static_cast<uint32_t>(raw_value));
}
return result;
}
std::vector<uint64_t> Message::GetUInt64Array(int32_t number) {
std::vector<uint64_t> result;
Field *field = GetField(number);
if (!field) {
return result;
}
if (field->type == FIELD_UINT64) {
result.reserve(field->value.v_uint64->size());
for (uint64_t value: *field->value.v_uint64) {
result.push_back(static_cast<uint64_t>(value));
}
} else if (field->type == FIELD_UINT32) {
result.reserve(field->value.v_uint32->size());
for (uint32_t value: *field->value.v_uint32) {
result.push_back(static_cast<uint64_t>(value));
}
} else if (field->type == FIELD_BYTES) {
for (std::pair<uint8_t *, size_t> data_info: *field->value.v_bytes) {
uint8_t *current = data_info.first;
size_t remaining = data_info.second;
while (remaining > 0) {
const uint64_t varint = ReadVarInt(&current, &remaining);
result.push_back(static_cast<int64_t>(varint));
}
}
} else {
PP_LOG(ERROR) << "Expected field type UINT32, UINT64, or BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
std::vector<bool> Message::GetBoolArray(int32_t number) {
std::vector<uint64_t> raw_array = GetUInt64Array(number);
std::vector<bool> result;
result.reserve(raw_array.size());
for (uint64_t raw_value: raw_array) {
result.push_back(raw_value != 0);
}
return result;
}
std::vector<float> Message::GetFloatArray(int32_t number) {
std::vector<float> result;
Field *field = GetField(number);
if (!field) {
return result;
}
if (field->type == FIELD_UINT32) {
result.reserve(field->value.v_uint32->size());
for (uint32_t value: *field->value.v_uint32) {
result.push_back(bit_cast<float>(value));
}
} else if (field->type == FIELD_BYTES) {
for (std::pair<uint8_t *, size_t> data_info: *field->value.v_bytes) {
uint8_t *current = data_info.first;
size_t remaining = data_info.second;
while (remaining > 0) {
const uint64_t varint = ReadVarInt(&current, &remaining);
const uint32_t varint32 = static_cast<uint32_t>(varint & 0xffffffff);
result.push_back(bit_cast<float>(varint32));
}
}
} else {
PP_LOG(ERROR) << "Expected field type UINT32 or BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
std::vector<double> Message::GetDoubleArray(int32_t number) {
std::vector<double> result;
Field *field = GetField(number);
if (!field) {
return result;
}
if (field->type == FIELD_UINT64) {
result.reserve(field->value.v_uint64->size());
for (uint64_t value: *field->value.v_uint64) {
result.push_back(bit_cast<double>(value));
}
} else if (field->type == FIELD_BYTES) {
for (std::pair<uint8_t *, size_t> data_info: *field->value.v_bytes) {
uint8_t *current = data_info.first;
size_t remaining = data_info.second;
while (remaining > 0) {
const uint64_t varint = ReadVarInt(&current, &remaining);
result.push_back(bit_cast<double>(varint));
}
}
} else {
PP_LOG(ERROR) << "Expected field type UINT64 or BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
std::vector<std::pair<uint8_t *, size_t>> Message::GetByteArray(int32_t number) {
std::vector<std::pair<uint8_t *, size_t>> result;
Field *field = GetField(number);
if (!field) {
return result;
}
if (field->type == FIELD_BYTES) {
result.reserve(field->value.v_bytes->size());
for (std::pair<uint8_t *, size_t> data_info: *field->value.v_bytes) {
result.push_back(data_info);
}
} else {
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
std::vector<std::string> Message::GetStringArray(int32_t number) {
std::vector<std::string> result;
Field *field = GetField(number);
if (!field)
return result;
if (field->type == FIELD_BYTES) {
result.reserve(field->value.v_bytes->size());
for (std::pair<uint8_t *, size_t> data_info: *field->value.v_bytes) {
result.push_back(std::string(data_info.first, data_info.first + data_info.second));
}
} else {
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
std::vector<Message *> Message::GetMessageArray(int32_t number) {
std::vector<Message *> result;
Field *field = GetField(number);
if (!field)
return result;
if (field->type == FIELD_BYTES) {
result.reserve(field->value.v_bytes->size());
for (size_t i = 0; i < field->value.v_bytes->size(); ++i) {
Message *cached_message = field->cached_messages->at(i);
if (!cached_message) {
std::pair<uint8_t *, size_t> value = field->value.v_bytes->at(i);
cached_message = new Message(copy_arrays);
cached_message->ParseFromBytes(value.first, value.second);
field->cached_messages->at(i) = cached_message;
}
result.push_back(cached_message);
}
} else {
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
}
return result;
}
} // namespace picoproto

View File

@ -1,208 +0,0 @@
/* Copyright 2016 Pete Warden. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
/*
See the README for full details, but this module lets you read in protobuf
encoded files with a minimal code footprint.
It doesn't create classes for each kind of message it encounters, it just has a
single Message interface that lets you access the members of the protobuf as a
key/value store. This loses a lot of the convenience of type-checked classes,
but it does mean that very little code is needed to access data from files.
As a simple example, if you had read a `bytes_size` long file into `bytes` that
contained a TensorFlow GraphDef proto:
Message graph_def;
graph_def.ParseFromBytes(bytes, bytes_size);
You can then access the different fields of the GraphDef using the field
numbers assigned in the .proto file:
std::vector<picoproto::Message*> nodes = graph_def.GetMessageArray(1);
One big difference between this minimal approach and normal protobufs is that
the calling code has to already know the field number and type of any members
it's trying to access. Here I know that the `node` field is number 1, and that
it should contain a repeated list of NodeDefs. Since they are not primitive
types like numbers or strings, they are accessed as an array of Messages.
Here are the design goals of this module:
- Keep the code size tiny (single-digit kilobytes on most platforms).
- Minimize memory usage (for example allow in-place references to byte data).
- Provide a simple, readable implementation that can be ported easily.
- Deserialize all saved protobuf files into a usable representation.
- No dependencies other than the standard C++ library.
- No build-time support (e.g. protoc) required.
Here's what it's explicitly not offering:
- Providing a readable and transparent way of accessing serialized data.
- Saving out data to protobuf format.
*/
#ifndef INCLUDE_PICOPROTO_H
#define INCLUDE_PICOPROTO_H
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <vector>
// To keep dependencies minimal, some bare-bones macros to make logging easier.
#define PP_LOG(X) PP_LOG_##X
#define PP_LOG_INFO std::cerr << __FILE__ << ":" << __LINE__ << " - INFO: "
#define PP_LOG_WARN std::cerr << __FILE__ << ":" << __LINE__ << " - WARN: "
#define PP_LOG_ERROR std::cerr << __FILE__ << ":" << __LINE__ << " - ERROR: "
#define PP_CHECK(X) \
if (!(X)) \
PP_LOG(ERROR) << "PP_CHECK(" << #X << ") failed. "
namespace picoproto {
// These roughly correspond to the wire types used to save data in protobuf
// files. The best reference to understand the full format is:
// https://developers.google.com/protocol-buffers/docs/encoding
// Because we don't know the bit-depth of VarInts, they're always stored as
// uint64 values, which is why there's no specific type for them.
enum FieldType {
FIELD_UNSET,
FIELD_UINT32,
FIELD_UINT64,
FIELD_BYTES,
};
// Gives a readable name for the field type for logging purposes.
std::string FieldTypeDebugString(enum FieldType type);
// Forward declare the main message class, since fields can contain them.
class Message;
// Fields are the building blocks of messages. They contain the values for each
// data member, and handle all the allocation and deallocation of storage.
// It's unlikely you'll want to access this class directly, since you'll
// normally want to use Message below to pull typed values.
class Field {
public:
// You need to specify the type of a Field on creation, so that the right
// storage can be set up for the values. You also need to indicate whether the
// underlying memory will be around for the lifetime of the message (in which
// case no copies are needed) or whether the class should make copies and take
// ownership in case the data goes away.
Field(FieldType type, bool owns_data);
Field(const Field &other);
~Field();
enum FieldType type;
// I know, this isn't very OOP, but the simplicity of keeping track of a type
// and deciding how to initialize and access the data based on that persuaded
// me this was the best approach. The `value` member contains whatever data
// the field should be holding.
union {
std::vector<uint32_t> *v_uint32;
std::vector<uint64_t> *v_uint64;
std::vector<std::pair<uint8_t *, size_t>> *v_bytes;
} value;
// One of the drawbacks of not requiring .proto files ahead of time is that I
// don't know if a length-delimited field contains raw bytes, strings, or
// sub-messages. The only time we know that a field should be interpreted as a
// message is when client code requests it in that form. Because parsing can
// be costly, here we cache the results of any such calls for subsequent
// accesses.
std::vector<Message *> *cached_messages;
// If this is set, then the object will allocate its own storage for
// length-delimited values, and copy from the input stream. If you know the
// underlying data will be around for the lifetime of the message, you can
// save memory and copies by leaving this as false.
bool owns_data;
};
// The main interface for loading and accessing serialized protobuf data.
class Message {
public:
// If you're not sure about the lifetime of any binary data you're reading
// from, just call this default constructor.
Message();
// In the case when you're sure the lifetime of the byte stream you'll be
// decoding is longer than the lifetime of the message, you can set
// copy_arrays to false. This is especially useful if you have a memory
// mapped file to read from containing large binary blobs, since you'll skip
// a lot of copying and extra allocation.
Message(bool copy_arrays);
Message(const Message &other);
~Message();
// Populates fields with all of the data from this stream of bytes.
// You can call this repeatedly with new messages, and the results will be
// merged together.
bool ParseFromBytes(uint8_t *binary, size_t binary_size);
// These are the accessor functions if you're expecting exactly one value in a
// field. As discussed above, the burden is on the client code to know the
// field number and type of each member it's trying to access, and so pick the
// correct accessor function.
// If the field isn't present, this will raise an error, so if it's optional
// you should use the array accessors below.
int32_t GetInt32(int32_t number);
int64_t GetInt64(int32_t number);
uint32_t GetUInt32(int32_t number);
uint64_t GetUInt64(int32_t number);
int64_t GetInt(int32_t number);
bool GetBool(int32_t number);
float GetFloat(int32_t number);
double GetDouble(int32_t number);
std::pair<uint8_t *, size_t> GetBytes(int32_t number);
std::string GetString(int32_t number);
Message *GetMessage(int32_t number);
// If you're not sure if a value will be present, or if it is repeated, you
// should call these array functions. If no such field has been seen, then the
// result will be an empty vector, otherwise you'll get back one or more
// entries.
std::vector<int32_t> GetInt32Array(int32_t number);
std::vector<int64_t> GetInt64Array(int32_t number);
std::vector<uint32_t> GetUInt32Array(int32_t number);
std::vector<uint64_t> GetUInt64Array(int32_t number);
std::vector<bool> GetBoolArray(int32_t number);
std::vector<float> GetFloatArray(int32_t number);
std::vector<double> GetDoubleArray(int32_t number);
std::vector<std::pair<uint8_t *, size_t>> GetByteArray(int32_t number);
std::vector<std::string> GetStringArray(int32_t number);
std::vector<Message *> GetMessageArray(int32_t number);
// It's unlikely you'll want to access fields directly, but here's an escape
// hatch in case you do have to manipulate them more directly.
Field *GetField(int32_t number);
private:
// Inserts a new field, updating all the internal data structures.
Field *AddField(int32_t number, enum FieldType type);
Field *GetFieldAndCheckType(int32_t number, enum FieldType type);
// Maps from a field number to an index in the `fields` vector.
std::map<int32_t, size_t> field_map;
// The core list of fields that have been parsed.
std::vector<Field> fields;
bool copy_arrays;
};
} // namespace picoproto
#endif // INCLUDE_PICOPROTO_H

View File

@ -92,15 +92,11 @@ set(PROJECT_SOURCES
3rdparty/qv2ray/v2/ui/LogHighlighter.cpp
3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp
3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp
3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp
3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp
3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp
3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui
3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp
3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp
rpc/gRPC.cpp
db/Database.cpp

View File

@ -453,14 +453,6 @@ namespace NekoGui {
inboundObj["sniff"] = true;
inboundObj["sniff_override_destination"] = dataStore->routing->sniffing_mode == SniffingMode::FOR_DESTINATION;
}
if (dataStore->inbound_auth->NeedAuth()) {
inboundObj["users"] = QJsonArray{
QJsonObject{
{"username", dataStore->inbound_auth->username},
{"password", dataStore->inbound_auth->password},
},
};
}
inboundObj["domain_strategy"] = dataStore->routing->domain_strategy;
status->inbounds += inboundObj;
}

View File

@ -121,80 +121,59 @@ namespace NekoGui_fmt {
}
QString VMessBean::ToShareLink() {
if (NekoGui::dataStore->old_share_link_format) {
// v2rayN format
QJsonObject N{
{"v", "2"},
{"ps", name},
{"add", serverAddress},
{"port", Int2String(serverPort)},
{"id", uuid},
{"aid", Int2String(aid)},
{"net", stream->network},
{"host", stream->host},
{"path", stream->path},
{"type", stream->header_type},
{"scy", security},
{"tls", stream->security == "tls" ? "tls" : ""},
{"sni", stream->sni},
};
return "vmess://" + QJsonObject2QString(N, true).toUtf8().toBase64();
QUrl url;
QUrlQuery query;
url.setScheme("vmess");
url.setUserName(uuid);
url.setHost(serverAddress);
url.setPort(serverPort);
if (!name.isEmpty()) url.setFragment(name);
query.addQueryItem("encryption", security);
// security
auto security = stream->security;
if (security == "tls" && !stream->reality_pbk.trimmed().isEmpty()) security = "reality";
query.addQueryItem("security", security);
if (!stream->sni.isEmpty()) query.addQueryItem("sni", stream->sni);
if (stream->allow_insecure) query.addQueryItem("allowInsecure", "1");
if (stream->utlsFingerprint.isEmpty()) {
query.addQueryItem("fp", NekoGui::dataStore->utlsFingerprint);
} else {
// ducksoft format
QUrl url;
QUrlQuery query;
url.setScheme("vmess");
url.setUserName(uuid);
url.setHost(serverAddress);
url.setPort(serverPort);
if (!name.isEmpty()) url.setFragment(name);
query.addQueryItem("encryption", security);
// security
auto security = stream->security;
if (security == "tls" && !stream->reality_pbk.trimmed().isEmpty()) security = "reality";
query.addQueryItem("security", security);
if (!stream->sni.isEmpty()) query.addQueryItem("sni", stream->sni);
if (stream->allow_insecure) query.addQueryItem("allowInsecure", "1");
if (stream->utlsFingerprint.isEmpty()) {
query.addQueryItem("fp", NekoGui::dataStore->utlsFingerprint);
} else {
query.addQueryItem("fp", stream->utlsFingerprint);
}
if (security == "reality") {
query.addQueryItem("pbk", stream->reality_pbk);
if (!stream->reality_sid.isEmpty()) query.addQueryItem("sid", stream->reality_sid);
if (!stream->reality_spx.isEmpty()) query.addQueryItem("spx", stream->reality_spx);
}
// type
query.addQueryItem("type", stream->network);
if (stream->network == "ws" || stream->network == "http" || stream->network == "httpupgrade") {
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
} else if (stream->network == "grpc") {
if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path);
} else if (stream->network == "tcp") {
if (stream->header_type == "http") {
query.addQueryItem("headerType", "http");
query.addQueryItem("host", stream->host);
}
}
// mux
if (mux_state == 1) {
query.addQueryItem("mux", "true");
} else if (mux_state == 2) {
query.addQueryItem("mux", "false");
}
url.setQuery(query);
return url.toString(QUrl::FullyEncoded);
query.addQueryItem("fp", stream->utlsFingerprint);
}
if (security == "reality") {
query.addQueryItem("pbk", stream->reality_pbk);
if (!stream->reality_sid.isEmpty()) query.addQueryItem("sid", stream->reality_sid);
if (!stream->reality_spx.isEmpty()) query.addQueryItem("spx", stream->reality_spx);
}
// type
query.addQueryItem("type", stream->network);
if (stream->network == "ws" || stream->network == "http" || stream->network == "httpupgrade") {
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
} else if (stream->network == "grpc") {
if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path);
} else if (stream->network == "tcp") {
if (stream->header_type == "http") {
query.addQueryItem("headerType", "http");
query.addQueryItem("host", stream->host);
}
}
// mux
if (mux_state == 1) {
query.addQueryItem("mux", "true");
} else if (mux_state == 2) {
query.addQueryItem("mux", "false");
}
url.setQuery(query);
return url.toString(QUrl::FullyEncoded);
}
QString NaiveBean::ToShareLink() {

View File

@ -3,6 +3,11 @@
#include <QByteArray>
#include <QMetaEnum>
#include <QTimer>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDir>
#include <QApplication>
#include "cpr/cpr.h"
#include "main/NekoGui.hpp"
@ -11,14 +16,10 @@ namespace NekoGui_network {
NekoHTTPResponse NetworkRequestHelper::HttpGet(const QString &url) {
cpr::Session session;
if (NekoGui::dataStore->sub_use_proxy) {
if (NekoGui::dataStore->sub_use_proxy || NekoGui::dataStore->spmode_system_proxy) {
session.SetProxies({{"http", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()},
{"https", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}});
if (NekoGui::dataStore->inbound_auth->NeedAuth()) {
session.SetProxyAuth(cpr::ProxyAuthentication{{"http",
cpr::EncodedAuthentication{NekoGui::dataStore->inbound_auth->username.toStdString(), NekoGui::dataStore->inbound_auth->password.toStdString()}}});
}
if (NekoGui::dataStore->started_id < 0) {
if (NekoGui::dataStore->started_id < 0 && NekoGui::dataStore->sub_use_proxy) {
return NekoHTTPResponse{QObject::tr("Request with proxy but no profile started.")};
}
}
@ -46,4 +47,59 @@ namespace NekoGui_network {
return "";
}
QString NetworkRequestHelper::GetLatestDownloadURL(const QString &url, const QString &assetName, bool* success) {
cpr::Session session;
session.SetUrl(cpr::Url{url.toStdString()});
session.SetTimeout(3000);
if (NekoGui::dataStore->spmode_system_proxy) {
session.SetProxies({{"http", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()},
{"https", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}});
}
cpr::Response r = session.Get();
if (r.status_code != 200) {
*success = false;
return {r.status_line.c_str()};
}
auto respObj = QString2QJsonObject(QString(r.text.c_str()));
auto assets = respObj["assets"].toArray();
for (const auto &asset: assets) {
auto assetObj = asset.toObject();
if (assetObj["name"] == assetName) {
*success = true;
return assetObj["browser_download_url"].toString();
}
}
*success = false;
return "not found";
}
QString NetworkRequestHelper::DownloadGeoAsset(const QString &url, const QString &fileName) {
cpr::Session session;
session.SetUrl(cpr::Url{url.toStdString()});
session.SetTimeout(3000);
if (NekoGui::dataStore->spmode_system_proxy) {
session.SetProxies({{"http", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()},
{"https", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}});
}
auto filePath = qApp->applicationDirPath()+ "/" + fileName;
std::ofstream fout;
fout.open(QString(filePath + ".1").toStdString(), std::ios::trunc | std::ios::out | std::ios::binary);
auto r = session.Download(fout);
fout.close();
auto tmpFile = QFile(filePath + ".1");
if (r.status_code != 200) {
tmpFile.remove();
if (r.status_code == 0) {
return "Please check the URL and your network Connectivity";
}
return r.status_line.c_str();
}
QFile(filePath).remove();
if (!tmpFile.rename(filePath)) {
return tmpFile.errorString();
}
return "";
}
} // namespace NekoGui_network

View File

@ -25,6 +25,10 @@ namespace NekoGui_network {
static NekoHTTPResponse HttpGet(const QString &url);
static QString GetHeader(const QList<QPair<QByteArray, QByteArray>> &header, const QString &name);
static QString GetLatestDownloadURL(const QString &url, const QString &assetName, bool* success);
static QString DownloadGeoAsset(const QString &url, const QString &fileName);
};
} // namespace NekoGui_network

View File

@ -240,16 +240,12 @@ namespace NekoGui {
DataStore::DataStore() : JsonStore() {
_add(new configItem("extraCore", dynamic_cast<JsonStore *>(extraCore), itemType::jsonStore));
_add(new configItem("inbound_auth", dynamic_cast<JsonStore *>(inbound_auth), itemType::jsonStore));
_add(new configItem("user_agent2", &user_agent, itemType::string));
_add(new configItem("test_url", &test_latency_url, itemType::string));
_add(new configItem("test_url_dl", &test_download_url, itemType::string));
_add(new configItem("test_dl_timeout", &test_download_timeout, itemType::integer));
_add(new configItem("current_group", &current_group, itemType::integer));
_add(new configItem("inbound_address", &inbound_address, itemType::string));
_add(new configItem("inbound_socks_port", &inbound_socks_port, itemType::integer));
_add(new configItem("inbound_http_port", &inbound_http_port, itemType::integer));
_add(new configItem("log_level", &log_level, itemType::string));
_add(new configItem("mux_protocol", &mux_protocol, itemType::string));
_add(new configItem("mux_concurrency", &mux_concurrency, itemType::integer));
@ -260,7 +256,6 @@ namespace NekoGui {
_add(new configItem("theme", &theme, itemType::string));
_add(new configItem("custom_inbound", &custom_inbound, itemType::string));
_add(new configItem("custom_route", &custom_route_global, itemType::string));
_add(new configItem("v2ray_asset_dir", &v2ray_asset_dir, itemType::string));
_add(new configItem("sub_use_proxy", &sub_use_proxy, itemType::boolean));
_add(new configItem("remember_id", &remember_id, itemType::integer));
_add(new configItem("remember_enable", &remember_enable, itemType::boolean));
@ -281,8 +276,6 @@ namespace NekoGui {
_add(new configItem("vpn_ipv6", &vpn_ipv6, itemType::boolean));
_add(new configItem("vpn_hide_console", &vpn_hide_console, itemType::boolean));
_add(new configItem("vpn_strict_route", &vpn_strict_route, itemType::boolean));
_add(new configItem("check_include_pre", &check_include_pre, itemType::boolean));
_add(new configItem("sp_format", &system_proxy_format, itemType::string));
_add(new configItem("sub_clear", &sub_clear, itemType::boolean));
_add(new configItem("sub_insecure", &sub_insecure, itemType::boolean));
_add(new configItem("sub_auto_update", &sub_auto_update, itemType::integer));
@ -300,6 +293,8 @@ namespace NekoGui {
_add(new configItem("ntp_server_address", &ntp_server_address, itemType::string));
_add(new configItem("ntp_server_port", &ntp_server_port, itemType::integer));
_add(new configItem("ntp_interval", &ntp_interval, itemType::string));
_add(new configItem("geoip_download_url", &geoip_download_url, itemType::string));
_add(new configItem("geosite_download_url", &geosite_download_url, itemType::string));
}
void DataStore::UpdateStartedId(int id) {
@ -373,25 +368,12 @@ namespace NekoGui {
core_map = QJsonObject2QString(obj, true);
}
InboundAuthorization::InboundAuthorization() : JsonStore() {
_add(new configItem("user", &this->username, itemType::string));
_add(new configItem("pass", &this->password, itemType::string));
}
bool InboundAuthorization::NeedAuth() const {
return !username.trimmed().isEmpty() && !password.trimmed().isEmpty();
}
// System Utils
QString FindCoreAsset(const QString &name) {
QStringList search{NekoGui::dataStore->v2ray_asset_dir};
search << QApplication::applicationDirPath();
QStringList search{QApplication::applicationDirPath()};
search << "/usr/share/sing-geoip";
search << "/usr/share/sing-geosite";
search << "/usr/share/xray";
search << "/usr/local/share/xray";
search << "/opt/xray";
search << "/usr/share/v2ray";
search << "/usr/local/share/v2ray";
search << "/opt/v2ray";

View File

@ -39,16 +39,6 @@ namespace NekoGui {
void Delete(const QString &id);
};
class InboundAuthorization : public JsonStore {
public:
QString username;
QString password;
InboundAuthorization();
[[nodiscard]] bool NeedAuth() const;
};
class DataStore : public JsonStore {
public:
// Running
@ -84,10 +74,7 @@ namespace NekoGui {
// Misc
QString log_level = "warning";
QString test_latency_url = "http://cp.cloudflare.com/";
QString test_download_url = "http://cachefly.cachefly.net/10mb.test";
int test_download_timeout = 30;
int test_concurrent = 5;
bool old_share_link_format = true;
int traffic_loop_interval = 1000;
bool disable_traffic_stats = false;
int current_group = 0; // group id
@ -96,11 +83,8 @@ namespace NekoGui {
int mux_concurrency = 8;
bool mux_default_on = false;
QString theme = "0";
QString v2ray_asset_dir = "";
int language = 0;
QString mw_size = "";
bool check_include_pre = true;
QString system_proxy_format = "";
QStringList log_ignore = {};
bool start_minimal = false;
int max_log_line = 200;
@ -113,6 +97,10 @@ namespace NekoGui {
bool sub_insecure = false;
int sub_auto_update = -30;
// Assets
QString geoip_download_url = "";
QString geosite_download_url = "";
// Security
bool skip_cert = false;
QString utlsFingerprint = "";
@ -124,9 +112,7 @@ namespace NekoGui {
// Socks & HTTP Inbound
QString inbound_address = "127.0.0.1";
int inbound_socks_port = 2080; // or Mixed
int inbound_http_port = 2081;
InboundAuthorization *inbound_auth = new InboundAuthorization;
int inbound_socks_port = 2080; // Mixed, actually
QString custom_inbound = "{\"inbounds\": []}";
// Routing

View File

@ -7,6 +7,7 @@
#include "ui/Icon.hpp"
#include "main/GuiUtils.hpp"
#include "main/NekoGui.hpp"
#include "main/HTTPRequestHelper.hpp"
#include <QStyleFactory>
#include <QFileDialog>
@ -58,48 +59,25 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
ADD_ASTERISK(this);
// Common
ui->groupBox_http->hide();
ui->inbound_socks_port_l->setText(ui->inbound_socks_port_l->text().replace("Socks", "Mixed (SOCKS+HTTP)"));
ui->log_level->addItems(QString("trace debug info warn error fatal panic").split(" "));
ui->mux_protocol->addItems({"h2mux", "smux", "yamux"});
ui->disable_stats->setChecked(NekoGui::dataStore->disable_traffic_stats);
refresh_auth();
D_LOAD_STRING(inbound_address)
D_LOAD_COMBO_STRING(log_level)
CACHE.custom_inbound = NekoGui::dataStore->custom_inbound;
D_LOAD_INT(inbound_socks_port)
D_LOAD_INT_ENABLE(inbound_http_port, http_enable)
D_LOAD_INT(test_concurrent)
D_LOAD_INT(test_download_timeout)
D_LOAD_STRING(test_latency_url)
D_LOAD_STRING(test_download_url)
D_LOAD_BOOL(old_share_link_format)
connect(ui->custom_inbound_edit, &QPushButton::clicked, this, [=] {
C_EDIT_JSON_ALLOW_EMPTY(custom_inbound)
});
#ifdef Q_OS_WIN
connect(ui->sys_proxy_format, &QPushButton::clicked, this, [=] {
bool ok;
auto str = QInputDialog::getItem(this, ui->sys_proxy_format->text() + " (Windows)",
tr("Advanced system proxy settings. Please select a format."),
Preset::Windows::system_proxy_format,
Preset::Windows::system_proxy_format.indexOf(NekoGui::dataStore->system_proxy_format),
false, &ok);
if (ok) NekoGui::dataStore->system_proxy_format = str;
});
#else
ui->sys_proxy_format->hide();
#endif
// Style
ui->connection_statistics_box->setDisabled(true);
//
D_LOAD_BOOL(check_include_pre)
D_LOAD_BOOL(start_minimal)
D_LOAD_INT(max_log_line)
//
@ -149,9 +127,47 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
D_LOAD_INT_ENABLE(sub_auto_update, sub_auto_update_enable)
// Core
ui->groupBox_core->setTitle(software_core_name);
ui->core_v2ray_asset->setText(NekoGui::dataStore->v2ray_asset_dir);
// Assets
ui->geoip_url->setText(NekoGui::dataStore->geoip_download_url);
ui->geosite_url->setText(NekoGui::dataStore->geosite_download_url);
connect(ui->geoip_auto_btn, &QPushButton::clicked, this, [=](){
bool success;
auto resp = NetworkRequestHelper::GetLatestDownloadURL("https://api.github.com/repos/SagerNet/sing-geoip/releases/latest", "geoip.db", &success);
if (!success) {
runOnUiThread([=](){
MessageBoxWarning("Error", resp);
});
return;
}
ui->geoip_url->setText(resp);
});
connect(ui->geosite_auto_btn, &QPushButton::clicked, this, [=](){
bool success;
auto resp = NetworkRequestHelper::GetLatestDownloadURL("https://api.github.com/repos/SagerNet/sing-geosite/releases/latest", "geosite.db", &success);
if (!success) {
runOnUiThread([=](){
MessageBoxWarning("Error", resp);
});
return;
}
ui->geosite_url->setText(resp);
});
connect(ui->download_geo_btn, &QPushButton::clicked, this, [=]() {
MW_dialog_message(Dialog_DialogBasicSettings, "DownloadAssets;"+ui->geoip_url->text()+";"+ui->geosite_url->text());
});
connect(ui->remove_srs_btn, &QPushButton::clicked, this, [=](){
auto rsDir = QDir(RULE_SETS_DIR);
auto entries = rsDir.entryList(QDir::Files);
for (const auto &item: entries) {
if (!QFile(RULE_SETS_DIR + "/" + item).remove()) {
MW_show_log("Failed to remove " + item + ", stop the core then try again");
}
}
MW_show_log("Removed all rule-set files");
});
//
CACHE.extraCore = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map);
if (!CACHE.extraCore.contains("naive")) CACHE.extraCore.insert("naive", "");
@ -164,16 +180,6 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
extra_core_layout->addWidget(new ExtraCoreWidget(&CACHE.extraCore, s));
}
//
connect(ui->core_v2ray_asset, &QLineEdit::textChanged, this, [=] {
CACHE.needRestart = true;
});
connect(ui->core_v2ray_asset_pick, &QPushButton::clicked, this, [=] {
auto fn = QFileDialog::getExistingDirectory(this, tr("Select"), QDir::currentPath(),
QFileDialog::Option::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (!fn.isEmpty()) {
ui->core_v2ray_asset->setText(fn);
}
});
connect(ui->extra_core_add, &QPushButton::clicked, this, [=] {
bool ok;
auto s = QInputDialog::getText(nullptr, tr("Add"),
@ -241,17 +247,12 @@ void DialogBasicSettings::accept() {
D_SAVE_COMBO_STRING(log_level)
NekoGui::dataStore->custom_inbound = CACHE.custom_inbound;
D_SAVE_INT(inbound_socks_port)
D_SAVE_INT_ENABLE(inbound_http_port, http_enable)
D_SAVE_INT(test_concurrent)
D_SAVE_INT(test_download_timeout)
D_SAVE_STRING(test_latency_url)
D_SAVE_STRING(test_download_url)
D_SAVE_BOOL(old_share_link_format)
// Style
NekoGui::dataStore->language = ui->language->currentIndex();
D_SAVE_BOOL(check_include_pre)
D_SAVE_BOOL(start_minimal)
D_SAVE_INT(max_log_line)
@ -288,11 +289,13 @@ void DialogBasicSettings::accept() {
D_SAVE_INT_ENABLE(sub_auto_update, sub_auto_update_enable)
// Core
NekoGui::dataStore->v2ray_asset_dir = ui->core_v2ray_asset->text();
NekoGui::dataStore->extraCore->core_map = QJsonObject2QString(CACHE.extraCore, true);
NekoGui::dataStore->disable_traffic_stats = ui->disable_stats->isChecked();
// Assets
NekoGui::dataStore->geoip_download_url = ui->geoip_url->text();
NekoGui::dataStore->geosite_download_url = ui->geosite_url->text();
// Mux
D_SAVE_INT(mux_concurrency)
D_SAVE_COMBO_STRING(mux_protocol)
@ -316,17 +319,6 @@ void DialogBasicSettings::accept() {
QDialog::accept();
}
// slots
void DialogBasicSettings::refresh_auth() {
ui->inbound_auth->setText({});
if (NekoGui::dataStore->inbound_auth->NeedAuth()) {
ui->inbound_auth->setIcon(Icon::GetMaterialIcon("lock-outline"));
} else {
ui->inbound_auth->setIcon(Icon::GetMaterialIcon("lock-open-outline"));
}
}
void DialogBasicSettings::on_set_custom_icon_clicked() {
auto title = ui->set_custom_icon->text();
QString user_icon_path = "./" + software_name.toLower() + ".png";
@ -350,40 +342,6 @@ void DialogBasicSettings::on_set_custom_icon_clicked() {
MW_dialog_message(Dialog_DialogBasicSettings, "UpdateIcon");
}
void DialogBasicSettings::on_inbound_auth_clicked() {
auto w = new QDialog(this);
w->setWindowTitle(tr("Inbound Auth"));
auto layout = new QGridLayout;
w->setLayout(layout);
//
auto user_l = new QLabel(tr("Username"));
auto pass_l = new QLabel(tr("Password"));
auto user = new MyLineEdit;
auto pass = new MyLineEdit;
user->setText(NekoGui::dataStore->inbound_auth->username);
pass->setText(NekoGui::dataStore->inbound_auth->password);
//
layout->addWidget(user_l, 0, 0);
layout->addWidget(user, 0, 1);
layout->addWidget(pass_l, 1, 0);
layout->addWidget(pass, 1, 1);
auto box = new QDialogButtonBox;
box->setOrientation(Qt::Horizontal);
box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
connect(box, &QDialogButtonBox::accepted, w, [=] {
NekoGui::dataStore->inbound_auth->username = user->text();
NekoGui::dataStore->inbound_auth->password = pass->text();
MW_dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore");
w->accept();
});
connect(box, &QDialogButtonBox::rejected, w, &QDialog::reject);
layout->addWidget(box, 2, 1);
//
w->exec();
w->deleteLater();
refresh_auth();
}
void DialogBasicSettings::on_core_settings_clicked() {
auto w = new QDialog(this);
w->setWindowTitle(software_core_name + " Core Options");
@ -449,5 +407,4 @@ void DialogBasicSettings::on_core_settings_clicked() {
ADD_ASTERISK(w)
w->exec();
w->deleteLater();
refresh_auth();
}

View File

@ -31,12 +31,8 @@ private:
private slots:
void refresh_auth();
void on_set_custom_icon_clicked();
void on_inbound_auth_clicked();
void on_core_settings_clicked();
};

View File

@ -30,7 +30,7 @@
</property>
</widget>
</item>
<item row="2" column="3">
<item row="0" column="3">
<widget class="QTabWidget" name="tabWidget">
<property name="minimumSize">
<size>
@ -61,47 +61,16 @@
<item>
<widget class="QLineEdit" name="inbound_address"/>
</item>
<item>
<widget class="QPushButton" name="inbound_auth">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupbox_custom_inbound">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Custom Inbound</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="custom_inbound_edit">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hlayout_l2">
<item>
<widget class="QGroupBox" name="horizontalGroupBox_2">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QLabel" name="inbound_socks_port_l">
<property name="text">
<string>Socks Listen Port</string>
<string>Listen Port</string>
</property>
</widget>
</item>
@ -118,30 +87,24 @@
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hlayout_l2">
<item>
<widget class="QGroupBox" name="groupBox_http">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<widget class="QGroupBox" name="groupbox_custom_inbound">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="label_5">
<widget class="QLabel" name="label_11">
<property name="text">
<string>HTTP Listen Port</string>
<string>Custom Inbound</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="inbound_http_port">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="http_enable">
<widget class="QPushButton" name="custom_inbound_edit">
<property name="text">
<string>Enable</string>
<string>Edit</string>
</property>
</widget>
</item>
@ -176,69 +139,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="horizontalGroupBox">
<layout class="QHBoxLayout" name="horizontalLayout_12" stretch="0,8,1,1">
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>Download Test URL</string>
</property>
</widget>
</item>
<item>
<widget class="MyLineEdit" name="test_download_url"/>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Timeout (s)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="test_download_timeout"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="horizontalGroupBox1">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<widget class="QCheckBox" name="check_include_pre">
<property name="text">
<string>Include Pre-release when checking update</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="sys_proxy_format_vline">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="old_share_link_format">
<property name="toolTip">
<string>Share VMess Link with v2rayN Format</string>
</property>
<property name="text">
<string>Old Share Link Format</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sys_proxy_format">
<property name="text">
<string>System proxy format</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="horizontalWidget_1" native="true">
<property name="sizePolicy">
@ -587,39 +487,7 @@
<item>
<widget class="QWidget" name="assest_group" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Asset Location</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="MyLineEdit" name="core_v2ray_asset">
<property name="placeholderText">
<string>Default: dir of &quot;nekoray&quot;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="core_v2ray_asset_pick">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">Loglevel</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<widget class="QComboBox" name="log_level">
@ -649,14 +517,21 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Multiplex (mux)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">Loglevel</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="mux_protocol"/>
@ -781,6 +656,93 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Assets</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Geo Assets and Rule-sets</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QPushButton" name="remove_srs_btn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Remove the currently generated rule-sets so that they can be regenerated&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Remove Generated Rule-sets</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLabel" name="geoip_url_l">
<property name="text">
<string>GeoIP URL</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="geoip_url"/>
</item>
<item>
<widget class="QPushButton" name="geoip_auto_btn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sets the URL to the latest sing-geoip release URL&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Sing-Geoip</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QLabel" name="geosite_url_l">
<property name="text">
<string>GeoSite URL</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="geosite_url"/>
</item>
<item>
<widget class="QPushButton" name="geosite_auto_btn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sets the URL to the latest sing-geosite release URL&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Sing-Geosite</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="download_geo_btn">
<property name="text">
<string>Download and Replace Geo files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_6">
<attribute name="title">
<string>Extra Core</string>
@ -799,8 +761,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>686</width>
<height>377</height>
<width>680</width>
<height>355</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">

View File

@ -3,7 +3,6 @@
#include "db/Database.hpp"
#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
#include "3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp"
#include "main/GuiUtils.hpp"
#include "fmt/Preset.hpp"

View File

@ -44,6 +44,7 @@
#include <QDir>
#include <QFileInfo>
#include <QStyleHints>
#include <main/HTTPRequestHelper.hpp>
void UI_InitMainWindow() {
mainwindow = new MainWindow;
@ -531,6 +532,12 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
on_menu_exit_triggered();
}
}
if (info.contains("DownloadAssets")) {
auto splitted = info.split(";");
runOnNewThread([=](){
DownloadAssets(splitted[1], splitted[2]);
});
}
//
if (info == "RestartProgram") {
this->exit_reason = 2;
@ -803,13 +810,8 @@ void MainWindow::refresh_status(const QString &traffic_update) {
ui->label_running->setText(txt);
}
//
auto display_http = tr("None");
if (IsValidPort(NekoGui::dataStore->inbound_http_port)) {
display_http = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_http_port);
}
auto display_socks = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_socks_port);
auto inbound_txt = QString("Socks: %1\nHTTP: %2").arg(display_socks, display_http);
inbound_txt = QString("Mixed: %1").arg(display_socks);
auto inbound_txt = QString("Mixed: %1").arg(display_socks);
ui->label_inbound->setText(inbound_txt);
//
ui->checkBox_VPN->setChecked(NekoGui::dataStore->spmode_vpn);
@ -1673,3 +1675,35 @@ bool MainWindow::StopVPNProcess(bool unconditional) {
}
return true;
}
void MainWindow::DownloadAssets(const QString &geoipUrl, const QString &geositeUrl) {
if (!mu_download_assets.tryLock()) {
runOnUiThread([=](){
MessageBoxWarning("Cannot start", "Last download request has not finished yet");
});
return;
}
MW_show_log("Start downloading...");
QString errors;
if (!geoipUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadGeoAsset(geoipUrl, "geoip.db");
if (!resp.isEmpty()) {
MW_show_log(QString("Failed to download geoip: %1").arg(resp));
errors += "geoip: " + resp;
}
}
if (!geositeUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadGeoAsset(geositeUrl, "geosite.db");
if (!resp.isEmpty()) {
MW_show_log(QString("Failed to download geosite: %1").arg(resp));
errors += "\ngeosite: " + resp;
}
}
mu_download_assets.unlock();
if (!errors.isEmpty()) {
runOnUiThread([=](){
MessageBoxWarning("Failed to download geo assets", errors);
});
}
MW_show_log("Geo Asset update completed!");
}

View File

@ -68,6 +68,8 @@ public:
bool StopVPNProcess(bool unconditional = false);
void DownloadAssets(const QString &geoipUrl, const QString &geositeUrl);
signals:
void profile_selected(int id);
@ -159,6 +161,8 @@ private:
QMutex mu_exit;
QSemaphore sem_stopped;
int exit_reason = 0;
//
QMutex mu_download_assets;
QList<std::shared_ptr<NekoGui::ProxyEntity>> get_now_selected_list();

View File

@ -491,7 +491,6 @@ void MainWindow::CheckUpdate() {
bool ok;
libcore::UpdateReq request;
request.set_action(libcore::UpdateAction::Check);
request.set_check_pre_release(NekoGui::dataStore->check_include_pre);
auto response = NekoGui_rpc::defaultClient->Update(&ok, request);
if (!ok) return;