Merge branch 'dev' Before renaming the project

The project will be renamed
This commit is contained in:
Leroy.H.Y 2019-07-05 19:07:46 +08:00
commit 75e106d156
No known key found for this signature in database
GPG Key ID: 6AC1673B587DC37D
42 changed files with 3219 additions and 1601 deletions

View File

@ -2,6 +2,7 @@ install:
- set QTDIR=C:\Qt\5.10\mingw53_32
- choco install -y InnoSetup
- set PATH=%QTDIR%\bin;C:\Qt\Tools\mingw730_32\bin;%PATH%;"C:\Program Files (x86)\Inno Setup 5"
- git submodule update --init
build_script:
- mkdir python37 && xcopy C:\Python37 python37 /E /H /Q

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ v2ctl.sig
build/
python37/
tools/python-3.7.3.exe
./.vscode

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "3rdparty/jsoncons"]
path = 3rdparty/jsoncons
url = https://github.com/danielaparker/jsoncons
[submodule "3rdparty/x2struct"]
path = 3rdparty/x2struct
url = https://github.com/xyz347/x2struct

View File

@ -21,6 +21,7 @@ before_script:
- if [ "$BADGE" = "osx" ]; then export PATH="/usr/local/opt/qt/bin:$PATH"; fi
script:
- git submodule update --init
- lrelease ./Hv2ray.pro
- mkdir build && cd ./build
- QT_SELECT=5 QTDIR=/usr/share/qt5 qmake ../

1
3rdparty/jsoncons vendored Submodule

@ -0,0 +1 @@
Subproject commit 11d0da64cf9e8725aa9963c307c8df4957359eef

1
3rdparty/x2struct vendored Submodule

@ -0,0 +1 @@
Subproject commit 084c3cd183ae2450473befa53e987a0ed347a6ff

View File

@ -24,109 +24,104 @@ DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
VPATH += ./src
SOURCES += \
main.cpp \
MainWindow.cpp \
ConnectionEditWindow.cpp \
ImportConfig.cpp \
PrefrencesWindow.cpp \
vinteract.cpp \
utils.cpp \
runguard.cpp
src/HUtils.cpp \
src/w_MainWindow.cpp \
src/w_ConnectionEditWindow.cpp \
src/w_ImportConfig.cpp \
src/w_PrefrencesWindow.cpp \
src/main.cpp \
src/vinteract.cpp \
src/runguard.cpp
HEADERS += \
MainWindow.h \
ConnectionEditWindow.h \
ImportConfig.h \
PrefrencesWindow.h \
vinteract.h \
utils.h \
runguard.h
src/HConfigObjects.hpp \
src/HUtils.hpp \
src/V2ConfigObjects.hpp \
src/runguard.hpp \
src/vinteract.hpp \
src/w_MainWindow.h \
src/w_ConnectionEditWindow.h \
src/w_ImportConfig.h \
src/w_PrefrencesWindow.h
FORMS += \
MainWindow.ui \
ConnectionEditWindow.ui \
ImportConfig.ui \
PrefrencesWindow.ui
src/w_MainWindow.ui \
src/w_ConnectionEditWindow.ui \
src/w_ImportConfig.ui \
src/w_PrefrencesWindow.ui
RESOURCES += \
resources.qrc
resources.qrc
TRANSLATIONS += \
./translations/zh-CN.ts \
./translations/en-US.ts
translations/zh-CN.ts \
translations/en-US.ts
RC_ICONS += ./icons/Hv2ray.ico
INCLUDEPATH += 3rdparty/\
3rdparty/jsoncons/include
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
WITH_PYTHON = no
PYTHONVER = null
unix:!macx {
exists( "/usr/include/python3.7m/Python.h" ) {
equals(WITH_PYTHON, "no") {
message("Will build with python lib version 3.7.")
INCLUDEPATH += /usr/include/python3.7m/
LIBS += -lpython3.7m
WITH_PYTHON = yes
}
}
}
unix:!macx {
exists( "/usr/include/python3.6m/Python.h" ) {
equals(WITH_PYTHON, "no") {
message("Will build with python lib version 3.6.")
INCLUDEPATH += /usr/include/python3.6m/
LIBS += -lpython3.6m
WITH_PYTHON = yes
}
}
}
unix:!macx {
# Python Headers check.
exists( "/usr/include/python3.5m/Python.h" ) {
equals(WITH_PYTHON, "no") {
message("Will build with python lib version 3.5.")
INCLUDEPATH += /usr/include/python3.5m/
LIBS += -lpython3.5m
WITH_PYTHON = yes
}
PYTHONVER = 3.5
WITH_PYTHON = yes
}
exists( "/usr/include/python3.6m/Python.h" ) {
PYTHONVER = 3.6
WITH_PYTHON = yes
}
exists( "/usr/include/python3.7m/Python.h" ) {
PYTHONVER = 3.7
WITH_PYTHON = yes
}
equals( WITH_PYTHON, "yes" ) {
INCLUDEPATH += /usr/include/python$${PYTHONVER}m/
LIBS += -lpython$${PYTHONVER}m
message("Will build with python lib version $$PYTHONVER")
}
}
macx {
PYTHON_ROOT=/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions
exists( "$$PYTHON_ROOT/3.7/include/python3.7m/Python.h" ) {
equals(WITH_PYTHON, "no") {
message("Will build with python lib version 3.7.3.")
INCLUDEPATH += $$PYTHON_ROOT/3.7/include/python3.7m/
LIBS += -L$$PYTHON_ROOT/3.7/lib/python3.7/config-3.7m-darwin/ -lpython3.7m
WITH_PYTHON = yes
}
PYTHON_ROOT=/usr/local/Cellar/python
exists( "$${PYTHON_ROOT}/3.6.5_1/" ) {
PYTHONVER = 3.6
PYLDPATH=$${PYTHON_ROOT}/3.6.5_1/Frameworks/Python.framework/Versions/$${PYTHONVER}
WITH_PYTHON = yes
}
exists( "$$PYTHON_ROOT/3.7.3/" ) {
PYTHONVER = 3.7
PYLDPATH=$${PYTHON_ROOT}/3.7.3/Frameworks/Python.framework/Versions/$${PYTHONVER}
WITH_PYTHON = yes
}
INCLUDEPATH += $${PYLDPATH}/include/python$${PYTHONVER}m/
LIBS += -L$${PYLDPATH}/lib/python$${PYTHONVER}/config-$${PYTHONVER}m-darwin/ -lpython$${PYTHONVER}m
message("Will build with python lib version $$PYTHONVER")
}
win32 {
exists( "$$PWD/python37/libs/libpython37_mingw.a" ) {
LIBS += -L$$PWD/python37/libs/ -lpython37_mingw
INCLUDEPATH += $$PWD/python37/include
WITH_PYTHON = yes
}
}
macx {
PYTHON_ROOT=/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions
exists( "$$PYTHON_ROOT/3.6/include/python3.6m/Python.h" ) {
equals(WITH_PYTHON, "no") {
message("Will build with python lib version 3.6.5_1.")
INCLUDEPATH += $$PYTHON_ROOT/3.6/include/python3.6m/
LIBS += -L$$PYTHON_ROOT/3.6/lib/python3.6/config-3.6m-darwin/ -lpython3.6m
WITH_PYTHON = yes
}
}
equals(WITH_PYTHON, "no") {
error("No Python3 libs found, did you install dev packages such as python3-dev ?")
}
unix: equals(WITH_PYTHON, "no") {
error("No python libs found, did you install python3 dev package?")
}
win32: LIBS += -L$$PWD/python37/libs/ -lpython37_mingw
win32: INCLUDEPATH += $$PWD/python37/include

View File

@ -1,36 +0,0 @@
#include "ConnectionEditWindow.h"
#include "ui_ConnectionEditWindow.h"
#include <iostream>
#include "MainWindow.h"
#include <QDebug>
#include <QFile>
#include <QIntValidator>
ConnectionEditWindow::ConnectionEditWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::ConnectionEditWindow)
{
ui->setupUi(this);
ui->portLineEdit->setValidator(new QIntValidator());
ui->alterLineEdit->setValidator(new QIntValidator());
}
ConnectionEditWindow::~ConnectionEditWindow()
{
delete ui;
}
int Hv2Config::save()
{
return -1;
}
void Hv2Config::getConfigFromDialog(Ui::ConnectionEditWindow *ui)
{
this->host = ui->ipLineEdit->text();
this->port = ui->portLineEdit->text();
this->alias = ui->aliasLineEdit->text();
this->uuid = ui->idLineEdit->text();
this->alterid = ui->alterLineEdit->text();
this->security = ui->securityCombo->currentText();
this->isCustom = 0;
}

View File

@ -1,40 +0,0 @@
#ifndef CONFEDIT_H
#define CONFEDIT_H
#include <QDialog>
namespace Ui
{
class ConnectionEditWindow;
}
class Hv2Config
{
public:
QString host;
QString port;
QString alias;
QString uuid;
QString alterid;
QString security;
int isCustom;
int save();
void getConfigFromDialog(Ui::ConnectionEditWindow *ui);
private:
};
class ConnectionEditWindow : public QDialog
{
Q_OBJECT
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
~ConnectionEditWindow();
private:
Ui::ConnectionEditWindow *ui;
};
#endif // CONFEDIT_H

76
src/HConfigObjects.hpp Normal file
View File

@ -0,0 +1,76 @@
#ifndef HCONFIGOBJECTS_HPP
#define HCONFIGOBJECTS_HPP
#include <QDir>
#include <map>
#include "V2ConfigObjects.hpp"
#include "vinteract.hpp"
// Macros
#define HV2RAY_CONFIG_DIR_NAME "/.hv2ray/"
using namespace std;
namespace Hv2ray
{
namespace HConfigModels
{
struct HInbondSetting {
bool enabled;
string ip;
int port;
bool useAuthentication;
string authUsername;
string authPassword;
HInbondSetting(): enabled(), ip(), port(), useAuthentication(), authUsername(), authPassword() {}
HInbondSetting(bool _e, string _ip, int _p): HInbondSetting()
{
enabled = _e;
port = _p;
ip = _ip;
}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(enabled, ip, port, useAuthentication, authUsername, authPassword))
#endif
};
struct HvConfigList {
string alias;
string fileName;
int index;
HvConfigList(): alias(), fileName(), index() {}
XTOSTRUCT(O(alias, fileName, index))
};
struct Hv2Config {
string language;
bool runAsRoot;
string logLevel;
//Hv2ray::V2ConfigModels::MuxObject muxSetting;
HInbondSetting httpSetting;
HInbondSetting socksSetting;
list<HvConfigList> configs;
Hv2Config(): language(), runAsRoot(), logLevel(), httpSetting(), socksSetting(), configs() { }
Hv2Config(string lang, string log, HInbondSetting httpIn, HInbondSetting socksIN): Hv2Config()
{
language = lang;
logLevel = log;
httpSetting = httpIn;
socksSetting = socksIN;
runAsRoot = false;
}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(language, runAsRoot, logLevel, httpSetting, socksSetting, configs))
#endif
};
}
/// ConfigGlobalConfig is platform-independent as it's solved to be in the best
/// place at first in main.cpp
static QDir ConfigDir;
}
#if USE_TODO_FEATURES
JSONCONS_MEMBER_TRAITS_DECL(Hv2ray::HConfigModels::Hv2Config, language, runAsRoot, logLevel, httpSetting, socksSetting)
JSONCONS_MEMBER_TRAITS_DECL(Hv2ray::HConfigModels::HInbondSetting, enabled, ip, port, useAuthentication, authUsername, authPassword)
#endif
#endif // HCONFIGOBJECTS_HPP

