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
|
||||
|
||||
build_script:
|
||||
- mkdir python37 && xcopy C:\Python37 python37 /E /H /Q
|
||||
- tools\FixPythonWithMinGW.bat
|
||||
- lrelease.exe Qv2ray.pro
|
||||
- mkdir build && cd build
|
||||
- qmake ..\Qv2ray.pro
|
||||
|
@ -13,7 +13,7 @@ before_install:
|
||||
- if [ "$BADGE" = "linux" ]; then sudo apt-get update; fi
|
||||
|
||||
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 link --force qt; fi
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
# 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-01**: 休息了几天,主要是去关注别的项目了。现在开始重构 v2ray 交互部分。
|
||||
|
59
Qv2ray.pro
59
Qv2ray.pro
@ -59,7 +59,8 @@ TRANSLATIONS += \
|
||||
|
||||
RC_ICONS += ./icons/Qv2ray.ico
|
||||
|
||||
INCLUDEPATH += 3rdparty/\
|
||||
INCLUDEPATH += \
|
||||
3rdparty/\
|
||||
3rdparty/jsoncons/include
|
||||
|
||||
|
||||
@ -67,59 +68,3 @@ INCLUDEPATH += 3rdparty/\
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
WITH_PYTHON = no
|
||||
PYTHONVER = null
|
||||
|
||||
unix:!macx {
|
||||
# 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 ?")
|
||||
}
|
||||
|
19
README.md
19
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 [](http://hits.dwyl.io/lhy0403/Qv2ray)
|
||||
# Qv2ray
|
||||
|
||||
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) |
|
||||
| ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
@ -16,7 +15,7 @@ TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多
|
||||
|
||||
|
||||
|
||||
# 鸣谢
|
||||
## 鸣谢
|
||||
|
||||
[@aliyuchang33](https://github.com/aliyuchang33) - 项目原作者/发起人,感谢他使用 Qt 作为基础框架。
|
||||
|
||||
@ -24,18 +23,16 @@ TODO: 使用 Qt 的跨平台 v2ray 客户端,目前仍处于早期开发,多
|
||||
|
||||
|
||||
|
||||
## 项目依赖包
|
||||
## 项目依赖
|
||||
### Linux
|
||||
- Qt >= 5.12
|
||||
- Python (3.5 | 3.6 | 3.7)
|
||||
- `Qt >= 5.12`
|
||||
- `gcc >=8` (需要 c++11 支持)
|
||||
### Windows
|
||||
- Python 3.7
|
||||
- Qt Creator (MinGW & Qt5)
|
||||
Qt Creator (Qt5)
|
||||
|
||||
## MacOS
|
||||
### MacOS
|
||||
|
||||
- Qt >= 5.12
|
||||
- Python (3.6 | 3.7) 使用 [`brew install python3`] 解决依赖问题
|
||||
|
||||
## 编译
|
||||
- 如果想测试当前的开发分支,请使用 `git checkout dev` 切换到开发分支
|
||||
|
@ -26,6 +26,19 @@ namespace Qv2ray
|
||||
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)
|
||||
{
|
||||
using namespace Qv2ray::QvConfigModels;
|
||||
|
@ -27,16 +27,13 @@ namespace Qv2ray
|
||||
/// Get file list in a Dir
|
||||
QStringList getAllFilesList(QDir *dir);
|
||||
bool hasFile(QDir *dir, QString fileName);
|
||||
|
||||
QString base64_encode(QString string);
|
||||
QString base64_decode(QString string);
|
||||
template <typename TYPE>
|
||||
QString StructToJSON(const TYPE &t)
|
||||
{
|
||||
string s;
|
||||
#if USE_TODO_FEATURES
|
||||
encode_json<TYPE>(t, s, indenting::indent);
|
||||
#else
|
||||
s = X::tojson(t, "", 4, ' ');
|
||||
#endif
|
||||
cout << s << endl;
|
||||
return QString::fromStdString(s);
|
||||
}
|
||||
@ -45,11 +42,7 @@ namespace Qv2ray
|
||||
TYPE StructFromJSON(const string &str)
|
||||
{
|
||||
TYPE v;
|
||||
#if USE_TODO_FEATURES
|
||||
v = decode_json<TYPE>(str);
|
||||
#else
|
||||
X::loadjson(str, v, false);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,20 @@ namespace Qv2ray
|
||||
{
|
||||
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.
|
||||
struct XOutBoundsType {
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// Show MainWindow
|
||||
Ui::MainWindow w;
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return _qApp.exec();
|
||||
}
|
||||
|
@ -5,11 +5,10 @@
|
||||
#include <QIntValidator>
|
||||
#include <iostream>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
|
||||
ConnectionEditWindow::ConnectionEditWindow(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui_WConnectionEdit)
|
||||
, ui(new Ui::ConnectionEditWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->portLineEdit->setValidator(new QIntValidator());
|
||||
@ -31,4 +30,7 @@ namespace Ui
|
||||
//this->security = ui->securityCombo->currentText();
|
||||
//this->isCustom = 0;
|
||||
//}
|
||||
|
||||
void ConnectionEditWindow::on_buttonBox_accepted()
|
||||
{
|
||||
}
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ConnectionEditWindow;
|
||||
}
|
||||
|
||||
class ConnectionEditWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -14,8 +17,10 @@ namespace Ui
|
||||
explicit ConnectionEditWindow(QWidget *parent = nullptr);
|
||||
~ConnectionEditWindow();
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
private:
|
||||
Ui_WConnectionEdit *ui;
|
||||
Ui::ConnectionEditWindow *ui;
|
||||
};
|
||||
}
|
||||
#endif // CONFEDIT_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WConnectionEdit</class>
|
||||
<widget class="QDialog" name="WConnectionEdit">
|
||||
<class>ConnectionEditWindow</class>
|
||||
<widget class="QDialog" name="ConnectionEditWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -864,7 +864,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>WConnectionEdit</receiver>
|
||||
<receiver>ConnectionEditWindow</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@ -880,7 +880,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>WConnectionEdit</receiver>
|
||||
<receiver>ConnectionEditWindow</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -5,23 +5,17 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#pragma push_macro("slots")
|
||||
#undef slots
|
||||
#include "Python.h"
|
||||
#pragma pop_macro("slots")
|
||||
|
||||
#include "HUtils.hpp"
|
||||
#include "vinteract.hpp"
|
||||
#include "w_ConnectionEditWindow.h"
|
||||
#include "w_ImportConfig.h"
|
||||
|
||||
|
||||
using namespace Qv2ray;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
ImportConfig::ImportConfig(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui_WImportConfig)
|
||||
, ui(new Ui::ImportConfigWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(this, SIGNAL(updateConfTable()), parentWidget(), SLOT(updateConfTable()));
|
||||
@ -94,36 +88,36 @@ namespace Ui
|
||||
QString path = ui->fileLineTxt->text();
|
||||
bool isValid = v2Instance::checkConfigFile(path);
|
||||
|
||||
if (isValid) {
|
||||
savefromFile(path, alias);
|
||||
if (!isValid) {
|
||||
// Invalid file alert.
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!vmess.toLower().startsWith("vmess://")) {
|
||||
Utils::showWarnMessageBox(this, tr("#VMessDecodeError"), tr("#NotValidVMessProtocolString"));
|
||||
}
|
||||
|
||||
QFile::remove("config.json.tmp");
|
||||
} else {
|
||||
Utils::showWarnMessageBox(this, tr("ImportConfig"), tr("CannotGenerateConfig"));
|
||||
qDebug() << "ImportConfig::CannotGenerateConfig";
|
||||
}
|
||||
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 {
|
||||
// Utils::showWarnMessageBox(this, tr("ImportConfig"), tr("CannotGenerateConfig"));
|
||||
// qDebug() << "ImportConfig::CannotGenerateConfig";
|
||||
//}
|
||||
}
|
||||
|
||||
if (ui->useCurrentSettingRidBtn->isChecked()) {
|
||||
@ -132,4 +126,3 @@ namespace Ui
|
||||
// TODO: Override Inbound....
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ImportConfigWindow;
|
||||
}
|
||||
|
||||
class ImportConfig : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -22,8 +25,7 @@ namespace Ui
|
||||
void updateConfTable();
|
||||
|
||||
private:
|
||||
Ui_WImportConfig *ui;
|
||||
Ui::ImportConfigWindow *ui;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // IMPORTCONF_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WImportConfig</class>
|
||||
<widget class="QDialog" name="WImportConfig">
|
||||
<class>ImportConfigWindow</class>
|
||||
<widget class="QDialog" name="ImportConfigWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -162,7 +162,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>WImportConfig</receiver>
|
||||
<receiver>ImportConfigWindow</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@ -178,7 +178,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>WImportConfig</receiver>
|
||||
<receiver>ImportConfigWindow</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include "w_MainWindow.h"
|
||||
#include "w_PrefrencesWindow.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
void MainWindow::CreateTrayIcon()
|
||||
{
|
||||
hTray = new QSystemTrayIcon();
|
||||
@ -54,7 +52,7 @@ namespace Ui
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui_WMain)
|
||||
, ui(new Ui::MainWindow)
|
||||
{
|
||||
this->setWindowIcon(QIcon(":/icons/Qv2ray.ico"));
|
||||
ui->setupUi(this);
|
||||
@ -276,4 +274,3 @@ namespace Ui
|
||||
auto confedit = new ConnectionEditWindow();
|
||||
confedit->show();
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,11 @@
|
||||
#include "vinteract.hpp"
|
||||
#include "V2ConfigObjects.hpp"
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -50,12 +52,10 @@ namespace Ui
|
||||
void on_pushButton_clicked();
|
||||
|
||||
private:
|
||||
Ui_WMain *ui;
|
||||
Ui::MainWindow *ui;
|
||||
void closeEvent(QCloseEvent *);
|
||||
void createTrayAction();
|
||||
void CreateTrayIcon();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WMain</class>
|
||||
<widget class="QMainWindow" name="WMain">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -18,11 +18,9 @@
|
||||
|
||||
using namespace Qv2ray::Utils;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
|
||||
CurrentConfig(),
|
||||
ui(new Ui_WPrefrences)
|
||||
ui(new Ui::PrefrencesWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
CurrentConfig = GetGlobalConfig();
|
||||
@ -114,4 +112,3 @@ namespace Ui
|
||||
showWarnMessageBox(this, tr("Prefrences"), tr("RunAsRootNotOnWindows"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PrefrencesWindow;
|
||||
}
|
||||
|
||||
class PrefrencesWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -27,7 +30,6 @@ namespace Ui
|
||||
|
||||
private:
|
||||
Qv2ray::QvConfigModels::Qv2Config CurrentConfig;
|
||||
Ui_WPrefrences *ui;
|
||||
Ui::PrefrencesWindow *ui;
|
||||
};
|
||||
}
|
||||
#endif // HVCONF_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WPrefrences</class>
|
||||
<widget class="QDialog" name="WPrefrences">
|
||||
<class>PrefrencesWindow</class>
|
||||
<widget class="QDialog" name="PrefrencesWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -345,7 +345,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>WPrefrences</receiver>
|
||||
<receiver>PrefrencesWindow</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@ -361,7 +361,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>WPrefrences</receiver>
|
||||
<receiver>PrefrencesWindow</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<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