mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 02:40:20 +08:00
[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:
parent
1c194f7481
commit
6f9df68e11
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 交互部分。
|
||||||
|
59
Qv2ray.pro
59
Qv2ray.pro
@ -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 ?")
|
|
||||||
}
|
|
||||||
|
21
README.md
21
README.md
@ -1,9 +1,8 @@
|
|||||||
[](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 [](http://hits.dwyl.io/lhy0403/Qv2ray)
|
|
||||||
|
|
||||||
TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多数功能尚未齐全。
|
TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多数功能尚未齐全。
|
||||||
|
|
||||||
[](https://lgtm.com/projects/g/lhy0403/Qv2ray/context:cpp) [](https://lgtm.com/projects/g/lhy0403/Qv2ray/alerts/)
|
[](https://app.codacy.com/app/lhy0403/Qv2ray?utm_source=github.com&utm_medium=referral&utm_content=lhy0403/Qv2ray&utm_campaign=Badge_Grade_Dashboard)[](http://hits.dwyl.io/lhy0403/Qv2ray)[](https://lgtm.com/projects/g/lhy0403/Qv2ray/context:cpp) [](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` 切换到开发分支
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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....
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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%\..\
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
e21dde78d551a377ba813f6079ed610deaafadd8
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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)
|
|
Loading…
Reference in New Issue
Block a user