61
src/HUtils.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "HUtils.hpp"
#include <QTextStream>
namespace Hv2ray
{
namespace Utils
{
static HConfigModels::Hv2Config GlobalConfig;
void SetGlobalConfig(HConfigModels::Hv2Config conf)
{
GlobalConfig = conf;
}
HConfigModels::Hv2Config GetGlobalConfig()
{
return GlobalConfig;
}
void SaveConfig(QFile *configFile)
{
configFile->open(QFile::WriteOnly);
QString jsonConfig = StructToJSON(GetGlobalConfig());
QTextStream stream(configFile);
stream << jsonConfig << endl;
stream.flush();
configFile->close();
}
void LoadConfig(QFile *configFile)
{
using namespace Hv2ray::HConfigModels;
configFile->open(QFile::ReadOnly);
QTextStream stream(configFile);
auto str = stream.readAll();
auto config = StructFromJSON<Hv2Config>(str.toStdString());
SetGlobalConfig(config);
configFile->close();
}
QStringList getAllFilesList(QDir *dir)
{
return dir->entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files);
}
QTranslator *getTranslator(string lang)
{
QTranslator *translator = new QTranslator();
translator->load(QString::fromStdString(lang + ".qm"), ":/translations");
return translator;
}
bool hasFile(QDir *dir, QString fileName)
{
return getAllFilesList(dir).indexOf(fileName) >= 0;
}
void showWarnMessageBox(QWidget *parent, QString title, QString text)
{
QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
}
}

57
src/HUtils.hpp Normal file
View File

@ -0,0 +1,57 @@
#ifndef UTILS_H
#define UTILS_H
#include <QDir>
#include <QFile>
#include <QMap>
#include <QFileInfo>
#include <QMessageBox>
#include <QWidget>
#include <assert.h>
#include <QTranslator>
#include <QApplication>
#include <iostream>
#include "HConfigObjects.hpp"
namespace Hv2ray
{
namespace Utils
{
void showWarnMessageBox(QWidget *parent, QString title, QString text);
QTranslator *getTranslator(string lang);
void SetGlobalConfig(HConfigModels::Hv2Config conf);
HConfigModels::Hv2Config GetGlobalConfig();
void SaveConfig(QFile *configFile);
void LoadConfig(QFile *configFile);
/// Get file list in a Dir
QStringList getAllFilesList(QDir *dir);
bool hasFile(QDir *dir, QString fileName);
template <typename TYPE>
QString StructToJSON(const TYPE &t)
{
string s;
#if USE_TODO_FEATURES
encode_json<TYPE>(t, s, indenting::indent);
#else
s = X::tojson(t, "", 4, ' ');
#endif
cout << s << endl;
return QString::fromStdString(s);
}
template <typename TYPE>
TYPE StructFromJSON(const string &str)
{
TYPE v;
#if USE_TODO_FEATURES
v = decode_json<TYPE>(str);
#else
X::loadjson(str, v, false);
#endif
return v;
}
}
}
#endif // UTILS_H

View File

@ -1,130 +0,0 @@
#include <QFileDialog>
#include <QDebug>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#pragma push_macro("slots")
#undef slots
#include "Python.h"
#pragma pop_macro("slots")
#include "ConnectionEditWindow.h"
#include "vinteract.h"
#include "utils.h"
#include "ImportConfig.h"
#include "ui_ImportConfig.h"
ImportConfig::ImportConfig(QWidget *parent) :
QDialog(parent),
ui(new Ui::ImportConfig)
{
ui->setupUi(this);
connect(this, SIGNAL(updateConfTable()), parentWidget(), SLOT(updateConfTable()));
}
ImportConfig::~ImportConfig()
{
delete ui;
}
void ImportConfig::on_pushButton_clicked()
{
QString dir = QFileDialog::getOpenFileName(this, tr("OpenConfigFile"), "~/");
ui->fileLineTxt->setText(dir);
}
void ImportConfig::savefromFile(QString path, QString alias)
{
Hv2Config newConfig;
newConfig.alias = alias;
QFile configFile(path);
if(!configFile.open(QIODevice::ReadOnly)) {
showWarnMessageBox(this, tr("ImportConfig"), tr("CannotOpenFile"));
qDebug() << "ImportConfig::CannotOpenFile";
return;
}
QByteArray allData = configFile.readAll();
configFile.close();
QJsonDocument v2conf(QJsonDocument::fromJson(allData));
QJsonObject rootobj = v2conf.object();
QJsonObject outbound;
if(rootobj.contains("outbounds")) {
outbound = rootobj.value("outbounds").toArray().first().toObject();
} else {
outbound = rootobj.value("outbound").toObject();
}
QJsonObject vnext = switchJsonArrayObject(outbound.value("settings").toObject(), "vnext");
QJsonObject user = switchJsonArrayObject(vnext, "users");
newConfig.host = vnext.value("address").toString();
newConfig.port = QString::number(vnext.value("port").toInt());
newConfig.alterid = QString::number(user.value("alterId").toInt());
newConfig.uuid = user.value("id").toString();
newConfig.security = user.value("security").toString();
if (newConfig.security.isNull()) {
newConfig.security = "auto";
}
newConfig.isCustom = 1;
int id = newConfig.save();
if(id < 0)
{
showWarnMessageBox(this, tr("ImportConfig"), tr("SaveFailed"));
qDebug() << "ImportConfig::SaveFailed";
return;
}
emit updateConfTable();
QString newFile = "conf/" + QString::number(id) + ".conf";
if(!QFile::copy(path, newFile)) {
showWarnMessageBox(this, tr("ImportConfig"), tr("CannotCopyCustomConfig"));
qDebug() << "ImportConfig::CannotCopyCustomConfig";
}
}
void ImportConfig::on_buttonBox_accepted()
{
QString alias = ui->nameTxt->text();
if(ui->importSourceCombo->currentIndex() == 0) // From File...
{
QString path = ui->fileLineTxt->text();
bool isValid = validationCheck(path);
if(isValid) {
savefromFile(path, alias);
}
}
else
{
QString vmess = ui->vmessConnectionStringTxt->toPlainText();
Py_Initialize();
assert(Py_IsInitialized());
QString param = "--inbound socks:1080 " + vmess + " -o config.json.tmp";
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./utils')");
PyObject *pModule = PyImport_ImportModule("vmess2json");
PyObject *pFunc = PyObject_GetAttrString(pModule, "main");
PyObject *arg = PyTuple_New(1);
PyObject *arg1 = Py_BuildValue("s", param.toStdString().c_str());
PyTuple_SetItem(arg, 0, arg1);
PyObject_CallObject(pFunc, arg);
Py_Finalize();
if(QFile::exists(QCoreApplication::applicationDirPath() + "/config.json.tmp")) {
ImportConfig *im = new ImportConfig(this->parentWidget());
if (validationCheck(QCoreApplication::applicationDirPath() + "/config.json.tmp")) {
im->savefromFile("config.json.tmp", alias);
}
QFile::remove("config.json.tmp");
} else {
showWarnMessageBox(this, tr("ImportConfig"), tr("CannotGenerateConfig"));
qDebug() << "ImportConfig::CannotGenerateConfig";
}
}
if(ui->useCurrentSettingRidBtn->isChecked())
{
// TODO: Use Current Settings...
}
else
{
// TODO: Override Inbound....
}
}

View File

@ -1,30 +0,0 @@
#ifndef IMPORTCONF_H
#define IMPORTCONF_H
#include <QDialog>
namespace Ui
{
class ImportConfig;
}
class ImportConfig : public QDialog
{
Q_OBJECT
public:
explicit ImportConfig(QWidget *parent = nullptr);
void savefromFile(QString path, QString alias);
~ImportConfig();
private slots:
void on_pushButton_clicked();
void on_buttonBox_accepted();
signals:
void updateConfTable();
private:
Ui::ImportConfig *ui;
};
#endif // IMPORTCONF_H

View File

