[Remove] Removed Python depends. Added struct for vmess:// protocol.

FYI: https://github.com/lhy0403/Qv2ray/issues/12#issuecomment-507897806
FYI: https://github.com/lhy0403/Qv2ray/issues/15
FYI: #15

Signed-off-by: Leroy.H.Y <lhy20010403@hotmail.com>

Former-commit-id: 361ff763e6
This commit is contained in:
Leroy.H.Y 2019-07-05 23:47:31 +08:00
parent 1c194f7481
commit 6f9df68e11
27 changed files with 1837 additions and 2538 deletions

View File

@ -5,8 +5,6 @@ install:
- git submodule update --init - git submodule update --init
build_script: build_script:
- mkdir python37 && xcopy C:\Python37 python37 /E /H /Q
- tools\FixPythonWithMinGW.bat
- lrelease.exe Qv2ray.pro - lrelease.exe Qv2ray.pro
- mkdir build && cd build - mkdir build && cd build
- qmake ..\Qv2ray.pro - qmake ..\Qv2ray.pro

View File

@ -13,7 +13,7 @@ before_install:
- if [ "$BADGE" = "linux" ]; then sudo apt-get update; fi - if [ "$BADGE" = "linux" ]; then sudo apt-get update; fi
install: install:
- if [ "$BADGE" = "linux" ]; then sudo apt-get install qtchooser qt5-default qt5-qmake qtdeclarative5-dev python3-dev python-dev libpython3-dev qttools5-dev-tools; fi - if [ "$BADGE" = "linux" ]; then sudo apt-get install qtchooser qt5-default qt5-qmake qtdeclarative5-dev qttools5-dev-tools; fi
- if [ "$BADGE" = "osx" ]; then brew install cppcheck https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/qt.rb; fi - if [ "$BADGE" = "osx" ]; then brew install cppcheck https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/qt.rb; fi
- if [ "$BADGE" = "osx" ]; then brew link --force qt; fi - if [ "$BADGE" = "osx" ]; then brew link --force qt; fi

View File