@ -1,273 +0,0 @@
#include <QHeaderView>
#include <QStandardItemModel>
#include <QDebug>
#include <QMenu>
#include <QAction>
#include <QFile>
#include <QCloseEvent>
#include <QFileInfo>
#include <QInputDialog>
#include "ui_MainWindow.h"
#include "PrefrencesWindow.h"
#include "MainWindow.h"
#include "ConnectionEditWindow.h"
#include "ImportConfig.h"
#include "vinteract.h"
#include "utils.h"
void MainWindow::CreateTrayIcon()
{
hTray = new QSystemTrayIcon();
hTray->setToolTip(tr("Hv2ray"));
hTray->setIcon(this->windowIcon());
connect(hTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_activatedTray(QSystemTrayIcon::ActivationReason)));
QAction *actionShow = new QAction(this);
QAction *actionQuit = new QAction(this);
QAction *actionStart = new QAction(this);
QAction *actionRestart = new QAction(this);
QAction *actionStop = new QAction(this);
actionShow->setText(tr("#Hide"));
actionQuit->setText(tr("#Quit"));
actionStart->setText(tr("#Start"));
actionStop->setText(tr("#Stop"));
actionRestart->setText(tr("#Restart"));
actionStart->setEnabled(true);
actionStop->setEnabled(false);
actionRestart->setEnabled(false);
trayMenu->addAction(actionShow);
trayMenu->addSeparator();
trayMenu->addAction(actionStart);
trayMenu->addAction(actionStop);
trayMenu->addAction(actionRestart);
trayMenu->addSeparator();
trayMenu->addAction(actionQuit);
connect(actionShow, SIGNAL(triggered()), this, SLOT(toggleMainWindowVisibility()));
connect(actionStart, SIGNAL(triggered()), this, SLOT(on_startButton_clicked()));
connect(actionStop, SIGNAL(triggered()), this, SLOT(on_stopButton_clicked()));
connect(actionRestart, SIGNAL(triggered()), this, SLOT(on_restartButton_clicked()));
connect(actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
hTray->setContextMenu(trayMenu);
hTray->show();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
this->setWindowIcon(QIcon(":/icons/Hv2ray.ico"));
ui->setupUi(this);
UpdateConfigTable();
// ui->configTable->setContextMenuPolicy(Qt::CustomContextMenu);
// connect(ui->configTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
this->v2instance = new v2Instance();
CreateTrayIcon();
if(QFileInfo("config.json").exists()) {
v2instance->start(this);
}
// QAction *select = new QAction("Select", ui->configTable);
// QAction *del = new QAction("Delete", ui->configTable);
// QAction *rename = new QAction("Rename", ui->configTable);
// popMenu->addAction(select);
// popMenu->addAction(del);
// popMenu->addAction(rename);
// connect(select, SIGNAL(triggered()), this, SLOT(select_triggered()));
// connect(del, SIGNAL(triggered()), this, SLOT(delConf()));
// connect(rename, SIGNAL(triggered()), this, SLOT(renameRow()));
// connect(ui->logText, SIGNAL(textChanged()), this, SLOT(scrollToBottom()));
// bar = ui->logText->verticalScrollBar();
}
MainWindow::~MainWindow()
{
hTray->hide();
delete this->hTray;
delete this->v2instance;
delete ui;
}
void MainWindow::on_actionEdit_triggered()
{
ConnectionEditWindow *e = new ConnectionEditWindow(this);
e->setAttribute(Qt::WA_DeleteOnClose);
e->show();
}
void MainWindow::on_actionExisting_config_triggered()
{
ImportConfig *f = new ImportConfig(this);
f->setAttribute(Qt::WA_DeleteOnClose);
f->show();
}
void MainWindow::showMenu(QPoint pos)
{
// if(ui->configTable->indexAt(pos).column() != -1) {
// popMenu->move(cursor().pos());
// popMenu->show();
// }
}
void MainWindow::select_triggered()
{
// int row = ui->configTable->selectionModel()->currentIndex().row();
// int idIntable = ui->configTable->model()->data(ui->configTable->model()->index(row, 4)).toInt();
// this->geneConf(idIntable);
// if(this->v2Inst->v2Process->state() == QProcess::Running) {
// this->on_restartButton_clicked();
// }
}
void MainWindow::DeleteConfig()
{
}
void MainWindow::UpdateConfigTable()
{
}
void MainWindow::GenerateConfig(int idIntable)
{
Hv2Config tmpConf;
emit UpdateConfigTable();
if (tmpConf.isCustom == 1) {
QString src = "conf/" + QString::number(idIntable) + ".conf";
overrideInbounds(src);
if (QFile::exists("config.json")) {
QFile::remove("config.json");
}
QFile::copy(src, "config.json");
} else {
// TODO: Config generator
}
}
void MainWindow::UpdateLog()
{
ui->logText->insertPlainText(this->v2instance->vProcess->readAllStandardOutput());
}
void MainWindow::on_startButton_clicked()
{
ui->logText->clear();
bool startFlag = this->v2instance->start(this);
trayMenu->actions()[2]->setEnabled(!startFlag);
trayMenu->actions()[3]->setEnabled(startFlag);
trayMenu->actions()[4]->setEnabled(startFlag);
}
void MainWindow::on_stopButton_clicked()
{
this->v2instance->stop();
ui->logText->clear();
trayMenu->actions()[2]->setEnabled(true);
trayMenu->actions()[3]->setEnabled(false);
trayMenu->actions()[4]->setEnabled(false);
}
void MainWindow::on_restartButton_clicked()
{
on_stopButton_clicked();
on_startButton_clicked();
}
void MainWindow::on_clbutton_clicked()
{
ui->logText->clear();
}
void MainWindow::on_rtButton_clicked()
{
emit UpdateConfigTable();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
this->hide();
event->ignore();
}
void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) {
case QSystemTrayIcon::Trigger:
// Toggle Show/Hide
#ifndef __APPLE__
// Every single click will trigger the Show/Hide toggling.
// So, as a hobby on common MacOS Apps, we 'don't toggle visibility on click'.
toggleMainWindowVisibility();
#endif
break;
case QSystemTrayIcon::DoubleClick:
if(this->isHidden()) {
this->show();
}
break;
case QSystemTrayIcon::MiddleClick:
// TODO: Check if an alert message box is present.
// If so, do nothing but please wait for the message box to be closed.
if(this->v2instance->vProcess->state() == QProcess::ProcessState::Running) {
on_stopButton_clicked();
} else {
on_startButton_clicked();
}
break;
case QSystemTrayIcon::Unknown:
break;
case QSystemTrayIcon::Context:
break;
}
}
void MainWindow::toggleMainWindowVisibility()
{
if(this->isHidden()) {
this->show();
trayMenu->actions()[0]->setText(tr("#Hide"));
} else {
this->hide();
trayMenu->actions()[0]->setText(tr("#Show"));
}
}
void MainWindow::quit()
{
QCoreApplication::quit();
}
void MainWindow::on_actionExit_triggered()
{
quit();
}
void MainWindow::renameRow()
{
// QString text = QInputDialog::getText(this, "Rename config", "New name:", QLineEdit::Normal);
// int row = ui->configTable->currentIndex().row();
// int idIntable = ui->configTable->model()->data(ui->configTable->model()->index(row, 4)).toInt();
// SQLiteDB mydb;
// QString updateString = "update confs set alias = '" + text + "' where id = " + QString::number(idIntable);
// mydb.DoQuery(updateString);
// emit updateConfTable();
}
void MainWindow::scrollToBottom()
{
bar->setValue(bar->maximum());
}
void MainWindow::on_actionPreferences_triggered()
{
PrefrencesWindow *v = new PrefrencesWindow(this);
v->setAttribute(Qt::WA_DeleteOnClose);
v->show();
}
void MainWindow::on_pushButton_clicked()
{
auto confedit = new ConnectionEditWindow();
confedit->show();
}

View File

@ -1,63 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#define confDir "conf/"
#define confDatabase "conf/conf.db"
#include <QMainWindow>
#include "ConnectionEditWindow.h"
#include <QSystemTrayIcon>
#include <QMenu>
#include <QScrollBar>
#include "vinteract.h"
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
v2Instance *v2instance;
QSystemTrayIcon *hTray;
QMenu *trayMenu = new QMenu(this);
QMenu *popMenu = new QMenu(this);
QScrollBar *bar;
~MainWindow();
private slots:
void on_restartButton_clicked();
void on_actionEdit_triggered();
void on_actionExisting_config_triggered();
void UpdateConfigTable();
void DeleteConfig();
void showMenu(QPoint pos);
void UpdateLog();
void on_startButton_clicked();
void on_stopButton_clicked();
void select_triggered();
void on_clbutton_clicked();
void on_rtButton_clicked();
void GenerateConfig(int idIntable);
void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
void toggleMainWindowVisibility();
void quit();
void on_actionExit_triggered();
void renameRow();
void scrollToBottom();
void on_actionPreferences_triggered();
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
void closeEvent(QCloseEvent *);
void createTrayAction();
void CreateTrayIcon();
};
#endif // MAINWINDOW_H

View File

@ -1,158 +0,0 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QDebug>
#include <QJsonArray>
#include <QJsonValue>
#include <QIntValidator>
#include <QFileInfo>
#include <QProcess>
#include <unistd.h>
#include <ui_PrefrencesWindow.h>
#include "utils.h"
#include "PrefrencesWindow.h"
PrefrencesWindow::PrefrencesWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::PrefrencesWindow)
{
ui->setupUi(this);
rootObj = loadRootObjFromConf();
QJsonObject http = findValueFromJsonArray(rootObj.value("inbounds").toArray(), "tag", "http-in");
QJsonObject socks = findValueFromJsonArray(rootObj.value("inbounds").toArray(), "tag", "socks-in");
if(rootObj.value("v2suidEnabled").toBool()) {
ui->runAsRootCheckBox->setCheckState(Qt::Checked);
}
if(!http.isEmpty()) {
ui->httpPortLE->setText(http.value("port").toString());
ui->httpCB->setCheckState(Qt::Checked);
} else {
ui->httpPortLE->setDisabled(true);
}
if(!socks.isEmpty()) {
ui->socksPortLE->setText(socks.value("port").toString());
ui->socksCB->setCheckState(Qt::Checked);
} else {
ui->socksPortLE->setDisabled(true);
}
ui->httpPortLE->setValidator(new QIntValidator());
ui->socksPortLE->setValidator(new QIntValidator());
parentMW = parent;
}
PrefrencesWindow::~PrefrencesWindow()
{
delete ui;
}
void PrefrencesWindow::on_buttonBox_accepted()
{
if(checkVCoreExes()) {
if(ui->httpPortLE->text().toInt() != ui->socksPortLE->text().toInt()) {
QJsonArray inbounds;
QJsonDocument modifiedDoc;
inbounds = rootObj.value("inbounds").toArray();
int socksId = getIndexByValue(inbounds, "tag", "socks-in");
if(socksId != -1) {
inbounds.removeAt(socksId);
}
int httpId = getIndexByValue(inbounds, "tag", "http-in");
if(httpId != -1) {
inbounds.removeAt(httpId);
}
rootObj.remove("inbounds");
rootObj.remove("v2suidEnabled");
if(ui->socksCB->isChecked()) {
QJsonObject socks;
QJsonObject settings;
socks.insert("tag", "socks-in");
socks.insert("port", ui->socksPortLE->text().toInt());
socks.insert("listen", "127.0.0.1");
socks.insert("protocol", "socks");
settings.insert("auth", "noauth");
settings.insert("udp", true);
settings.insert("ip", "127.0.0.1");
socks.insert("settings", QJsonValue(settings));
inbounds.append(socks);
}
if(ui->httpCB->isChecked()) {
QJsonObject http;
QJsonObject settings;
http.insert("tag", "http-in");
http.insert("port", ui->httpPortLE->text().toInt());
http.insert("listen", "127.0.0.1");
http.insert("protocol", "http");
settings.insert("auth", "noauth");
settings.insert("udp", true);
settings.insert("ip", "127.0.0.1");
http.insert("settings", QJsonValue(settings));
inbounds.append(http);
}
rootObj.insert("inbounds", QJsonValue(inbounds));
#ifndef _WIN32
// Set UID and GID in *nix
QFileInfo v2rayCoreExeFile("v2ray");
if(ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() != 0) {
QProcess::execute("pkexec", QStringList() << "bash" << "-c" << "chown root:root " + QCoreApplication::applicationDirPath() + "/v2ray" + ";chmod +s " + QCoreApplication::applicationDirPath() + "/v2ray");
} else if (!ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() == 0) {
uid_t uid = getuid();
gid_t gid = getgid();
QProcess::execute("pkexec", QStringList() << "chown" << QString::number(uid) + ":" + QString::number(gid) << QCoreApplication::applicationDirPath() + "/v2ray");
}
v2rayCoreExeFile.refresh();
rootObj.insert("v2suidEnabled", v2rayCoreExeFile.ownerId() == 0);
#else
// No such uid gid thing on windows....
#endif
modifiedDoc.setObject(rootObj);
QByteArray byteArray = modifiedDoc.toJson(QJsonDocument::Indented);
QFile confFile("conf/Hv2ray.config.json");
if(!confFile.open(QIODevice::WriteOnly)) {
showWarnMessageBox(this, tr("#Prefrences"), tr("#CannotOpenConfigFile"));
qDebug() << "Cannot open Hv2ray.config.json for modifying";
}
confFile.write(byteArray);
confFile.close();
} else {
showWarnMessageBox(this, tr("Prefrences"), tr("PortNumbersCannotBeSame"));
}
}
}
void PrefrencesWindow::on_httpCB_stateChanged(int checked)
{
if(checked != Qt::Checked) {
ui->httpPortLE->setDisabled(true);
} else {
ui->httpPortLE->setEnabled(true);
ui->httpPortLE->setText("6666");
}
}
void PrefrencesWindow::on_socksCB_stateChanged(int checked)
{
if(checked != Qt::Checked) {
ui->socksPortLE->setDisabled(true);
} else {
ui->socksPortLE->setEnabled(true);
ui->socksPortLE->setText("1080");
}
}
void PrefrencesWindow::on_httpAuthCB_stateChanged(int checked)
{
if(checked)
{
}
}
void PrefrencesWindow::on_runAsRootCheckBox_stateChanged(int arg1)
{
Q_UNUSED(arg1);
#ifdef _WIN32
showWarnMessageBox(this, tr("Prefrences"), tr("RunAsRootNotOnWindows"));
#endif
}