@ -1,8 +1,11 @@
# News # News
## 最新 ## 最新
**2019-07-03**: 主配置文件序列化/反序列化工作完成,并添加更多协议配置 **2019-07-04**: 我们终于摆脱了对于 Python 的依赖,现在 Qv2ray 可以自行解析 `vmess://` 协议 [WIP]
## 历史 ## 历史
**2019-07-03**: 主配置文件序列化/反序列化工作完成,并添加更多协议配置
**2019-07-02**: 等待上游依赖完成更新 [JSON 序列化 std::list](https://github.com/xyz347/x2struct/issues/11#issuecomment-507671091) **2019-07-02**: 等待上游依赖完成更新 [JSON 序列化 std::list](https://github.com/xyz347/x2struct/issues/11#issuecomment-507671091)
**2019-07-01**: 休息了几天,主要是去关注别的项目了。现在开始重构 v2ray 交互部分。 **2019-07-01**: 休息了几天,主要是去关注别的项目了。现在开始重构 v2ray 交互部分。

View File

@ -59,7 +59,8 @@ TRANSLATIONS += \
RC_ICONS += ./icons/Qv2ray.ico RC_ICONS += ./icons/Qv2ray.ico
INCLUDEPATH += 3rdparty/\ INCLUDEPATH += \
3rdparty/\
3rdparty/jsoncons/include 3rdparty/jsoncons/include
@ -67,59 +68,3 @@ INCLUDEPATH += 3rdparty/\
qnx: target.path = /tmp/$${TARGET}/bin qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target !isEmpty(target.path): INSTALLS += target
WITH_PYTHON = no
PYTHONVER = null
unix:!macx {
# Python Headers check.
exists( "/usr/include/python3.5m/Python.h" ) {
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
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
}
}
equals(WITH_PYTHON, "no") {
error("No Python3 libs found, did you install dev packages such as python3-dev ?")
}

View File

@ -1,9 +1,8 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a034dd186c36408c92ffb04449fb6996)](https://app.codacy.com/app/lhy0403/Qv2ray?utm_source=github.com&utm_medium=referral&utm_content=lhy0403/Qv2ray&utm_campaign=Badge_Grade_Dashboard) # Qv2ray
# Qv2ray [![HitCount](http://hits.dwyl.io/lhy0403/Qv2ray.svg)](http://hits.dwyl.io/lhy0403/Qv2ray)
TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多数功能尚未齐全。 TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多数功能尚未齐全。
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/lhy0403/Qv2ray.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lhy0403/Qv2ray/context:cpp) [![Total alerts](https://img.shields.io/lgtm/alerts/g/lhy0403/Qv2ray.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lhy0403/Qv2ray/alerts/) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a034dd186c36408c92ffb04449fb6996)](https://app.codacy.com/app/lhy0403/Qv2ray?utm_source=github.com&utm_medium=referral&utm_content=lhy0403/Qv2ray&utm_campaign=Badge_Grade_Dashboard)[![HitCount](http://hits.dwyl.io/lhy0403/Qv2ray.svg)](http://hits.dwyl.io/lhy0403/Qv2ray)[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/lhy0403/Qv2ray.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lhy0403/Qv2ray/context:cpp) [![Total alerts](https://img.shields.io/lgtm/alerts/g/lhy0403/Qv2ray.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lhy0403/Qv2ray/alerts/)
| OS | 主分支 [master](https://github.com/lhy0403/Qv2ray/tree/master) | 开发分支 [dev](https://github.com/lhy0403/Qv2ray/tree/dev) | | OS | 主分支 [master](https://github.com/lhy0403/Qv2ray/tree/master) | 开发分支 [dev](https://github.com/lhy0403/Qv2ray/tree/dev) |
| ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
@ -16,7 +15,7 @@ TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多
# 鸣谢 ## 鸣谢
[@aliyuchang33](https://github.com/aliyuchang33) - 项目原作者/发起人,感谢他使用 Qt 作为基础框架。 [@aliyuchang33](https://github.com/aliyuchang33) - 项目原作者/发起人,感谢他使用 Qt 作为基础框架。
@ -24,18 +23,16 @@ TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多
## 项目依赖 ## 项目依赖
### Linux ### Linux
- Qt >= 5.12 - `Qt >= 5.12`
- Python (3.5 | 3.6 | 3.7) - `gcc >=8` (需要 c++11 支持)
### Windows ### Windows
- Python 3.7 Qt Creator (Qt5)
- Qt Creator (MinGW & Qt5)
## MacOS ### MacOS
- Qt >= 5.12 - Qt >= 5.12
- Python (3.6 | 3.7) 使用 [`brew install python3`] 解决依赖问题
## 编译 ## 编译
- 如果想测试当前的开发分支,请使用 `git checkout dev` 切换到开发分支 - 如果想测试当前的开发分支,请使用 `git checkout dev` 切换到开发分支

View File

@ -26,6 +26,19 @@ namespace Qv2ray
configFile->close(); configFile->close();
} }
QString base64_encode(QString string)
{
QByteArray ba;
ba.append(string);
return ba.toBase64();
}
QString base64_decode(QString string)
{
QByteArray ba;
ba.append(string);
return QString(QByteArray::fromBase64(ba));
}
void LoadConfig(QFile *configFile) void LoadConfig(QFile *configFile)
{ {
using namespace Qv2ray::QvConfigModels; using namespace Qv2ray::QvConfigModels;

View File

@ -27,16 +27,13 @@ namespace Qv2ray
/// Get file list in a Dir /// Get file list in a Dir
QStringList getAllFilesList(QDir *dir); QStringList getAllFilesList(QDir *dir);
bool hasFile(QDir *dir, QString fileName); bool hasFile(QDir *dir, QString fileName);
QString base64_encode(QString string);
QString base64_decode(QString string);
template <typename TYPE> template <typename TYPE>
QString StructToJSON(const TYPE &t) QString StructToJSON(const TYPE &t)
{ {
string s; string s;
#if USE_TODO_FEATURES
encode_json<TYPE>(t, s, indenting::indent);
#else
s = X::tojson(t, "", 4, ' '); s = X::tojson(t, "", 4, ' ');
#endif
cout << s << endl; cout << s << endl;
return QString::fromStdString(s); return QString::fromStdString(s);
} }
@ -45,11 +42,7 @@ namespace Qv2ray
TYPE StructFromJSON(const string &str) TYPE StructFromJSON(const string &str)
{ {
TYPE v; TYPE v;
#if USE_TODO_FEATURES
v = decode_json<TYPE>(str);
#else
X::loadjson(str, v, false); X::loadjson(str, v, false);
#endif
return v; return v;
} }
} }

View File

@ -17,6 +17,20 @@ namespace Qv2ray
{ {
namespace V2ConfigModels namespace V2ConfigModels
{ {
struct VMessProtocolConfigObject {
string v;
string ps;
string add;
string port;
string id;
string aid;
string net;
string type;
string host;
string path;
string tls;
XTOSTRUCT(O(v, ps, add, port, id, aid, net, type, host, path, tls))
};
// Two struct defining TYPE parameter to be passed into inbound configs and outbound configs. // Two struct defining TYPE parameter to be passed into inbound configs and outbound configs.
struct XOutBoundsType { struct XOutBoundsType {
}; };

View File

@ -92,7 +92,7 @@ int main(int argc, char *argv[])
} }
// Show MainWindow // Show MainWindow
Ui::MainWindow w; MainWindow w;
w.show(); w.show();
return _qApp.exec(); return _qApp.exec();
} }

View File

@ -5,30 +5,32 @@
#include <QIntValidator> #include <QIntValidator>
#include <iostream> #include <iostream>
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;
//}
void ConnectionEditWindow::on_buttonBox_accepted()
{ {
ConnectionEditWindow::ConnectionEditWindow(QWidget *parent)
: QDialog(parent)
, ui(new Ui_WConnectionEdit)
{
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

@ -6,16 +6,21 @@
namespace Ui namespace Ui
{ {
class ConnectionEditWindow : public QDialog class ConnectionEditWindow;
{
Q_OBJECT
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
~ConnectionEditWindow();
private:
Ui_WConnectionEdit *ui;
};
} }
class ConnectionEditWindow : public QDialog
{
Q_OBJECT
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
~ConnectionEditWindow();
private slots:
void on_buttonBox_accepted();
private:
Ui::ConnectionEditWindow *ui;
};
#endif // CONFEDIT_H #endif // CONFEDIT_H

View File

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

View File

@ -5,131 +5,124 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#pragma push_macro("slots")
#undef slots
#include "Python.h"
#pragma pop_macro("slots")
#include "HUtils.hpp" #include "HUtils.hpp"
#include "vinteract.hpp" #include "vinteract.hpp"
#include "w_ConnectionEditWindow.h" #include "w_ConnectionEditWindow.h"
#include "w_ImportConfig.h" #include "w_ImportConfig.h"
using namespace Qv2ray; using namespace Qv2ray;
namespace Ui ImportConfig::ImportConfig(QWidget *parent)
: QDialog(parent)
, ui(new Ui::ImportConfigWindow)
{ {
ImportConfig::ImportConfig(QWidget *parent) ui->setupUi(this);
: QDialog(parent) connect(this, SIGNAL(updateConfTable()), parentWidget(), SLOT(updateConfTable()));
, ui(new Ui_WImportConfig) }
{
ui->setupUi(this);
connect(this, SIGNAL(updateConfTable()), parentWidget(), SLOT(updateConfTable()));
}
ImportConfig::~ImportConfig() ImportConfig::~ImportConfig()
{ {
delete ui; delete ui;
} }
void ImportConfig::on_pushButton_clicked() void ImportConfig::on_pushButton_clicked()
{ {
QString dir = QFileDialog::getOpenFileName(this, tr("OpenConfigFile"), "~/"); QString dir = QFileDialog::getOpenFileName(this, tr("OpenConfigFile"), "~/");
ui->fileLineTxt->setText(dir); ui->fileLineTxt->setText(dir);
} }
void ImportConfig::savefromFile(QString path, QString alias) void ImportConfig::savefromFile(QString path, QString alias)
{ {
Q_UNUSED(path) Q_UNUSED(path)
Q_UNUSED(alias) Q_UNUSED(alias)
//Hv2Config newConfig; //Hv2Config newConfig;
//newConfig.alias = alias; //newConfig.alias = alias;
//QFile configFile(path); //QFile configFile(path);
//if(!configFile.open(QIODevice::ReadOnly)) { //if(!configFile.open(QIODevice::ReadOnly)) {
// showWarnMessageBox(this, tr("ImportConfig"), tr("CannotOpenFile")); // showWarnMessageBox(this, tr("ImportConfig"), tr("CannotOpenFile"));
// qDebug() << "ImportConfig::CannotOpenFile"; // qDebug() << "ImportConfig::CannotOpenFile";
// return; // return;
//} //}
//QByteArray allData = configFile.readAll(); //QByteArray allData = configFile.readAll();
//configFile.close(); //configFile.close();
//QJsonDocument v2conf(QJsonDocument::fromJson(allData)); //QJsonDocument v2conf(QJsonDocument::fromJson(allData));
//QJsonObject rootobj = v2conf.object(); //QJsonObject rootobj = v2conf.object();
//QJsonObject outbound; //QJsonObject outbound;
//if(rootobj.contains("outbounds")) { //if(rootobj.contains("outbounds")) {
// outbound = rootobj.value("outbounds").toArray().first().toObject(); // 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) {
// Invalid file alert.
return;
}
savefromFile(path, alias);
} else {
QString vmess = ui->vmessConnectionStringTxt->toPlainText();
if (!vmess.toLower().startsWith("vmess://")) {
Utils::showWarnMessageBox(this, tr("#VMessDecodeError"), tr("#NotValidVMessProtocolString"));
}
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
auto vmessString = Utils::base64_decode(vmessJsonB64.toString());
auto vmessConf = Utils::StructFromJSON<V2ConfigModels::VMessProtocolConfigObject>(vmessString.toStdString());
// TODO: SAVE CONFIG!!!!
//
//if (QFile::exists(ConfigDir.path() + "/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 { //} else {
// outbound = rootobj.value("outbound").toObject(); // Utils::showWarnMessageBox(this, tr("ImportConfig"), tr("CannotGenerateConfig"));
//} // qDebug() << "ImportConfig::CannotGenerateConfig";
//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() if (ui->useCurrentSettingRidBtn->isChecked()) {
{ // TODO: Use Current Settings...
QString alias = ui->nameTxt->text(); } else {
// TODO: Override Inbound....
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....
}
} }
} }

View File

@ -6,24 +6,26 @@
namespace Ui namespace Ui
{ {
class ImportConfig : public QDialog class ImportConfigWindow;
{
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_WImportConfig *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::ImportConfigWindow *ui;
};
#endif // IMPORTCONF_H #endif // IMPORTCONF_H

View File

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

View File

@ -15,265 +15,262 @@
#include "w_MainWindow.h" #include "w_MainWindow.h"
#include "w_PrefrencesWindow.h" #include "w_PrefrencesWindow.h"
namespace Ui void MainWindow::CreateTrayIcon()
{ {
void MainWindow::CreateTrayIcon() hTray = new QSystemTrayIcon();
{ hTray->setToolTip(tr("Qv2ray"));
hTray = new QSystemTrayIcon(); hTray->setIcon(this->windowIcon());
hTray->setToolTip(tr("Qv2ray")); connect(hTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_activatedTray(QSystemTrayIcon::ActivationReason)));
hTray->setIcon(this->windowIcon()); QAction *actionShow = new QAction(this);
connect(hTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_activatedTray(QSystemTrayIcon::ActivationReason))); QAction *actionQuit = new QAction(this);
QAction *actionShow = new QAction(this); QAction *actionStart = new QAction(this);
QAction *actionQuit = new QAction(this); QAction *actionRestart = new QAction(this);
QAction *actionStart = new QAction(this); QAction *actionStop = new QAction(this);
QAction *actionRestart = new QAction(this); actionShow->setText(tr("#Hide"));
QAction *actionStop = new QAction(this); actionQuit->setText(tr("#Quit"));
actionShow->setText(tr("#Hide")); actionStart->setText(tr("#Start"));
actionQuit->setText(tr("#Quit")); actionStop->setText(tr("#Stop"));
actionStart->setText(tr("#Start")); actionRestart->setText(tr("#Restart"));
actionStop->setText(tr("#Stop")); actionStart->setEnabled(true);
actionRestart->setText(tr("#Restart")); actionStop->setEnabled(false);
actionStart->setEnabled(true); actionRestart->setEnabled(false);
actionStop->setEnabled(false); trayMenu->addAction(actionShow);
actionRestart->setEnabled(false); trayMenu->addSeparator();
trayMenu->addAction(actionShow); trayMenu->addAction(actionStart);
trayMenu->addSeparator(); trayMenu->addAction(actionStop);
trayMenu->addAction(actionStart); trayMenu->addAction(actionRestart);
trayMenu->addAction(actionStop); trayMenu->addSeparator();
trayMenu->addAction(actionRestart); trayMenu->addAction(actionQuit);
trayMenu->addSeparator(); connect(actionShow, SIGNAL(triggered()), this, SLOT(toggleMainWindowVisibility()));
trayMenu->addAction(actionQuit); connect(actionStart, SIGNAL(triggered()), this, SLOT(on_startButton_clicked()));
connect(actionShow, SIGNAL(triggered()), this, SLOT(toggleMainWindowVisibility())); connect(actionStop, SIGNAL(triggered()), this, SLOT(on_stopButton_clicked()));
connect(actionStart, SIGNAL(triggered()), this, SLOT(on_startButton_clicked())); connect(actionRestart, SIGNAL(triggered()), this, SLOT(on_restartButton_clicked()));
connect(actionStop, SIGNAL(triggered()), this, SLOT(on_stopButton_clicked())); connect(actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
connect(actionRestart, SIGNAL(triggered()), this, SLOT(on_restartButton_clicked())); hTray->setContextMenu(trayMenu);
connect(actionQuit, SIGNAL(triggered()), this, SLOT(quit())); hTray->show();
hTray->setContextMenu(trayMenu); }
hTray->show();
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
this->setWindowIcon(QIcon(":/icons/Qv2ray.ico"));
ui->setupUi(this);
UpdateConfigTable();
// ui->configTable->setContextMenuPolicy(Qt::CustomContextMenu);
// connect(ui->configTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
this->vinstance = new Qv2ray::v2Instance(this);
CreateTrayIcon();
if (QFileInfo("config.json").exists()) {
vinstance->start();
} }
MainWindow::MainWindow(QWidget *parent) // QAction *select = new QAction("Select", ui->configTable);
: QMainWindow(parent) // QAction *del = new QAction("Delete", ui->configTable);
, ui(new Ui_WMain) // QAction *rename = new QAction("Rename", ui->configTable);
{ // popMenu->addAction(select);
this->setWindowIcon(QIcon(":/icons/Qv2ray.ico")); // popMenu->addAction(del);
ui->setupUi(this); // popMenu->addAction(rename);
UpdateConfigTable(); // connect(select, SIGNAL(triggered()), this, SLOT(select_triggered()));
// ui->configTable->setContextMenuPolicy(Qt::CustomContextMenu); // connect(del, SIGNAL(triggered()), this, SLOT(delConf()));
// connect(ui->configTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); // connect(rename, SIGNAL(triggered()), this, SLOT(renameRow()));
this->vinstance = new Qv2ray::v2Instance(this); // connect(ui->logText, SIGNAL(textChanged()), this, SLOT(scrollToBottom()));
CreateTrayIcon(); // bar = ui->logText->verticalScrollBar();
}
if (QFileInfo("config.json").exists()) { MainWindow::~MainWindow()
vinstance->start(); {
} hTray->hide();
delete this->hTray;
delete this->vinstance;
delete ui;
}
// QAction *select = new QAction("Select", ui->configTable); void MainWindow::on_actionEdit_triggered()
// QAction *del = new QAction("Delete", ui->configTable); {
// QAction *rename = new QAction("Rename", ui->configTable); ConnectionEditWindow *e = new ConnectionEditWindow(this);
// popMenu->addAction(select); e->setAttribute(Qt::WA_DeleteOnClose);
// popMenu->addAction(del); e->show();
// 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() void MainWindow::on_actionExisting_config_triggered()
{ {
hTray->hide(); ImportConfig *f = new ImportConfig(this);
delete this->hTray; f->setAttribute(Qt::WA_DeleteOnClose);
delete this->vinstance; f->show();
delete ui; }
}
void MainWindow::on_actionEdit_triggered() void MainWindow::showMenu(QPoint pos)
{ {
ConnectionEditWindow *e = new ConnectionEditWindow(this); Q_UNUSED(pos)
e->setAttribute(Qt::WA_DeleteOnClose); // if(ui->configTable->indexAt(pos).column() != -1) {
e->show(); // 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::on_actionExisting_config_triggered() void MainWindow::DeleteConfig()
{ {
ImportConfig *f = new ImportConfig(this); }
f->setAttribute(Qt::WA_DeleteOnClose); void MainWindow::UpdateConfigTable()
f->show(); {
} }
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::showMenu(QPoint pos) void MainWindow::on_startButton_clicked()
{ {
Q_UNUSED(pos) ui->logText->clear();
// if(ui->configTable->indexAt(pos).column() != -1) { bool startFlag = this->vinstance->start();
// popMenu->move(cursor().pos()); trayMenu->actions()[2]->setEnabled(!startFlag);
// popMenu->show(); trayMenu->actions()[3]->setEnabled(startFlag);
// } trayMenu->actions()[4]->setEnabled(startFlag);
} }
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::on_stopButton_clicked()
{ {
} this->vinstance->stop();
void MainWindow::UpdateConfigTable() ui->logText->clear();
{ trayMenu->actions()[2]->setEnabled(true);
} trayMenu->actions()[3]->setEnabled(false);
void MainWindow::GenerateConfig(int idIntable) trayMenu->actions()[4]->setEnabled(false);
{ }
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() void MainWindow::on_restartButton_clicked()
{ {
ui->logText->clear(); on_stopButton_clicked();
bool startFlag = this->vinstance->start(); on_startButton_clicked();
trayMenu->actions()[2]->setEnabled(!startFlag); }
trayMenu->actions()[3]->setEnabled(startFlag);
trayMenu->actions()[4]->setEnabled(startFlag);
}
void MainWindow::on_stopButton_clicked() void MainWindow::on_clbutton_clicked()
{ {
this->vinstance->stop(); ui->logText->clear();
ui->logText->clear(); }
trayMenu->actions()[2]->setEnabled(true);
trayMenu->actions()[3]->setEnabled(false);
trayMenu->actions()[4]->setEnabled(false);
}
void MainWindow::on_restartButton_clicked() void MainWindow::on_rtButton_clicked()
{ {
on_stopButton_clicked(); emit UpdateConfigTable();
on_startButton_clicked(); }
}
void MainWindow::on_clbutton_clicked() void MainWindow::closeEvent(QCloseEvent *event)
{ {
ui->logText->clear(); this->hide();
} event->ignore();
}
void MainWindow::on_rtButton_clicked() void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
{ {
emit UpdateConfigTable(); switch (reason) {
} case QSystemTrayIcon::Trigger:
// Toggle Show/Hide
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__ #ifndef __APPLE__
// Every single click will trigger the Show/Hide toggling. // Every single click will trigger the Show/Hide toggling.
// So, as a hobby on common MacOS Apps, we 'don't toggle visibility on click'. // So, as a hobby on common MacOS Apps, we 'don't toggle visibility on click'.
toggleMainWindowVisibility(); toggleMainWindowVisibility();
#endif #endif
break; break;
case QSystemTrayIcon::DoubleClick: case QSystemTrayIcon::DoubleClick:
if (this->isHidden()) { if (this->isHidden()) {
this->show(); this->show();
} }
break; break;
case QSystemTrayIcon::MiddleClick: case QSystemTrayIcon::MiddleClick:
// TODO: Check if an alert message box is present. // TODO: Check if an alert message box is present.
// If so, do nothing but please wait for the message box to be closed. // If so, do nothing but please wait for the message box to be closed.
if (this->vinstance->vProcess->state() == QProcess::ProcessState::Running) { if (this->vinstance->vProcess->state() == QProcess::ProcessState::Running) {
on_stopButton_clicked(); on_stopButton_clicked();
} else { } else {
on_startButton_clicked(); on_startButton_clicked();
} }
break; break;
case QSystemTrayIcon::Unknown: case QSystemTrayIcon::Unknown:
break; break;
case QSystemTrayIcon::Context: case QSystemTrayIcon::Context:
break; 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();
} }
} }
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

@ -10,52 +10,52 @@
#include "vinteract.hpp" #include "vinteract.hpp"
#include "V2ConfigObjects.hpp" #include "V2ConfigObjects.hpp"
namespace Ui namespace Ui
{ {
class MainWindow : public QMainWindow class MainWindow;
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
Qv2ray::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_WMain *ui;
void closeEvent(QCloseEvent *);
void createTrayAction();
void CreateTrayIcon();
};
} }
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
Qv2ray::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 #endif // MAINWINDOW_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>WMain</class> <class>MainWindow</class>
<widget class="QMainWindow" name="WMain"> <widget class="QMainWindow" name="MainWindow">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>

View File

@ -18,100 +18,97 @@
using namespace Qv2ray::Utils; using namespace Qv2ray::Utils;
namespace Ui PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
CurrentConfig(),
ui(new Ui::PrefrencesWindow)
{ {
PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent), ui->setupUi(this);
CurrentConfig(), CurrentConfig = GetGlobalConfig();
ui(new Ui_WPrefrences) ui->languageComboBox->setCurrentText(QString::fromStdString(CurrentConfig.language));
{ ui->runAsRootCheckBox->setChecked(CurrentConfig.runAsRoot);
ui->setupUi(this); ui->logLevelCheckBox->setCurrentText(QString::fromStdString(CurrentConfig.logLevel));
CurrentConfig = GetGlobalConfig(); //
ui->languageComboBox->setCurrentText(QString::fromStdString(CurrentConfig.language)); ui->httpCB->setChecked(CurrentConfig.httpSetting.enabled);
ui->runAsRootCheckBox->setChecked(CurrentConfig.runAsRoot); ui->httpPortLE->setText(QString::fromStdString(to_string(CurrentConfig.httpSetting.port)));
ui->logLevelCheckBox->setCurrentText(QString::fromStdString(CurrentConfig.logLevel)); ui->httpAuthCB->setChecked(CurrentConfig.httpSetting.useAuthentication);
// ui->httpAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authUsername));
ui->httpCB->setChecked(CurrentConfig.httpSetting.enabled); ui->httpAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authPassword));
ui->httpPortLE->setText(QString::fromStdString(to_string(CurrentConfig.httpSetting.port))); ui->httpPortLE->setValidator(new QIntValidator());
ui->httpAuthCB->setChecked(CurrentConfig.httpSetting.useAuthentication); //
ui->httpAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authUsername)); ui->socksCB->setChecked(CurrentConfig.socksSetting.enabled);
ui->httpAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.httpSetting.authPassword)); ui->socksPortLE->setText(QString::fromStdString(to_string(CurrentConfig.socksSetting.port)));
ui->httpPortLE->setValidator(new QIntValidator()); ui->socksAuthCB->setChecked(CurrentConfig.socksSetting.useAuthentication);
// ui->socksAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authUsername));
ui->socksCB->setChecked(CurrentConfig.socksSetting.enabled); ui->socksAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authPassword));
ui->socksPortLE->setText(QString::fromStdString(to_string(CurrentConfig.socksSetting.port))); //
ui->socksAuthCB->setChecked(CurrentConfig.socksSetting.useAuthentication); ui->httpPortLE->setValidator(new QIntValidator());
ui->socksAuthUsernameTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authUsername)); ui->socksPortLE->setValidator(new QIntValidator());
ui->socksAuthPasswordTxt->setText(QString::fromStdString(CurrentConfig.socksSetting.authPassword)); parentMW = parent;
// }
ui->httpPortLE->setValidator(new QIntValidator());
ui->socksPortLE->setValidator(new QIntValidator());
parentMW = parent;
}
PrefrencesWindow::~PrefrencesWindow() PrefrencesWindow::~PrefrencesWindow()
{ {
delete ui; delete ui;
} }
void PrefrencesWindow::on_buttonBox_accepted() void PrefrencesWindow::on_buttonBox_accepted()
{ {
if (Qv2ray::v2Instance::checkVCoreExes()) { if (Qv2ray::v2Instance::checkVCoreExes()) {
if (ui->httpPortLE->text().toInt() != ui->socksPortLE->text().toInt()) { if (ui->httpPortLE->text().toInt() != ui->socksPortLE->text().toInt()) {
#ifndef _WIN32 #ifndef _WIN32
// Set UID and GID in *nix // Set UID and GID in *nix
// The file is actually not here // The file is actually not here
QFileInfo v2rayCoreExeFile("v2ray"); QFileInfo v2rayCoreExeFile("v2ray");
if (ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() != 0) { if (ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() != 0) {
QProcess::execute("pkexec", QStringList() << "bash" QProcess::execute("pkexec", QStringList() << "bash"
<< "-c" << "-c"
<< "chown root:root " + QCoreApplication::applicationDirPath() + "/v2ray" + ";chmod +s " + QCoreApplication::applicationDirPath() + "/v2ray"); << "chown root:root " + QCoreApplication::applicationDirPath() + "/v2ray" + ";chmod +s " + QCoreApplication::applicationDirPath() + "/v2ray");
} else if (!ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() == 0) { } else if (!ui->runAsRootCheckBox->isChecked() && v2rayCoreExeFile.ownerId() == 0) {
uid_t uid = getuid(); uid_t uid = getuid();
gid_t gid = getgid(); gid_t gid = getgid();
QProcess::execute("pkexec", QStringList() << "chown" << QString::number(uid) + ":" + QString::number(gid) << QCoreApplication::applicationDirPath() + "/v2ray"); 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) v2rayCoreExeFile.refresh();
{ //rootObj.insert("v2suidEnabled", v2rayCoreExeFile.ownerId() == 0);
if (checked != Qt::Checked) { #else
ui->httpPortLE->setDisabled(true); // No such uid gid thing on windows....
} 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 #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
}

View File

@ -7,27 +7,29 @@
namespace Ui namespace Ui
{ {
class PrefrencesWindow : public QDialog class PrefrencesWindow;
{
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:
Qv2ray::QvConfigModels::Qv2Config CurrentConfig;
Ui_WPrefrences *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:
Qv2ray::QvConfigModels::Qv2Config CurrentConfig;
Ui::PrefrencesWindow *ui;
};
#endif // HVCONF_H #endif // HVCONF_H

View File

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

View File

@ -1,6 +0,0 @@
set CURRENT_PATH=%~dp0%
cd %~dp0\..\python37\libs\
echo EXPORTS > python37.def
nm python37.lib | grep " T _" | sed "s/.* T _//" >> python37.def
dlltool --input-def python37.def --dllname python37 --output-lib libpython37_mingw.a
cd %CURRENT_PATH%\..\

View File

@ -1,24 +0,0 @@
@echo off
REM Install Python
ECHO Downloading Python using wget...
%~dp0\wget.exe https://www.python.org/ftp/python/3.7.3/python-3.7.3.exe -O %~dp0\python-3.7.3.exe
REM Get install path
CALL :NORMALIZEPATH "\python37\"
ECHO INSTALL_PATH=%RETVAL%
REM Installing Python...
start /w "" "%~dp0\python-3.7.3.exe" /quiet TargetDir=%RETVAL%
:CONTINUE
ECHO Python headers and libs are installed!
exit 0
REM ========================================================================================
REM Path resolving using: https://stackoverflow.com/a/33404867/8364323
:: ========== FUNCTIONS ==========
:NORMALIZEPATH
SET RETVAL=%~dpfn1
EXIT /B

View File

@ -1 +0,0 @@
e21dde78d551a377ba813f6079ed610deaafadd8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,599 +0,0 @@
#!/usr/bin/env python3
#Imported and modified from "https://github.com/boypt/vmess2json.git", thanks to the author boypt.
import os
import sys
import json
import base64
import pprint
import argparse
import random
import hashlib
import socket
import urllib.request
TPL = {}
TPL["CLIENT"] = """
{
"log": {
"access": "",
"error": "",
"loglevel": "error"
},
"inbounds": [
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "host.host",
"port": 1234,
"users": [
{
"email": "user@v2ray.com",
"id": "",
"alterId": 0,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
},
"mux": {
"enabled": true
},
"tag": "proxy"
},
{
"protocol": "freedom",
"tag": "direct"
}
],
"dns": {
"servers": [
"8.8.8.8",
"8.8.4.4",
"1.1.1.1"
]
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:private",
"geoip:cn"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:cn"
],
"outboundTag": "direct"
}
]
}
}
"""
# tcpSettings
TPL["http"] = """
{
"header": {
"type": "http",
"request": {
"version": "1.1",
"method": "GET",
"path": [
"/"
],
"headers": {
"Host": [
"www.cloudflare.com",
"www.amazon.com"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"
],
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
],
"Accept-language": [
"zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"Cache-Control": [
"no-cache"
],
"Pragma": "no-cache"
}
}
}
}
"""
# kcpSettings
TPL["kcp"] = """
{
"mtu": 1350,
"tti": 50,
"uplinkCapacity": 12,
"downlinkCapacity": 100,
"congestion": false,
"readBufferSize": 2,
"writeBufferSize": 2,
"header": {
"type": "wechat-video"
}
}
"""
# wsSettings
TPL["ws"] = """
{
"connectionReuse": true,
"path": "/path",
"headers": {
"Host": "host.host.host"
}
}
"""
# httpSettings
TPL["h2"] = """
{
"host": [
"host.com"
],
"path": "/host"
}
"""
TPL["quic"] = """
{
"security": "none",
"key": "",
"header": {
"type": "none"
}
}
"""
TPL["in_socks"] = """
{
"tag":"socks-in",
"port": 10808,
"listen": "::",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
}
}
"""
TPL["in_http"] = """
{
"tag":"http-in",
"port": 8123,
"listen": "::",
"protocol": "http"
}
"""
TPL["in_mt"] = """
{
"tag": "mt-in",
"port": 6666,
"protocol": "mtproto",
"settings": {
"users": [
{
"secret": ""
}
]
}
}
"""
TPL["out_mt"] = """
{
"tag": "mt-out",
"protocol": "mtproto",
"proxySettings": {
"tag": "proxy"
}
}
"""
TPL["in_dns"] = """
{
"port": 53,
"tag": "dns-in",
"protocol": "dokodemo-door",
"settings": {
"address": "1.1.1.1",
"port": 53,
"network": "tcp,udp"
}
}
"""
TPL["conf_dns"] = """
{
"hosts": {
"geosite:category-ads": "127.0.0.1",
"domain:googleapis.cn": "googleapis.com"
},
"servers": [
"1.0.0.1",
{
"address": "1.2.4.8",
"domains": [
"geosite:cn"
],
"port": 53
}
]
}
"""
TPL["in_tproxy"] = """
{
"tag":"tproxy-in",
"port": 1080,
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"streamSettings": {
"sockopt": {
"tproxy":"tproxy"
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
"""
TPL["in_api"] = """
{
"tag": "api",
"port": 10085,
"listen": "127.0.0.1",
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
}
}
"""
def parseVmess(vmesslink):
"""
return:
{
"v": "2",
"ps": "remark",
"add": "4.3.2.1",
"port": "1024",
"id": "xxx",
"aid": "64",
"net": "tcp",
"type": "none",
"host": "",
"path": "",
"tls": ""
}
"""
vmscheme = "vmess://"
if vmesslink.startswith(vmscheme):
bs = vmesslink[len(vmscheme):]
#paddings
blen = len(bs)
if blen % 4 > 0:
bs += "=" * (4 - blen % 4)
vms = base64.b64decode(bs).decode()
return json.loads(vms)
else:
raise Exception("vmess link invalid")
def load_TPL(stype):
s = TPL[stype]
return json.loads(s)
def fill_basic(_c, _v):
_outbound = _c["outbounds"][0]
_vnext = _outbound["settings"]["vnext"][0]
_vnext["address"] = _v["add"]
_vnext["port"] = int(_v["port"])
_vnext["users"][0]["id"] = _v["id"]
_vnext["users"][0]["alterId"] = int(_v["aid"])
_outbound["streamSettings"]["network"] = _v["net"]
if _v["tls"] == "tls":
_outbound["streamSettings"]["security"] = "tls"
return _c
def fill_tcp_http(_c, _v):
tcps = load_TPL("http")
tcps["header"]["type"] = _v["type"]
if _v["host"] != "":
# multiple host
tcps["header"]["request"]["headers"]["Host"] = _v["host"].split(",")
if _v["path"] != "":
tcps["header"]["request"]["path"] = [ _v["path"] ]
_c["outbounds"][0]["streamSettings"]["tcpSettings"] = tcps
return _c
def fill_kcp(_c, _v):
kcps = load_TPL("kcp")
kcps["header"]["type"] = _v["type"]
_c["outbounds"][0]["streamSettings"]["kcpSettings"] = kcps
return _c
def fill_ws(_c, _v):
wss = load_TPL("ws")
wss["path"] = _v["path"]
wss["headers"]["Host"] = _v["host"]
_c["outbounds"][0]["streamSettings"]["wsSettings"] = wss
return _c
def fill_h2(_c, _v):
h2s = load_TPL("h2")
h2s["path"] = _v["path"]
h2s["host"] = [ _v["host"] ]
_c["outbounds"][0]["streamSettings"]["httpSettings"] = h2s
return _c
def fill_quic(_c, _v):
quics = load_TPL("quic")
quics["header"]["type"] = _v["type"]
quics["security"] = _v["host"]
quics["key"] = _v["path"]
_c["outbounds"][0]["streamSettings"]["quicSettings"] = quics
return _c
def vmess2client(_t, _v):
_c = fill_basic(_t, _v)
_net = _v["net"]
_type = _v["type"]
if _net == "kcp":
return fill_kcp(_c, _v)
elif _net == "ws":
return fill_ws(_c, _v)
elif _net == "h2":
return fill_h2(_c, _v)
elif _net == "quic":
return fill_quic(_c, _v)
elif _net == "tcp":
if _type == "http":
return fill_tcp_http(_c, _v)
return _c
else:
pprint.pprint(_v)
raise Exception("this link seem invalid to the script, please report to dev.")
def parseMultiple(lines):
def genPath(ps, rand=False):
# add random in case list "ps" share common names
curdir = os.environ.get("PWD", '/tmp/')
rnd = "-{}".format(random.randrange(100)) if rand else ""
name = "{}{}.json".format(vc["ps"], rnd)
return os.path.join(curdir, name)
for line in lines:
vc = parseVmess(line.strip())
if int(vc["v"]) != 2:
print("Version mismatched, skiped. This script only supports version 2.")
continue
cc = vmess2client(load_TPL("CLIENT"), vc)
cc = fillInbounds(cc)
jsonpath = genPath(vc["ps"])
while os.path.exists(jsonpath):
jsonpath = genPath(vc["ps"], True)
print("Wrote: " + jsonpath)
with open(jsonpath, 'w') as f:
jsonDump(cc, f)
def jsonDump(obj, fobj):
if option.outbound:
json.dump(obj["outbounds"][0], fobj, indent=4)
else:
json.dump(obj, fobj, indent=4)
def fillInbounds(_c):
_ins = option.inbounds.split(",")
for _in in _ins:
_proto, _port = _in.split(":", 2)
_tplKey = "in_"+_proto
if _tplKey in TPL:
_inobj = load_TPL(_tplKey)
_inobj["port"] = int(_port)
_c["inbounds"].append(_inobj)
if _proto == "dns":
_c["dns"] = load_TPL("conf_dns")
_c["routing"]["rules"].insert(0, {
"type": "field",
"inboundTag": ["dns-in"],
"outboundTag": "dns-out"
})
_c["outbounds"].append({
"protocol": "dns",
"tag": "dns-out"
})
elif _proto == "api":
_c["api"] = {
"tag": "api",
"services": [ "HandlerService", "LoggerService", "StatsService" ]
}
_c["stats"] = {}
_c["policy"] = {
"levels": { "0": { "statsUserUplink": True, "statsUserDownlink": True }},
"system": { "statsInboundUplink": True, "statsInboundDownlink": True }
}
_c["routing"]["rules"].insert(0, {
"type": "field",
"inboundTag": ["api"],
"outboundTag": "api"
})
elif _proto == "mt":
_inobj["settings"]["users"][0]["secret"] = \
option.secret if option.secret != "" else hashlib.md5(str(random.random()).encode()).hexdigest()
_c["outbounds"].append(load_TPL("out_mt"))
_c["routing"]["rules"].insert(0, {
"type": "field",
"inboundTag": ["mt-in"],
"outboundTag": "mt-out"
})
else:
print("Error Inbound: " + _in)
return _c
def read_subscribe(sub_url):
print("Reading from subscribe ...")
socket.setdefaulttimeout(10)
with urllib.request.urlopen(sub_url) as response:
_subs = response.read()
return base64.b64decode(_subs).decode().split("\n")
def select_multiple(lines):
vmesses = []
for _v in lines:
if _v.startswith("vmess://"):
_vinfo = parseVmess(_v)
vmesses.append({ "ps": "[{ps}] {add}:{port}/{net}".format(**_vinfo), "vm": _v })
print("Found {} items.".format(len(vmesses)))
for i, item in enumerate(vmesses):
print("[{}] - {}".format(i+1, item["ps"]))
print()
if not sys.stdin.isatty() and os.path.exists('/dev/tty'):
sys.stdin.close()
sys.stdin = open('/dev/tty', 'r')
if sys.stdin.isatty():
sel = input("Choose >>> ")
idx = int(sel) - 1
elif int(option.select) > -1:
idx = int(option.select) - 1
else:
raise Exception("Current session cant open a tty to select. Specify the index to --select argument.")
item = vmesses[idx]["vm"]
cc = vmess2client(load_TPL("CLIENT"), parseVmess(item))
cc = fillInbounds(cc)
jsonDump(cc, option.output)
def main(argv):
sys.argv = ["vmess2json.py"]
for i in argv.split():
sys.argv.append(i)
parser = argparse.ArgumentParser(description="vmess2json convert vmess link to client json config.")
parser.add_argument('-m', '--multiple',
action="store_true",
default=False,
help="read multiple lines from stdin, "
"each write to a json file named by remark, saving in current dir (PWD).")
parser.add_argument('-s', '--select',
action="store",
const="-1",
nargs='?',
help="use together with -m/--multiple or --subscribe. Select one of the vmess link from inputs. Argument is the index(1,2,3...).")
parser.add_argument('-o', '--output',
type=argparse.FileType('w'),
default=sys.stdout,
help="write output to file. default to stdout")
parser.add_argument('--outbound',
action="store_true",
default=False,
help="only output as an outbound object.")
parser.add_argument('--inbounds',
action="store",
default="socks:1080,http:8123",
help="inbounds usage, default: \"socks:1080,http:8123\". Available proto: socks,http,dns,mt,tproxy")
parser.add_argument('--secret',
action="store",
default="",
help="mtproto secret code. if unsepecified, a random one will be generated.")
parser.add_argument('--subscribe',
action="store",
default="",
help="read from a subscribe url, output a menu to choose from.")
parser.add_argument('vmess',
nargs='?',
help="A vmess:// link. If absent, reads a line from stdin.")
global option
option = parser.parse_args()
if option.subscribe != "":
if option.select != "":
select_multiple(read_subscribe(option.subscribe))
elif option.multiple and not option.select:
parseMultiple(read_subscribe(option.subscribe))
sys.exit(0)
if option.multiple and option.select != "":
select_multiple(sys.stdin.readlines())
elif option.multiple and option.select == "":
parseMultiple(sys.stdin.readlines())
else:
if option.vmess is None and sys.stdin.isatty():
parser.print_help()
sys.exit(1)
elif option.vmess is None:
vmess = sys.stdin.readline()
else:
vmess = option.vmess
vc = parseVmess(vmess.strip())
if int(vc["v"]) != 2:
print("ERROR: Vmess link version mismatch. This script only supports version 2.")
sys.exit(1)
cc = vmess2client(load_TPL("CLIENT"), vc)
cc = fillInbounds(cc)
jsonDump(cc, option.output)