View File

@ -1,35 +0,0 @@
#ifndef HVCONF_H
#define HVCONF_H
#include <QDialog>
#include <QJsonObject>
namespace Ui
{
class PrefrencesWindow;
}
class PrefrencesWindow : public QDialog
{
Q_OBJECT
public:
explicit PrefrencesWindow(QWidget *parent = nullptr);
~PrefrencesWindow();
QJsonObject rootObj;
QWidget *parentMW;
private slots:
void on_buttonBox_accepted();
void on_httpCB_stateChanged(int arg1);
void on_socksCB_stateChanged(int arg1);
void on_httpAuthCB_stateChanged(int arg1);
void on_runAsRootCheckBox_stateChanged(int arg1);
private:
Ui::PrefrencesWindow *ui;
};
#endif // HVCONF_H

638
src/V2ConfigObjects.hpp Normal file
View File

@ -0,0 +1,638 @@
#include <list>
#include <string>
// TODO Features
#define USE_TODO_FEATURES false
#if USE_TODO_FEATURES
#include <jsoncons/json.hpp>
using namespace jsoncons;
#else
#include <x2struct/x2struct.hpp>
using namespace x2struct;
#endif
#ifndef V2CONFIG_H
#define V2CONFIG_H
using namespace std;
/*------------------------------------------------------------------------------------------------------------*/
namespace Hv2ray
{
namespace V2ConfigModels
{
// Two struct defining TYPE parameter to be passed into inbound configs and outbound configs.
struct XOutBoundsType {
};
struct XInBoundsType {
};
struct LogObject {
string access;
string error;
string loglevel;
LogObject(): access(), error(), loglevel() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(access, error, loglevel))
#endif
};
struct ApiObject {
string tag;
list<string> services;
ApiObject() : tag(), services() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tag, services))
#endif
};
namespace DNSObjects
{
struct ServerObject {
string address;
int port;
list<string> domains;
ServerObject(): address(), port(), domains() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(address, port, domains))
#endif
};
}
struct DnsObject {
map<string, string> hosts;
#if USE_TODO_FEATURES
tuple<string, string, list<DNSObjects::ServerObject>> servers;
#else
// Currently does not support ServerObject as tuple is.... quite complicated...
list<string> servers;
#endif
DnsObject(): hosts(), servers() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(hosts, servers))
#endif
};
namespace ROUTINGObjects
{
struct RuleObject {
string type = "field";
list<string> domain;
list<string> ip;
string port;
string network;
list<string> source;
list<string> user;
string inboundTag;
string protocol;
string attrs;
RuleObject() : type(), domain(), ip(), port(), network(), source(), user(), inboundTag(), protocol(), attrs() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(type, domain, ip, port, network, source, user, inboundTag, protocol, attrs))
#endif
};
struct BalancerObject {
string tag ;
list<string> selector;
BalancerObject() : tag(), selector() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tag, selector))
#endif
};
}
struct RoutingObject {
string domainStrategy;
list<ROUTINGObjects::RuleObject> rules;
list<ROUTINGObjects::BalancerObject> balancers;
RoutingObject() : domainStrategy(), rules(), balancers() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(domainStrategy, rules, balancers))
#endif
};
namespace POLICYObjects
{
struct SystemPolicyObject {
bool statsInboundUplink;
bool statsInboundDownlink;
SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink))
#endif
};
struct LevelPolicyObject {
int handshake;
int connIdle;
int uplinkOnly;
int downlinkOnly;
bool statsUserUplink;
bool statsUserDownlink;
int bufferSize;
LevelPolicyObject(): handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
#endif
};
}
struct PolicyObject {
map<int, POLICYObjects::LevelPolicyObject> level;
list<POLICYObjects::SystemPolicyObject> system;
PolicyObject(): level(), system() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(level, system))
#endif
};
namespace TRANSFERObjects
{
namespace TRANSFERObjectsInternal
{
struct HTTPRequestObject {
string version;
string method;
list<string> path;
map<string, list<string>> headers;
HTTPRequestObject(): version(), method(), path(), headers() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(version, method, path, headers))
#endif
};
struct HTTPResponseObject {
string version;
string status;
string reason;
map<string, list<string>> headers;
HTTPResponseObject(): version(), status(), reason(), headers() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(version, status, reason, headers))
#endif
};
struct TCPHeader_M_Object {
string type;
HTTPRequestObject request;
HTTPResponseObject response;
TCPHeader_M_Object(): type(), request(), response() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(type, request, response))
#endif
};
struct HeaderObject {
string type;
HeaderObject(): type() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(type))
#endif
};
}
struct TCPObject {
TRANSFERObjectsInternal:: TCPHeader_M_Object header;
TCPObject(): header() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(header))
#endif
};
struct KCPObject {
int mtu;
int tti;
int uplinkCapacity;
int downlinkCapacity;
bool congestion;
int readBufferSize;
int writeBufferSize;
TRANSFERObjectsInternal:: HeaderObject header;
KCPObject(): mtu(), tti(), uplinkCapacity(), downlinkCapacity(), congestion(), readBufferSize(), writeBufferSize(), header() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header))
#endif
};
struct WebSocketObject {
string path;
map<string, string> headers;
WebSocketObject(): path(), headers() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(path, headers))
#endif
};
struct HttpObject {
list<string> host;
string path;
HttpObject() : host(), path() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(host, path))
#endif
};
struct DomainSocketObject {
string path;
DomainSocketObject(): path() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(path))
#endif
};
struct QuicObject {
string security;
string key;
TRANSFERObjectsInternal::HeaderObject header;
QuicObject(): security(), key(), header() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(security, key, header))
#endif
};
}
struct TransportObject {
TRANSFERObjects::TCPObject tcpSettings;
TRANSFERObjects::KCPObject kcpSettings;
TRANSFERObjects::WebSocketObject wsSettings;
TRANSFERObjects::HttpObject httpSettings;
TRANSFERObjects::DomainSocketObject dsSettings;
TRANSFERObjects::QuicObject quicSettings;
TransportObject(): tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tcpSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings))
#endif
};
namespace INBOUNDObjects
{
struct SniffingObject {
bool enabled;
string destOverride;
SniffingObject(): enabled(), destOverride() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(enabled, destOverride))
#endif
};
struct AllocateObject {
string strategy;
int refresh;
int concurrency;
AllocateObject(): strategy(), refresh(), concurrency() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(strategy, refresh, concurrency))
#endif
};
}
namespace STREAMSETTINGSObjects
{
struct SockoptObject {
int mark;
bool tcpFastOpen;
string tproxy;
SockoptObject(): mark(), tcpFastOpen(), tproxy() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(mark, tcpFastOpen, tproxy))
#endif
};
struct CertificateObject {
string usage;
string certificateFile;
string keyFile;
list<string> certificate;
list<string> key;
CertificateObject(): usage(), certificateFile(), keyFile(), certificate(), key() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key))
#endif
};
struct TLSObject {
string serverName;
bool allowInsecure;
list<string> alpn;
list<CertificateObject> certificates;
bool disableSystemRoot;
TLSObject(): serverName(), allowInsecure(), certificates(), disableSystemRoot() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot))
#endif
};
}
struct StreamSettingsObject {
string network;
string security;
STREAMSETTINGSObjects::SockoptObject sockopt;
STREAMSETTINGSObjects::TLSObject tlsSettings;
TRANSFERObjects::TCPObject tcpSettings;
TRANSFERObjects::KCPObject kcpSettings;
TRANSFERObjects::WebSocketObject wsSettings;
TRANSFERObjects::HttpObject httpSettings;
TRANSFERObjects::DomainSocketObject dsSettings;
TRANSFERObjects::QuicObject quicSettings;
StreamSettingsObject(): network(), security(), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings))
#endif
};
template<typename XINBOUNDSETTINGOBJECT>
struct InboundObject {
static_assert(std::is_base_of<XInBoundsType, XINBOUNDSETTINGOBJECT>::value, "XINBOUNDSETTINGOBJECT must extend XInBoundsType");
int port;
string listen;
string protocol;
XINBOUNDSETTINGOBJECT settings;
StreamSettingsObject streamSettings;
string tag;
INBOUNDObjects::SniffingObject sniffing;
INBOUNDObjects::AllocateObject allocate;
InboundObject(): port(), listen(), protocol(), settings(), streamSettings(), tag(), sniffing(), allocate() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(port, listen, protocol, settings, streamSettings, tag, sniffing, allocate))
#endif
};
namespace OUTBOUNDObjects
{
struct ProxySettingsObject {
string tag;
ProxySettingsObject(): tag() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tag))
#endif
};
struct MuxObject {
bool enabled;
int concurrency;
MuxObject(): enabled(), concurrency() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(enabled, concurrency))
#endif
};
}
template <typename XOUTBOUNDSETTINGOBJECT>
struct OutboundObject {
static_assert(std::is_base_of<XOutBoundsType, XOUTBOUNDSETTINGOBJECT>::value, "XOUTBOUNDSETTINGOBJECT must extend XOutBoundsType");
string sendThrough;
string protocol;
XOUTBOUNDSETTINGOBJECT settings;
string tag;
StreamSettingsObject streamSettings;
OUTBOUNDObjects::ProxySettingsObject proxySettings;
OUTBOUNDObjects::MuxObject mux;
OutboundObject(): sendThrough(), protocol(), settings(), tag(), streamSettings(), proxySettings(), mux() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(sendThrough, protocol, settings, tag, streamSettings, proxySettings, mux))
#endif
};
struct StatsObject {
bool _; // Placeholder...
StatsObject(): _() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(_))
#endif
};
namespace REVERSEObjects
{
struct BridgeObject {
string tag;
string domain;
BridgeObject() : tag(), domain() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tag, domain))
#endif
};
struct PortalObject {
string tag;
string domain;
PortalObject() : tag(), domain() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(tag, domain))
#endif
};
}
struct ReverseObject {
list<REVERSEObjects::BridgeObject> bridges;
list<REVERSEObjects::PortalObject> portals;
ReverseObject() : bridges(), portals() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(bridges, portals))
#endif
};
#if USE_TODO_FEATURES
template<typename XIN1, typename XIN2, typename XIN3, typename XIN4, typename XIN5, typename XOUT1, typename XOUT2, typename XOUT3, typename XOUT4, typename XOUT5>
#else
template <typename XInBound_T, typename XOutBound_T>
#endif
struct RootObject {
LogObject log;
ApiObject api;
DnsObject dns;
RoutingObject routing;
#if USE_TODO_FEATURES
// Default support 5 inBounds and 5 outBounds
tuple<InboundObject<XIN1>, InboundObject<XIN2>, InboundObject<XIN3>, InboundObject<XIN4>, InboundObject<XIN5>> inbounds;
tuple<OutboundObject<XIN1>, OutboundObject<XIN2>, OutboundObject<XIN3>, OutboundObject<XIN4>, OutboundObject<XIN5>> outbounds;
#else
list<InboundObject<XInBound_T>> inbounds;
list<OutboundObject<XOutBound_T>> outbounds;
#endif
TransportObject transport;
StatsObject stats;
ReverseObject reverse;
PolicyObject policy;
RootObject(): log(), api(), dns(), routing(), inbounds(), outbounds(), transport(), stats(), reverse(), policy() {}
#if USE_TODO_FEATURES == false
XTOSTRUCT(O(log, api, dns, routing, inbounds, outbounds, transport, stats, reverse, policy))
#endif
};
}
}
namespace Hv2ray
{
namespace V2ConfigModels
{
/// Some protocols from: https://v2ray.com/chapter_02/02_protocols.html
namespace Protocols
{
/// BlackHole Protocol, OutBound
struct BlackHole : XOutBoundsType {
struct ResponseObject {
string type;
};
ResponseObject response;
};
/// DNS, OutBound
struct DNS: XOutBoundsType {
string network;
string address;
int port;
};
/// Dokodemo-door, InBound
struct Dokodemo_door : XInBoundsType {
string address;
int port;
string network;
int timeout;
bool followRedirect;
int userLevel;
};
/// Freedom, OutBound
struct Freedom: XOutBoundsType {
string domainStrategy;
string redirect;
int userLevel;
};
struct AccountObject {
string user;
string pass;
XTOSTRUCT(O(user, pass))
};
/// HTTP, InBound
struct HTTP: XInBoundsType {
int timeout;
list<AccountObject> accounts;
bool allowTransparent;
int userLevel;
XTOSTRUCT(O(timeout, accounts, allowTransparent, userLevel))
};
/// MTProto, InBound || OutBound
struct MTProto: XInBoundsType, XOutBoundsType {
struct UserObject {
string email;
int level;
string secret;
};
list<UserObject> users;
};
// We don't add shadowsocks, (As it's quite complex and I'm quite lazy...)
/// Socks, InBound, OutBound
struct Socks: XInBoundsType, XOutBoundsType {
struct UserObject {
};
struct ServerObject {
string address;
int port;
list<UserObject> users;
};
list<ServerObject> servers;
string auth;
list<AccountObject> accounts;
bool udp;
string ip;
int userLevel;
};
struct VMess: XInBoundsType, XOutBoundsType {
struct ServerObject {
struct UserObject {
string id;
int alterId;
string security;
int level;
XTOSTRUCT(O(id, alterId, security, level))
};
// OUTBound;
string address;
int port;
list<UserObject> users;
XTOSTRUCT(O(address, port, users))
};
list<ServerObject> vnext;
// INBound;
struct ClientObject {
string id;
int level;
int alterId;
string email;
XTOSTRUCT(O(id, level, alterId, email))
};
list<ClientObject> clients;
// detour and default will not be implemented as it's complicated...
bool disableInsecureEncryption;
XTOSTRUCT(O(vnext, clients, disableInsecureEncryption))
};
}
}
}
#if USE_TODO_FEATURES
using namespace Hv2ray;
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::LogObject, access, error, loglevel)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::ApiObject, tag, services)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::DNSObjects::ServerObject, address, port, domains)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::DnsObject, hosts, servers)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::ROUTINGObjects::RuleObject, type, domain, ip, port, network, source, user, inboundTag, protocol, attrs)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::ROUTINGObjects::BalancerObject, tag, selector)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::RoutingObject, domainStrategy, rules, balancers)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::POLICYObjects::SystemPolicyObject, statsInboundUplink, statsInboundDownlink)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::POLICYObjects::LevelPolicyObject, handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::PolicyObject, level, system)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::TRANSFERObjectsInternal::HTTPRequestObject, version, method, path, headers)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::TRANSFERObjectsInternal::HTTPResponseObject, version, status, reason, headers)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::TRANSFERObjectsInternal::TCPHeader_M_Object, type, request, response)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::TRANSFERObjectsInternal::HeaderObject, type)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::TCPObject, header)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::KCPObject, mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::WebSocketObject, path, headers)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::HttpObject, host, path)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::DomainSocketObject, path)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TRANSFERObjects::QuicObject, security, key, header)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::TransportObject, tcpSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::INBOUNDObjects::SniffingObject, enabled, destOverride)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::INBOUNDObjects::AllocateObject, strategy, refresh, concurrency)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::OUTBOUNDObjects::ProxySettingsObject, tag)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::OUTBOUNDObjects::MuxObject, enabled, concurrency)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::REVERSEObjects::BridgeObject, tag, domain)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::REVERSEObjects::PortalObject, tag, domain)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::ReverseObject, bridges, portals)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::StatsObject, _)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::StreamSettingsObject, tcpSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings, tlsSettings, sockopt, network, security)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::STREAMSETTINGSObjects::TLSObject, serverName, allowInsecure, alpn, certificates, disableSystemRoot)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::STREAMSETTINGSObjects::CertificateObject, usage, certificateFile, keyFile, certificate, key)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::STREAMSETTINGSObjects::SockoptObject, mark, tcpFastOpen, tproxy)
// These 3 are used as templates.
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::RootObject, log, api, dns, routing, inbounds, outbounds, transport, stats, reverse, policy)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::InboundObject, port, listen, protocol, settings, streamSettings, tag, sniffing, allocate)
JSONCONS_MEMBER_TRAITS_DECL(V2ConfigModels::OutboundObject, sendThrough, protocol, settings, tag, streamSettings, proxySettings, mux)
#endif
/// Code above has passed these tests.
//using namespace Hv2ray::V2ConfigModels;
//RootObject<Protocols::HTTP, Protocols::VMess> x;
//InboundObject<Protocols::HTTP> inH;
//x.inbounds.insert(x.inbounds.end(), inH);
//OutboundObject<Protocols::VMess> inV;
//x.outbounds.insert(x.outbounds.end(), inV);
//QString jsonConfig = Utils::StructToJSON(x);
//cout << jsonConfig.toStdString() << endl;
///
#endif

View File

@ -1,87 +1,98 @@
#include <QApplication>
#include <QDir>
#include <iostream>
#include <QDebug>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QStandardPaths>
#include <QTranslator>
#include <iostream>
#include "runguard.h"
#include "utils.h"
#include "MainWindow.h"
#include "ConnectionEditWindow.h"
#include "HUtils.hpp"
#include "HConfigObjects.hpp"
#include "runguard.hpp"
#include "w_MainWindow.h"
using namespace std;
using namespace Hv2ray;
using namespace Hv2ray::Utils;
using namespace Hv2ray::HConfigModels;
void firstRunCheck()
bool initializeHv()
{
if(!QDir("conf").exists()) {
QDir().mkdir("conf");
qDebug() << "Config directory created.";
}
/// Hv2ray Config Path and ends with "/"
QString configPath = "";
#if defined(__WIN32) || defined(__APPLE__)
// For Windows and MacOS, there's no such 'installation' of a software
// package, So as what ShadowSocks and v2rayX does, save config files next to
// the executable.
configPath = HV2RAY_CONFIG_DIR_NAME;
#else
// However, for linux, this software can be and/or will be provided as a
// package and install to whatever /usr/bin or /usr/local/bin or even /opt/
// Thus we save config files in the user's home directory.
configPath = QDir::homePath() + HV2RAY_CONFIG_DIR_NAME;
#endif
ConfigDir = QDir(configPath);
QFileInfo hvConfInfo("conf/Hv2ray.config.json");
if (!ConfigDir.exists()) {
auto result = QDir().mkdir(configPath);
// First Run?
if(!hvConfInfo.exists()) {
QFile confFile("conf/Hv2ray.config.json");
if(!confFile.open(QIODevice::ReadWrite)) {
qDebug() << "Can not open Hv2ray.conf.json for read and write.";
if (result) {
qDebug() << "Created hv2ray config file path at: " + configPath;
} else {
// We cannot continue as it failed to create a dir.
qDebug() << "Failed to create config file folder under " + configPath;
return false;
}
QJsonObject settings;
settings.insert("auth", "noauth");
settings.insert("udp", true);
settings.insert("ip", "127.0.0.1");
QJsonObject socks;
socks.insert("settings", QJsonValue(settings));
socks.insert("tag", "socks-in");
socks.insert("port", 1080);
socks.insert("listen", "127.0.0.1");
socks.insert("protocol", "socks");
QJsonArray inbounds;
inbounds.append(socks);
QJsonObject rootObj;
rootObj.insert("inbounds", QJsonValue(inbounds));
rootObj.insert("v2suidEnabled", false);
QJsonDocument defaultConf;
defaultConf.setObject(rootObj);
QByteArray byteArray = defaultConf.toJson(QJsonDocument::Indented);
confFile.write(byteArray);
confFile.close();
}
QFile configFile(configPath + "hv2ray.conf");
if (!Utils::hasFile(&ConfigDir, ".initialised")) {
// This is first run!
// These below genenrated very basic global config.
HInbondSetting inHttp = HInbondSetting(true, "127.0.0.1", 8080);
HInbondSetting inSocks = HInbondSetting(true, "127.0.0.1", 1080);
Hv2Config conf = Hv2Config("zh-CN", "info", inHttp, inSocks);
SetGlobalConfig(conf);
SaveConfig(&configFile);
// Create Placeholder for initialise indicator.
QFile initPlaceHolder(configPath + ".initialised");
initPlaceHolder.open(QFile::WriteOnly);
initPlaceHolder.close();
} else {
LoadConfig(&configFile);
}
return true;
}
int main(int argc, char *argv[])
{
QApplication _qApp(argc, argv);
RunGuard guard("Hv2ray-Instance-Identifier");
QTranslator translator;
if (translator.load(QString(":/translations/zh-CN.qm"), QString("translations")))
{
cout << "Loaded Chinese translations" << endl;
if (!guard.isSingleInstance()) {
Utils::showWarnMessageBox(nullptr, QObject::tr("Hv2Ray"), QObject::tr("AnotherInstanceRunning"));
return -1;
}
_qApp.installTranslator(&translator);
RunGuard guard("Hv2ray");
if(!guard.isSingleInstance()) {
showWarnMessageBox(nullptr, QObject::tr("Hv2Ray"), QObject::tr("AnotherInstanceRunning"));
return -1;
}
// Set file startup path as Path
// WARNING: This may be changed in the future.
QDir::setCurrent(QFileInfo(QCoreApplication::applicationFilePath()).path());
// Hv2ray Initialize
initializeHv();
firstRunCheck();
MainWindow w;
if (_qApp.installTranslator(getTranslator(GetGlobalConfig().language))) {
cout << "Loaded translations " << GetGlobalConfig().language << endl;
} else if (_qApp.installTranslator(getTranslator("en-US"))) {
cout << "Loaded default translations" << endl;
} else {
showWarnMessageBox(
nullptr, "Failed to load translations 无法加载语言文件",
"Failed to load translations, user experience may be downgraded. \r\n"
"无法加载语言文件,用户体验可能会降级.");
}
// Show MainWindow
Ui::MainWindow w;
w.show();
return _qApp.exec();
}

View File

@ -1,77 +1,81 @@
#include <QCryptographicHash>
#include "runguard.h"
//from https://stackoverflow.com/a/28172162
namespace
#include "runguard.hpp"
namespace Hv2ray
{
QString generateKeyHash( const QString& key, const QString& salt )
//from https://stackoverflow.com/a/28172162
QString RunGuard::generateKeyHash(const QString &key, const QString &salt)
{
QByteArray data;
data.append( key.toUtf8() );
data.append( salt.toUtf8() );
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
data.append(key.toUtf8());
data.append(salt.toUtf8());
data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex();
return data;
}
}
RunGuard::RunGuard( const QString& key )
: key( key )
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
, sharedMem( sharedmemKey )
, memLock( memLockKey, 1 )
{
memLock.acquire();
RunGuard::RunGuard(const QString &key)
: key(key)
, memLockKey(generateKeyHash(key, "_memLockKey"))
, sharedmemKey(generateKeyHash(key, "_sharedmemKey"))
, sharedMem(sharedmemKey)
, memLock(memLockKey, 1)
{
QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
memLock.acquire();
{
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
memLock.release();
}
memLock.release();
}
RunGuard::~RunGuard()
{
release();
}
bool RunGuard::isAnotherRunning()
{
if ( sharedMem.isAttached() ) {
return false;
}
memLock.acquire();
const bool isRunning = sharedMem.attach();
if ( isRunning ) {
sharedMem.detach();
}
memLock.release();
return isRunning;
}
bool RunGuard::isSingleInstance()
{
if ( isAnotherRunning() ) { // Extra check
return false;
}
memLock.acquire();
const bool result = sharedMem.create( sizeof( quint64 ) );
memLock.release();
if ( !result ) {
RunGuard::~RunGuard()
{
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if ( sharedMem.isAttached() ) {
sharedMem.detach();
bool RunGuard::isAnotherRunning()
{
if (sharedMem.isAttached()) {
return false;
}
memLock.acquire();
const bool isRunning = sharedMem.attach();
if (isRunning) {
sharedMem.detach();
}
memLock.release();
return isRunning;
}
bool RunGuard::isSingleInstance()
{
if (isAnotherRunning()) { // Extra check
return false;
}
memLock.acquire();
const bool result = sharedMem.create(sizeof(quint64));
memLock.release();
if (!result) {
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if (sharedMem.isAttached()) {
sharedMem.detach();
}
memLock.release();
}
memLock.release();
}

View File

@ -1,31 +0,0 @@
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
// From https://stackoverflow.com/a/28172162
class RunGuard
{
public:
RunGuard( const QString& key );
~RunGuard();
bool isAnotherRunning();
bool isSingleInstance();
void release();
private:
const QString key;
const QString memLockKey;
const QString sharedmemKey;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
Q_DISABLE_COPY( RunGuard )
};
#endif // RUNGUARD_H

33
src/runguard.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
namespace Hv2ray
{
// From https://stackoverflow.com/a/28172162
class RunGuard
{
public:
explicit RunGuard(const QString &key);
~RunGuard();
bool isAnotherRunning();
bool isSingleInstance();
void release();
private:
QString generateKeyHash(const QString &key, const QString &salt);
const QString key;
const QString memLockKey;
const QString sharedmemKey;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
Q_DISABLE_COPY(RunGuard)
};
}
#endif // RUNGUARD_H

View File

@ -1,97 +0,0 @@
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include "utils.h"
QJsonObject switchJsonArrayObject(QJsonObject obj, QString value)
{
QJsonObject returnObj = obj.value(value).isNull()
? obj.value(value).toObject()
: obj.value(value).toArray().first().toObject();
return returnObj;
}
QJsonObject findValueFromJsonArray(QJsonArray arr, QString key, QString val)
{
for (const auto obj : arr) {
if (obj.toObject().value(key).toString() == val) {
return obj.toObject();
}
}
return QJsonObject();
}
QJsonObject loadRootObjFromConf()
{
QFile globalConfigFile("Hv2ray.conf");
globalConfigFile.open(QIODevice::ReadOnly);
QByteArray conf = globalConfigFile.readAll();
globalConfigFile.close();
QJsonDocument v2conf(QJsonDocument::fromJson(conf));
QJsonObject rootObj = v2conf.object();
return rootObj;
}
QJsonArray getInbounds()
{
QJsonArray inbounds;
inbounds = loadRootObjFromConf().value("inbounds").toArray();
return inbounds;
}
bool getRootEnabled()
{
return loadRootObjFromConf().value("v2suidEnabled").toBool();
}
bool checkVCoreExes()
{
if (QFileInfo("v2ray").exists() && QFileInfo("geoip.dat").exists() && QFileInfo("geosite.dat").exists() && QFileInfo("v2ctl").exists()) {
return true;
} else {
showWarnMessageBox(nullptr, QObject::tr("CoreNotFound"), QObject::tr("CoreFileNotFoundExplaination"));
return false;
}
}
void showWarnMessageBox(QWidget* parent, QString title, QString text)
{
QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
void overrideInbounds(QString path)
{
QFile confFile(path);
confFile.open(QIODevice::ReadOnly);
QByteArray conf = confFile.readAll();
confFile.close();
QJsonDocument v2conf(QJsonDocument::fromJson(conf));
QJsonObject rootObj = v2conf.object();
QJsonArray modifiedIn = getInbounds();
rootObj.remove("inbounds");
rootObj.remove("inbound");
rootObj.remove("inboundDetour");
rootObj.insert("inbounds", QJsonValue(modifiedIn));
v2conf.setObject(rootObj);
conf = v2conf.toJson(QJsonDocument::Indented);
confFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
confFile.write(conf);
confFile.close();
}
int getIndexByValue(QJsonArray array, QString key, QString val)
{
QJsonArray::iterator it;
int index = 0;
for(it = array.begin(); it != array.end(); it ++) {
if(it->toObject().value(key) == val) {
return index;
}
index ++;
}
return -1;
}

View File

@ -1,14 +0,0 @@
#ifndef UTILS_H
#define UTILS_H
#include <QJsonObject>
QJsonObject switchJsonArrayObject(QJsonObject objest, QString value);
QJsonObject findValueFromJsonArray(QJsonArray arr, QString key, QString val);
QJsonObject loadRootObjFromConf();
QJsonArray getInbounds();
bool checkVCoreExes();
void showWarnMessageBox(QWidget* parent, QString title, QString text);
void overrideInbounds(QString path);
int getIndexByValue(QJsonArray array, QString key, QString val);
#endif // UTILS_H

View File

@ -1,66 +1,87 @@
#include <QProcess>
#include <QDebug>
#include <QProcess>
#include <QDir>
#include <QProcess>
#include "utils.h"
#include "MainWindow.h"
#include "vinteract.h"
#include "HUtils.hpp"
#include "vinteract.hpp"
#include "w_MainWindow.h"
bool validationCheck(QString path)
namespace Hv2ray
{
if(checkVCoreExes()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QDir::currentPath());
bool v2Instance::checkConfigFile(const QString path)
{
if (checkVCoreExes()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QDir::currentPath());
QProcess process;
process.setProcessEnvironment(env);
process.start("v2ray", QStringList() << "-test"
<< "-config" << path,
QIODevice::ReadWrite | QIODevice::Text);
QProcess process;
process.setProcessEnvironment(env);
process.start("v2ray", QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text);
if (!process.waitForFinished()) {
qDebug() << "v2ray core failed with exit code " << process.exitCode();
return false;
}
if(!process.waitForFinished()) {
qDebug() << "v2ray core failed with exit code " << process.exitCode();
QString output = QString(process.readAllStandardOutput());
if (!output.contains("Configuration OK")) {
Utils::showWarnMessageBox(nullptr, QObject::tr("ConfigurationError"), output.mid(output.indexOf("anti-censorship.") + 17));
return false;
} else
return true;
} else
return false;
}
QString output = QString(process.readAllStandardOutput());
if (!output.contains("Configuration OK")) {
showWarnMessageBox(nullptr, QObject::tr("ConfigurationError"), output.mid(output.indexOf("anti-censorship.") + 17));
return false;
}
else return true;
}
else return false;
}
v2Instance::v2Instance()
{
this->vProcess = new QProcess();
}
v2Instance::v2Instance(QWidget *parent)
{
QProcess *proc = new QProcess();
this->vProcess = proc;
QObject::connect(vProcess, SIGNAL(readyReadStandardOutput()), parent, SLOT(updateLog()));
processStatus = STOPPED;
}
v2Instance::~v2Instance()
{
this->stop();
}
bool v2Instance::checkVCoreExes()
{
if (QFileInfo("v2ray").exists() && QFileInfo("geoip.dat").exists() && QFileInfo("geosite.dat").exists() && QFileInfo("v2ctl").exists()) {
return true;
} else {
Utils::showWarnMessageBox(nullptr, QObject::tr("CoreNotFound"), QObject::tr("CoreFileNotFoundExplaination"));
return false;
}
}
bool v2Instance::start(QWidget *parent)
{
if(this->vProcess->state() == QProcess::Running) {
bool v2Instance::start()
{
if (this->vProcess->state() == QProcess::Running) {
this->stop();
}
if (checkVCoreExes()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QDir::currentPath());
this->vProcess->setProcessEnvironment(env);
this->vProcess->start("./v2ray", QStringList() << "-config"
<< "config.json",
QIODevice::ReadWrite | QIODevice::Text);
this->vProcess->waitForStarted();
processStatus = STARTED;
return true;
} else
return false;
}
void v2Instance::stop()
{
this->vProcess->close();
processStatus = STOPPED;
}
v2Instance::~v2Instance()
{
this->stop();
}
if (checkVCoreExes()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QDir::currentPath());
this->vProcess->setProcessEnvironment(env);
this->vProcess->start("./v2ray", QStringList() << "-config" << "config.json", QIODevice::ReadWrite | QIODevice::Text);
this->vProcess->waitForStarted();
QObject::connect(vProcess, SIGNAL(readyReadStandardOutput()), parent, SLOT(updateLog()));
return true;
}
else return false;
}
void v2Instance::stop()
{
this->vProcess->close();
}

View File

@ -1,19 +0,0 @@
#ifndef VINTERACT_H
#define VINTERACT_H
#include <QString>
#include <QProcess>
bool validationCheck(QString path);
class v2Instance
{
public:
explicit v2Instance();
bool start(QWidget *parent);
void stop();
void restart();
QProcess *vProcess;
~v2Instance();
};
#endif // VINTERACT_H

33
src/vinteract.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef VINTERACT_H
#define VINTERACT_H
#include <QProcess>
#include <QString>
namespace Hv2ray
{
enum V2RAY_INSTANCE_STARTUP_STATUS {
STOPPED,
STARTING,
STARTED
};
class v2Instance
{
public:
explicit v2Instance(QWidget *parent);
bool start();
void stop();
void restart();
static bool checkVCoreExes();
static bool checkConfigFile(QString path);
~v2Instance();
QProcess *vProcess;
private:
V2RAY_INSTANCE_STARTUP_STATUS processStatus;
};
}
#endif // VINTERACT_H

View File

@ -0,0 +1,37 @@
#include "w_ConnectionEditWindow.h"
#include "w_MainWindow.h"
#include <QDebug>
#include <QFile>
#include <QIntValidator>
#include <iostream>
namespace Hv2ray
{
namespace Ui
{
ConnectionEditWindow::ConnectionEditWindow(QWidget *parent)
: QDialog(parent)
, ui(new Ui_ConnectionEditWindow)
{
ui->setupUi(this);
ui->portLineEdit->setValidator(new QIntValidator());
ui->alterLineEdit->setValidator(new QIntValidator());
}
ConnectionEditWindow::~ConnectionEditWindow()
{
delete ui;
}
//void ConnectionEditWindow::getConfigFromDialog(Ui::ConnectionEditWindow *ui)
//{
//this->host = ui->ipLineEdit->text();
//this->port = ui->portLineEdit->text();
//this->alias = ui->aliasLineEdit->text();
//this->uuid = ui->idLineEdit->text();
//this->alterid = ui->alterLineEdit->text();
//this->security = ui->securityCombo->currentText();
//this->isCustom = 0;
//}
}
}

View File

@ -0,0 +1,24 @@
#ifndef CONFEDIT_H
#define CONFEDIT_H
#include <QDialog>
#include "ui_w_ConnectionEditWindow.h"
namespace Hv2ray
{
namespace Ui
{
class ConnectionEditWindow : public QDialog
{
Q_OBJECT
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
~ConnectionEditWindow();
private:
Ui_ConnectionEditWindow *ui;
};
}
}
#endif // CONFEDIT_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConnectionEditWindow</class>
<widget class="QDialog" name="ConnectionEditWindow">
<class>Hv2ray::Ui::ConnectionEditWindow</class>
<widget class="QDialog" name="Hv2ray::Ui::ConnectionEditWindow">
<property name="geometry">
<rect>
<x>0</x>
@ -864,7 +864,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConnectionEditWindow</receiver>
<receiver>Hv2ray::Ui::ConnectionEditWindow</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -880,7 +880,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConnectionEditWindow</receiver>
<receiver>Hv2ray::Ui::ConnectionEditWindow</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

137
src/w_ImportConfig.cpp Normal file
View File

@ -0,0 +1,137 @@
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#pragma push_macro("slots")
#undef slots
#include "Python.h"
#pragma pop_macro("slots")
#include "HUtils.hpp"
#include "vinteract.hpp"
#include "w_ConnectionEditWindow.h"
#include "w_ImportConfig.h"
using namespace Hv2ray;
namespace Hv2ray
{
namespace Ui
{
ImportConfig::ImportConfig(QWidget *parent)
: QDialog(parent)
, ui(new Ui_ImportConfig)
{
ui->setupUi(this);
connect(this, SIGNAL(updateConfTable()), parentWidget(), SLOT(updateConfTable()));
}
ImportConfig::~ImportConfig()
{
delete ui;
}
void ImportConfig::on_pushButton_clicked()
{
QString dir = QFileDialog::getOpenFileName(this, tr("OpenConfigFile"), "~/");
ui->fileLineTxt->setText(dir);
}
void ImportConfig::savefromFile(QString path, QString alias)
{
Q_UNUSED(path)
Q_UNUSED(alias)
//Hv2Config newConfig;
//newConfig.alias = alias;
//QFile configFile(path);
//if(!configFile.open(QIODevice::ReadOnly)) {
// showWarnMessageBox(this, tr("ImportConfig"), tr("CannotOpenFile"));
// qDebug() << "ImportConfig::CannotOpenFile";
// return;
//}
//QByteArray allData = configFile.readAll();
//configFile.close();
//QJsonDocument v2conf(QJsonDocument::fromJson(allData));
//QJsonObject rootobj = v2conf.object();
//QJsonObject outbound;
//if(rootobj.contains("outbounds")) {
// outbound = rootobj.value("outbounds").toArray().first().toObject();
//} else {
// outbound = rootobj.value("outbound").toObject();
//}
//QJsonObject vnext = switchJsonArrayObject(outbound.value("settings").toObject(), "vnext");
//QJsonObject user = switchJsonArrayObject(vnext, "users");
//newConfig.host = vnext.value("address").toString();
//newConfig.port = QString::number(vnext.value("port").toInt());
//newConfig.alterid = QString::number(user.value("alterId").toInt());
//newConfig.uuid = user.value("id").toString();
//newConfig.security = user.value("security").toString();
//if (newConfig.security.isNull()) {
// newConfig.security = "auto";
//}
//newConfig.isCustom = 1;
//int id = newConfig.save();
//if(id < 0)
//{
// showWarnMessageBox(this, tr("ImportConfig"), tr("SaveFailed"));
// qDebug() << "ImportConfig::SaveFailed";
// return;
//}
//emit updateConfTable();
//QString newFile = "conf/" + QString::number(id) + ".conf";
//if(!QFile::copy(path, newFile)) {
// showWarnMessageBox(this, tr("ImportConfig"), tr("CannotCopyCustomConfig"));
// qDebug() << "ImportConfig::CannotCopyCustomConfig";
//}
}
void ImportConfig::on_buttonBox_accepted()
{
QString alias = ui->nameTxt->text();
if (ui->importSourceCombo->currentIndex() == 0) { // From File...
QString path = ui->fileLineTxt->text();
bool isValid = v2Instance::checkConfigFile(path);
if (isValid) {
savefromFile(path, alias);
}
} else {
QString vmess = ui->vmessConnectionStringTxt->toPlainText();
Py_Initialize();
assert(Py_IsInitialized());
QString param = "--inbound socks:1080 " + vmess + " -o config.json.tmp";
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./utils')");
PyObject *pModule = PyImport_ImportModule("vmess2json");
PyObject *pFunc = PyObject_GetAttrString(pModule, "main");
PyObject *arg = PyTuple_New(1);
PyObject *arg1 = Py_BuildValue("s", param.toStdString().c_str());
PyTuple_SetItem(arg, 0, arg1);
PyObject_CallObject(pFunc, arg);
Py_Finalize();
if (QFile::exists(QCoreApplication::applicationDirPath() + "/config.json.tmp")) {
ImportConfig *im = new ImportConfig(this->parentWidget());
if (v2Instance::checkConfigFile(QCoreApplication::applicationDirPath() + "/config.json.tmp")) {
im->savefromFile("config.json.tmp", alias);
}
QFile::remove("config.json.tmp");
} else {
Utils::showWarnMessageBox(this, tr("ImportConfig"), tr("CannotGenerateConfig"));
qDebug() << "ImportConfig::CannotGenerateConfig";
}
}
if (ui->useCurrentSettingRidBtn->isChecked()) {
// TODO: Use Current Settings...
} else {
// TODO: Override Inbound....
}
}
}
}

32
src/w_ImportConfig.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef IMPORTCONF_H
#define IMPORTCONF_H
#include <QDialog>
#include "ui_w_ImportConfig.h"
namespace Hv2ray
{
namespace Ui
{
class ImportConfig : public QDialog
{
Q_OBJECT
public:
explicit ImportConfig(QWidget *parent = nullptr);
void savefromFile(QString path, QString alias);
~ImportConfig();
private slots:
void on_pushButton_clicked();
void on_buttonBox_accepted();
signals:
void updateConfTable();
private:
Ui_ImportConfig *ui;
};
}
}
#endif // IMPORTCONF_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImportConfig</class>
<widget class="QDialog" name="ImportConfig">
<class>Hv2ray::Ui::ImportConfig</class>
<widget class="QDialog" name="Hv2ray::Ui::ImportConfig">
<property name="geometry">
<rect>
<x>0</x>
@ -162,7 +162,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ImportConfig</receiver>
<receiver>Hv2ray::Ui::ImportConfig</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -178,7 +178,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ImportConfig</receiver>
<receiver>Hv2ray::Ui::ImportConfig</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

282
src/w_MainWindow.cpp Normal file
View File

@ -0,0 +1,282 @@
#include <QAction>
#include <QCloseEvent>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QHeaderView>
#include <QInputDialog>
#include <QMenu>
#include <QStandardItemModel>
#include "HUtils.hpp"
#include "vinteract.hpp"
#include "w_ConnectionEditWindow.h"
#include "w_ImportConfig.h"
#include "w_MainWindow.h"
#include "w_PrefrencesWindow.h"
namespace Hv2ray
{
namespace Ui
{
void MainWindow::CreateTrayIcon()
{
hTray = new QSystemTrayIcon();
hTray->setToolTip(tr("Hv2ray"));
hTray->setIcon(this->windowIcon());
connect(hTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_activatedTray(QSystemTrayIcon::ActivationReason)));
QAction *actionShow = new QAction(this);
QAction *actionQuit = new QAction(this);
QAction *actionStart = new QAction(this);
QAction *actionRestart = new QAction(this);
QAction *actionStop = new QAction(this);
actionShow->setText(tr("#Hide"));
actionQuit->setText(tr("#Quit"));
actionStart->setText(tr("#Start"));
actionStop->setText(tr("#Stop"));
actionRestart->setText(tr("#Restart"));
actionStart->setEnabled(true);
actionStop->setEnabled(false);
actionRestart->setEnabled(false);
trayMenu->addAction(actionShow);
trayMenu->addSeparator();
trayMenu->addAction(actionStart);
trayMenu->addAction(actionStop);
trayMenu->addAction(actionRestart);
trayMenu->addSeparator();
trayMenu->addAction(actionQuit);
connect(actionShow, SIGNAL(triggered()), this, SLOT(toggleMainWindowVisibility()));
connect(actionStart, SIGNAL(triggered()), this, SLOT(on_startButton_clicked()));
connect(actionStop, SIGNAL(triggered()), this, SLOT(on_stopButton_clicked()));
connect(actionRestart, SIGNAL(triggered()), this, SLOT(on_restartButton_clicked()));
connect(actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
hTray->setContextMenu(trayMenu);
hTray->show();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui_MainWindow)
{
this->setWindowIcon(QIcon(":/icons/Hv2ray.ico"));
ui->setupUi(this);
UpdateConfigTable();
// ui->configTable->setContextMenuPolicy(Qt::CustomContextMenu);
// connect(ui->configTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
this->vinstance = new v2Instance(this);
CreateTrayIcon();
if (QFileInfo("config.json").exists()) {
vinstance->start();
}
// QAction *select = new QAction("Select", ui->configTable);
// QAction *del = new QAction("Delete", ui->configTable);
// QAction *rename = new QAction("Rename", ui->configTable);
// popMenu->addAction(select);
// popMenu->addAction(del);
// popMenu->addAction(rename);
// connect(select, SIGNAL(triggered()), this, SLOT(select_triggered()));
// connect(del, SIGNAL(triggered()), this, SLOT(delConf()));
// connect(rename, SIGNAL(triggered()), this, SLOT(renameRow()));
// connect(ui->logText, SIGNAL(textChanged()), this, SLOT(scrollToBottom()));
// bar = ui->logText->verticalScrollBar();
}
MainWindow::~MainWindow()
{
hTray->hide();
delete this->hTray;
delete this->vinstance;
delete ui;
}
void MainWindow::on_actionEdit_triggered()
{
ConnectionEditWindow *e = new ConnectionEditWindow(this);
e->setAttribute(Qt::WA_DeleteOnClose);
e->show();
}
void MainWindow::on_actionExisting_config_triggered()
{
ImportConfig *f = new ImportConfig(this);
f->setAttribute(Qt::WA_DeleteOnClose);
f->show();
}
void MainWindow::showMenu(QPoint pos)
{
Q_UNUSED(pos)
// if(ui->configTable->indexAt(pos).column() != -1) {
// popMenu->move(cursor().pos());
// popMenu->show();
// }
}
void MainWindow::select_triggered()
{
// int row = ui->configTable->selectionModel()->currentIndex().row();
// int idIntable = ui->configTable->model()->data(ui->configTable->model()->index(row, 4)).toInt();
// this->geneConf(idIntable);
// if(this->v2Inst->v2Process->state() == QProcess::Running) {
// this->on_restartButton_clicked();
// }
}
void MainWindow::DeleteConfig()
{
}
void MainWindow::UpdateConfigTable()
{
}
void MainWindow::GenerateConfig(int idIntable)
{
Q_UNUSED(idIntable)
//Hv2Config tmpConf;
//emit UpdateConfigTable();
//if (tmpConf.isCustom == 1) {
// QString src = "conf/" + QString::number(idIntable) + ".conf";
// overrideInbounds(src);
// if (QFile::exists("config.json")) {
// QFile::remove("config.json");
// }
// QFile::copy(src, "config.json");
//} else {
// // TODO: Config generator
//}
}
void MainWindow::UpdateLog()
{
ui->logText->insertPlainText(this->vinstance->vProcess->readAllStandardOutput());
}
void MainWindow::on_startButton_clicked()
{
ui->logText->clear();
bool startFlag = this->vinstance->start();
trayMenu->actions()[2]->setEnabled(!startFlag);
trayMenu->actions()[3]->setEnabled(startFlag);
trayMenu->actions()[4]->setEnabled(startFlag);
}
void MainWindow::on_stopButton_clicked()
{
this->vinstance->stop();
ui->logText->clear();
trayMenu->actions()[2]->setEnabled(true);
trayMenu->actions()[3]->setEnabled(false);
trayMenu->actions()[4]->setEnabled(false);
}
void MainWindow::on_restartButton_clicked()
{
on_stopButton_clicked();
on_startButton_clicked();
}
void MainWindow::on_clbutton_clicked()
{
ui->logText->clear();
}
void MainWindow::on_rtButton_clicked()
{
emit UpdateConfigTable();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
this->hide();
event->ignore();
}
void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) {
case QSystemTrayIcon::Trigger:
// Toggle Show/Hide
#ifndef __APPLE__
// Every single click will trigger the Show/Hide toggling.
// So, as a hobby on common MacOS Apps, we 'don't toggle visibility on click'.
toggleMainWindowVisibility();
#endif
break;
case QSystemTrayIcon::DoubleClick:
if (this->isHidden()) {
this->show();
}
break;
case QSystemTrayIcon::MiddleClick:
// TODO: Check if an alert message box is present.
// If so, do nothing but please wait for the message box to be closed.
if (this->vinstance->vProcess->state() == QProcess::ProcessState::Running) {
on_stopButton_clicked();
} else {
on_startButton_clicked();
}
break;
case QSystemTrayIcon::Unknown:
break;
case QSystemTrayIcon::Context:
break;
}
}
void MainWindow::toggleMainWindowVisibility()
{
if (this->isHidden()) {
this->show();
trayMenu->actions()[0]->setText(tr("#Hide"));
} else {
this->hide();
trayMenu->actions()[0]->setText(tr("#Show"));
}
}
void MainWindow::quit()
{
QCoreApplication::quit();
}
void MainWindow::on_actionExit_triggered()
{
quit();
}
void MainWindow::renameRow()
{
// QString text = QInputDialog::getText(this, "Rename config", "New name:", QLineEdit::Normal);
// int row = ui->configTable->currentIndex().row();
// int idIntable = ui->configTable->model()->data(ui->configTable->model()->index(row, 4)).toInt();
// SQLiteDB mydb;
// QString updateString = "update confs set alias = '" + text + "' where id = " + QString::number(idIntable);
// mydb.DoQuery(updateString);
// emit updateConfTable();
}
void MainWindow::scrollToBottom()
{
bar->setValue(bar->maximum());
}
void MainWindow::on_actionPreferences_triggered()
{
PrefrencesWindow *v = new PrefrencesWindow(this);
v->setAttribute(Qt::WA_DeleteOnClose);
v->show();
}
void MainWindow::on_pushButton_clicked()
{
auto confedit = new ConnectionEditWindow();
confedit->show();
}
}
}

63
src/w_MainWindow.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "w_ConnectionEditWindow.h"
#include <QMainWindow>
#include <QMenu>
#include <QScrollBar>
#include <QSystemTrayIcon>
#include "ui_w_MainWindow.h"
#include "vinteract.hpp"
#include "V2ConfigObjects.hpp"
namespace Hv2ray
{
namespace Ui
{
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
v2Instance *vinstance;
QSystemTrayIcon *hTray;
QMenu *trayMenu = new QMenu(this);
QMenu *popMenu = new QMenu(this);
QScrollBar *bar;
~MainWindow();
private slots:
void on_restartButton_clicked();
void on_actionEdit_triggered();
void on_actionExisting_config_triggered();
void UpdateConfigTable();
void DeleteConfig();
void showMenu(QPoint pos);
void UpdateLog();
void on_startButton_clicked();
void on_stopButton_clicked();
void select_triggered();
void on_clbutton_clicked();
void on_rtButton_clicked();
void GenerateConfig(int idIntable);
void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
void toggleMainWindowVisibility();
void quit();
void on_actionExit_triggered();
void renameRow();
void scrollToBottom();
void on_actionPreferences_triggered();
void on_pushButton_clicked();
private:
Ui_MainWindow *ui;
void closeEvent(QCloseEvent *);
void createTrayAction();
void CreateTrayIcon();
};
}
}
#endif // MAINWINDOW_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<class>Hv2ray::Ui::MainWindow</class>
<widget class="QMainWindow" name="Hv2ray::Ui::MainWindow">
<property name="geometry">
<rect>
<x>0</x>
@ -143,6 +143,9 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@ -199,6 +202,13 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>#ConnectionSettings</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="pushButton">
<property name="sizePolicy">
@ -212,13 +222,6 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>#ConnectionSettings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -241,7 +244,7 @@
<x>0</x>
<y>0</y>
<width>568</width>
<height>21</height>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -291,7 +294,6 @@
<tabstop>restartButton</tabstop>
<tabstop>clearlogButton</tabstop>
<tabstop>listWidget</tabstop>
<tabstop>pushButton</tabstop>
<tabstop>logText</tabstop>
</tabstops>
<resources/>

119
src/w_PrefrencesWindow.cpp Normal file
View File

@ -0,0 +1,119 @@
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QIntValidator>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QProcess>
#include "HUtils.hpp"
#include "vinteract.hpp"
#include "w_PrefrencesWindow.h"
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace Hv2ray;
using namespace Utils;
namespace Hv2ray
{
namespace Ui
{
PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent), CurrentConfig(), ui(new Ui_PrefrencesWindow)
{
ui->setupUi(this);
CurrentConfig = GetGlobalConfig();
ui->languageComboBox->setCurrentText(QString::fromStdString(CurrentConfig.language));
ui->runAsRootCheckBox->setChecked(CurrentConfig.runAsRoot);
ui->logLevelCheckBox->setCurrentText(QString::fromStdString(CurrentConfig.logLevel));
//
ui->httpCB->setChecked(CurrentConfig.httpSetting.enabled);
ui->httpPortLE->setText(QString::fromStdString(to_string(CurrentConfig.httpSetting.port)));
ui->httpAuthCB->setChecked(CurrentConfig.httpSetting.useAuthentication);
ui->httpAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authUsername));
ui->httpAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authPassword));
ui->httpPortLE->setValidator(new QIntValidator());
//
ui->socksCB->setChecked(CurrentConfig.socksSetting.enabled);
ui->socksPortLE->setText(QString::fromStdString(to_string(CurrentConfig.socksSetting.port)));
ui->socksAuthCB->setChecked(CurrentConfig.socksSetting.useAuthentication);
ui->socksAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authUsername));
ui->socksAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authPassword));
//
ui->httpPortLE->setValidator(new QIntValidator());
ui->socksPortLE->setValidator(new QIntValidator());
parentMW = parent;
}
PrefrencesWindow::~PrefrencesWindow()
{
delete ui;
}
void PrefrencesWindow::on_buttonBox_accepted()
{
if (v2Instance::checkVCoreExes()) {
if (ui->httpPortLE->text().toInt() != ui->socksPortLE->text().toInt()) {
#ifndef _WIN32
// Set UID and GID in *nix
// The file is actually not here
QFileInfo v2rayCoreExeFile("v2ray");
if (ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() != 0) {
QProcess::execute("pkexec", QStringList() << "bash"
<< "-c"
<< "chown root:root " + QCoreApplication::applicationDirPath() + "/v2ray" + ";chmod +s " + QCoreApplication::applicationDirPath() + "/v2ray");
} else if (!ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() == 0) {
uid_t uid = getuid();
gid_t gid = getgid();
QProcess::execute("pkexec", QStringList() << "chown" << QString::number(uid) + ":" + QString::number(gid) << QCoreApplication::applicationDirPath() + "/v2ray");
}
v2rayCoreExeFile.refresh();
//rootObj.insert("v2suidEnabled", v2rayCoreExeFile.ownerId() == 0);
#else
// No such uid gid thing on windows....
#endif
} else {
showWarnMessageBox(this, tr("Prefrences"), tr("PortNumbersCannotBeSame"));
}
}
}
void PrefrencesWindow::on_httpCB_stateChanged(int checked)
{
if (checked != Qt::Checked) {
ui->httpPortLE->setDisabled(true);
} else {
ui->httpPortLE->setEnabled(true);
}
}
void PrefrencesWindow::on_socksCB_stateChanged(int checked)
{
if (checked != Qt::Checked) {
ui->socksPortLE->setEnabled(false);
} else {
ui->socksPortLE->setEnabled(true);
}
}
void PrefrencesWindow::on_httpAuthCB_stateChanged(int checked)
{
if (checked) {
}
}
void PrefrencesWindow::on_runAsRootCheckBox_stateChanged(int arg1)
{
Q_UNUSED(arg1)
#ifdef _WIN32
showWarnMessageBox(this, tr("Prefrences"), tr("RunAsRootNotOnWindows"));
#endif
}
}
}

36
src/w_PrefrencesWindow.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef HVCONF_H
#define HVCONF_H
#include <QDialog>
#include <ui_w_PrefrencesWindow.h>
#include "HConfigObjects.hpp"
namespace Hv2ray
{
namespace Ui
{
class PrefrencesWindow : public QDialog
{
Q_OBJECT
public:
explicit PrefrencesWindow(QWidget *parent = nullptr);
~PrefrencesWindow();
QWidget *parentMW;
private slots:
void on_buttonBox_accepted();
void on_httpCB_stateChanged(int arg1);
void on_socksCB_stateChanged(int arg1);
void on_httpAuthCB_stateChanged(int arg1);
void on_runAsRootCheckBox_stateChanged(int arg1);
private:
Hv2ray::HConfigModels::Hv2Config CurrentConfig;
Ui_PrefrencesWindow *ui;
};
}
}
#endif // HVCONF_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PrefrencesWindow</class>
<widget class="QDialog" name="PrefrencesWindow">
<class>Hv2ray::Ui::PrefrencesWindow</class>
<widget class="QDialog" name="Hv2ray::Ui::PrefrencesWindow">
<property name="geometry">
<rect>
<x>0</x>
@ -345,7 +345,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PrefrencesWindow</receiver>
<receiver>Hv2ray::Ui::PrefrencesWindow</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -361,7 +361,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PrefrencesWindow</receiver>
<receiver>Hv2ray::Ui::PrefrencesWindow</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff