From c4840c5b6918010d2085a4b0d84b66c3beb3d449 Mon Sep 17 00:00:00 2001 From: Carter Zhang Date: Tue, 25 Feb 2025 18:24:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=A0=E5=A6=88=E6=AD=BB=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdparty/QHotkey | 1 - 3rdparty/QThreadCreateThread.hpp | 39 - 3rdparty/QtExtKeySequenceEdit.cpp | 22 - 3rdparty/QtExtKeySequenceEdit.h | 11 - 3rdparty/RunGuard.hpp | 98 - 3rdparty/VT100Parser.hpp | 22 - 3rdparty/WinCommander.cpp | 105 - 3rdparty/WinCommander.hpp | 41 - 3rdparty/ZxingQtReader.hpp | 454 ---- 3rdparty/base64.cpp | 92 - 3rdparty/base64.h | 47 - 3rdparty/fix_old_qt.h | 11 - 3rdparty/qrcodegen.cpp | 830 -------- 3rdparty/qrcodegen.hpp | 549 ----- 3rdparty/qscopeguard.h | 96 - .../components/proxy/QvProxyConfigurator.cpp | 440 ---- .../components/proxy/QvProxyConfigurator.hpp | 12 - .../qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp | 157 -- .../qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp | 85 - .../v2/ui/widgets/common/QJsonModel.cpp | 336 --- .../v2/ui/widgets/common/QJsonModel.hpp | 92 - .../v2/ui/widgets/editors/w_JsonEditor.cpp | 97 - .../v2/ui/widgets/editors/w_JsonEditor.hpp | 30 - .../v2/ui/widgets/editors/w_JsonEditor.ui | 172 -- .../GeositeReader/GeositeReader.cpp | 42 - .../GeositeReader/GeositeReader.hpp | 7 - .../v3/components/GeositeReader/picoproto.cpp | 551 ----- .../v3/components/GeositeReader/picoproto.hpp | 208 -- 3rdparty/qv2ray/wrapper.hpp | 41 - CMakeLists.txt | 302 --- LICENSE | 674 ------ README.md | 119 -- cmake/linux/linux.cmake | 2 - cmake/myproto.cmake | 14 - cmake/nkr.cmake | 12 - cmake/print.cmake | 43 - cmake/windows/VersionInfo.in | 82 - cmake/windows/VersionResource.rc | 52 - cmake/windows/generate_product_version.cmake | 107 - cmake/windows/windows.cmake | 24 - db/ConfigBuilder.cpp | 818 -------- db/ConfigBuilder.hpp | 59 - db/Database.cpp | 394 ---- db/Database.hpp | 62 - db/Group.hpp | 31 - db/ProfileFilter.cpp | 75 - db/ProfileFilter.hpp | 35 - db/ProxyEntity.hpp | 76 - db/traffic/TrafficData.hpp | 40 - db/traffic/TrafficLooper.cpp | 135 -- db/traffic/TrafficLooper.hpp | 32 - docs/Build_Core.md | 24 - docs/Build_Linux.md | 76 - docs/Build_Windows.md | 64 - docs/RunFlags.md | 5 - docs/Run_Linux.md | 81 - docs/readme.md | 6 - fmt/AbstractBean.cpp | 79 - fmt/AbstractBean.hpp | 69 - fmt/Bean2CoreObj_box.cpp | 237 --- fmt/Bean2External.cpp | 265 --- fmt/Bean2Link.cpp | 225 -- fmt/ChainBean.hpp | 18 - fmt/CustomBean.hpp | 52 - fmt/Link2Bean.cpp | 297 --- fmt/NaiveBean.hpp | 41 - fmt/Preset.hpp | 18 - fmt/QUICBean.hpp | 113 - fmt/ShadowSocksBean.hpp | 32 - fmt/SocksHttpBean.hpp | 35 - fmt/TrojanVLESSBean.hpp | 33 - fmt/V2RayStreamSettings.hpp | 65 - fmt/VMessBean.hpp | 30 - fmt/includes.h | 10 - go/.gitignore | 8 - go/cmd/nekobox_core/core_box.go | 50 - go/cmd/nekobox_core/go.mod | 105 - go/cmd/nekobox_core/go.sum | 298 --- go/cmd/nekobox_core/grpc_box.go | 152 -- go/cmd/nekobox_core/main.go | 28 - go/cmd/updater/.gitignore | 2 - go/cmd/updater/go.mod | 11 - go/cmd/updater/go.sum | 19 - go/cmd/updater/launcher.go | 12 - go/cmd/updater/launcher_linux.go | 41 - go/cmd/updater/main.go | 62 - go/cmd/updater/msgbox.go | 7 - go/cmd/updater/msgbox_windows.go | 26 - go/cmd/updater/updater.go | 129 -- go/grpc_server/auth/auth.go | 54 - go/grpc_server/fulltest.go | 180 -- go/grpc_server/gen/libcore.pb.go | 1146 ---------- go/grpc_server/gen/libcore.proto | 94 - go/grpc_server/gen/libcore_grpc.pb.go | 321 --- go/grpc_server/gen/update_proto.sh | 3 - go/grpc_server/go.mod | 21 - go/grpc_server/go.sum | 178 -- go/grpc_server/grpc.go | 103 - go/grpc_server/update.go | 116 -- libs/.gitignore | 2 - libs/build_deps_all.sh | 75 - libs/build_go.sh | 27 - libs/build_public_res.sh | 16 - libs/deploy_linux64.sh | 51 - libs/deploy_windows64.sh | 26 - libs/download_qtsdk_win.sh | 10 - libs/env_deploy.sh | 4 - libs/env_qtsdk.sh | 8 - libs/format_cpp.sh | 2 - libs/get_source.sh | 41 - libs/get_source_env.sh | 3 - libs/package_appimage.sh | 38 - libs/package_debian.sh | 45 - main/Const.hpp | 25 - main/GuiUtils.hpp | 93 - main/HTTPRequestHelper.cpp | 85 - main/HTTPRequestHelper.hpp | 31 - main/NekoGui.cpp | 462 ----- main/NekoGui.hpp | 21 - main/NekoGui_ConfigItem.hpp | 70 - main/NekoGui_DataStore.hpp | 182 -- main/NekoGui_Utils.cpp | 261 --- main/NekoGui_Utils.hpp | 176 -- main/main.cpp | 242 --- nekoray_version.txt | 1 - res/dashboard-notice.html | 17 - res/icon/dialog-question.svg | 29 - res/icon/internet-web-browser.svg | 1 - res/icon/material/cancel.svg | 1 - res/icon/material/delete.svg | 1 - res/icon/material/history.svg | 1 - res/icon/material/lock-open-outline.svg | 1 - res/icon/material/lock-outline.svg | 1 - res/icon/material/swap-horizontal.svg | 1 - res/icon/material/swap-vertical.svg | 1 - res/icon/network-server.svg | 35 - res/icon/preferences.svg | 141 -- res/icon/system-run.svg | 190 -- res/icon/system-software-update.svg | 32 - res/neko.css | 3 - res/neko.qrc | 24 - res/nekobox.ico | Bin 67646 -> 0 bytes res/public/nekobox.png | Bin 64105 -> 0 bytes res/public/qtbase_zh_CN.qm | Bin 133919 -> 0 bytes res/theme/feiyangqingyun/qss.qrc | 76 - res/theme/feiyangqingyun/qss/blacksoft.css | 679 ------ .../qss/blacksoft/add_bottom.png | Bin 335 -> 0 bytes .../feiyangqingyun/qss/blacksoft/add_left.png | Bin 377 -> 0 bytes .../qss/blacksoft/add_right.png | Bin 352 -> 0 bytes .../feiyangqingyun/qss/blacksoft/add_top.png | Bin 348 -> 0 bytes .../qss/blacksoft/arrow_bottom.png | Bin 335 -> 0 bytes .../qss/blacksoft/arrow_left.png | Bin 370 -> 0 bytes .../qss/blacksoft/arrow_right.png | Bin 354 -> 0 bytes .../qss/blacksoft/arrow_top.png | Bin 349 -> 0 bytes .../qss/blacksoft/branch_close.png | Bin 257 -> 0 bytes .../qss/blacksoft/branch_open.png | Bin 445 -> 0 bytes .../qss/blacksoft/calendar_nextmonth.png | Bin 623 -> 0 bytes .../qss/blacksoft/calendar_prevmonth.png | Bin 667 -> 0 bytes .../qss/blacksoft/checkbox_checked.png | Bin 593 -> 0 bytes .../blacksoft/checkbox_checked_disable.png | Bin 656 -> 0 bytes .../qss/blacksoft/checkbox_parcial.png | Bin 326 -> 0 bytes .../blacksoft/checkbox_parcial_disable.png | Bin 333 -> 0 bytes .../qss/blacksoft/checkbox_unchecked.png | Bin 572 -> 0 bytes .../blacksoft/checkbox_unchecked_disable.png | Bin 624 -> 0 bytes .../qss/blacksoft/menu_checked.png | Bin 501 -> 0 bytes .../qss/blacksoft/radiobutton_checked.png | Bin 1421 -> 0 bytes .../blacksoft/radiobutton_checked_disable.png | Bin 1614 -> 0 bytes .../qss/blacksoft/radiobutton_unchecked.png | Bin 1229 -> 0 bytes .../radiobutton_unchecked_disable.png | Bin 1365 -> 0 bytes res/theme/feiyangqingyun/qss/flatgray.css | 679 ------ .../qss/flatgray/add_bottom.png | Bin 336 -> 0 bytes .../feiyangqingyun/qss/flatgray/add_left.png | Bin 370 -> 0 bytes .../feiyangqingyun/qss/flatgray/add_right.png | Bin 358 -> 0 bytes .../feiyangqingyun/qss/flatgray/add_top.png | Bin 332 -> 0 bytes .../qss/flatgray/arrow_bottom.png | Bin 337 -> 0 bytes .../qss/flatgray/arrow_left.png | Bin 376 -> 0 bytes .../qss/flatgray/arrow_right.png | Bin 360 -> 0 bytes .../feiyangqingyun/qss/flatgray/arrow_top.png | Bin 361 -> 0 bytes .../qss/flatgray/branch_close.png | Bin 263 -> 0 bytes .../qss/flatgray/branch_open.png | Bin 444 -> 0 bytes .../qss/flatgray/calendar_nextmonth.png | Bin 655 -> 0 bytes .../qss/flatgray/calendar_prevmonth.png | Bin 740 -> 0 bytes .../qss/flatgray/checkbox_checked.png | Bin 616 -> 0 bytes .../qss/flatgray/checkbox_checked_disable.png | Bin 639 -> 0 bytes .../qss/flatgray/checkbox_parcial.png | Bin 341 -> 0 bytes .../qss/flatgray/checkbox_parcial_disable.png | Bin 331 -> 0 bytes .../qss/flatgray/checkbox_unchecked.png | Bin 612 -> 0 bytes .../flatgray/checkbox_unchecked_disable.png | Bin 646 -> 0 bytes .../qss/flatgray/menu_checked.png | Bin 542 -> 0 bytes .../qss/flatgray/radiobutton_checked.png | Bin 1513 -> 0 bytes .../flatgray/radiobutton_checked_disable.png | Bin 1628 -> 0 bytes .../qss/flatgray/radiobutton_unchecked.png | Bin 1294 -> 0 bytes .../radiobutton_unchecked_disable.png | Bin 1374 -> 0 bytes res/theme/feiyangqingyun/qss/lightblue.css | 679 ------ .../qss/lightblue/add_bottom.png | Bin 348 -> 0 bytes .../feiyangqingyun/qss/lightblue/add_left.png | Bin 383 -> 0 bytes .../qss/lightblue/add_right.png | Bin 364 -> 0 bytes .../feiyangqingyun/qss/lightblue/add_top.png | Bin 367 -> 0 bytes .../qss/lightblue/arrow_bottom.png | Bin 348 -> 0 bytes .../qss/lightblue/arrow_left.png | Bin 383 -> 0 bytes .../qss/lightblue/arrow_right.png | Bin 364 -> 0 bytes .../qss/lightblue/arrow_top.png | Bin 361 -> 0 bytes .../qss/lightblue/branch_close.png | Bin 269 -> 0 bytes .../qss/lightblue/branch_open.png | Bin 462 -> 0 bytes .../qss/lightblue/calendar_nextmonth.png | Bin 670 -> 0 bytes .../qss/lightblue/calendar_prevmonth.png | Bin 758 -> 0 bytes .../qss/lightblue/checkbox_checked.png | Bin 636 -> 0 bytes .../lightblue/checkbox_checked_disable.png | Bin 664 -> 0 bytes .../qss/lightblue/checkbox_parcial.png | Bin 351 -> 0 bytes .../lightblue/checkbox_parcial_disable.png | Bin 344 -> 0 bytes .../qss/lightblue/checkbox_unchecked.png | Bin 613 -> 0 bytes .../lightblue/checkbox_unchecked_disable.png | Bin 648 -> 0 bytes .../qss/lightblue/menu_checked.png | Bin 554 -> 0 bytes .../qss/lightblue/radiobutton_checked.png | Bin 1516 -> 0 bytes .../lightblue/radiobutton_checked_disable.png | Bin 1645 -> 0 bytes .../qss/lightblue/radiobutton_unchecked.png | Bin 1298 -> 0 bytes .../radiobutton_unchecked_disable.png | Bin 1385 -> 0 bytes res/vpn/sing-box-vpn.json | 152 -- res/vpn/vpn-run-root.sh | 34 - rpc/gRPC.cpp | 299 --- rpc/gRPC.h | 43 - sub/GroupUpdater.cpp | 665 ------ sub/GroupUpdater.hpp | 34 - sys/AutoRun.cpp | 231 --- sys/AutoRun.hpp | 5 - sys/ExternalProcess.cpp | 158 -- sys/ExternalProcess.hpp | 51 - sys/linux/LinuxCap.cpp | 48 - sys/linux/LinuxCap.h | 11 - sys/windows/MiniDump.cpp | 75 - sys/windows/MiniDump.h | 3 - sys/windows/guihelper.cpp | 24 - sys/windows/guihelper.h | 7 - test/test-qt512-sdk-build.sh | 7 - test/test-qt6-build.sh | 5 - translations/fa_IR.ts | 1651 --------------- translations/ru_RU.ts | 1664 --------------- translations/translations.qrc | 7 - translations/zh_CN.ts | 1664 --------------- ui/GroupSort.hpp | 20 - ui/Icon.cpp | 53 - ui/Icon.hpp | 18 - ui/ThemeManager.cpp | 86 - ui/ThemeManager.hpp | 11 - ui/dialog_basic_settings.cpp | 416 ---- ui/dialog_basic_settings.h | 43 - ui/dialog_basic_settings.ui | 780 ------- ui/dialog_hotkey.cpp | 25 - ui/dialog_hotkey.h | 22 - ui/dialog_hotkey.ui | 118 -- ui/dialog_manage_groups.cpp | 58 - ui/dialog_manage_groups.h | 32 - ui/dialog_manage_groups.ui | 55 - ui/dialog_manage_routes.cpp | 251 --- ui/dialog_manage_routes.h | 59 - ui/dialog_manage_routes.ui | 687 ------ ui/dialog_vpn_settings.cpp | 81 - ui/dialog_vpn_settings.h | 30 - ui/dialog_vpn_settings.ui | 262 --- ui/edit/dialog_edit_group.cpp | 104 - ui/edit/dialog_edit_group.h | 36 - ui/edit/dialog_edit_group.ui | 230 -- ui/edit/dialog_edit_profile.cpp | 523 ----- ui/edit/dialog_edit_profile.h | 66 - ui/edit/dialog_edit_profile.ui | 705 ------- ui/edit/edit_chain.cpp | 78 - ui/edit/edit_chain.h | 37 - ui/edit/edit_chain.ui | 57 - ui/edit/edit_custom.cpp | 153 -- ui/edit/edit_custom.h | 35 - ui/edit/edit_custom.ui | 209 -- ui/edit/edit_naive.cpp | 68 - ui/edit/edit_naive.h | 40 - ui/edit/edit_naive.ui | 131 -- ui/edit/edit_quic.cpp | 135 -- ui/edit/edit_quic.h | 39 - ui/edit/edit_quic.ui | 372 ---- ui/edit/edit_shadowsocks.cpp | 43 - ui/edit/edit_shadowsocks.h | 28 - ui/edit/edit_shadowsocks.ui | 126 -- ui/edit/edit_socks_http.cpp | 48 - ui/edit/edit_socks_http.h | 25 - ui/edit/edit_socks_http.ui | 67 - ui/edit/edit_trojan_vless.cpp | 35 - ui/edit/edit_trojan_vless.h | 27 - ui/edit/edit_trojan_vless.ui | 54 - ui/edit/edit_vmess.cpp | 34 - ui/edit/edit_vmess.h | 27 - ui/edit/edit_vmess.ui | 124 -- ui/edit/profile_editor.h | 24 - ui/mainwindow.cpp | 1844 ----------------- ui/mainwindow.h | 206 -- ui/mainwindow.ui | 932 --------- ui/mainwindow_grpc.cpp | 554 ----- ui/mainwindow_interface.h | 5 - ui/widget/FloatCheckBox.h | 33 - ui/widget/GroupItem.cpp | 126 -- ui/widget/GroupItem.h | 43 - ui/widget/GroupItem.ui | 128 -- ui/widget/MessageBoxTimer.h | 33 - ui/widget/MyLineEdit.h | 15 - ui/widget/MyTableWidget.h | 110 - ui/widget/ProxyItem.cpp | 48 - ui/widget/ProxyItem.h | 38 - ui/widget/ProxyItem.ui | 146 -- 305 files changed, 35392 deletions(-) delete mode 160000 3rdparty/QHotkey delete mode 100644 3rdparty/QThreadCreateThread.hpp delete mode 100644 3rdparty/QtExtKeySequenceEdit.cpp delete mode 100644 3rdparty/QtExtKeySequenceEdit.h delete mode 100644 3rdparty/RunGuard.hpp delete mode 100644 3rdparty/VT100Parser.hpp delete mode 100644 3rdparty/WinCommander.cpp delete mode 100644 3rdparty/WinCommander.hpp delete mode 100644 3rdparty/ZxingQtReader.hpp delete mode 100644 3rdparty/base64.cpp delete mode 100644 3rdparty/base64.h delete mode 100644 3rdparty/fix_old_qt.h delete mode 100644 3rdparty/qrcodegen.cpp delete mode 100644 3rdparty/qrcodegen.hpp delete mode 100644 3rdparty/qscopeguard.h delete mode 100644 3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp delete mode 100644 3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp delete mode 100644 3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp delete mode 100644 3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp delete mode 100644 3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp delete mode 100644 3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp delete mode 100644 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp delete mode 100644 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp delete mode 100644 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui delete mode 100644 3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp delete mode 100644 3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp delete mode 100644 3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp delete mode 100644 3rdparty/qv2ray/v3/components/GeositeReader/picoproto.hpp delete mode 100644 3rdparty/qv2ray/wrapper.hpp delete mode 100644 CMakeLists.txt delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 cmake/linux/linux.cmake delete mode 100644 cmake/myproto.cmake delete mode 100644 cmake/nkr.cmake delete mode 100644 cmake/print.cmake delete mode 100644 cmake/windows/VersionInfo.in delete mode 100644 cmake/windows/VersionResource.rc delete mode 100644 cmake/windows/generate_product_version.cmake delete mode 100644 cmake/windows/windows.cmake delete mode 100644 db/ConfigBuilder.cpp delete mode 100644 db/ConfigBuilder.hpp delete mode 100644 db/Database.cpp delete mode 100644 db/Database.hpp delete mode 100644 db/Group.hpp delete mode 100644 db/ProfileFilter.cpp delete mode 100644 db/ProfileFilter.hpp delete mode 100644 db/ProxyEntity.hpp delete mode 100644 db/traffic/TrafficData.hpp delete mode 100644 db/traffic/TrafficLooper.cpp delete mode 100644 db/traffic/TrafficLooper.hpp delete mode 100644 docs/Build_Core.md delete mode 100644 docs/Build_Linux.md delete mode 100644 docs/Build_Windows.md delete mode 100644 docs/RunFlags.md delete mode 100644 docs/Run_Linux.md delete mode 100644 docs/readme.md delete mode 100644 fmt/AbstractBean.cpp delete mode 100644 fmt/AbstractBean.hpp delete mode 100644 fmt/Bean2CoreObj_box.cpp delete mode 100644 fmt/Bean2External.cpp delete mode 100644 fmt/Bean2Link.cpp delete mode 100644 fmt/ChainBean.hpp delete mode 100644 fmt/CustomBean.hpp delete mode 100644 fmt/Link2Bean.cpp delete mode 100644 fmt/NaiveBean.hpp delete mode 100644 fmt/Preset.hpp delete mode 100644 fmt/QUICBean.hpp delete mode 100644 fmt/ShadowSocksBean.hpp delete mode 100644 fmt/SocksHttpBean.hpp delete mode 100644 fmt/TrojanVLESSBean.hpp delete mode 100644 fmt/V2RayStreamSettings.hpp delete mode 100644 fmt/VMessBean.hpp delete mode 100644 fmt/includes.h delete mode 100644 go/.gitignore delete mode 100644 go/cmd/nekobox_core/core_box.go delete mode 100644 go/cmd/nekobox_core/go.mod delete mode 100644 go/cmd/nekobox_core/go.sum delete mode 100644 go/cmd/nekobox_core/grpc_box.go delete mode 100644 go/cmd/nekobox_core/main.go delete mode 100644 go/cmd/updater/.gitignore delete mode 100644 go/cmd/updater/go.mod delete mode 100644 go/cmd/updater/go.sum delete mode 100644 go/cmd/updater/launcher.go delete mode 100644 go/cmd/updater/launcher_linux.go delete mode 100644 go/cmd/updater/main.go delete mode 100644 go/cmd/updater/msgbox.go delete mode 100644 go/cmd/updater/msgbox_windows.go delete mode 100644 go/cmd/updater/updater.go delete mode 100644 go/grpc_server/auth/auth.go delete mode 100644 go/grpc_server/fulltest.go delete mode 100644 go/grpc_server/gen/libcore.pb.go delete mode 100644 go/grpc_server/gen/libcore.proto delete mode 100644 go/grpc_server/gen/libcore_grpc.pb.go delete mode 100644 go/grpc_server/gen/update_proto.sh delete mode 100644 go/grpc_server/go.mod delete mode 100644 go/grpc_server/go.sum delete mode 100644 go/grpc_server/grpc.go delete mode 100644 go/grpc_server/update.go delete mode 100644 libs/.gitignore delete mode 100755 libs/build_deps_all.sh delete mode 100755 libs/build_go.sh delete mode 100755 libs/build_public_res.sh delete mode 100755 libs/deploy_linux64.sh delete mode 100755 libs/deploy_windows64.sh delete mode 100644 libs/download_qtsdk_win.sh delete mode 100644 libs/env_deploy.sh delete mode 100644 libs/env_qtsdk.sh delete mode 100755 libs/format_cpp.sh delete mode 100755 libs/get_source.sh delete mode 100644 libs/get_source_env.sh delete mode 100644 libs/package_appimage.sh delete mode 100644 libs/package_debian.sh delete mode 100644 main/Const.hpp delete mode 100644 main/GuiUtils.hpp delete mode 100644 main/HTTPRequestHelper.cpp delete mode 100644 main/HTTPRequestHelper.hpp delete mode 100644 main/NekoGui.cpp delete mode 100644 main/NekoGui.hpp delete mode 100644 main/NekoGui_ConfigItem.hpp delete mode 100644 main/NekoGui_DataStore.hpp delete mode 100644 main/NekoGui_Utils.cpp delete mode 100644 main/NekoGui_Utils.hpp delete mode 100644 main/main.cpp delete mode 100644 nekoray_version.txt delete mode 100644 res/dashboard-notice.html delete mode 100644 res/icon/dialog-question.svg delete mode 100644 res/icon/internet-web-browser.svg delete mode 100644 res/icon/material/cancel.svg delete mode 100644 res/icon/material/delete.svg delete mode 100644 res/icon/material/history.svg delete mode 100644 res/icon/material/lock-open-outline.svg delete mode 100644 res/icon/material/lock-outline.svg delete mode 100644 res/icon/material/swap-horizontal.svg delete mode 100644 res/icon/material/swap-vertical.svg delete mode 100644 res/icon/network-server.svg delete mode 100644 res/icon/preferences.svg delete mode 100644 res/icon/system-run.svg delete mode 100644 res/icon/system-software-update.svg delete mode 100644 res/neko.css delete mode 100644 res/neko.qrc delete mode 100644 res/nekobox.ico delete mode 100644 res/public/nekobox.png delete mode 100644 res/public/qtbase_zh_CN.qm delete mode 100644 res/theme/feiyangqingyun/qss.qrc delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft.css delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/add_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/add_left.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/add_right.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/add_top.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/arrow_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/arrow_left.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/arrow_right.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/arrow_top.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/branch_close.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/branch_open.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/calendar_nextmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/calendar_prevmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/menu_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray.css delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/add_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/add_left.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/add_right.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/add_top.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/arrow_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/arrow_left.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/arrow_right.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/arrow_top.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/branch_close.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/branch_open.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/calendar_nextmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/calendar_prevmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/menu_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue.css delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/add_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/add_left.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/add_right.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/add_top.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/arrow_bottom.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/arrow_left.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/arrow_right.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/arrow_top.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/branch_close.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/branch_open.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/calendar_nextmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/calendar_prevmonth.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/menu_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked_disable.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked.png delete mode 100644 res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked_disable.png delete mode 100644 res/vpn/sing-box-vpn.json delete mode 100755 res/vpn/vpn-run-root.sh delete mode 100644 rpc/gRPC.cpp delete mode 100644 rpc/gRPC.h delete mode 100644 sub/GroupUpdater.cpp delete mode 100644 sub/GroupUpdater.hpp delete mode 100644 sys/AutoRun.cpp delete mode 100644 sys/AutoRun.hpp delete mode 100644 sys/ExternalProcess.cpp delete mode 100644 sys/ExternalProcess.hpp delete mode 100644 sys/linux/LinuxCap.cpp delete mode 100644 sys/linux/LinuxCap.h delete mode 100644 sys/windows/MiniDump.cpp delete mode 100644 sys/windows/MiniDump.h delete mode 100644 sys/windows/guihelper.cpp delete mode 100644 sys/windows/guihelper.h delete mode 100644 test/test-qt512-sdk-build.sh delete mode 100644 test/test-qt6-build.sh delete mode 100644 translations/fa_IR.ts delete mode 100644 translations/ru_RU.ts delete mode 100644 translations/translations.qrc delete mode 100644 translations/zh_CN.ts delete mode 100644 ui/GroupSort.hpp delete mode 100644 ui/Icon.cpp delete mode 100644 ui/Icon.hpp delete mode 100644 ui/ThemeManager.cpp delete mode 100644 ui/ThemeManager.hpp delete mode 100644 ui/dialog_basic_settings.cpp delete mode 100644 ui/dialog_basic_settings.h delete mode 100644 ui/dialog_basic_settings.ui delete mode 100644 ui/dialog_hotkey.cpp delete mode 100644 ui/dialog_hotkey.h delete mode 100644 ui/dialog_hotkey.ui delete mode 100644 ui/dialog_manage_groups.cpp delete mode 100644 ui/dialog_manage_groups.h delete mode 100644 ui/dialog_manage_groups.ui delete mode 100644 ui/dialog_manage_routes.cpp delete mode 100644 ui/dialog_manage_routes.h delete mode 100644 ui/dialog_manage_routes.ui delete mode 100644 ui/dialog_vpn_settings.cpp delete mode 100644 ui/dialog_vpn_settings.h delete mode 100644 ui/dialog_vpn_settings.ui delete mode 100644 ui/edit/dialog_edit_group.cpp delete mode 100644 ui/edit/dialog_edit_group.h delete mode 100644 ui/edit/dialog_edit_group.ui delete mode 100644 ui/edit/dialog_edit_profile.cpp delete mode 100644 ui/edit/dialog_edit_profile.h delete mode 100644 ui/edit/dialog_edit_profile.ui delete mode 100644 ui/edit/edit_chain.cpp delete mode 100644 ui/edit/edit_chain.h delete mode 100644 ui/edit/edit_chain.ui delete mode 100644 ui/edit/edit_custom.cpp delete mode 100644 ui/edit/edit_custom.h delete mode 100644 ui/edit/edit_custom.ui delete mode 100644 ui/edit/edit_naive.cpp delete mode 100644 ui/edit/edit_naive.h delete mode 100644 ui/edit/edit_naive.ui delete mode 100644 ui/edit/edit_quic.cpp delete mode 100644 ui/edit/edit_quic.h delete mode 100644 ui/edit/edit_quic.ui delete mode 100644 ui/edit/edit_shadowsocks.cpp delete mode 100644 ui/edit/edit_shadowsocks.h delete mode 100644 ui/edit/edit_shadowsocks.ui delete mode 100644 ui/edit/edit_socks_http.cpp delete mode 100644 ui/edit/edit_socks_http.h delete mode 100644 ui/edit/edit_socks_http.ui delete mode 100644 ui/edit/edit_trojan_vless.cpp delete mode 100644 ui/edit/edit_trojan_vless.h delete mode 100644 ui/edit/edit_trojan_vless.ui delete mode 100644 ui/edit/edit_vmess.cpp delete mode 100644 ui/edit/edit_vmess.h delete mode 100644 ui/edit/edit_vmess.ui delete mode 100644 ui/edit/profile_editor.h delete mode 100644 ui/mainwindow.cpp delete mode 100644 ui/mainwindow.h delete mode 100644 ui/mainwindow.ui delete mode 100644 ui/mainwindow_grpc.cpp delete mode 100644 ui/mainwindow_interface.h delete mode 100644 ui/widget/FloatCheckBox.h delete mode 100644 ui/widget/GroupItem.cpp delete mode 100644 ui/widget/GroupItem.h delete mode 100644 ui/widget/GroupItem.ui delete mode 100644 ui/widget/MessageBoxTimer.h delete mode 100644 ui/widget/MyLineEdit.h delete mode 100644 ui/widget/MyTableWidget.h delete mode 100644 ui/widget/ProxyItem.cpp delete mode 100644 ui/widget/ProxyItem.h delete mode 100644 ui/widget/ProxyItem.ui diff --git a/3rdparty/QHotkey b/3rdparty/QHotkey deleted file mode 160000 index 52e25ac..0000000 --- a/3rdparty/QHotkey +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 52e25acf221e5ac86ce648f6922620fb2d6a7121 diff --git a/3rdparty/QThreadCreateThread.hpp b/3rdparty/QThreadCreateThread.hpp deleted file mode 100644 index ba6fe5f..0000000 --- a/3rdparty/QThreadCreateThread.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -// FOR OLD QT - -class QThreadCreateThread : public QThread { -public: - explicit QThreadCreateThread(std::future &&future) - : m_future(std::move(future)) { - // deleteLater - connect(this, &QThread::finished, this, &QThread::deleteLater); - } - -private: - void run() override { - m_future.get(); - } - - std::future m_future; -}; - -inline QThread *createThreadImpl(std::future &&future) { - return new QThreadCreateThread(std::move(future)); -} - -template -QThread *createQThread(Function &&f, Args &&... args) { - using DecayedFunction = typename std::decay::type; - auto threadFunction = - [f = static_cast(std::forward(f))](auto &&... largs) mutable -> void { - (void) std::invoke(std::move(f), std::forward(largs)...); - }; - - return createThreadImpl(std::async(std::launch::deferred, - std::move(threadFunction), - std::forward(args)...)); -} diff --git a/3rdparty/QtExtKeySequenceEdit.cpp b/3rdparty/QtExtKeySequenceEdit.cpp deleted file mode 100644 index 02bb80c..0000000 --- a/3rdparty/QtExtKeySequenceEdit.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "QtExtKeySequenceEdit.h" - -QtExtKeySequenceEdit::QtExtKeySequenceEdit(QWidget *parent) - : QKeySequenceEdit(parent) { -} - -QtExtKeySequenceEdit::~QtExtKeySequenceEdit() { -} - -void QtExtKeySequenceEdit::keyPressEvent(QKeyEvent *pEvent) { - QKeySequenceEdit::keyPressEvent(pEvent); - - QKeySequence keySeq = keySequence(); - if (keySeq.count() <= 0) { - return; - } - int key = keySeq[0]; - if (key == Qt::Key_Backspace || key == Qt::Key_Delete) { - key = 0; - } - setKeySequence(key); -} diff --git a/3rdparty/QtExtKeySequenceEdit.h b/3rdparty/QtExtKeySequenceEdit.h deleted file mode 100644 index ebeb848..0000000 --- a/3rdparty/QtExtKeySequenceEdit.h +++ /dev/null @@ -1,11 +0,0 @@ -#include - -class QtExtKeySequenceEdit : public QKeySequenceEdit { -public: - QtExtKeySequenceEdit(QWidget *parent); - - ~QtExtKeySequenceEdit(); - -protected: - virtual void keyPressEvent(QKeyEvent *pEvent); -}; diff --git a/3rdparty/RunGuard.hpp b/3rdparty/RunGuard.hpp deleted file mode 100644 index 6a38d55..0000000 --- a/3rdparty/RunGuard.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNGUARD_H -#define RUNGUARD_H - -#include -#include -#include -#include - -class RunGuard { -public: - RunGuard(const QString &key); - - ~RunGuard(); - - bool isAnotherRunning(quint64 *data_out); - - bool tryToRun(quint64 *data_in); - - void release(); - -private: - const QString key; - const QString memLockKey; - const QString sharedmemKey; - - QSharedMemory sharedMem; - QSystemSemaphore memLock; - - Q_DISABLE_COPY(RunGuard) -}; - -namespace { - - QString generateKeyHash(const QString &key, const QString &salt) { - QByteArray data; - - data.append(key.toUtf8()); - data.append(salt.toUtf8()); - data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); - - return data; - } - -} // namespace - -RunGuard::RunGuard(const QString &key) - : key(key), memLockKey(generateKeyHash(key, "_memLockKey")), sharedmemKey(generateKeyHash(key, "_sharedmemKey")), sharedMem(sharedmemKey), memLock(memLockKey, 1) { - memLock.acquire(); - { - QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/ - fix.attach(); - } - memLock.release(); -} - -RunGuard::~RunGuard() { - release(); -} - -bool RunGuard::isAnotherRunning(quint64 *data_out) { - if (sharedMem.isAttached()) - return false; - - memLock.acquire(); - const bool isRunning = sharedMem.attach(); - if (isRunning) { - if (data_out != nullptr) { - memcpy(data_out, sharedMem.data(), sizeof(quint64)); - } - sharedMem.detach(); - } - memLock.release(); - - return isRunning; -} - -bool RunGuard::tryToRun(quint64 *data_in) { - memLock.acquire(); - const bool result = sharedMem.create(sizeof(quint64)); - if (result) memcpy(sharedMem.data(), data_in, sizeof(quint64)); - memLock.release(); - - if (!result) { - release(); - return false; - } - - return true; -} - -void RunGuard::release() { - memLock.acquire(); - if (sharedMem.isAttached()) - sharedMem.detach(); - memLock.release(); -} - -#endif // RUNGUARD_H diff --git a/3rdparty/VT100Parser.hpp b/3rdparty/VT100Parser.hpp deleted file mode 100644 index 9887a3f..0000000 --- a/3rdparty/VT100Parser.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -inline QString cleanVT100String(const QString &in) { - QString out; - bool in_033 = false; - for (auto &&chr: in) { - if (chr == '\033') { - in_033 = true; - continue; - } - if (in_033) { - if (chr == 'm') { - in_033 = false; - } - continue; - } - out += chr; - } - return out; -} diff --git a/3rdparty/WinCommander.cpp b/3rdparty/WinCommander.cpp deleted file mode 100644 index 1d65450..0000000 --- a/3rdparty/WinCommander.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 UpdateNode UG (haftungsbeschränkt) -** Contact: code@updatenode.com -** -** This file is part of the UpdateNode Client. -** -** Commercial License Usage -** Licensees holding valid commercial UpdateNode license may use this file -** under the terms of the the Apache License, Version 2.0 -** Full license description file: LICENSE.COM -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation. Please review the following information to ensure the -** GNU General Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** Full license description file: LICENSE.GPL -** -****************************************************************************/ - -#include "WinCommander.hpp" - -#include -#include - -#ifdef Q_OS_WIN -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 -#endif - - -/*! -Executes a command elevated specified by \apath , using paramters \aparameters. -\n -Parameter /aaWait decides if the function should return immediatelly after it's\n -execution or wait for the exit of the launched process -\n -Returns the return value of the executed command -*/ -uint WinCommander::runProcessElevated(const QString &path, - const QStringList ¶meters, - const QString &workingDir, - int nShow, bool aWait) { - uint result = 0; - -#ifdef Q_OS_WIN - QString params; - HWND hwnd = NULL; - LPCTSTR pszPath = (LPCTSTR)path.utf16(); - foreach(QString item, parameters) - params += "\"" + item + "\" "; - - LPCTSTR pszParameters = (LPCTSTR)params.utf16(); - QString dir; - if (workingDir.count() == 0) - dir = QDir::toNativeSeparators(QDir::currentPath()); - else - dir = QDir::toNativeSeparators(workingDir); - LPCTSTR pszDirectory = (LPCTSTR)dir.utf16(); - - SHELLEXECUTEINFO shex; - DWORD dwCode = 0; - - ZeroMemory(&shex, sizeof(shex)); - - shex.cbSize = sizeof(shex); - shex.fMask = SEE_MASK_NOCLOSEPROCESS; - shex.hwnd = hwnd; - shex.lpVerb = TEXT("runas"); - shex.lpFile = pszPath; - shex.lpParameters = pszParameters; - shex.lpDirectory = pszDirectory; - // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow - shex.nShow = nShow; - - ShellExecuteEx(&shex); - if (shex.hProcess) - { - if(aWait) - { - WaitForSingleObject(shex.hProcess, INFINITE ); - GetExitCodeProcess(shex.hProcess, &dwCode); - } - CloseHandle (shex.hProcess) ; - } - else - return -1; - - result = (uint)dwCode; -#else - Q_UNUSED(path); - Q_UNUSED(parameters); - Q_UNUSED(workingDir); - Q_UNUSED(aWait); -#endif - return result; -} diff --git a/3rdparty/WinCommander.hpp b/3rdparty/WinCommander.hpp deleted file mode 100644 index 4aca562..0000000 --- a/3rdparty/WinCommander.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 UpdateNode UG (haftungsbeschränkt) -** Contact: code@updatenode.com -** -** This file is part of the UpdateNode Client. -** -** Commercial License Usage -** Licensees holding valid commercial UpdateNode license may use this file -** under the terms of the the Apache License, Version 2.0 -** Full license description file: LICENSE.COM -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation. Please review the following information to ensure the -** GNU General Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** Full license description file: LICENSE.GPL -** -****************************************************************************/ - -#ifndef WINCOMMANDER_H -#define WINCOMMANDER_H - -#include -#include - -class WinCommander { -public: - static const int SW_HIDE = 0; - static const int SW_NORMAL = 1; - static const int SW_SHOWMINIMIZED = 2; - - static uint runProcessElevated(const QString &path, - const QStringList ¶meters = QStringList(), - const QString &workingDir = QString(), - int nShow = SW_SHOWMINIMIZED, bool aWait = true); -}; - -#endif // WINCOMMANDER_H \ No newline at end of file diff --git a/3rdparty/ZxingQtReader.hpp b/3rdparty/ZxingQtReader.hpp deleted file mode 100644 index cbd41b7..0000000 --- a/3rdparty/ZxingQtReader.hpp +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Copyright 2020 Axel Waggershauser - */ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include "ZXing/ReadBarcode.h" - -#include -#include -#include -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#include "qscopeguard.h" -#else -#include -#endif - -#ifdef QT_MULTIMEDIA_LIB -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -#else -#include -#include -#endif -#include -#endif - -// This is some sample code to start a discussion about how a minimal and header-only Qt wrapper/helper could look like. - -namespace ZXingQt { - -Q_NAMESPACE - -//TODO: find a better way to export these enums to QML than to duplicate their definition -// #ifdef Q_MOC_RUN produces meta information in the moc output but it does end up working in qml -#ifdef QT_QML_LIB -enum class BarcodeFormat -{ - None = 0, ///< Used as a return value if no valid barcode has been detected - Aztec = (1 << 0), ///< Aztec - Codabar = (1 << 1), ///< Codabar - Code39 = (1 << 2), ///< Code39 - Code93 = (1 << 3), ///< Code93 - Code128 = (1 << 4), ///< Code128 - DataBar = (1 << 5), ///< GS1 DataBar, formerly known as RSS 14 - DataBarExpanded = (1 << 6), ///< GS1 DataBar Expanded, formerly known as RSS EXPANDED - DataMatrix = (1 << 7), ///< DataMatrix - EAN8 = (1 << 8), ///< EAN-8 - EAN13 = (1 << 9), ///< EAN-13 - ITF = (1 << 10), ///< ITF (Interleaved Two of Five) - MaxiCode = (1 << 11), ///< MaxiCode - PDF417 = (1 << 12), ///< PDF417 or - QRCode = (1 << 13), ///< QR Code - UPCA = (1 << 14), ///< UPC-A - UPCE = (1 << 15), ///< UPC-E - MicroQRCode = (1 << 16), ///< Micro QR Code - - LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, -}; - -enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; - -#else -using ZXing::BarcodeFormat; -using ZXing::ContentType; -#endif - -using ZXing::DecodeHints; -using ZXing::Binarizer; -using ZXing::BarcodeFormats; - -Q_ENUM_NS(BarcodeFormat) -Q_ENUM_NS(ContentType) - -template -QDebug operator<<(QDebug dbg, const T& v) -{ - return dbg.noquote() << QString::fromStdString(ToString(v)); -} - -class Position : public ZXing::Quadrilateral -{ - Q_GADGET - - Q_PROPERTY(QPoint topLeft READ topLeft) - Q_PROPERTY(QPoint topRight READ topRight) - Q_PROPERTY(QPoint bottomRight READ bottomRight) - Q_PROPERTY(QPoint bottomLeft READ bottomLeft) - - using Base = ZXing::Quadrilateral; - -public: - using Base::Base; -}; - -class Result : private ZXing::Result -{ - Q_GADGET - - Q_PROPERTY(BarcodeFormat format READ format) - Q_PROPERTY(QString formatName READ formatName) - Q_PROPERTY(QString text READ text) - Q_PROPERTY(QByteArray bytes READ bytes) - Q_PROPERTY(bool isValid READ isValid) - Q_PROPERTY(ContentType contentType READ contentType) - Q_PROPERTY(Position position READ position) - - QString _text; - QByteArray _bytes; - Position _position; - -public: - Result() = default; // required for qmetatype machinery - - explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { - _text = QString::fromStdString(ZXing::Result::text()); - _bytes = QByteArray(reinterpret_cast(ZXing::Result::bytes().data()), Size(ZXing::Result::bytes())); - auto& pos = ZXing::Result::position(); - auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; - _position = {qp(0), qp(1), qp(2), qp(3)}; - } - - using ZXing::Result::isValid; - - BarcodeFormat format() const { return static_cast(ZXing::Result::format()); } - ContentType contentType() const { return static_cast(ZXing::Result::contentType()); } - QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } - const QString& text() const { return _text; } - const QByteArray& bytes() const { return _bytes; } - const Position& position() const { return _position; } - - // For debugging/development - int runTime = 0; - Q_PROPERTY(int runTime MEMBER runTime) -}; - -inline QList QListResults(ZXing::Results&& zxres) -{ - QList res; - for (auto&& r : zxres) - res.push_back(Result(std::move(r))); - return res; -} - -inline QList ReadBarcodes(const QImage& img, const DecodeHints& hints = {}) -{ - using namespace ZXing; - - auto ImgFmtFromQImg = [](const QImage& img) { - switch (img.format()) { - case QImage::Format_ARGB32: - case QImage::Format_RGB32: -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return ImageFormat::BGRX; -#else - return ImageFormat::XRGB; -#endif - case QImage::Format_RGB888: return ImageFormat::RGB; - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: return ImageFormat::RGBX; - case QImage::Format_Grayscale8: return ImageFormat::Lum; - default: return ImageFormat::None; - } - }; - - auto exec = [&](const QImage& img) { - return QListResults(ZXing::ReadBarcodes( - {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast(img.bytesPerLine())}, hints)); - }; - - return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); -} - -inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) -{ - auto res = ReadBarcodes(img, DecodeHints(hints).setMaxNumberOfSymbols(1)); - return !res.isEmpty() ? res.takeFirst() : Result(); -} - -#ifdef QT_MULTIMEDIA_LIB -inline QList ReadBarcodes(const QVideoFrame& frame, const DecodeHints& hints = {}) -{ - using namespace ZXing; - - ImageFormat fmt = ImageFormat::None; - int pixStride = 0; - int pixOffset = 0; - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#define FORMAT(F5, F6) QVideoFrame::Format_##F5 -#define FIRST_PLANE -#else -#define FORMAT(F5, F6) QVideoFrameFormat::Format_##F6 -#define FIRST_PLANE 0 -#endif - - switch (frame.pixelFormat()) { - case FORMAT(ARGB32, ARGB8888): - case FORMAT(ARGB32_Premultiplied, ARGB8888_Premultiplied): - case FORMAT(RGB32, RGBX8888): -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - fmt = ImageFormat::BGRX; -#else - fmt = ImageFormat::XRGB; -#endif - break; - - case FORMAT(BGRA32, BGRA8888): - case FORMAT(BGRA32_Premultiplied, BGRA8888_Premultiplied): - case FORMAT(BGR32, BGRX8888): -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - fmt = ImageFormat::RGBX; -#else - fmt = ImageFormat::XBGR; -#endif - break; - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - case QVideoFrame::Format_RGB24: fmt = ImageFormat::RGB; break; - case QVideoFrame::Format_BGR24: fmt = ImageFormat::BGR; break; - case QVideoFrame::Format_YUV444: fmt = ImageFormat::Lum, pixStride = 3; break; -#else - case QVideoFrameFormat::Format_P010: - case QVideoFrameFormat::Format_P016: fmt = ImageFormat::Lum, pixStride = 1; break; -#endif - - case FORMAT(AYUV444, AYUV): - case FORMAT(AYUV444_Premultiplied, AYUV_Premultiplied): -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - fmt = ImageFormat::Lum, pixStride = 4, pixOffset = 3; -#else - fmt = ImageFormat::Lum, pixStride = 4, pixOffset = 2; -#endif - break; - - case FORMAT(YUV420P, YUV420P): - case FORMAT(NV12, NV12): - case FORMAT(NV21, NV21): - case FORMAT(IMC1, IMC1): - case FORMAT(IMC2, IMC2): - case FORMAT(IMC3, IMC3): - case FORMAT(IMC4, IMC4): - case FORMAT(YV12, YV12): fmt = ImageFormat::Lum; break; - case FORMAT(UYVY, UYVY): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; - case FORMAT(YUYV, YUYV): fmt = ImageFormat::Lum, pixStride = 2; break; - - case FORMAT(Y8, Y8): fmt = ImageFormat::Lum; break; - case FORMAT(Y16, Y16): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - case FORMAT(ABGR32, ABGR8888): -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - fmt = ImageFormat::RGBX; -#else - fmt = ImageFormat::XBGR; -#endif - break; -#endif -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - case FORMAT(YUV422P, YUV422P): fmt = ImageFormat::Lum; break; -#endif - default: break; - } - - if (fmt != ImageFormat::None) { - auto img = frame; // shallow copy just get access to non-const map() function -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (!img.isValid() || !img.map(QAbstractVideoBuffer::ReadOnly)){ -#else - if (!img.isValid() || !img.map(QVideoFrame::ReadOnly)){ -#endif - qWarning() << "invalid QVideoFrame: could not map memory"; - return {}; - } - QScopeGuard unmap([&] { img.unmap(); }); - - return QListResults(ZXing::ReadBarcodes( - {img.bits(FIRST_PLANE) + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(FIRST_PLANE), pixStride}, hints)); - } - else { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()) != QImage::Format_Invalid) { - qWarning() << "unsupported QVideoFrame::pixelFormat"; - return {}; - } - auto qimg = frame.image(); -#else - auto qimg = frame.toImage(); -#endif - if (qimg.format() != QImage::Format_Invalid) - return ReadBarcodes(qimg, hints); - qWarning() << "failed to convert QVideoFrame to QImage"; - return {}; - } -} - -inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = {}) -{ - auto res = ReadBarcodes(frame, DecodeHints(hints).setMaxNumberOfSymbols(1)); - return !res.isEmpty() ? res.takeFirst() : Result(); -} - -#define ZQ_PROPERTY(Type, name, setter) \ -public: \ - Q_PROPERTY(Type name READ name WRITE setter NOTIFY name##Changed) \ - Type name() const noexcept { return DecodeHints::name(); } \ - Q_SLOT void setter(const Type& newVal) \ - { \ - if (name() != newVal) { \ - DecodeHints::setter(newVal); \ - emit name##Changed(); \ - } \ - } \ - Q_SIGNAL void name##Changed(); - - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -class BarcodeReader : public QAbstractVideoFilter, private DecodeHints -#else -class BarcodeReader : public QObject, private DecodeHints -#endif -{ - Q_OBJECT - -public: -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - BarcodeReader(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} -#else - BarcodeReader(QObject* parent = nullptr) : QObject(parent) {} -#endif - - // TODO: find out how to properly expose QFlags to QML - // simply using ZQ_PROPERTY(BarcodeFormats, formats, setFormats) - // results in the runtime error "can't assign int to formats" - Q_PROPERTY(int formats READ formats WRITE setFormats NOTIFY formatsChanged) - int formats() const noexcept - { - auto fmts = DecodeHints::formats(); - return *reinterpret_cast(&fmts); - } - Q_SLOT void setFormats(int newVal) - { - if (formats() != newVal) { - DecodeHints::setFormats(static_cast(newVal)); - emit formatsChanged(); - qDebug() << DecodeHints::formats(); - } - } - Q_SIGNAL void formatsChanged(); - - ZQ_PROPERTY(bool, tryRotate, setTryRotate) - ZQ_PROPERTY(bool, tryHarder, setTryHarder) - ZQ_PROPERTY(bool, tryDownscale, setTryDownscale) - -public slots: - ZXingQt::Result process(const QVideoFrame& image) - { - QElapsedTimer t; - t.start(); - - auto res = ReadBarcode(image, *this); - - res.runTime = t.elapsed(); - - emit newResult(res); - if (res.isValid()) - emit foundBarcode(res); - return res; - } - -signals: - void newResult(ZXingQt::Result result); - void foundBarcode(ZXingQt::Result result); - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -public: - QVideoFilterRunnable *createFilterRunnable() override; -#else -private: - QVideoSink *_sink = nullptr; - -public: - void setVideoSink(QVideoSink* sink) { - if (_sink == sink) - return; - - if (_sink) - disconnect(_sink, nullptr, this, nullptr); - - _sink = sink; - connect(_sink, &QVideoSink::videoFrameChanged, this, &BarcodeReader::process); - } - Q_PROPERTY(QVideoSink* videoSink WRITE setVideoSink) -#endif - -}; - -#undef ZX_PROPERTY - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -class VideoFilterRunnable : public QVideoFilterRunnable -{ - BarcodeReader* _filter = nullptr; - -public: - explicit VideoFilterRunnable(BarcodeReader* filter) : _filter(filter) {} - - QVideoFrame run(QVideoFrame* input, const QVideoSurfaceFormat& /*surfaceFormat*/, RunFlags /*flags*/) override - { - _filter->process(*input); - return *input; - } -}; - -inline QVideoFilterRunnable* BarcodeReader::createFilterRunnable() -{ - return new VideoFilterRunnable(this); -} -#endif - -#endif // QT_MULTIMEDIA_LIB - -} // namespace ZXingQt - - -Q_DECLARE_METATYPE(ZXingQt::Position) -Q_DECLARE_METATYPE(ZXingQt::Result) - -#ifdef QT_QML_LIB - -#include - -namespace ZXingQt { - -inline void registerQmlAndMetaTypes() -{ - qRegisterMetaType("BarcodeFormat"); - qRegisterMetaType("ContentType"); - - // supposedly the Q_DECLARE_METATYPE should be used with the overload without a custom name - // but then the qml side complains about "unregistered type" - qRegisterMetaType("Position"); - qRegisterMetaType("Result"); - - qmlRegisterUncreatableMetaObject( - ZXingQt::staticMetaObject, "ZXing", 1, 0, "ZXing", "Access to enums & flags only"); - qmlRegisterType("ZXing", 1, 0, "BarcodeReader"); -} - -} // namespace ZXingQt - -#endif // QT_QML_LIB \ No newline at end of file diff --git a/3rdparty/base64.cpp b/3rdparty/base64.cpp deleted file mode 100644 index 88aae27..0000000 --- a/3rdparty/base64.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "base64.h" - -#ifndef qsizetype -#define qsizetype size_t -#endif - -namespace Qt515Base64 { - namespace { - struct fromBase64_helper_result { - qsizetype decodedLength; - Base64DecodingStatus status; - }; - - fromBase64_helper_result fromBase64_helper(const char *input, qsizetype inputSize, - char *output /* may alias input */, - Base64Options options) { - fromBase64_helper_result result{0, Base64DecodingStatus::Ok}; - - unsigned int buf = 0; - int nbits = 0; - - qsizetype offset = 0; - for (qsizetype i = 0; i < inputSize; ++i) { - int ch = input[i]; - int d; - - if (ch >= 'A' && ch <= 'Z') { - d = ch - 'A'; - } else if (ch >= 'a' && ch <= 'z') { - d = ch - 'a' + 26; - } else if (ch >= '0' && ch <= '9') { - d = ch - '0' + 52; - } else if (ch == '+' && (options & Base64UrlEncoding) == 0) { - d = 62; - } else if (ch == '-' && (options & Base64UrlEncoding) != 0) { - d = 62; - } else if (ch == '/' && (options & Base64UrlEncoding) == 0) { - d = 63; - } else if (ch == '_' && (options & Base64UrlEncoding) != 0) { - d = 63; - } else { - if (options & AbortOnBase64DecodingErrors) { - if (ch == '=') { - // can have 1 or 2 '=' signs, in both cases padding base64Size to - // a multiple of 4. Any other case is illegal. - if ((inputSize % 4) != 0) { - result.status = Base64DecodingStatus::IllegalInputLength; - return result; - } else if ((i == inputSize - 1) || - (i == inputSize - 2 && input[++i] == '=')) { - d = -1; // ... and exit the loop, normally - } else { - result.status = Base64DecodingStatus::IllegalPadding; - return result; - } - } else { - result.status = Base64DecodingStatus::IllegalCharacter; - return result; - } - } else { - d = -1; - } - } - - if (d != -1) { - buf = (buf << 6) | d; - nbits += 6; - if (nbits >= 8) { - nbits -= 8; - Q_ASSERT(offset < i); - output[offset++] = buf >> nbits; - buf &= (1 << nbits) - 1; - } - } - } - - result.decodedLength = offset; - return result; - } - } // namespace - - FromBase64Result QByteArray_fromBase64Encoding(const QByteArray &base64, Base64Options options) { - const auto base64Size = base64.size(); - QByteArray result((base64Size * 3) / 4, Qt::Uninitialized); - const auto base64result = fromBase64_helper(base64.data(), - base64Size, - const_cast(result.constData()), - options); - result.truncate(int(base64result.decodedLength)); - return {std::move(result), base64result.status}; - } -} // namespace Qt515Base64 diff --git a/3rdparty/base64.h b/3rdparty/base64.h deleted file mode 100644 index 1211304..0000000 --- a/3rdparty/base64.h +++ /dev/null @@ -1,47 +0,0 @@ -#include - -namespace Qt515Base64 { - enum Base64Option { - Base64Encoding = 0, - Base64UrlEncoding = 1, - - KeepTrailingEquals = 0, - OmitTrailingEquals = 2, - - IgnoreBase64DecodingErrors = 0, - AbortOnBase64DecodingErrors = 4, - }; - Q_DECLARE_FLAGS(Base64Options, Base64Option) - Q_DECLARE_OPERATORS_FOR_FLAGS(Base64Options) - - enum class Base64DecodingStatus { - Ok, - IllegalInputLength, - IllegalCharacter, - IllegalPadding, - }; - - class FromBase64Result { - public: - QByteArray decoded; - Base64DecodingStatus decodingStatus; - - void swap(FromBase64Result &other) noexcept { - qSwap(decoded, other.decoded); - qSwap(decodingStatus, other.decodingStatus); - } - - explicit operator bool() const noexcept { return decodingStatus == Base64DecodingStatus::Ok; } - -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(Q_QDOC) - QByteArray &operator*() &noexcept { return decoded; } - const QByteArray &operator*() const &noexcept { return decoded; } - QByteArray &&operator*() &&noexcept { return std::move(decoded); } -#else - QByteArray &operator*() noexcept { return decoded; } - const QByteArray &operator*() const noexcept { return decoded; } -#endif - }; - - FromBase64Result QByteArray_fromBase64Encoding(const QByteArray &base64, Base64Options options); -} // namespace Qt515Base64 diff --git a/3rdparty/fix_old_qt.h b/3rdparty/fix_old_qt.h deleted file mode 100644 index 2620809..0000000 --- a/3rdparty/fix_old_qt.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - -inline QString qEnvironmentVariable(const char *varName) { - return qgetenv(varName); -} - -#endif diff --git a/3rdparty/qrcodegen.cpp b/3rdparty/qrcodegen.cpp deleted file mode 100644 index 0957b79..0000000 --- a/3rdparty/qrcodegen.cpp +++ /dev/null @@ -1,830 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "qrcodegen.hpp" - -using std::int8_t; -using std::uint8_t; -using std::size_t; -using std::vector; - - -namespace qrcodegen { - -/*---- Class QrSegment ----*/ - -QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : - modeBits(mode) { - numBitsCharCount[0] = cc0; - numBitsCharCount[1] = cc1; - numBitsCharCount[2] = cc2; -} - - -int QrSegment::Mode::getModeBits() const { - return modeBits; -} - - -int QrSegment::Mode::numCharCountBits(int ver) const { - return numBitsCharCount[(ver + 7) / 17]; -} - - -const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); -const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); -const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); -const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); -const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); - - -QrSegment QrSegment::makeBytes(const vector &data) { - if (data.size() > static_cast(INT_MAX)) - throw std::length_error("Data too long"); - BitBuffer bb; - for (uint8_t b : data) - bb.appendBits(b, 8); - return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); -} - - -QrSegment QrSegment::makeNumeric(const char *digits) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *digits != '\0'; digits++, charCount++) { - char c = *digits; - if (c < '0' || c > '9') - throw std::domain_error("String contains non-numeric characters"); - accumData = accumData * 10 + (c - '0'); - accumCount++; - if (accumCount == 3) { - bb.appendBits(static_cast(accumData), 10); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(static_cast(accumData), accumCount * 3 + 1); - return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); -} - - -QrSegment QrSegment::makeAlphanumeric(const char *text) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *text != '\0'; text++, charCount++) { - const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); - if (temp == nullptr) - throw std::domain_error("String contains unencodable characters in alphanumeric mode"); - accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); - accumCount++; - if (accumCount == 2) { - bb.appendBits(static_cast(accumData), 11); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - bb.appendBits(static_cast(accumData), 6); - return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); -} - - -vector QrSegment::makeSegments(const char *text) { - // Select the most efficient segment encoding automatically - vector result; - if (*text == '\0'); // Leave result empty - else if (isNumeric(text)) - result.push_back(makeNumeric(text)); - else if (isAlphanumeric(text)) - result.push_back(makeAlphanumeric(text)); - else { - vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - result.push_back(makeBytes(bytes)); - } - return result; -} - - -QrSegment QrSegment::makeEci(long assignVal) { - BitBuffer bb; - if (assignVal < 0) - throw std::domain_error("ECI assignment value out of range"); - else if (assignVal < (1 << 7)) - bb.appendBits(static_cast(assignVal), 8); - else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(static_cast(assignVal), 14); - } else if (assignVal < 1000000L) { - bb.appendBits(6, 3); - bb.appendBits(static_cast(assignVal), 21); - } else - throw std::domain_error("ECI assignment value out of range"); - return QrSegment(Mode::ECI, 0, std::move(bb)); -} - - -QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : - mode(&md), - numChars(numCh), - data(dt) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : - mode(&md), - numChars(numCh), - data(std::move(dt)) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -int QrSegment::getTotalBits(const vector &segs, int version) { - int result = 0; - for (const QrSegment &seg : segs) { - int ccbits = seg.mode->numCharCountBits(version); - if (seg.numChars >= (1L << ccbits)) - return -1; // The segment's length doesn't fit the field's bit width - if (4 + ccbits > INT_MAX - result) - return -1; // The sum will overflow an int type - result += 4 + ccbits; - if (seg.data.size() > static_cast(INT_MAX - result)) - return -1; // The sum will overflow an int type - result += static_cast(seg.data.size()); - } - return result; -} - - -bool QrSegment::isNumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < '0' || c > '9') - return false; - } - return true; -} - - -bool QrSegment::isAlphanumeric(const char *text) { - for (; *text != '\0'; text++) { - if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) - return false; - } - return true; -} - - -const QrSegment::Mode &QrSegment::getMode() const { - return *mode; -} - - -int QrSegment::getNumChars() const { - return numChars; -} - - -const std::vector &QrSegment::getData() const { - return data; -} - - -const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - - - -/*---- Class QrCode ----*/ - -int QrCode::getFormatBits(Ecc ecl) { - switch (ecl) { - case Ecc::LOW : return 1; - case Ecc::MEDIUM : return 0; - case Ecc::QUARTILE: return 3; - case Ecc::HIGH : return 2; - default: throw std::logic_error("Unreachable"); - } -} - - -QrCode QrCode::encodeText(const char *text, Ecc ecl) { - vector segs = QrSegment::makeSegments(text); - return encodeSegments(segs, ecl); -} - - -QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { - vector segs{QrSegment::makeBytes(data)}; - return encodeSegments(segs, ecl); -} - - -QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl) { - if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) - throw std::invalid_argument("Invalid value"); - - // Find the minimal version number to use - int version, dataUsedBits; - for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = QrSegment::getTotalBits(segs, version); - if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - if (version >= maxVersion) { // All versions in the range could not fit the given data - std::ostringstream sb; - if (dataUsedBits == -1) - sb << "Segment too long"; - else { - sb << "Data length = " << dataUsedBits << " bits, "; - sb << "Max capacity = " << dataCapacityBits << " bits"; - } - throw data_too_long(sb.str()); - } - } - assert(dataUsedBits != -1); - - // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; - } - - // Concatenate all segments to create the data bit string - BitBuffer bb; - for (const QrSegment &seg : segs) { - bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); - bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); - bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); - } - assert(bb.size() == static_cast(dataUsedBits)); - - // Add terminator and pad up to a byte if applicable - size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; - assert(bb.size() <= dataCapacityBits); - bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); - bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); - assert(bb.size() % 8 == 0); - - // Pad with alternating bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - - // Pack bits into bytes in big endian - vector dataCodewords(bb.size() / 8); - for (size_t i = 0; i < bb.size(); i++) - dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); - - // Create the QR Code object - return QrCode(version, ecl, dataCodewords, mask); -} - - -QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : - // Initialize fields and check arguments - version(ver), - errorCorrectionLevel(ecl) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version value out of range"); - if (msk < -1 || msk > 7) - throw std::domain_error("Mask value out of range"); - size = ver * 4 + 17; - size_t sz = static_cast(size); - modules = vector >(sz, vector(sz)); // Initially all light - isFunction = vector >(sz, vector(sz)); - - // Compute ECC, draw modules - drawFunctionPatterns(); - const vector allCodewords = addEccAndInterleave(dataCodewords); - drawCodewords(allCodewords); - - // Do masking - if (msk == -1) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (int i = 0; i < 8; i++) { - applyMask(i); - drawFormatBits(i); - long penalty = getPenaltyScore(); - if (penalty < minPenalty) { - msk = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } - } - assert(0 <= msk && msk <= 7); - mask = msk; - applyMask(msk); // Apply the final choice of mask - drawFormatBits(msk); // Overwrite old format bits - - isFunction.clear(); - isFunction.shrink_to_fit(); -} - - -int QrCode::getVersion() const { - return version; -} - - -int QrCode::getSize() const { - return size; -} - - -QrCode::Ecc QrCode::getErrorCorrectionLevel() const { - return errorCorrectionLevel; -} - - -int QrCode::getMask() const { - return mask; -} - - -bool QrCode::getModule(int x, int y) const { - return 0 <= x && x < size && 0 <= y && y < size && module(x, y); -} - - -void QrCode::drawFunctionPatterns() { - // Draw horizontal and vertical timing patterns - for (int i = 0; i < size; i++) { - setFunctionModule(6, i, i % 2 == 0); - setFunctionModule(i, 6, i % 2 == 0); - } - - // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(3, 3); - drawFinderPattern(size - 4, 3); - drawFinderPattern(3, size - 4); - - // Draw numerous alignment patterns - const vector alignPatPos = getAlignmentPatternPositions(); - size_t numAlign = alignPatPos.size(); - for (size_t i = 0; i < numAlign; i++) { - for (size_t j = 0; j < numAlign; j++) { - // Don't draw on the three finder corners - if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) - drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); - } - } - - // Draw configuration data - drawFormatBits(0); // Dummy mask value; overwritten later in the constructor - drawVersion(); -} - - -void QrCode::drawFormatBits(int msk) { - // Calculate error correction code and pack bits - int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 - int rem = data; - for (int i = 0; i < 10; i++) - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - int bits = (data << 10 | rem) ^ 0x5412; // uint15 - assert(bits >> 15 == 0); - - // Draw first copy - for (int i = 0; i <= 5; i++) - setFunctionModule(8, i, getBit(bits, i)); - setFunctionModule(8, 7, getBit(bits, 6)); - setFunctionModule(8, 8, getBit(bits, 7)); - setFunctionModule(7, 8, getBit(bits, 8)); - for (int i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(bits, i)); - - // Draw second copy - for (int i = 0; i < 8; i++) - setFunctionModule(size - 1 - i, 8, getBit(bits, i)); - for (int i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always dark -} - - -void QrCode::drawVersion() { - if (version < 7) - return; - - // Calculate error correction code and pack bits - int rem = version; // version is uint6, in the range [7, 40] - for (int i = 0; i < 12; i++) - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long bits = static_cast(version) << 12 | rem; // uint18 - assert(bits >> 18 == 0); - - // Draw two copies - for (int i = 0; i < 18; i++) { - bool bit = getBit(bits, i); - int a = size - 11 + i % 3; - int b = i / 3; - setFunctionModule(a, b, bit); - setFunctionModule(b, a, bit); - } -} - - -void QrCode::drawFinderPattern(int x, int y) { - for (int dy = -4; dy <= 4; dy++) { - for (int dx = -4; dx <= 4; dx++) { - int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm - int xx = x + dx, yy = y + dy; - if (0 <= xx && xx < size && 0 <= yy && yy < size) - setFunctionModule(xx, yy, dist != 2 && dist != 4); - } - } -} - - -void QrCode::drawAlignmentPattern(int x, int y) { - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) - setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); - } -} - - -void QrCode::setFunctionModule(int x, int y, bool isDark) { - size_t ux = static_cast(x); - size_t uy = static_cast(y); - modules .at(uy).at(ux) = isDark; - isFunction.at(uy).at(ux) = true; -} - - -bool QrCode::module(int x, int y) const { - return modules.at(static_cast(y)).at(static_cast(x)); -} - - -vector QrCode::addEccAndInterleave(const vector &data) const { - if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) - throw std::invalid_argument("Invalid argument"); - - // Calculate parameter numbers - int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; - int rawCodewords = getNumRawDataModules(version) / 8; - int numShortBlocks = numBlocks - rawCodewords % numBlocks; - int shortBlockLen = rawCodewords / numBlocks; - - // Split data into blocks and append ECC to each block - vector > blocks; - const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); - for (int i = 0, k = 0; i < numBlocks; i++) { - vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); - k += static_cast(dat.size()); - const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); - if (i < numShortBlocks) - dat.push_back(0); - dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); - blocks.push_back(std::move(dat)); - } - - // Interleave (not concatenate) the bytes from every block into a single sequence - vector result; - for (size_t i = 0; i < blocks.at(0).size(); i++) { - for (size_t j = 0; j < blocks.size(); j++) { - // Skip the padding byte in short blocks - if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) - result.push_back(blocks.at(j).at(i)); - } - } - assert(result.size() == static_cast(rawCodewords)); - return result; -} - - -void QrCode::drawCodewords(const vector &data) { - if (data.size() != static_cast(getNumRawDataModules(version) / 8)) - throw std::invalid_argument("Invalid argument"); - - size_t i = 0; // Bit index into the data - // Do the funny zigzag scan - for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) - right = 5; - for (int vert = 0; vert < size; vert++) { // Vertical counter - for (int j = 0; j < 2; j++) { - size_t x = static_cast(right - j); // Actual x coordinate - bool upward = ((right + 1) & 2) == 0; - size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate - if (!isFunction.at(y).at(x) && i < data.size() * 8) { - modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); - i++; - } - // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/light by the constructor and are left unchanged by this method - } - } - } - assert(i == data.size() * 8); -} - - -void QrCode::applyMask(int msk) { - if (msk < 0 || msk > 7) - throw std::domain_error("Mask value out of range"); - size_t sz = static_cast(size); - for (size_t y = 0; y < sz; y++) { - for (size_t x = 0; x < sz; x++) { - bool invert; - switch (msk) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: throw std::logic_error("Unreachable"); - } - modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); - } - } -} - - -long QrCode::getPenaltyScore() const { - long result = 0; - - // Adjacent modules in row having same color, and finder-like patterns - for (int y = 0; y < size; y++) { - bool runColor = false; - int runX = 0; - std::array runHistory = {}; - for (int x = 0; x < size; x++) { - if (module(x, y) == runColor) { - runX++; - if (runX == 5) - result += PENALTY_N1; - else if (runX > 5) - result++; - } else { - finderPenaltyAddHistory(runX, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = module(x, y); - runX = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; - } - // Adjacent modules in column having same color, and finder-like patterns - for (int x = 0; x < size; x++) { - bool runColor = false; - int runY = 0; - std::array runHistory = {}; - for (int y = 0; y < size; y++) { - if (module(x, y) == runColor) { - runY++; - if (runY == 5) - result += PENALTY_N1; - else if (runY > 5) - result++; - } else { - finderPenaltyAddHistory(runY, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = module(x, y); - runY = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; - } - - // 2*2 blocks of modules having same color - for (int y = 0; y < size - 1; y++) { - for (int x = 0; x < size - 1; x++) { - bool color = module(x, y); - if ( color == module(x + 1, y) && - color == module(x, y + 1) && - color == module(x + 1, y + 1)) - result += PENALTY_N2; - } - } - - // Balance of dark and light modules - int dark = 0; - for (const vector &row : modules) { - for (bool color : row) { - if (color) - dark++; - } - } - int total = size * size; // Note that size is odd, so dark/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% - int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; - assert(0 <= k && k <= 9); - result += k * PENALTY_N4; - assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 - return result; -} - - -vector QrCode::getAlignmentPatternPositions() const { - if (version == 1) - return vector(); - else { - int numAlign = version / 7 + 2; - int step = (version == 32) ? 26 : - (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; - vector result; - for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) - result.insert(result.begin(), pos); - result.insert(result.begin(), 6); - return result; - } -} - - -int QrCode::getNumRawDataModules(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version number out of range"); - int result = (16 * ver + 128) * ver + 64; - if (ver >= 2) { - int numAlign = ver / 7 + 2; - result -= (25 * numAlign - 10) * numAlign - 55; - if (ver >= 7) - result -= 36; - } - assert(208 <= result && result <= 29648); - return result; -} - - -int QrCode::getNumDataCodewords(int ver, Ecc ecl) { - return getNumRawDataModules(ver) / 8 - - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] - * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; -} - - -vector QrCode::reedSolomonComputeDivisor(int degree) { - if (degree < 1 || degree > 255) - throw std::domain_error("Degree out of range"); - // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - vector result(static_cast(degree)); - result.at(result.size() - 1) = 1; // Start off with the monomial x^0 - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // and drop the highest monomial term which is always 1x^degree. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - uint8_t root = 1; - for (int i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (size_t j = 0; j < result.size(); j++) { - result.at(j) = reedSolomonMultiply(result.at(j), root); - if (j + 1 < result.size()) - result.at(j) ^= result.at(j + 1); - } - root = reedSolomonMultiply(root, 0x02); - } - return result; -} - - -vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { - vector result(divisor.size()); - for (uint8_t b : data) { // Polynomial division - uint8_t factor = b ^ result.at(0); - result.erase(result.begin()); - result.push_back(0); - for (size_t i = 0; i < result.size(); i++) - result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); - } - return result; -} - - -uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { - // Russian peasant multiplication - int z = 0; - for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - assert(z >> 8 == 0); - return static_cast(z); -} - - -int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { - int n = runHistory.at(1); - assert(n <= size * 3); - bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; - return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) - + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); -} - - -int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { - if (currentRunColor) { // Terminate dark run - finderPenaltyAddHistory(currentRunLength, runHistory); - currentRunLength = 0; - } - currentRunLength += size; // Add light border to final run - finderPenaltyAddHistory(currentRunLength, runHistory); - return finderPenaltyCountPatterns(runHistory); -} - - -void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { - if (runHistory.at(0) == 0) - currentRunLength += size; // Add light border to initial run - std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); - runHistory.at(0) = currentRunLength; -} - - -bool QrCode::getBit(long x, int i) { - return ((x >> i) & 1) != 0; -} - - -/*---- Tables of constants ----*/ - -const int QrCode::PENALTY_N1 = 3; -const int QrCode::PENALTY_N2 = 3; -const int QrCode::PENALTY_N3 = 40; -const int QrCode::PENALTY_N4 = 10; - - -const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low - {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium - {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile - {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High -}; - -const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low - {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium - {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile - {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High -}; - - -data_too_long::data_too_long(const std::string &msg) : - std::length_error(msg) {} - - - -/*---- Class BitBuffer ----*/ - -BitBuffer::BitBuffer() - : std::vector() {} - - -void BitBuffer::appendBits(std::uint32_t val, int len) { - if (len < 0 || len > 31 || val >> len != 0) - throw std::domain_error("Value out of range"); - for (int i = len - 1; i >= 0; i--) // Append bit by bit - this->push_back(((val >> i) & 1) != 0); -} - -} diff --git a/3rdparty/qrcodegen.hpp b/3rdparty/qrcodegen.hpp deleted file mode 100644 index 9448982..0000000 --- a/3rdparty/qrcodegen.hpp +++ /dev/null @@ -1,549 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include -#include -#include - - -namespace qrcodegen { - -/* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this class are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment::makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment() constructor with appropriate values. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ -class QrSegment final { - - /*---- Public helper enumeration ----*/ - - /* - * Describes how a segment's data bits are interpreted. Immutable. - */ - public: class Mode final { - - /*-- Constants --*/ - - public: static const Mode NUMERIC; - public: static const Mode ALPHANUMERIC; - public: static const Mode BYTE; - public: static const Mode KANJI; - public: static const Mode ECI; - - - /*-- Fields --*/ - - // The mode indicator bits, which is a uint4 value (range 0 to 15). - private: int modeBits; - - // Number of character count bits for three different version ranges. - private: int numBitsCharCount[3]; - - - /*-- Constructor --*/ - - private: Mode(int mode, int cc0, int cc1, int cc2); - - - /*-- Methods --*/ - - /* - * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). - */ - public: int getModeBits() const; - - /* - * (Package-private) Returns the bit width of the character count field for a segment in - * this mode in a QR Code at the given version number. The result is in the range [0, 16]. - */ - public: int numCharCountBits(int ver) const; - - }; - - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte vectors are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ - public: static QrSegment makeBytes(const std::vector &data); - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - public: static QrSegment makeNumeric(const char *digits); - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static QrSegment makeAlphanumeric(const char *text); - - - /* - * Returns a list of zero or more segments to represent the given text string. The result - * may use various segment modes and switch modes to optimize the length of the bit stream. - */ - public: static std::vector makeSegments(const char *text); - - - /* - * Returns a segment representing an Extended Channel Interpretation - * (ECI) designator with the given assignment value. - */ - public: static QrSegment makeEci(long assignVal); - - - /*---- Public static helper functions ----*/ - - /* - * Tests whether the given string can be encoded as a segment in numeric mode. - * A string is encodable iff each character is in the range 0 to 9. - */ - public: static bool isNumeric(const char *text); - - - /* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static bool isAlphanumeric(const char *text); - - - - /*---- Instance fields ----*/ - - /* The mode indicator of this segment. Accessed through getMode(). */ - private: const Mode *mode; - - /* The length of this segment's unencoded data. Measured in characters for - * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. Not the same as the data's bit length. - * Accessed through getNumChars(). */ - private: int numChars; - - /* The data bits of this segment. Accessed through getData(). */ - private: std::vector data; - - - /*---- Constructors (low level) ----*/ - - /* - * Creates a new QR Code segment with the given attributes and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is copied and stored. - */ - public: QrSegment(const Mode &md, int numCh, const std::vector &dt); - - - /* - * Creates a new QR Code segment with the given parameters and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is moved and stored. - */ - public: QrSegment(const Mode &md, int numCh, std::vector &&dt); - - - /*---- Methods ----*/ - - /* - * Returns the mode field of this segment. - */ - public: const Mode &getMode() const; - - - /* - * Returns the character count field of this segment. - */ - public: int getNumChars() const; - - - /* - * Returns the data bits of this segment. - */ - public: const std::vector &getData() const; - - - // (Package-private) Calculates the number of bits needed to encode the given segments at - // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a - // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. - public: static int getTotalBits(const std::vector &segs, int version); - - - /*---- Private constant ----*/ - - /* The set of all legal characters in alphanumeric mode, where - * each character value maps to the index in the string. */ - private: static const char *ALPHANUMERIC_CHARSET; - -}; - - - -/* - * A QR Code symbol, which is a type of two-dimension barcode. - * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of dark and light cells. - * The class provides static factory functions to create a QR Code from text or binary data. - * The class covers the QR Code Model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * - * Ways to create a QR Code object: - * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). - * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). - * - Low level: Custom-make the array of data codeword bytes (including - * segment headers and final padding, excluding error correction codewords), - * supply the appropriate version number, and call the QrCode() constructor. - * (Note that all ways require supplying the desired error correction level.) - */ -class QrCode final { - - /*---- Public helper enumeration ----*/ - - /* - * The error correction level in a QR Code symbol. - */ - public: enum class Ecc { - LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords - MEDIUM , // The QR Code can tolerate about 15% erroneous codewords - QUARTILE, // The QR Code can tolerate about 25% erroneous codewords - HIGH , // The QR Code can tolerate about 30% erroneous codewords - }; - - - // Returns a value in the range 0 to 3 (unsigned 2-bit integer). - private: static int getFormatBits(Ecc ecl); - - - - /*---- Static factory functions (high level) ----*/ - - /* - * Returns a QR Code representing the given Unicode text string at the given error correction level. - * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer - * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible - * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than - * the ecl argument if it can be done without increasing the version. - */ - public: static QrCode encodeText(const char *text, Ecc ecl); - - - /* - * Returns a QR Code representing the given binary data at the given error correction level. - * This function always encodes using the binary segment mode, not any text mode. The maximum number of - * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - */ - public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a QR Code representing the given segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). - */ - public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, - int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters - - - - /*---- Instance fields ----*/ - - // Immutable scalar parameters: - - /* The version number of this QR Code, which is between 1 and 40 (inclusive). - * This determines the size of this barcode. */ - private: int version; - - /* The width and height of this QR Code, measured in modules, between - * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ - private: int size; - - /* The error correction level used in this QR Code. */ - private: Ecc errorCorrectionLevel; - - /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). - * Even if a QR Code is created with automatic masking requested (mask = -1), - * the resulting object still has a mask value between 0 and 7. */ - private: int mask; - - // Private grids of modules/pixels, with dimensions of size*size: - - // The modules of this QR Code (false = light, true = dark). - // Immutable after constructor finishes. Accessed through getModule(). - private: std::vector > modules; - - // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. - private: std::vector > isFunction; - - - - /*---- Constructor (low level) ----*/ - - /* - * Creates a new QR Code with the given version number, - * error correction level, data codeword bytes, and mask number. - * This is a low-level API that most users should not use directly. - * A mid-level API is the encodeSegments() function. - */ - public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); - - - - /*---- Public instance methods ----*/ - - /* - * Returns this QR Code's version, in the range [1, 40]. - */ - public: int getVersion() const; - - - /* - * Returns this QR Code's size, in the range [21, 177]. - */ - public: int getSize() const; - - - /* - * Returns this QR Code's error correction level. - */ - public: Ecc getErrorCorrectionLevel() const; - - - /* - * Returns this QR Code's mask, in the range [0, 7]. - */ - public: int getMask() const; - - - /* - * Returns the color of the module (pixel) at the given coordinates, which is false - * for light or true for dark. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (light) is returned. - */ - public: bool getModule(int x, int y) const; - - - - /*---- Private helper methods for constructor: Drawing function modules ----*/ - - // Reads this object's version field, and draws and marks all function modules. - private: void drawFunctionPatterns(); - - - // Draws two copies of the format bits (with its own error correction code) - // based on the given mask and this object's error correction level field. - private: void drawFormatBits(int msk); - - - // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field, iff 7 <= version <= 40. - private: void drawVersion(); - - - // Draws a 9*9 finder pattern including the border separator, - // with the center module at (x, y). Modules can be out of bounds. - private: void drawFinderPattern(int x, int y); - - - // Draws a 5*5 alignment pattern, with the center module - // at (x, y). All modules must be in bounds. - private: void drawAlignmentPattern(int x, int y); - - - // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in bounds. - private: void setFunctionModule(int x, int y, bool isDark); - - - // Returns the color of the module at the given coordinates, which must be in range. - private: bool module(int x, int y) const; - - - /*---- Private helper methods for constructor: Codewords and masking ----*/ - - // Returns a new byte string representing the given data with the appropriate error correction - // codewords appended to it, based on this object's version and error correction level. - private: std::vector addEccAndInterleave(const std::vector &data) const; - - - // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code. Function modules need to be marked off before this is called. - private: void drawCodewords(const std::vector &data); - - - // XORs the codeword modules in this QR Code with the given mask pattern. - // The function modules must be marked and the codeword bits must be drawn - // before masking. Due to the arithmetic of XOR, calling applyMask() with - // the same mask value a second time will undo the mask. A final well-formed - // QR Code needs exactly one (not zero, two, etc.) mask applied. - private: void applyMask(int msk); - - - // Calculates and returns the penalty score based on state of this QR Code's current modules. - // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. - private: long getPenaltyScore() const; - - - - /*---- Private helper functions ----*/ - - // Returns an ascending list of positions of alignment patterns for this version number. - // Each position is in the range [0,177), and are used on both the x and y axes. - // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. - private: std::vector getAlignmentPatternPositions() const; - - - // Returns the number of data bits that can be stored in a QR Code of the given version number, after - // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. - // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. - private: static int getNumRawDataModules(int ver); - - - // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any - // QR Code of the given version number and error correction level, with remainder bits discarded. - // This stateless pure function could be implemented as a (40*4)-cell lookup table. - private: static int getNumDataCodewords(int ver, Ecc ecl); - - - // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be - // implemented as a lookup table over all possible parameter values, instead of as an algorithm. - private: static std::vector reedSolomonComputeDivisor(int degree); - - - // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. - private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); - - - // Returns the product of the two given field elements modulo GF(2^8/0x11D). - // All inputs are valid. This could be implemented as a 256*256 lookup table. - private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); - - - // Can only be called immediately after a light run is added, and - // returns either 0, 1, or 2. A helper function for getPenaltyScore(). - private: int finderPenaltyCountPatterns(const std::array &runHistory) const; - - - // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). - private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; - - - // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; - - - // Returns true iff the i'th bit of x is set to 1. - private: static bool getBit(long x, int i); - - - /*---- Constants and tables ----*/ - - // The minimum version number supported in the QR Code Model 2 standard. - public: static constexpr int MIN_VERSION = 1; - - // The maximum version number supported in the QR Code Model 2 standard. - public: static constexpr int MAX_VERSION = 40; - - - // For use in getPenaltyScore(), when evaluating which mask is best. - private: static const int PENALTY_N1; - private: static const int PENALTY_N2; - private: static const int PENALTY_N3; - private: static const int PENALTY_N4; - - - private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; - private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; - -}; - - - -/*---- Public exception class ----*/ - -/* - * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: - * - Decrease the error correction level if it was greater than Ecc::LOW. - * - If the encodeSegments() function was called with a maxVersion argument, then increase - * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other - * factory functions because they search all versions up to QrCode::MAX_VERSION.) - * - Split the text data into better or optimal segments in order to reduce the number of bits required. - * - Change the text or binary data to be shorter. - * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). - * - Propagate the error upward to the caller/user. - */ -class data_too_long : public std::length_error { - - public: explicit data_too_long(const std::string &msg); - -}; - - - -/* - * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. - */ -class BitBuffer final : public std::vector { - - /*---- Constructor ----*/ - - // Creates an empty bit buffer (length 0). - public: BitBuffer(); - - - - /*---- Method ----*/ - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and val < 2^len. - public: void appendBits(std::uint32_t val, int len); - -}; - -} diff --git a/3rdparty/qscopeguard.h b/3rdparty/qscopeguard.h deleted file mode 100644 index 31100fc..0000000 --- a/3rdparty/qscopeguard.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSCOPEGUARD_H -#define QSCOPEGUARD_H - -#include - - -QT_BEGIN_NAMESPACE - - -template class QScopeGuard; -template QScopeGuard qScopeGuard(F f); - -template -class QScopeGuard -{ -public: - QScopeGuard(QScopeGuard &&other) Q_DECL_NOEXCEPT - : m_func(std::move(other.m_func)) - , m_invoke(other.m_invoke) - { - other.dismiss(); - } - - ~QScopeGuard() - { - if (m_invoke) - m_func(); - } - - void dismiss() Q_DECL_NOEXCEPT - { - m_invoke = false; - } - -private: - explicit QScopeGuard(F f) Q_DECL_NOEXCEPT - : m_func(std::move(f)) - { - } - - Q_DISABLE_COPY(QScopeGuard) - - F m_func; - bool m_invoke = true; - friend QScopeGuard qScopeGuard(F); -}; - - -template -QScopeGuard qScopeGuard(F f) -{ - return QScopeGuard(std::move(f)); -} - -QT_END_NAMESPACE - -#endif // QSCOPEGUARD_H diff --git a/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp b/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp deleted file mode 100644 index f3a283d..0000000 --- a/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include "QvProxyConfigurator.hpp" - -#ifdef Q_OS_WIN -// -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -// -#include -#include -#include -#include -#endif - -#include -#include - -#include "3rdparty/fix_old_qt.h" -#include "3rdparty/qv2ray/wrapper.hpp" -#include "fmt/Preset.hpp" -#include "main/NekoGui.hpp" - -#define QV_MODULE_NAME "SystemProxy" - -#define QSTRN(num) QString::number(num) - -namespace Qv2ray::components::proxy { - - using ProcessArgument = QPair; -#ifdef Q_OS_MACOS - QStringList macOSgetNetworkServices() { - QProcess p; - p.setProgram("/usr/sbin/networksetup"); - p.setArguments(QStringList{"-listallnetworkservices"}); - p.start(); - p.waitForStarted(); - p.waitForFinished(); - LOG(p.errorString()); - auto str = p.readAllStandardOutput(); - auto lines = SplitLines(str); - QStringList result; - - // Start from 1 since first line is unneeded. - for (auto i = 1; i < lines.count(); i++) { - // * means disabled. - if (!lines[i].contains("*")) { - result << lines[i]; - } - } - - LOG("Found " + QSTRN(result.size()) + " network services: " + result.join(";")); - return result; - } -#endif -#ifdef Q_OS_WIN -#define NO_CONST(expr) const_cast(expr) - // static auto DEFAULT_CONNECTION_NAME = - // NO_CONST(L"DefaultConnectionSettings"); - /// - /// INTERNAL FUNCTION - bool __QueryProxyOptions() { - INTERNET_PER_CONN_OPTION_LIST List; - INTERNET_PER_CONN_OPTION Option[5]; - // - unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST); - Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL; - Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS; - Option[2].dwOption = INTERNET_PER_CONN_FLAGS; - Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; - Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER; - // - List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST); - List.pszConnection = nullptr; // NO_CONST(DEFAULT_CONNECTION_NAME); - List.dwOptionCount = 5; - List.dwOptionError = 0; - List.pOptions = Option; - - if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) { - LOG("InternetQueryOption failed, GLE=" + QSTRN(GetLastError())); - } - - LOG("System default proxy info:"); - - if (Option[0].Value.pszValue != nullptr) { - LOG(QString::fromWCharArray(Option[0].Value.pszValue)); - } - - if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) { - LOG("PROXY_TYPE_AUTO_PROXY_URL"); - } - - if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) { - LOG("PROXY_TYPE_AUTO_DETECT"); - } - - if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) { - LOG("PROXY_TYPE_DIRECT"); - } - - if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) { - LOG("PROXY_TYPE_PROXY"); - } - - if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) { - LOG("InternetQueryOption failed,GLE=" + QSTRN(GetLastError())); - } - - if (Option[4].Value.pszValue != nullptr) { - LOG(QString::fromStdWString(Option[4].Value.pszValue)); - } - - INTERNET_VERSION_INFO Version; - nSize = sizeof(INTERNET_VERSION_INFO); - InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize); - - if (Option[0].Value.pszValue != nullptr) { - GlobalFree(Option[0].Value.pszValue); - } - - if (Option[3].Value.pszValue != nullptr) { - GlobalFree(Option[3].Value.pszValue); - } - - if (Option[4].Value.pszValue != nullptr) { - GlobalFree(Option[4].Value.pszValue); - } - - return false; - } - bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC) { - INTERNET_PER_CONN_OPTION_LIST list; - DWORD dwBufSize = sizeof(list); - // Fill the list structure. - list.dwSize = sizeof(list); - // NULL == LAN, otherwise connectoid name. - list.pszConnection = nullptr; - - if (nullptr == proxy_full_addr) { - LOG("Clearing system proxy"); - // - list.dwOptionCount = 1; - list.pOptions = new INTERNET_PER_CONN_OPTION[1]; - - // Ensure that the memory was allocated. - if (nullptr == list.pOptions) { - // Return if the memory wasn't allocated. - return false; - } - - // Set flags. - list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS; - list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT; - } else if (isPAC) { - LOG("Setting system proxy for PAC"); - // - list.dwOptionCount = 2; - list.pOptions = new INTERNET_PER_CONN_OPTION[2]; - - if (nullptr == list.pOptions) { - return false; - } - - // Set flags. - list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS; - list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL; - // Set proxy name. - list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL; - list.pOptions[1].Value.pszValue = proxy_full_addr; - } else { - LOG("Setting system proxy for Global Proxy"); - // - list.dwOptionCount = 2; - list.pOptions = new INTERNET_PER_CONN_OPTION[2]; - - if (nullptr == list.pOptions) { - return false; - } - - // Set flags. - list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS; - list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY; - // Set proxy name. - list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER; - list.pOptions[1].Value.pszValue = proxy_full_addr; - // Set proxy override. - // list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; - // auto localhost = L"localhost"; - // list.pOptions[2].Value.pszValue = NO_CONST(localhost); - } - - // Set proxy for LAN. - if (!InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize)) { - LOG("InternetSetOption failed for LAN, GLE=" + QSTRN(GetLastError())); - } - - RASENTRYNAME entry; - entry.dwSize = sizeof(entry); - std::vector entries; - DWORD size = sizeof(entry), count; - LPRASENTRYNAME entryAddr = &entry; - auto ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count); - if (ERROR_BUFFER_TOO_SMALL == ret) { - entries.resize(count); - entries[0].dwSize = sizeof(RASENTRYNAME); - entryAddr = entries.data(); - ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count); - } - if (ERROR_SUCCESS != ret) { - LOG("Failed to list entry names"); - return false; - } - - // Set proxy for each connectoid. - for (DWORD i = 0; i < count; ++i) { - list.pszConnection = entryAddr[i].szEntryName; - if (!InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize)) { - LOG("InternetSetOption failed for connectoid " + QString::fromWCharArray(list.pszConnection) + ", GLE=" + QSTRN(GetLastError())); - } - } - - delete[] list.pOptions; - InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0); - InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0); - return true; - } -#endif - - void SetSystemProxy(int httpPort, int socksPort) { - const QString &address = "127.0.0.1"; - bool hasHTTP = (httpPort > 0 && httpPort < 65536); - bool hasSOCKS = (socksPort > 0 && socksPort < 65536); - -#ifdef Q_OS_WIN - if (!hasHTTP) { - LOG("Nothing?"); - return; - } else { - LOG("Qv2ray will set system proxy to use HTTP"); - } -#else - if (!hasHTTP && !hasSOCKS) { - LOG("Nothing?"); - return; - } - - if (hasHTTP) { - LOG("Qv2ray will set system proxy to use HTTP"); - } - - if (hasSOCKS) { - LOG("Qv2ray will set system proxy to use SOCKS"); - } -#endif - -#ifdef Q_OS_WIN - QString str = NekoGui::dataStore->system_proxy_format; - if (str.isEmpty()) str = Preset::Windows::system_proxy_format[0]; - str = str.replace("{ip}", address) - .replace("{http_port}", Int2String(httpPort)) - .replace("{socks_port}", Int2String(socksPort)); - // - LOG("Windows proxy string: " + str); - auto proxyStrW = new WCHAR[str.length() + 1]; - wcscpy(proxyStrW, str.toStdWString().c_str()); - // - __QueryProxyOptions(); - - if (!__SetProxyOptions(proxyStrW, false)) { - LOG("Failed to set proxy."); - } - - __QueryProxyOptions(); -#elif defined(Q_OS_LINUX) - QList actions; - actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "manual"}}; - // - bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE" || - qEnvironmentVariable("XDG_SESSION_DESKTOP") == "plasma"; - const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - - // - // Configure HTTP Proxies for HTTP, FTP and HTTPS - if (hasHTTP) { - // iterate over protocols... - for (const auto &protocol: QStringList{"http", "ftp", "https"}) { - // for GNOME: - { - actions << ProcessArgument{"gsettings", - {"set", "org.gnome.system.proxy." + protocol, "host", address}}; - actions << ProcessArgument{"gsettings", - {"set", "org.gnome.system.proxy." + protocol, "port", QSTRN(httpPort)}}; - } - - // for KDE: - if (isKDE) { - actions << ProcessArgument{"kwriteconfig5", - {"--file", configPath + "/kioslaverc", // - "--group", "Proxy Settings", // - "--key", protocol + "Proxy", // - "http://" + address + " " + QSTRN(httpPort)}}; - } - } - } - - // Configure SOCKS5 Proxies - if (hasSOCKS) { - // for GNOME: - { - actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy.socks", "host", address}}; - actions << ProcessArgument{"gsettings", - {"set", "org.gnome.system.proxy.socks", "port", QSTRN(socksPort)}}; - - // for KDE: - if (isKDE) { - actions << ProcessArgument{"kwriteconfig5", - {"--file", configPath + "/kioslaverc", // - "--group", "Proxy Settings", // - "--key", "socksProxy", // - "socks://" + address + " " + QSTRN(socksPort)}}; - } - } - } - // Setting Proxy Mode to Manual - { - // for GNOME: - { - actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "manual"}}; - } - - // for KDE: - if (isKDE) { - actions << ProcessArgument{"kwriteconfig5", - {"--file", configPath + "/kioslaverc", // - "--group", "Proxy Settings", // - "--key", "ProxyType", "1"}}; - } - } - - // Notify kioslaves to reload system proxy configuration. - if (isKDE) { - actions << ProcessArgument{"dbus-send", - {"--type=signal", "/KIO/Scheduler", // - "org.kde.KIO.Scheduler.reparseSlaveConfiguration", // - "string:''"}}; - } - // Execute them all! - // - // note: do not use std::all_of / any_of / none_of, - // because those are short-circuit and cannot guarantee atomicity. - QList results; - for (const auto &action: actions) { - // execute and get the code - const auto returnCode = QProcess::execute(action.first, action.second); - // print out the commands and result codes - DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";"))); - // give the code back - results << (returnCode == QProcess::NormalExit); - } - - if (results.count(true) != actions.size()) { - LOG("Something wrong when setting proxies."); - } -#else - - for (const auto &service: macOSgetNetworkServices()) { - LOG("Setting proxy for interface: " + service); - if (hasHTTP) { - QProcess::execute("/usr/sbin/networksetup", {"-setwebproxystate", service, "on"}); - QProcess::execute("/usr/sbin/networksetup", {"-setsecurewebproxystate", service, "on"}); - QProcess::execute("/usr/sbin/networksetup", {"-setwebproxy", service, address, QSTRN(httpPort)}); - QProcess::execute("/usr/sbin/networksetup", {"-setsecurewebproxy", service, address, QSTRN(httpPort)}); - } - - if (hasSOCKS) { - QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxystate", service, "on"}); - QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxy", service, address, QSTRN(socksPort)}); - } - } - -#endif - } - - void ClearSystemProxy() { - LOG("Clearing System Proxy"); - -#ifdef Q_OS_WIN - if (!__SetProxyOptions(nullptr, false)) { - LOG("Failed to clear proxy."); - } -#elif defined(Q_OS_LINUX) - QList actions; - const bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE" || - qEnvironmentVariable("XDG_SESSION_DESKTOP") == "plasma"; - const auto configRoot = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - - // Setting System Proxy Mode to: None - { - // for GNOME: - { - actions << ProcessArgument{"gsettings", {"set", "org.gnome.system.proxy", "mode", "none"}}; - } - - // for KDE: - if (isKDE) { - actions << ProcessArgument{"kwriteconfig5", - {"--file", configRoot + "/kioslaverc", // - "--group", "Proxy Settings", // - "--key", "ProxyType", "0"}}; - } - } - - // Notify kioslaves to reload system proxy configuration. - if (isKDE) { - actions << ProcessArgument{"dbus-send", - {"--type=signal", "/KIO/Scheduler", // - "org.kde.KIO.Scheduler.reparseSlaveConfiguration", // - "string:''"}}; - } - - // Execute the Actions - for (const auto &action: actions) { - // execute and get the code - const auto returnCode = QProcess::execute(action.first, action.second); - // print out the commands and result codes - DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";"))); - } - -#else - for (const auto &service: macOSgetNetworkServices()) { - LOG("Clearing proxy for interface: " + service); - QProcess::execute("/usr/sbin/networksetup", {"-setautoproxystate", service, "off"}); - QProcess::execute("/usr/sbin/networksetup", {"-setwebproxystate", service, "off"}); - QProcess::execute("/usr/sbin/networksetup", {"-setsecurewebproxystate", service, "off"}); - QProcess::execute("/usr/sbin/networksetup", {"-setsocksfirewallproxystate", service, "off"}); - } - -#endif - } -} // namespace Qv2ray::components::proxy diff --git a/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp b/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp deleted file mode 100644 index eaa2c0f..0000000 --- a/3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include -#include -// -namespace Qv2ray::components::proxy { - void ClearSystemProxy(); - void SetSystemProxy(int http_port, int socks_port); -} // namespace Qv2ray::components::proxy - -using namespace Qv2ray::components; -using namespace Qv2ray::components::proxy; diff --git a/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp b/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp deleted file mode 100644 index e4edd82..0000000 --- a/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "QvAutoCompleteTextEdit.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Qv2ray::ui::widgets { - AutoCompleteTextEdit::AutoCompleteTextEdit(const QString &prefix, const QStringList &sourceStrings, QWidget *parent) : QPlainTextEdit(parent) { - this->prefix = prefix; - this->setLineWrapMode(QPlainTextEdit::NoWrap); - c = new QCompleter(this); - c->setModel(new QStringListModel(sourceStrings, c)); - c->setWidget(this); - c->setCompletionMode(QCompleter::PopupCompletion); - c->setCaseSensitivity(Qt::CaseInsensitive); - QObject::connect(c, static_cast(&QCompleter::activated), this, &AutoCompleteTextEdit::insertCompletion); - } - - AutoCompleteTextEdit::~AutoCompleteTextEdit() { - } - - void AutoCompleteTextEdit::insertCompletion(const QString &completion) { - QTextCursor tc = textCursor(); - int extra = completion.length() - c->completionPrefix().length(); - tc.movePosition(QTextCursor::Left); - tc.movePosition(QTextCursor::EndOfWord); - tc.insertText(completion.right(extra).toLower()); - setTextCursor(tc); - } - - QString AutoCompleteTextEdit::lineUnderCursor() const { - QTextCursor tc = textCursor(); - tc.select(QTextCursor::LineUnderCursor); - return tc.selectedText(); - } - - QString AutoCompleteTextEdit::wordUnderCursor() const { - QTextCursor tc = textCursor(); - tc.select(QTextCursor::WordUnderCursor); - return tc.selectedText(); - } - - void AutoCompleteTextEdit::focusInEvent(QFocusEvent *e) { - if (c) - c->setWidget(this); - - QPlainTextEdit::focusInEvent(e); - } - - void AutoCompleteTextEdit::keyPressEvent(QKeyEvent *e) { - const bool hasCtrlOrShiftModifier = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::ShiftModifier); - const bool hasOtherModifiers = (e->modifiers() != Qt::NoModifier) && !hasCtrlOrShiftModifier; // has other modifiers - // - const bool isSpace = (e->modifiers().testFlag(Qt::ShiftModifier) || e->modifiers().testFlag(Qt::NoModifier)) // - && e->key() == Qt::Key_Space; - const bool isTab = (e->modifiers().testFlag(Qt::NoModifier) && e->key() == Qt::Key_Tab); - const bool isOtherSpace = e->text() == " "; - // - if (isSpace || isTab || isOtherSpace) { - QToolTip::showText(this->mapToGlobal(QPoint(0, 0)), tr("You can not input space characters here."), this, QRect{}, 2000); - return; - } - // - if (c && c->popup()->isVisible()) { - // The following keys are forwarded by the completer to the widget - switch (e->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - case Qt::Key_Escape: - case Qt::Key_Tab: - case Qt::Key_Backtab: - e->ignore(); - return; // let the completer do default behavior - - default: - break; - } - } - - QPlainTextEdit::keyPressEvent(e); - - if (!c || (hasCtrlOrShiftModifier && e->text().isEmpty())) - return; - - // if we have other modifiers, or the text is empty, or the line does not start with our prefix. - if (hasOtherModifiers || e->text().isEmpty() || !lineUnderCursor().startsWith(prefix)) { - c->popup()->hide(); - return; - } - - if (auto word = wordUnderCursor(); word != c->completionPrefix()) { - c->setCompletionPrefix(word); - c->popup()->setCurrentIndex(c->completionModel()->index(0, 0)); - } - - QRect cr = cursorRect(); - cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width()); - c->complete(cr); // popup it up! - } -} // namespace Qv2ray::ui::widgets diff --git a/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp b/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp deleted file mode 100644 index b7bf570..0000000 --- a/3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#pragma once -#include -#include -QT_BEGIN_NAMESPACE -class QCompleter; -QT_END_NAMESPACE - -namespace Qv2ray { - namespace ui { - namespace widgets { - class AutoCompleteTextEdit : public QPlainTextEdit { - Q_OBJECT - - public: - AutoCompleteTextEdit(const QString &prefix, const QStringList &sourceStrings, QWidget *parent = nullptr); - ~AutoCompleteTextEdit(); - - protected: - void keyPressEvent(QKeyEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - - private slots: - void insertCompletion(const QString &completion); - - private: - QString lineUnderCursor() const; - QString wordUnderCursor() const; - - QString prefix; - QCompleter *c = nullptr; - }; - } // namespace widgets - } // namespace ui -} // namespace Qv2ray -using namespace Qv2ray::ui::widgets; diff --git a/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp b/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp deleted file mode 100644 index 54cb9fe..0000000 --- a/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2011 SCHUTZ Sacha - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "QJsonModel.hpp" - -#include -#include - -QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) { - mParent = parent; -} - -QJsonTreeItem::~QJsonTreeItem() { - qDeleteAll(mChilds); -} - -void QJsonTreeItem::appendChild(QJsonTreeItem *item) { - mChilds.append(item); -} - -QJsonTreeItem *QJsonTreeItem::child(int row) { - return mChilds.value(row); -} - -QJsonTreeItem *QJsonTreeItem::parent() { - return mParent; -} - -int QJsonTreeItem::childCount() const { - return mChilds.count(); -} - -int QJsonTreeItem::row() const { - if (mParent) - return mParent->mChilds.indexOf(const_cast(this)); - - return 0; -} - -void QJsonTreeItem::setKey(const QString &key) { - mKey = key; -} - -void QJsonTreeItem::setValue(const QString &value) { - mValue = value; -} - -void QJsonTreeItem::setType(const QJsonValue::Type &type) { - mType = type; -} - -QString QJsonTreeItem::key() const { - return mKey; -} - -QString QJsonTreeItem::value() const { - return mValue; -} - -QJsonValue::Type QJsonTreeItem::type() const { - return mType; -} - -QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value, QJsonTreeItem *parent) { - QJsonTreeItem *rootItem = new QJsonTreeItem(parent); - rootItem->setKey("root"); - - if (value.isObject()) { - // Get all QJsonValue childs - for (QString key: value.toObject().keys()) { - QJsonValue v = value.toObject().value(key); - QJsonTreeItem *child = load(v, rootItem); - child->setKey(key); - child->setType(v.type()); - rootItem->appendChild(child); - } - } else if (value.isArray()) { - // Get all QJsonValue childs - int index = 0; - - for (QJsonValue v: value.toArray()) { - QJsonTreeItem *child = load(v, rootItem); - child->setKey(QString::number(index)); - child->setType(v.type()); - rootItem->appendChild(child); - ++index; - } - } else { - rootItem->setValue(value.toVariant().toString()); - rootItem->setType(value.type()); - } - - return rootItem; -} - -//========================================================================= - -QJsonModel::QJsonModel(QObject *parent) : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); -} - -QJsonModel::QJsonModel(const QString &fileName, QObject *parent) : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - load(fileName); -} - -QJsonModel::QJsonModel(QIODevice *device, QObject *parent) : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - load(device); -} - -QJsonModel::QJsonModel(const QByteArray &json, QObject *parent) : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - loadJson(json); -} - -QJsonModel::~QJsonModel() { - delete mRootItem; -} - -bool QJsonModel::load(const QString &fileName) { - QFile file(fileName); - bool success = false; - - if (file.open(QIODevice::ReadOnly)) { - success = load(&file); - file.close(); - } else - success = false; - - return success; -} - -bool QJsonModel::load(QIODevice *device) { - return loadJson(device->readAll()); -} - -bool QJsonModel::loadJson(const QByteArray &json) { - auto const &jdoc = QJsonDocument::fromJson(json); - - if (!jdoc.isNull()) { - beginResetModel(); - delete mRootItem; - - if (jdoc.isArray()) { - mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array())); - mRootItem->setType(QJsonValue::Array); - } else { - mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object())); - mRootItem->setType(QJsonValue::Object); - } - - endResetModel(); - return true; - } - - qDebug() << Q_FUNC_INFO << "cannot load json"; - return false; -} - -QVariant QJsonModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) - return QVariant(); - - QJsonTreeItem *item = static_cast(index.internalPointer()); - - if (role == Qt::DisplayRole) { - if (index.column() == 0) - return QStringLiteral("%1").arg(item->key()); - - if (index.column() == 1) - return QStringLiteral("%1").arg(item->value()); - } else if (Qt::EditRole == role) { - if (index.column() == 1) { - return QStringLiteral("%1").arg(item->value()); - } - } - - return QVariant(); -} - -bool QJsonModel::setData(const QModelIndex &index, const QVariant &value, int role) { - int col = index.column(); - - if (Qt::EditRole == role) { - if (col == 1) { - QJsonTreeItem *item = static_cast(index.internalPointer()); - item->setValue(value.toString()); - emit dataChanged(index, index, {Qt::EditRole}); - return true; - } - } - - return false; -} - -QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) { - return mHeaders.value(section); - } else - return QVariant(); -} - -QModelIndex QJsonModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - QJsonTreeItem *parentItem; - - if (!parent.isValid()) - parentItem = mRootItem; - else - parentItem = static_cast(parent.internalPointer()); - - QJsonTreeItem *childItem = parentItem->child(row); - - if (childItem) - return createIndex(row, column, childItem); - else - return QModelIndex(); -} - -QModelIndex QJsonModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return QModelIndex(); - - QJsonTreeItem *childItem = static_cast(index.internalPointer()); - QJsonTreeItem *parentItem = childItem->parent(); - - if (parentItem == mRootItem) - return QModelIndex(); - - return createIndex(parentItem->row(), 0, parentItem); -} - -int QJsonModel::rowCount(const QModelIndex &parent) const { - QJsonTreeItem *parentItem; - - if (parent.column() > 0) - return 0; - - if (!parent.isValid()) - parentItem = mRootItem; - else - parentItem = static_cast(parent.internalPointer()); - - return parentItem->childCount(); -} - -int QJsonModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent) - return 2; -} - -Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const { - int col = index.column(); - auto item = static_cast(index.internalPointer()); - auto isArray = QJsonValue::Array == item->type(); - auto isObject = QJsonValue::Object == item->type(); - - if ((col == 1) && !(isArray || isObject)) { - return Qt::ItemIsEditable | QAbstractItemModel::flags(index); - } else { - return QAbstractItemModel::flags(index); - } -} - -QJsonDocument QJsonModel::json() const { - auto v = genJson(mRootItem); - QJsonDocument doc; - - if (v.isObject()) { - doc = QJsonDocument(v.toObject()); - } else { - doc = QJsonDocument(v.toArray()); - } - - return doc; -} - -QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const { - auto type = item->type(); - int nchild = item->childCount(); - - if (QJsonValue::Object == type) { - QJsonObject jo; - - for (int i = 0; i < nchild; ++i) { - auto ch = item->child(i); - auto key = ch->key(); - jo.insert(key, genJson(ch)); - } - - return jo; - } else if (QJsonValue::Array == type) { - QJsonArray arr; - - for (int i = 0; i < nchild; ++i) { - auto ch = item->child(i); - arr.append(genJson(ch)); - } - - return arr; - } else { - QJsonValue va(item->value()); - return va; - } -} diff --git a/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp b/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp deleted file mode 100644 index d24979a..0000000 --- a/3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2011 SCHUTZ Sacha - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -class QJsonModel; -class QJsonItem; - -class QJsonTreeItem { -public: - QJsonTreeItem(QJsonTreeItem *parent = nullptr); - ~QJsonTreeItem(); - void appendChild(QJsonTreeItem *item); - QJsonTreeItem *child(int row); - QJsonTreeItem *parent(); - int childCount() const; - int row() const; - void setKey(const QString &key); - void setValue(const QString &value); - void setType(const QJsonValue::Type &type); - QString key() const; - QString value() const; - QJsonValue::Type type() const; - - static QJsonTreeItem *load(const QJsonValue &value, QJsonTreeItem *parent = 0); - -protected: -private: - QString mKey; - QString mValue; - QJsonValue::Type mType; - QList mChilds; - QJsonTreeItem *mParent; -}; - -//--------------------------------------------------- - -class QJsonModel : public QAbstractItemModel { - Q_OBJECT -public: - explicit QJsonModel(QObject *parent = nullptr); - QJsonModel(const QString &fileName, QObject *parent = nullptr); - QJsonModel(QIODevice *device, QObject *parent = nullptr); - QJsonModel(const QByteArray &json, QObject *parent = nullptr); - ~QJsonModel(); - bool load(const QString &fileName); - bool load(QIODevice *device); - bool loadJson(const QByteArray &json); - QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE; - QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; - QJsonDocument json() const; - -private: - QJsonValue genJson(QJsonTreeItem *) const; - - QJsonTreeItem *mRootItem; - QStringList mHeaders; -}; diff --git a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp b/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp deleted file mode 100644 index 2eb6bdb..0000000 --- a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "w_JsonEditor.hpp" - -#include "main/NekoGui.hpp" - -JsonEditor::JsonEditor(const QJsonObject& rootObject, QWidget* parent) : QDialog(parent) { - setupUi(this); - // QvMessageBusConnect(JsonEditor); - // - original = rootObject; - final = rootObject; - QString jsonString = JsonToString(rootObject); - - if (VerifyJsonString(jsonString).isEmpty()) { - jsonTree->setModel(&model); - model.loadJson(QJsonDocument(rootObject).toJson()); - } else { - QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), - tr("Original Json may contain syntax errors. Json tree is disabled.")); - } - - jsonEditor->setText(JsonToString(rootObject)); - jsonTree->expandAll(); - jsonTree->resizeColumnToContents(0); -} - -// QvMessageBusSlotImpl(JsonEditor) -// { -// switch (msg) -// { -// MBShowDefaultImpl; -// MBHideDefaultImpl; -// MBRetranslateDefaultImpl; -// case UPDATE_COLORSCHEME: -// break; -// } -// } - -QJsonObject JsonEditor::OpenEditor() { - int resultCode = this->exec(); - auto string = jsonEditor->toPlainText(); - - while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) { - if (string.isEmpty()) { - resultCode = QDialog::Accepted; - final = {}; - break; - } - QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), - tr("You must correct these errors before continuing.")); - resultCode = this->exec(); - string = jsonEditor->toPlainText(); - } - - return resultCode == QDialog::Accepted ? final : original; -} - -JsonEditor::~JsonEditor() { -} - -void JsonEditor::on_jsonEditor_textChanged() { - auto string = jsonEditor->toPlainText(); - auto VerifyResult = VerifyJsonString(string); - jsonValidateStatus->setText(VerifyResult); - - if (VerifyResult.isEmpty()) { - BLACK(jsonEditor); - final = JsonFromString(string); - model.loadJson(QJsonDocument(final).toJson()); - jsonTree->expandAll(); - jsonTree->resizeColumnToContents(0); - } else { - RED(jsonEditor); - } -} - -void JsonEditor::on_formatJsonBtn_clicked() { - auto string = jsonEditor->toPlainText(); - auto VerifyResult = VerifyJsonString(string); - jsonValidateStatus->setText(VerifyResult); - - if (VerifyResult.isEmpty()) { - BLACK(jsonEditor); - jsonEditor->setPlainText(JsonToString(JsonFromString(string))); - model.loadJson(QJsonDocument(JsonFromString(string)).toJson()); - jsonTree->setModel(&model); - jsonTree->expandAll(); - jsonTree->resizeColumnToContents(0); - } else { - RED(jsonEditor); - QvMessageBoxWarn(this, tr("Syntax Errors"), - tr("Please fix the JSON errors or remove the comments before continue")); - } -} - -void JsonEditor::on_removeCommentsBtn_clicked() { - jsonEditor->setPlainText(JsonToString(JsonFromString(jsonEditor->toPlainText()))); -} diff --git a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp b/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp deleted file mode 100644 index ea08afa..0000000 --- a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "3rdparty/qv2ray/wrapper.hpp" -#include "3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp" -#include "ui_w_JsonEditor.h" - -#include - -class JsonEditor - : public QDialog, - private Ui::JsonEditor { - Q_OBJECT - -public: - explicit JsonEditor(const QJsonObject& rootObject, QWidget* parent = nullptr); - ~JsonEditor(); - QJsonObject OpenEditor(); - -private slots: - void on_jsonEditor_textChanged(); - - void on_formatJsonBtn_clicked(); - - void on_removeCommentsBtn_clicked(); - -private: - QJsonModel model; - QJsonObject original; - QJsonObject final; -}; diff --git a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui b/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui deleted file mode 100644 index 51dcd76..0000000 --- a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui +++ /dev/null @@ -1,172 +0,0 @@ - - - JsonEditor - - - Qt::ApplicationModal - - - - 0 - 0 - 889 - 572 - - - - JSON Editor - - - true - - - - - - Qt::Horizontal - - - false - - - - - - - - Monospace - - - - QTextEdit::NoWrap - - - false - - - - - - - Format JSON - - - - - - - Remove All Comments - - - - - - - Json Editor - - - - - - - - - - - Structure Preview - - - - - - - QAbstractItemView::NoEditTriggers - - - true - - - 15 - - - true - - - true - - - true - - - 132 - - - 152 - - - - - - - - - - - OK - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - jsonEditor - formatJsonBtn - removeCommentsBtn - jsonTree - - - - - buttonBox - accepted() - JsonEditor - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - JsonEditor - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp b/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp deleted file mode 100644 index 562e986..0000000 --- a/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "GeositeReader.hpp" - -#include "3rdparty/qv2ray/wrapper.hpp" -#include "picoproto.hpp" - -#include -#include - -namespace Qv2ray::components::GeositeReader { - QMap GeositeEntries; - - QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache) { - if (GeositeEntries.contains(filepath) && allowCache) - return GeositeEntries.value(filepath); - - QStringList list; - qInfo() << "Reading geosites from:" << filepath; - QFile f(filepath); - bool opened = f.open(QFile::OpenModeFlag::ReadOnly); - - if (!opened) { - qInfo() << "File cannot be opened:" << filepath; - return list; - } - - const auto content = f.readAll(); - f.close(); - { - picoproto::Message root; - root.ParseFromBytes((unsigned char *) content.data(), content.size()); - - list.reserve(root.GetMessageArray(1).size()); - for (const auto &geosite: root.GetMessageArray(1)) - list << QString::fromStdString(geosite->GetString(1)); - } - - qInfo() << "Loaded" << list.count() << "geosite entries from data file."; - list.sort(); - GeositeEntries[filepath] = list; - return list; - } -} // namespace Qv2ray::components::GeositeReader diff --git a/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp b/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp deleted file mode 100644 index 8bafd6d..0000000 --- a/3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -namespace Qv2ray::components::GeositeReader { - QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache = true); -} // namespace Qv2ray::components::GeositeReader diff --git a/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp b/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp deleted file mode 100644 index 786c1fd..0000000 --- a/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp +++ /dev/null @@ -1,551 +0,0 @@ -/* Copyright 2016 Pete Warden. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ==============================================================================*/ - -#include "picoproto.hpp" - -namespace picoproto { - - namespace { - - // To keep the dependencies down, here's a local copy of the widespread bit_cast - // operator. This is necessary because in practice weird things can happen if - // you just try to use reinterpret_cast. - template - inline Dest bit_cast(const Source &source) { - static_assert(sizeof(Dest) == sizeof(Source), "Sizes do not match"); - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; - } - - // These are defined in: - // https://developers.google.com/protocol-buffers/docs/encoding - enum WireType { - WIRETYPE_VARINT = 0, - WIRETYPE_64BIT = 1, - WIRETYPE_LENGTH_DELIMITED = 2, - WIRETYPE_GROUP_START = 3, - WIRETYPE_GROUP_END = 4, - WIRETYPE_32BIT = 5, - }; - - // Pull bytes from the stream, updating the state. - bool ConsumeBytes(uint8_t **current, size_t how_many, size_t *remaining) { - if (how_many > *remaining) { - PP_LOG(ERROR) << "ReadBytes overrun!"; - return false; - } - *current += how_many; - *remaining -= how_many; - return true; - } - - // Grabs a particular type from the byte stream. - template - T ReadFromBytes(uint8_t **current, size_t *remaining) { - PP_CHECK(ConsumeBytes(current, sizeof(T), remaining)); - const T result = *(bit_cast(*current - sizeof(T))); - return result; - } - - uint64_t ReadVarInt(uint8_t **current, size_t *remaining) { - uint64_t result = 0; - bool keep_going; - int shift = 0; - do { - const uint8_t next_number = ReadFromBytes(current, remaining); - keep_going = (next_number >= 128); - result += (uint64_t) (next_number & 0x7f) << shift; - shift += 7; - } while (keep_going); - return result; - } - - void ReadWireTypeAndFieldNumber(uint8_t **current, size_t *remaining, uint8_t *wire_type, uint32_t *field_number) { - uint64_t wire_type_and_field_number = ReadVarInt(current, remaining); - *wire_type = wire_type_and_field_number & 0x07; - *field_number = wire_type_and_field_number >> 3; - } - - } // namespace - - std::string FieldTypeDebugString(enum FieldType type) { - switch (type) { - case FIELD_UNSET: - return "UNSET"; - break; - case FIELD_UINT32: - return "UINT32"; - break; - case FIELD_UINT64: - return "UINT64"; - break; - case FIELD_BYTES: - return "BYTES"; - break; - default: - return "Unknown field type"; - break; - } - return "Should never get here"; - } - - Field::Field(FieldType type, bool owns_data) : type(type), owns_data(owns_data) { - cached_messages = nullptr; - switch (type) { - case FIELD_UINT32: { - value.v_uint32 = new std::vector(); - } break; - case FIELD_UINT64: { - value.v_uint64 = new std::vector(); - } break; - case FIELD_BYTES: { - value.v_bytes = new std::vector>(); - cached_messages = new std::vector(); - } break; - default: { - PP_LOG(ERROR) << "Bad field type when constructing field: " << type; - } break; - } - } - - Field::Field(const Field &other) : type(other.type), owns_data(other.owns_data) { - switch (type) { - case FIELD_UINT32: { - value.v_uint32 = new std::vector(*other.value.v_uint32); - } break; - case FIELD_UINT64: { - value.v_uint64 = new std::vector(*other.value.v_uint64); - } break; - case FIELD_BYTES: { - if (owns_data) { - value.v_bytes = new std::vector>(); - for (std::pair data_info: *other.value.v_bytes) { - uint8_t *new_data = new uint8_t[data_info.second]; - std::copy_n(data_info.first, data_info.second, new_data); - value.v_bytes->push_back({new_data, data_info.second}); - } - } else { - value.v_bytes = new std::vector>(*other.value.v_bytes); - } - cached_messages = new std::vector(); - cached_messages->reserve(other.cached_messages->size()); - for (Message *other_cached_message: *other.cached_messages) { - Message *cached_message; - if (other_cached_message) { - cached_message = new Message(*other_cached_message); - } else { - cached_message = nullptr; - } - cached_messages->push_back(cached_message); - } - } break; - default: { - PP_LOG(ERROR) << "Bad field type when constructing field: " << type; - } break; - } - } - - Field::~Field() { - switch (type) { - case FIELD_UINT32: - delete value.v_uint32; - break; - case FIELD_UINT64: - delete value.v_uint64; - break; - case FIELD_BYTES: { - if (owns_data) - for (std::pair data_info: *value.v_bytes) - delete[] data_info.first; - delete value.v_bytes; - - for (Message *cached_message: *cached_messages) - if (cached_message) - delete cached_message; - delete cached_messages; - break; - } - default: - PP_LOG(ERROR) << "Bad field type when destroying field: " << type; - break; - } - } - - Message::Message() : Message(true){}; - - Message::Message(bool copy_arrays) : copy_arrays(copy_arrays){}; - - Message::Message(const Message &other) : field_map(other.field_map), fields(other.fields), copy_arrays(other.copy_arrays){}; - - Message::~Message(){}; - - bool Message::ParseFromBytes(uint8_t *bytes, size_t bytes_size) { - uint8_t *current = bytes; - size_t remaining = bytes_size; - while (remaining > 0) { - uint8_t wire_type; - uint32_t field_number; - ReadWireTypeAndFieldNumber(¤t, &remaining, &wire_type, &field_number); - switch (wire_type) { - case WIRETYPE_VARINT: { - Field *field = AddField(field_number, FIELD_UINT64); - const uint64_t varint = ReadVarInt(¤t, &remaining); - field->value.v_uint64->push_back(varint); - break; - } - case WIRETYPE_64BIT: { - Field *field = AddField(field_number, FIELD_UINT64); - const uint64_t value = ReadFromBytes(¤t, &remaining); - field->value.v_uint64->push_back(value); - break; - } - case WIRETYPE_LENGTH_DELIMITED: { - Field *field = AddField(field_number, FIELD_BYTES); - const uint64_t size = ReadVarInt(¤t, &remaining); - uint8_t *data; - if (copy_arrays) { - data = new uint8_t[size]; - std::copy_n(current, size, data); - field->owns_data = true; - } else { - data = current; - field->owns_data = false; - } - field->value.v_bytes->push_back({data, size}); - field->cached_messages->push_back(nullptr); - current += size; - remaining -= size; - break; - } - case WIRETYPE_GROUP_START: { - PP_LOG(INFO) << field_number << ": GROUPSTART" << std::endl; - PP_LOG(ERROR) << "Unhandled wire type encountered"; - break; - } - case WIRETYPE_GROUP_END: { - PP_LOG(INFO) << field_number << ": GROUPEND" << std::endl; - PP_LOG(ERROR) << "Unhandled wire type encountered"; - break; - } - case WIRETYPE_32BIT: { - Field *field = AddField(field_number, FIELD_UINT32); - const uint32_t value = ReadFromBytes(¤t, &remaining); - field->value.v_uint32->push_back(value); - break; - } - default: { - PP_LOG(ERROR) << "Unknown wire type encountered: " << static_cast(wire_type) << " at offset" << (bytes_size - remaining); - return false; - break; - } - } - } - return true; - } - - Field *Message::AddField(int32_t number, enum FieldType type) { - Field *field = GetField(number); - if (!field) { - fields.push_back(Field(type, copy_arrays)); - field = &fields.back(); - field_map.insert({number, fields.size() - 1}); - } - return field; - } - - Field *Message::GetField(int32_t number) { - if (field_map.count(number) == 0) - return nullptr; - return &(fields[field_map[number]]); - } - - Field *Message::GetFieldAndCheckType(int32_t number, enum FieldType type) { - Field *field = GetField(number); - PP_CHECK(field) << "No field for " << number; - PP_CHECK(field->type == type) << "For field " << number << " wanted type " << FieldTypeDebugString(type) << " but found " << FieldTypeDebugString(field->type); - return field; - } - - int32_t Message::GetInt32(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_UINT32); - uint32_t first_value = (*(field->value.v_uint32))[0]; - int32_t zig_zag_decoded = static_cast((first_value >> 1) ^ (-(first_value & 1))); - return zig_zag_decoded; - } - - int64_t Message::GetInt64(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_UINT64); - uint64_t first_value = (*(field->value.v_uint64))[0]; - int64_t zig_zag_decoded = static_cast((first_value >> 1) ^ (-(first_value & 1))); - return zig_zag_decoded; - } - - uint32_t Message::GetUInt32(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_UINT32); - uint32_t first_value = (*(field->value.v_uint32))[0]; - return first_value; - } - - uint64_t Message::GetUInt64(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_UINT64); - uint64_t first_value = (*(field->value.v_uint64))[0]; - return first_value; - } - - int64_t Message::GetInt(int32_t number) { - Field *field = GetField(number); - PP_CHECK(field) << "No field for " << number; - PP_CHECK((field->type == FIELD_UINT32) || (field->type == FIELD_UINT64)) - << "For field " << number << " wanted integer type but found " << FieldTypeDebugString(field->type); - switch (field->type) { - case FIELD_UINT32: - return GetInt32(number); - break; - case FIELD_UINT64: - return GetInt64(number); - break; - default: { - // Should never get here. - } break; - } - // Should never get here. - return 0; - } - - bool Message::GetBool(int32_t number) { - return (GetInt(number) != 0); - } - - float Message::GetFloat(int32_t number) { - uint32_t int_value = GetUInt32(number); - float float_value = *(bit_cast(&int_value)); - return float_value; - } - - double Message::GetDouble(int32_t number) { - uint64_t int_value = GetUInt64(number); - return *(bit_cast(&int_value)); - } - - std::pair Message::GetBytes(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_BYTES); - std::pair first_value = (*(field->value.v_bytes))[0]; - return first_value; - } - - std::string Message::GetString(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_BYTES); - std::pair first_value = (*(field->value.v_bytes))[0]; - std::string result(first_value.first, first_value.first + first_value.second); - return result; - } - - Message *Message::GetMessage(int32_t number) { - Field *field = GetFieldAndCheckType(number, FIELD_BYTES); - Message *cached_message = field->cached_messages->at(0); - if (!cached_message) { - std::pair first_value = (*(field->value.v_bytes))[0]; - cached_message = new Message(copy_arrays); - cached_message->ParseFromBytes(first_value.first, first_value.second); - field->cached_messages->at(0) = cached_message; - } - return cached_message; - } - - std::vector Message::GetInt32Array(int32_t number) { - std::vector raw_array = GetUInt64Array(number); - std::vector result; - result.reserve(raw_array.size()); - for (uint64_t raw_value: raw_array) { - int32_t zig_zag_decoded = static_cast((raw_value >> 1) ^ (-(raw_value & 1))); - result.push_back(zig_zag_decoded); - } - return result; - } - - std::vector Message::GetInt64Array(int32_t number) { - std::vector raw_array = GetUInt64Array(number); - std::vector result; - result.reserve(raw_array.size()); - for (uint64_t raw_value: raw_array) { - int64_t zig_zag_decoded = static_cast((raw_value >> 1) ^ (-(raw_value & 1))); - result.push_back(zig_zag_decoded); - } - return result; - } - - std::vector Message::GetUInt32Array(int32_t number) { - std::vector raw_array = GetUInt64Array(number); - std::vector result; - result.reserve(raw_array.size()); - for (uint64_t raw_value: raw_array) { - result.push_back(static_cast(raw_value)); - } - return result; - } - - std::vector Message::GetUInt64Array(int32_t number) { - std::vector result; - Field *field = GetField(number); - if (!field) { - return result; - } - if (field->type == FIELD_UINT64) { - result.reserve(field->value.v_uint64->size()); - for (uint64_t value: *field->value.v_uint64) { - result.push_back(static_cast(value)); - } - } else if (field->type == FIELD_UINT32) { - result.reserve(field->value.v_uint32->size()); - for (uint32_t value: *field->value.v_uint32) { - result.push_back(static_cast(value)); - } - } else if (field->type == FIELD_BYTES) { - for (std::pair data_info: *field->value.v_bytes) { - uint8_t *current = data_info.first; - size_t remaining = data_info.second; - while (remaining > 0) { - const uint64_t varint = ReadVarInt(¤t, &remaining); - result.push_back(static_cast(varint)); - } - } - } else { - PP_LOG(ERROR) << "Expected field type UINT32, UINT64, or BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - - std::vector Message::GetBoolArray(int32_t number) { - std::vector raw_array = GetUInt64Array(number); - std::vector result; - result.reserve(raw_array.size()); - for (uint64_t raw_value: raw_array) { - result.push_back(raw_value != 0); - } - return result; - } - - std::vector Message::GetFloatArray(int32_t number) { - std::vector result; - Field *field = GetField(number); - if (!field) { - return result; - } - if (field->type == FIELD_UINT32) { - result.reserve(field->value.v_uint32->size()); - for (uint32_t value: *field->value.v_uint32) { - result.push_back(bit_cast(value)); - } - } else if (field->type == FIELD_BYTES) { - for (std::pair data_info: *field->value.v_bytes) { - uint8_t *current = data_info.first; - size_t remaining = data_info.second; - while (remaining > 0) { - const uint64_t varint = ReadVarInt(¤t, &remaining); - const uint32_t varint32 = static_cast(varint & 0xffffffff); - result.push_back(bit_cast(varint32)); - } - } - } else { - PP_LOG(ERROR) << "Expected field type UINT32 or BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - - std::vector Message::GetDoubleArray(int32_t number) { - std::vector result; - Field *field = GetField(number); - if (!field) { - return result; - } - if (field->type == FIELD_UINT64) { - result.reserve(field->value.v_uint64->size()); - for (uint64_t value: *field->value.v_uint64) { - result.push_back(bit_cast(value)); - } - } else if (field->type == FIELD_BYTES) { - for (std::pair data_info: *field->value.v_bytes) { - uint8_t *current = data_info.first; - size_t remaining = data_info.second; - while (remaining > 0) { - const uint64_t varint = ReadVarInt(¤t, &remaining); - result.push_back(bit_cast(varint)); - } - } - } else { - PP_LOG(ERROR) << "Expected field type UINT64 or BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - - std::vector> Message::GetByteArray(int32_t number) { - std::vector> result; - Field *field = GetField(number); - if (!field) { - return result; - } - if (field->type == FIELD_BYTES) { - result.reserve(field->value.v_bytes->size()); - for (std::pair data_info: *field->value.v_bytes) { - result.push_back(data_info); - } - } else { - PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - - std::vector Message::GetStringArray(int32_t number) { - std::vector result; - Field *field = GetField(number); - if (!field) - return result; - if (field->type == FIELD_BYTES) { - result.reserve(field->value.v_bytes->size()); - for (std::pair data_info: *field->value.v_bytes) { - result.push_back(std::string(data_info.first, data_info.first + data_info.second)); - } - } else { - PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - - std::vector Message::GetMessageArray(int32_t number) { - std::vector result; - Field *field = GetField(number); - if (!field) - return result; - - if (field->type == FIELD_BYTES) { - result.reserve(field->value.v_bytes->size()); - for (size_t i = 0; i < field->value.v_bytes->size(); ++i) { - Message *cached_message = field->cached_messages->at(i); - if (!cached_message) { - std::pair value = field->value.v_bytes->at(i); - cached_message = new Message(copy_arrays); - cached_message->ParseFromBytes(value.first, value.second); - field->cached_messages->at(i) = cached_message; - } - result.push_back(cached_message); - } - } else { - PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type); - } - return result; - } - -} // namespace picoproto diff --git a/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.hpp b/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.hpp deleted file mode 100644 index 76a1b6e..0000000 --- a/3rdparty/qv2ray/v3/components/GeositeReader/picoproto.hpp +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright 2016 Pete Warden. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ==============================================================================*/ - -/* - See the README for full details, but this module lets you read in protobuf - encoded files with a minimal code footprint. - - It doesn't create classes for each kind of message it encounters, it just has a - single Message interface that lets you access the members of the protobuf as a - key/value store. This loses a lot of the convenience of type-checked classes, - but it does mean that very little code is needed to access data from files. - - As a simple example, if you had read a `bytes_size` long file into `bytes` that - contained a TensorFlow GraphDef proto: - - Message graph_def; - graph_def.ParseFromBytes(bytes, bytes_size); - - You can then access the different fields of the GraphDef using the field - numbers assigned in the .proto file: - - std::vector nodes = graph_def.GetMessageArray(1); - - One big difference between this minimal approach and normal protobufs is that - the calling code has to already know the field number and type of any members - it's trying to access. Here I know that the `node` field is number 1, and that - it should contain a repeated list of NodeDefs. Since they are not primitive - types like numbers or strings, they are accessed as an array of Messages. - - Here are the design goals of this module: - - Keep the code size tiny (single-digit kilobytes on most platforms). - - Minimize memory usage (for example allow in-place references to byte data). - - Provide a simple, readable implementation that can be ported easily. - - Deserialize all saved protobuf files into a usable representation. - - No dependencies other than the standard C++ library. - - No build-time support (e.g. protoc) required. - - Here's what it's explicitly not offering: - - Providing a readable and transparent way of accessing serialized data. - - Saving out data to protobuf format. - -*/ - -#ifndef INCLUDE_PICOPROTO_H -#define INCLUDE_PICOPROTO_H - -#include -#include -#include -#include -#include -#include -#include -#include - -// To keep dependencies minimal, some bare-bones macros to make logging easier. -#define PP_LOG(X) PP_LOG_##X -#define PP_LOG_INFO std::cerr << __FILE__ << ":" << __LINE__ << " - INFO: " -#define PP_LOG_WARN std::cerr << __FILE__ << ":" << __LINE__ << " - WARN: " -#define PP_LOG_ERROR std::cerr << __FILE__ << ":" << __LINE__ << " - ERROR: " -#define PP_CHECK(X) \ - if (!(X)) \ - PP_LOG(ERROR) << "PP_CHECK(" << #X << ") failed. " - -namespace picoproto { - - // These roughly correspond to the wire types used to save data in protobuf - // files. The best reference to understand the full format is: - // https://developers.google.com/protocol-buffers/docs/encoding - // Because we don't know the bit-depth of VarInts, they're always stored as - // uint64 values, which is why there's no specific type for them. - enum FieldType { - FIELD_UNSET, - FIELD_UINT32, - FIELD_UINT64, - FIELD_BYTES, - }; - - // Gives a readable name for the field type for logging purposes. - std::string FieldTypeDebugString(enum FieldType type); - - // Forward declare the main message class, since fields can contain them. - class Message; - - // Fields are the building blocks of messages. They contain the values for each - // data member, and handle all the allocation and deallocation of storage. - // It's unlikely you'll want to access this class directly, since you'll - // normally want to use Message below to pull typed values. - class Field { - public: - // You need to specify the type of a Field on creation, so that the right - // storage can be set up for the values. You also need to indicate whether the - // underlying memory will be around for the lifetime of the message (in which - // case no copies are needed) or whether the class should make copies and take - // ownership in case the data goes away. - Field(FieldType type, bool owns_data); - Field(const Field &other); - ~Field(); - - enum FieldType type; - // I know, this isn't very OOP, but the simplicity of keeping track of a type - // and deciding how to initialize and access the data based on that persuaded - // me this was the best approach. The `value` member contains whatever data - // the field should be holding. - union { - std::vector *v_uint32; - std::vector *v_uint64; - std::vector> *v_bytes; - } value; - // One of the drawbacks of not requiring .proto files ahead of time is that I - // don't know if a length-delimited field contains raw bytes, strings, or - // sub-messages. The only time we know that a field should be interpreted as a - // message is when client code requests it in that form. Because parsing can - // be costly, here we cache the results of any such calls for subsequent - // accesses. - std::vector *cached_messages; - // If this is set, then the object will allocate its own storage for - // length-delimited values, and copy from the input stream. If you know the - // underlying data will be around for the lifetime of the message, you can - // save memory and copies by leaving this as false. - bool owns_data; - }; - - // The main interface for loading and accessing serialized protobuf data. - class Message { - public: - // If you're not sure about the lifetime of any binary data you're reading - // from, just call this default constructor. - Message(); - // In the case when you're sure the lifetime of the byte stream you'll be - // decoding is longer than the lifetime of the message, you can set - // copy_arrays to false. This is especially useful if you have a memory - // mapped file to read from containing large binary blobs, since you'll skip - // a lot of copying and extra allocation. - Message(bool copy_arrays); - Message(const Message &other); - ~Message(); - - // Populates fields with all of the data from this stream of bytes. - // You can call this repeatedly with new messages, and the results will be - // merged together. - bool ParseFromBytes(uint8_t *binary, size_t binary_size); - - // These are the accessor functions if you're expecting exactly one value in a - // field. As discussed above, the burden is on the client code to know the - // field number and type of each member it's trying to access, and so pick the - // correct accessor function. - // If the field isn't present, this will raise an error, so if it's optional - // you should use the array accessors below. - int32_t GetInt32(int32_t number); - int64_t GetInt64(int32_t number); - uint32_t GetUInt32(int32_t number); - uint64_t GetUInt64(int32_t number); - int64_t GetInt(int32_t number); - bool GetBool(int32_t number); - float GetFloat(int32_t number); - double GetDouble(int32_t number); - std::pair GetBytes(int32_t number); - std::string GetString(int32_t number); - Message *GetMessage(int32_t number); - - // If you're not sure if a value will be present, or if it is repeated, you - // should call these array functions. If no such field has been seen, then the - // result will be an empty vector, otherwise you'll get back one or more - // entries. - std::vector GetInt32Array(int32_t number); - std::vector GetInt64Array(int32_t number); - std::vector GetUInt32Array(int32_t number); - std::vector GetUInt64Array(int32_t number); - std::vector GetBoolArray(int32_t number); - std::vector GetFloatArray(int32_t number); - std::vector GetDoubleArray(int32_t number); - std::vector> GetByteArray(int32_t number); - std::vector GetStringArray(int32_t number); - std::vector GetMessageArray(int32_t number); - - // It's unlikely you'll want to access fields directly, but here's an escape - // hatch in case you do have to manipulate them more directly. - Field *GetField(int32_t number); - - private: - // Inserts a new field, updating all the internal data structures. - Field *AddField(int32_t number, enum FieldType type); - - Field *GetFieldAndCheckType(int32_t number, enum FieldType type); - - // Maps from a field number to an index in the `fields` vector. - std::map field_map; - // The core list of fields that have been parsed. - std::vector fields; - bool copy_arrays; - }; - -} // namespace picoproto - -#endif // INCLUDE_PICOPROTO_H diff --git a/3rdparty/qv2ray/wrapper.hpp b/3rdparty/qv2ray/wrapper.hpp deleted file mode 100644 index 3a2cc38..0000000 --- a/3rdparty/qv2ray/wrapper.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -// Qv2ray wrapper - -#include -#include - -#define LOG(...) Qv2ray::base::log_internal(__VA_ARGS__) -#define DEBUG(...) Qv2ray::base::log_internal(__VA_ARGS__) -namespace Qv2ray { - namespace base { - template - inline void log_internal(T... v) {} - } // namespace base -} // namespace Qv2ray - -#define JsonToString(a) QJsonObject2QString(a, false) -#define JsonFromString(a) QString2QJsonObject(a) -#define QvMessageBoxWarn(a, b, c) MessageBoxWarning(b, c) - -inline QString VerifyJsonString(const QString &source) { - QJsonParseError error{}; - QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error); - Q_UNUSED(doc) - - if (error.error == QJsonParseError::NoError) { - return ""; - } else { - // LOG("WARNING: Json parse returns: " + error.errorString()); - return error.errorString(); - } -} - -#define RED(obj) \ - { \ - auto _temp = obj->palette(); \ - _temp.setColor(QPalette::Text, Qt::red); \ - obj->setPalette(_temp); \ - } - -#define BLACK(obj) obj->setPalette(QWidget::palette()); diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 7321c0f..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,302 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(nekobox VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# WINDOWS PDB FILE -if (WIN32) - if (MSVC) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") - endif () -endif () - -# Find Qt -if (NOT QT_VERSION_MAJOR) - set(QT_VERSION_MAJOR 5) -endif () -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network Svg LinguistTools) - -if (NKR_CROSS) - set_property(TARGET Qt5::moc PROPERTY IMPORTED_LOCATION /usr/bin/moc) - set_property(TARGET Qt5::uic PROPERTY IMPORTED_LOCATION /usr/bin/uic) - set_property(TARGET Qt5::rcc PROPERTY IMPORTED_LOCATION /usr/bin/rcc) - set_property(TARGET Qt5::lrelease PROPERTY IMPORTED_LOCATION /usr/bin/lrelease) - set_property(TARGET Qt5::lupdate PROPERTY IMPORTED_LOCATION /usr/bin/lupdate) -endif () - -#### Platform Variables #### -if (WIN32) - include("cmake/windows/windows.cmake") -else () - include("cmake/linux/linux.cmake") -endif () - -#### default prefix path #### - -if (NOT NKR_LIBS) - if (NKR_PACKAGE) - list(APPEND NKR_LIBS ${CMAKE_SOURCE_DIR}/libs/deps/package) - else () - list(APPEND NKR_LIBS ${CMAKE_SOURCE_DIR}/libs/deps/built) - endif () -endif () - -if (NOT NKR_DISABLE_LIBS) - list(APPEND CMAKE_PREFIX_PATH ${NKR_LIBS}) -endif () - -message("[CMAKE_PREFIX_PATH] ${CMAKE_PREFIX_PATH}") - -# for some cross toolchain -list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH}) -message("[CMAKE_FIND_ROOT_PATH] ${CMAKE_FIND_ROOT_PATH}") - -#### NKR #### - -include("cmake/print.cmake") -include("cmake/nkr.cmake") - -find_package(Threads) - -#### NKR EXTERNAL #### - -if (NKR_NO_EXTERNAL) - set(NKR_NO_GRPC 1) - set(NKR_NO_YAML 1) - set(NKR_NO_ZXING 1) - set(NKR_NO_QHOTKEY 1) -endif () - -# grpc -if (NKR_NO_GRPC) - nkr_add_compile_definitions(NKR_NO_GRPC) -else () - # My proto - include("cmake/myproto.cmake") - list(APPEND NKR_EXTERNAL_TARGETS myproto) -endif () - -# yaml-cpp -if (NKR_NO_YAML) - nkr_add_compile_definitions(NKR_NO_YAML) -else () - find_package(yaml-cpp CONFIG REQUIRED) # only Release is built - list(APPEND NKR_EXTERNAL_TARGETS yaml-cpp) -endif () - -# zxing-cpp -if (NKR_NO_ZXING) - nkr_add_compile_definitions(NKR_NO_ZXING) -else () - find_package(ZXing CONFIG REQUIRED) - list(APPEND NKR_EXTERNAL_TARGETS ZXing::ZXing) -endif () - -# QHotkey (static submodule) -if (NKR_NO_QHOTKEY) - nkr_add_compile_definitions(NKR_NO_QHOTKEY) -else () - set(QHOTKEY_INSTALL OFF) - set(BUILD_SHARED_LIBS OFF) - add_subdirectory(3rdparty/QHotkey) - list(APPEND NKR_EXTERNAL_TARGETS qhotkey) -endif () - -#### debug print #### - -if (DBG_CMAKE) - print_all_variables() - print_target_properties(myproto) - print_target_properties(yaml-cpp) - print_target_properties(ZXing::ZXing) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time") -endif () - -# Sources -set(PROJECT_SOURCES - ${PLATFORM_SOURCES} - - main/main.cpp - main/NekoGui.cpp - main/NekoGui_Utils.cpp - main/HTTPRequestHelper.cpp - - 3rdparty/base64.cpp - 3rdparty/qrcodegen.cpp - 3rdparty/QtExtKeySequenceEdit.cpp - - 3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp - 3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.cpp - 3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.cpp - 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp - 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp - 3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui - - 3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp - 3rdparty/qv2ray/v3/components/GeositeReader/picoproto.cpp - - rpc/gRPC.cpp - - db/Database.cpp - db/traffic/TrafficLooper.cpp - db/ProfileFilter.cpp - db/ConfigBuilder.cpp - - fmt/AbstractBean.cpp - fmt/Bean2CoreObj_box.cpp - fmt/Bean2External.cpp - fmt/Bean2Link.cpp - fmt/Link2Bean.cpp - fmt/ChainBean.hpp # translate - - sub/GroupUpdater.cpp - - sys/ExternalProcess.cpp - sys/AutoRun.cpp - - ui/ThemeManager.cpp - ui/Icon.cpp - - ui/mainwindow_grpc.cpp - ui/mainwindow.cpp - ui/mainwindow.h - ui/mainwindow.ui - - ui/edit/dialog_edit_profile.h - ui/edit/dialog_edit_profile.cpp - ui/edit/dialog_edit_profile.ui - ui/edit/dialog_edit_group.h - ui/edit/dialog_edit_group.cpp - ui/edit/dialog_edit_group.ui - - ui/edit/edit_chain.h - ui/edit/edit_chain.cpp - ui/edit/edit_chain.ui - ui/edit/edit_socks_http.h - ui/edit/edit_socks_http.cpp - ui/edit/edit_socks_http.ui - ui/edit/edit_shadowsocks.h - ui/edit/edit_shadowsocks.cpp - ui/edit/edit_shadowsocks.ui - ui/edit/edit_vmess.h - ui/edit/edit_vmess.cpp - ui/edit/edit_vmess.ui - ui/edit/edit_trojan_vless.h - ui/edit/edit_trojan_vless.cpp - ui/edit/edit_trojan_vless.ui - - ui/edit/edit_naive.h - ui/edit/edit_naive.cpp - ui/edit/edit_naive.ui - - ui/edit/edit_quic.h - ui/edit/edit_quic.cpp - ui/edit/edit_quic.ui - - ui/edit/edit_custom.h - ui/edit/edit_custom.cpp - ui/edit/edit_custom.ui - - ui/dialog_basic_settings.cpp - ui/dialog_basic_settings.h - ui/dialog_basic_settings.ui - - ui/dialog_manage_groups.cpp - ui/dialog_manage_groups.h - ui/dialog_manage_groups.ui - - ui/dialog_manage_routes.cpp - ui/dialog_manage_routes.h - ui/dialog_manage_routes.ui - - ui/dialog_vpn_settings.cpp - ui/dialog_vpn_settings.h - ui/dialog_vpn_settings.ui - - ui/dialog_hotkey.cpp - ui/dialog_hotkey.h - ui/dialog_hotkey.ui - - ui/widget/ProxyItem.cpp - ui/widget/ProxyItem.h - ui/widget/ProxyItem.ui - ui/widget/GroupItem.cpp - ui/widget/GroupItem.h - ui/widget/GroupItem.ui - - res/neko.qrc - res/theme/feiyangqingyun/qss.qrc - ${QV2RAY_RC} -) - -# Qt exe -if (${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_executable(nekobox - MANUAL_FINALIZATION - ${PROJECT_SOURCES} - ) - # Define target properties for Android with Qt 6 as: - # set_property(TARGET nekobox APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR - # ${CMAKE_CURRENT_SOURCE_DIR}/android) - # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation -else () - if (ANDROID) - add_library(nekobox SHARED - ${PROJECT_SOURCES} - ) - # Define properties for Android with Qt 5 after find_package() calls as: - # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") - else () - add_executable(nekobox - ${PROJECT_SOURCES} - ) - endif () -endif () - -# Target - -set_property(TARGET nekobox PROPERTY AUTOUIC ON) -set_property(TARGET nekobox PROPERTY AUTOMOC ON) -set_property(TARGET nekobox PROPERTY AUTORCC ON) - -set_target_properties(nekobox PROPERTIES - WIN32_EXECUTABLE TRUE -) - -# Target Source Translations - -set(TS_FILES - translations/zh_CN.ts - translations/fa_IR.ts - translations/ru_RU.ts -) -set(LUPDATE_OPTIONS - -locations none -no-obsolete -) -if (${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_lupdate(nekobox TS_FILES ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS}) - qt_add_lrelease(nekobox TS_FILES ${TS_FILES} QM_FILES_OUTPUT_VARIABLE QM_FILES) -else () - qt5_create_translation(QM_FILES ${PROJECT_SOURCES} ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS}) -endif () -configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY) -target_sources(nekobox PRIVATE ${CMAKE_BINARY_DIR}/translations.qrc) - -# Target Link - -target_link_libraries(nekobox PRIVATE - Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Svg - Threads::Threads - ${NKR_EXTERNAL_TARGETS} - ${PLATFORM_LIBRARIES} -) - -if (QT_VERSION_MAJOR EQUAL 6) - qt_finalize_executable(nekobox) -endif () diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md deleted file mode 100644 index 9cff256..0000000 --- a/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# NekoBox For PC - -Qt based cross-platform GUI proxy configuration manager (backend: sing-box) - -Support Windows / Linux out of the box now. - -基于 Qt 的跨平台代理配置管理器 (后端 sing-box) - -目前支持 Windows / Linux 开箱即用 - -## 下载 / Download - -### GitHub Releases (Portable ZIP) - -便携格式,无安装器。转到 Releases 下载预编译的二进制文件,解压后即可使用。 - -[![GitHub All Releases](https://img.shields.io/github/downloads/Matsuridayo/nekoray/total?label=downloads-total&logo=github&style=flat-square)](https://github.com/Matsuridayo/nekoray/releases) - -[下载 / Download](https://github.com/Matsuridayo/nekoray/releases) - -[安装包的说明,如果你不知道要下载哪一个](https://github.com/MatsuriDayo/nekoray/wiki/Installation-package-description) - -### Package - -#### AUR - -- [nekoray](https://aur.archlinux.org/packages/nekoray) -- [nekoray-git](https://aur.archlinux.org/packages/nekoray-git) - -#### archlinuxcn - -- [nekoray](https://github.com/archlinuxcn/repo/tree/master/archlinuxcn/nekoray) -- [nekoray-git](https://github.com/archlinuxcn/repo/tree/master/archlinuxcn/nekoray-git) - -#### Scoop Extras - -`scoop install nekoray` - -## 更改记录 & 发布频道 / Changelog & Telegram Channel - -https://t.me/Matsuridayo - -## 项目主页 & 文档 / Homepage & Documents - -https://matsuridayo.github.io - -## 代理 / Proxy - -- SOCKS (4/4a/5) -- HTTP(S) -- Shadowsocks -- VMess -- VLESS -- Trojan -- TUIC ( sing-box ) -- NaïveProxy ( Custom Core ) -- Hysteria2 ( Custom Core or sing-box ) -- Custom Outbound -- Custom Config -- Custom Core - -## 订阅 / Subscription - -- Raw: some widely used formats (like Shadowsocks, Clash and v2rayN) -- 原始格式: 一些广泛使用的格式 (如 Shadowsocks、Clash 和 v2rayN) - -## 运行参数 - -[运行参数](docs/RunFlags.md) - -## Windows 运行 - -若提示 DLL 缺失,无法运行,请下载 安装 [微软 C++ 运行库](https://aka.ms/vs/17/release/vc_redist.x64.exe) - -## Linux 运行 - -[Linux 运行教程](docs/Run_Linux.md) - -## 编译教程 / Compile Tutorial - -请看 [技术文档 / Technical documentation](https://github.com/MatsuriDayo/nekoray/tree/main/docs) - -## 捐助 / Donate - -如果这个项目对您有帮助,可以通过捐赠的方式帮助我们维持这个项目。 - -捐赠满等额 50 USD 可以在「[捐赠榜](https://mtrdnt.pages.dev/donation_list)」显示头像,如果您未被添加到这里,欢迎联系我们补充。 - -Donations of 50 USD or more can display your avatar on the [Donation List](https://mtrdnt.pages.dev/donation_list). If you are not added here, please contact us to add it. - -USDT TRC20 - -`TRhnA7SXE5Sap5gSG3ijxRmdYFiD4KRhPs` - -XMR - -`49bwESYQjoRL3xmvTcjZKHEKaiGywjLYVQJMUv79bXonGiyDCs8AzE3KiGW2ytTybBCpWJUvov8SjZZEGg66a4e59GXa6k5` - -## Credits - -Core: - -- [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core) ( < 3.10 ) -- [MatsuriDayo/Matsuri](https://github.com/MatsuriDayo/Matsuri) ( < 3.10 ) -- [MatsuriDayo/v2ray-core](https://github.com/MatsuriDayo/v2ray-core) ( < 3.10 ) -- [XTLS/Xray-core](https://github.com/XTLS/Xray-core) ( 3.10 <= Version <= 3.26 ) -- [MatsuriDayo/Xray-core](https://github.com/MatsuriDayo/Xray-core) ( 3.10 <= Version <= 3.26 ) -- [SagerNet/sing-box](https://github.com/SagerNet/sing-box) -- [Matsuridayo/sing-box-extra](https://github.com/MatsuriDayo/sing-box-extra) - -Gui: - -- [Qv2ray](https://github.com/Qv2ray/Qv2ray) -- [Qt](https://www.qt.io/) -- [protobuf](https://github.com/protocolbuffers/protobuf) -- [yaml-cpp](https://github.com/jbeder/yaml-cpp) -- [zxing-cpp](https://github.com/nu-book/zxing-cpp) -- [QHotkey](https://github.com/Skycoder42/QHotkey) -- [AppImageKit](https://github.com/AppImage/AppImageKit) diff --git a/cmake/linux/linux.cmake b/cmake/linux/linux.cmake deleted file mode 100644 index f9458e0..0000000 --- a/cmake/linux/linux.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(PLATFORM_SOURCES sys/linux/LinuxCap.cpp) -set(PLATFORM_LIBRARIES dl) diff --git a/cmake/myproto.cmake b/cmake/myproto.cmake deleted file mode 100644 index 4f387f1..0000000 --- a/cmake/myproto.cmake +++ /dev/null @@ -1,14 +0,0 @@ -find_package(Protobuf CONFIG REQUIRED) - -set(PROTO_FILES - go/grpc_server/gen/libcore.proto - ) - -add_library(myproto STATIC ${PROTO_FILES}) -target_link_libraries(myproto - PUBLIC - protobuf::libprotobuf - ) -target_include_directories(myproto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - -protobuf_generate(TARGET myproto LANGUAGE cpp) diff --git a/cmake/nkr.cmake b/cmake/nkr.cmake deleted file mode 100644 index ac501ab..0000000 --- a/cmake/nkr.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# Release -file(STRINGS nekoray_version.txt NKR_VERSION) -add_compile_definitions(NKR_VERSION=\"${NKR_VERSION}\") - -# Debug -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DNKR_CPP_DEBUG") - -# Func -function(nkr_add_compile_definitions arg) - message("[add_compile_definitions] ${ARGV}") - add_compile_definitions(${ARGV}) -endfunction() diff --git a/cmake/print.cmake b/cmake/print.cmake deleted file mode 100644 index 89705d2..0000000 --- a/cmake/print.cmake +++ /dev/null @@ -1,43 +0,0 @@ -macro(print_all_variables) - message(STATUS "print_all_variables------------------------------------------{") - get_cmake_property(_variableNames VARIABLES) - foreach (_variableName ${_variableNames}) - message(STATUS "${_variableName}=${${_variableName}}") - endforeach() - message(STATUS "print_all_variables------------------------------------------}") -endmacro() - -# Get all propreties that cmake supports -if(NOT CMAKE_PROPERTY_LIST) - execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) - - # Convert command output into a CMake list - string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") - string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") -endif() - -function(print_properties) - message("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}") -endfunction() - -function(print_target_properties target) - if(NOT TARGET ${target}) - message(STATUS "There is no target named '${target}'") - return() - endif() - - foreach(property ${CMAKE_PROPERTY_LIST}) - string(REPLACE "" "${CMAKE_BUILD_TYPE}" property ${property}) - - # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i - if(property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$") - continue() - endif() - - get_property(was_set TARGET ${target} PROPERTY ${property} SET) - if(was_set) - get_target_property(value ${target} ${property}) - message("${target} ${property} = ${value}") - endif() - endforeach() -endfunction() diff --git a/cmake/windows/VersionInfo.in b/cmake/windows/VersionInfo.in deleted file mode 100644 index 89625c1..0000000 --- a/cmake/windows/VersionInfo.in +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#ifndef PRODUCT_VERSION_MAJOR - #define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ -#endif - -#ifndef PRODUCT_VERSION_MINOR - #define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ -#endif - -#ifndef PRODUCT_VERSION_PATCH - #define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@ -#endif - -#ifndef PRODUCT_VERSION_BUILD - #define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ -#endif - -#ifndef FILE_VERSION_MAJOR - #define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ -#endif - -#ifndef FILE_VERSION_MINOR - #define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ -#endif - -#ifndef FILE_VERSION_PATCH - #define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@ -#endif - -#ifndef FILE_VERSION_BUILD - #define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ -#endif - -#ifndef __TO_STRING - #define __TO_STRING_IMPL(x) #x - #define __TO_STRING(x) __TO_STRING_IMPL(x) -#endif - -#define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR) -#define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH) -#define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD) -#define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD -#define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0" - -#define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR) -#define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH) -#define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD) -#define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD -#define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" - -#ifndef PRODUCT_ICON - #define PRODUCT_ICON "@PRODUCT_ICON@" -#endif - -#ifndef PRODUCT_COMMENTS - #define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0" -#endif - -#ifndef PRODUCT_COMPANY_NAME - #define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0" -#endif - -#ifndef PRODUCT_COMPANY_COPYRIGHT - #define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0" -#endif - -#ifndef PRODUCT_FILE_DESCRIPTION - #define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0" -#endif - -#ifndef PRODUCT_INTERNAL_NAME - #define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0" -#endif - -#ifndef PRODUCT_ORIGINAL_FILENAME - #define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0" -#endif - -#ifndef PRODUCT_BUNDLE - #define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0" -#endif diff --git a/cmake/windows/VersionResource.rc b/cmake/windows/VersionResource.rc deleted file mode 100644 index b5462ac..0000000 --- a/cmake/windows/VersionResource.rc +++ /dev/null @@ -1,52 +0,0 @@ -#include "VersionInfo.h" - -#if defined(__MINGW64__) || defined(__MINGW32__) - // MinGW-w64, MinGW - #if defined(__has_include) && __has_include() - #include - #else - #include - #include - #endif -#else - // MSVC, Windows SDK - #include -#endif - -IDI_ICON1 ICON PRODUCT_ICON - -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT - -VS_VERSION_INFO VERSIONINFO - FILEVERSION FILE_VERSION_RESOURCE - PRODUCTVERSION PRODUCT_VERSION_RESOURCE - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000904b0" - BEGIN - VALUE "Comments", PRODUCT_COMMENTS - VALUE "CompanyName", PRODUCT_COMPANY_NAME - VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION - VALUE "FileVersion", FILE_VERSION_RESOURCE_STR - VALUE "InternalName", PRODUCT_INTERNAL_NAME - VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT - VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME - VALUE "ProductName", PRODUCT_BUNDLE - VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x9, 1200 - END -END diff --git a/cmake/windows/generate_product_version.cmake b/cmake/windows/generate_product_version.cmake deleted file mode 100644 index bc395d4..0000000 --- a/cmake/windows/generate_product_version.cmake +++ /dev/null @@ -1,107 +0,0 @@ -include (CMakeParseArguments) - -set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR}) - -# generate_product_version() function -# -# This function uses VersionInfo.in template file and VersionResource.rc file -# to generate WIN32 resource with version information and general resource strings. -# -# Usage: -# generate_product_version( -# SomeOutputResourceVariable -# NAME MyGreatProject -# ICON ${PATH_TO_APP_ICON} -# VERSION_MAJOR 2 -# VERSION_MINOR 3 -# VERSION_PATCH ${BUILD_COUNTER} -# VERSION_REVISION ${BUILD_REVISION} -# ) -# where BUILD_COUNTER and BUILD_REVISION could be values from your CI server. -# -# You can use generated resource for your executable targets: -# add_executable(target-name ${target-files} ${SomeOutputResourceVariable}) -# -# You can specify resource strings in arguments: -# NAME - name of executable (no defaults, ex: Microsoft Word) -# BUNDLE - bundle (${NAME} is default, ex: Microsoft Office) -# ICON - path to application icon (${CMAKE_SOURCE_DIR}/product.ico by default) -# VERSION_MAJOR - 1 is default -# VERSION_MINOR - 0 is default -# VERSION_PATCH - 0 is default -# VERSION_REVISION - 0 is default -# COMPANY_NAME - your company name (no defaults) -# COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default -# COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default -# ORIGINAL_FILENAME - ${NAME} is default -# INTERNAL_NAME - ${NAME} is default -# FILE_DESCRIPTION - ${NAME} is default -function(generate_product_version outfiles) - set (options) - set (oneValueArgs - NAME - BUNDLE - ICON - VERSION_MAJOR - VERSION_MINOR - VERSION_PATCH - VERSION_REVISION - COMPANY_NAME - COMPANY_COPYRIGHT - COMMENTS - ORIGINAL_FILENAME - INTERNAL_NAME - FILE_DESCRIPTION) - set (multiValueArgs) - cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "") - set(PRODUCT_BUNDLE "${PRODUCT_NAME}") - endif() - if (NOT PRODUCT_ICON OR "${PRODUCT_ICON}" STREQUAL "") - set(PRODUCT_ICON "${CMAKE_SOURCE_DIR}/product.ico") - endif() - - if (NOT PRODUCT_VERSION_MAJOR EQUAL 0 AND (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "")) - set(PRODUCT_VERSION_MAJOR 1) - endif() - if (NOT PRODUCT_VERSION_MINOR EQUAL 0 AND (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "")) - set(PRODUCT_VERSION_MINOR 0) - endif() - if (NOT PRODUCT_VERSION_PATCH EQUAL 0 AND (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "")) - set(PRODUCT_VERSION_PATCH 0) - endif() - if (NOT PRODUCT_VERSION_REVISION EQUAL 0 AND (NOT PRODUCT_VERSION_REVISION OR "${PRODUCT_VERSION_REVISION}" STREQUAL "")) - set(PRODUCT_VERSION_REVISION 0) - endif() - - if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "") - string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y") - set(PRODUCT_COMPANY_COPYRIGHT "${PRODUCT_COMPANY_NAME} (C) Copyright ${PRODUCT_CURRENT_YEAR}") - endif() - if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "") - set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}") - endif() - if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "") - set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}") - endif() - if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "") - set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}") - endif() - if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "") - set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}") - endif() - - set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h) - set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc) - configure_file( - ${GenerateProductVersionCurrentDir}/VersionInfo.in - ${_VersionInfoFile} - @ONLY) - configure_file( - ${GenerateProductVersionCurrentDir}/VersionResource.rc - ${_VersionResourceFile} - COPYONLY) - list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile}) - set (${outfiles} ${${outfiles}} PARENT_SCOPE) -endfunction() diff --git a/cmake/windows/windows.cmake b/cmake/windows/windows.cmake deleted file mode 100644 index 5859588..0000000 --- a/cmake/windows/windows.cmake +++ /dev/null @@ -1,24 +0,0 @@ -set(PLATFORM_SOURCES 3rdparty/WinCommander.cpp sys/windows/guihelper.cpp sys/windows/MiniDump.cpp) -set(PLATFORM_LIBRARIES wininet wsock32 ws2_32 user32 rasapi32 iphlpapi) - -include(cmake/windows/generate_product_version.cmake) -generate_product_version( - QV2RAY_RC - ICON "${CMAKE_SOURCE_DIR}/res/nekobox.ico" - NAME "nekobox" - BUNDLE "nekobox" - COMPANY_NAME "nekobox" - COMPANY_COPYRIGHT "nekobox" - FILE_DESCRIPTION "nekobox" -) -add_definitions(-DUNICODE -D_UNICODE -DNOMINMAX) -set(GUI_TYPE WIN32) -if (MINGW) - if (NOT DEFINED MinGW_ROOT) - set(MinGW_ROOT "C:/msys64/mingw64") - endif () -else () - add_compile_options("/utf-8") - add_compile_options("/std:c++17") - add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) -endif () diff --git a/db/ConfigBuilder.cpp b/db/ConfigBuilder.cpp deleted file mode 100644 index 379fff7..0000000 --- a/db/ConfigBuilder.cpp +++ /dev/null @@ -1,818 +0,0 @@ -#include "db/ConfigBuilder.hpp" -#include "db/Database.hpp" -#include "fmt/includes.h" -#include "fmt/Preset.hpp" - -#include -#include -#include - -#define BOX_UNDERLYING_DNS dataStore->core_box_underlying_dns.isEmpty() ? "local" : dataStore->core_box_underlying_dns - -namespace NekoGui { - - QStringList getAutoBypassExternalProcessPaths(const std::shared_ptr &result) { - QStringList paths; - for (const auto &extR: result->extRs) { - auto path = extR->program; - if (path.trimmed().isEmpty()) continue; - paths << path.replace("\\", "/"); - } - return paths; - } - - QString genTunName() { - auto tun_name = "neko-tun"; -#ifdef Q_OS_MACOS - tun_name = "utun9"; -#endif - return tun_name; - } - - void MergeJson(QJsonObject &dst, const QJsonObject &src) { - // 合并 - if (src.isEmpty()) return; - for (const auto &key: src.keys()) { - auto v_src = src[key]; - if (dst.contains(key)) { - auto v_dst = dst[key]; - if (v_src.isObject() && v_dst.isObject()) { // isObject 则合并? - auto v_src_obj = v_src.toObject(); - auto v_dst_obj = v_dst.toObject(); - MergeJson(v_dst_obj, v_src_obj); - dst[key] = v_dst_obj; - } else { - dst[key] = v_src; - } - } else if (v_src.isArray()) { - if (key.startsWith("+")) { - auto key2 = SubStrAfter(key, "+"); - auto v_dst = dst[key2]; - auto v_src_arr = v_src.toArray(); - auto v_dst_arr = v_dst.toArray(); - QJSONARRAY_ADD(v_src_arr, v_dst_arr) - dst[key2] = v_src_arr; - } else if (key.endsWith("+")) { - auto key2 = SubStrBefore(key, "+"); - auto v_dst = dst[key2]; - auto v_src_arr = v_src.toArray(); - auto v_dst_arr = v_dst.toArray(); - QJSONARRAY_ADD(v_dst_arr, v_src_arr) - dst[key2] = v_dst_arr; - } else { - dst[key] = v_src; - } - } else { - dst[key] = v_src; - } - } - } - - // Common - - std::shared_ptr BuildConfig(const std::shared_ptr &ent, bool forTest, bool forExport) { - auto result = std::make_shared(); - auto status = std::make_shared(); - status->ent = ent; - status->result = result; - status->forTest = forTest; - status->forExport = forExport; - - auto customBean = dynamic_cast(ent->bean.get()); - if (customBean != nullptr && customBean->core == "internal-full") { - result->coreConfig = QString2QJsonObject(customBean->config_simple); - } else { - BuildConfigSingBox(status); - } - - // apply custom config - MergeJson(result->coreConfig, QString2QJsonObject(ent->bean->custom_config)); - - return result; - } - - QString BuildChain(int chainId, const std::shared_ptr &status) { - auto group = profileManager->GetGroup(status->ent->gid); - if (group == nullptr) { - status->result->error = QStringLiteral("This profile is not in any group, your data may be corrupted."); - return {}; - } - - auto resolveChain = [=](const std::shared_ptr &ent) { - QList> resolved; - if (ent->type == "chain") { - auto list = ent->ChainBean()->list; - std::reverse(std::begin(list), std::end(list)); - for (auto id: list) { - resolved += profileManager->GetProfile(id); - if (resolved.last() == nullptr) { - status->result->error = QStringLiteral("chain missing ent: %1").arg(id); - break; - } - if (resolved.last()->type == "chain") { - status->result->error = QStringLiteral("chain in chain is not allowed: %1").arg(id); - break; - } - } - } else { - resolved += ent; - }; - return resolved; - }; - - // Make list - auto ents = resolveChain(status->ent); - if (!status->result->error.isEmpty()) return {}; - - if (group->front_proxy_id >= 0) { - auto fEnt = profileManager->GetProfile(group->front_proxy_id); - if (fEnt == nullptr) { - status->result->error = QStringLiteral("front proxy ent not found."); - return {}; - } - ents += resolveChain(fEnt); - if (!status->result->error.isEmpty()) return {}; - } - - // BuildChain - QString chainTagOut = BuildChainInternal(0, ents, status); - - // Chain ent traffic stat - if (ents.length() > 1) { - status->ent->traffic_data->id = status->ent->id; - status->ent->traffic_data->tag = chainTagOut.toStdString(); - status->result->outboundStats += status->ent->traffic_data; - } - - return chainTagOut; - } - -#define DOMAIN_USER_RULE \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->proxy_domain)) { \ - if (dataStore->routing->dns_routing) status->domainListDNSRemote += line; \ - status->domainListRemote += line; \ - } \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->direct_domain)) { \ - if (dataStore->routing->dns_routing) status->domainListDNSDirect += line; \ - status->domainListDirect += line; \ - } \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->block_domain)) { \ - status->domainListBlock += line; \ - } - -#define IP_USER_RULE \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->block_ip)) { \ - status->ipListBlock += line; \ - } \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->proxy_ip)) { \ - status->ipListRemote += line; \ - } \ - for (const auto &line: SplitLinesSkipSharp(dataStore->routing->direct_ip)) { \ - status->ipListDirect += line; \ - } - - QString BuildChainInternal(int chainId, const QList> &ents, - const std::shared_ptr &status) { - QString chainTag = "c-" + Int2String(chainId); - QString chainTagOut; - bool muxApplied = false; - - QString pastTag; - int pastExternalStat = 0; - int index = 0; - - for (const auto &ent: ents) { - // tagOut: v2ray outbound tag for a profile - // profile2 (in) (global) tag g-(id) - // profile1 tag (chainTag)-(id) - // profile0 (out) tag (chainTag)-(id) / single: chainTag=g-(id) - auto tagOut = chainTag + "-" + Int2String(ent->id); - - // needGlobal: can only contain one? - bool needGlobal = false; - - // first profile set as global - auto isFirstProfile = index == ents.length() - 1; - if (isFirstProfile) { - needGlobal = true; - tagOut = "g-" + Int2String(ent->id); - } - - // last profile set as "proxy" - if (chainId == 0 && index == 0) { - needGlobal = false; - tagOut = "proxy"; - } - - // ignoreConnTag - if (index != 0) { - status->result->ignoreConnTag << tagOut; - } - - if (needGlobal) { - if (status->globalProfiles.contains(ent->id)) { - continue; - } - status->globalProfiles += ent->id; - } - - if (index > 0) { - // chain rules: past - if (pastExternalStat == 0) { - auto replaced = status->outbounds.last().toObject(); - replaced["detour"] = tagOut; - status->outbounds.removeLast(); - status->outbounds += replaced; - } else { - status->routingRules += QJsonObject{ - {"inbound", QJsonArray{pastTag + "-mapping"}}, - {"outbound", tagOut}, - }; - } - } else { - // index == 0 means last profile in chain / not chain - chainTagOut = tagOut; - status->result->outboundStat = ent->traffic_data; - } - - // chain rules: this - auto ext_mapping_port = 0; - auto ext_socks_port = 0; - auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile); - if (thisExternalStat < 0) { - status->result->error = "This configuration cannot be set automatically, please try another."; - return {}; - } - - // determine port - if (thisExternalStat > 0) { - if (ent->type == "custom") { - auto bean = ent->CustomBean(); - if (IsValidPort(bean->mapping_port)) { - ext_mapping_port = bean->mapping_port; - } else { - ext_mapping_port = MkPort(); - } - if (IsValidPort(bean->socks_port)) { - ext_socks_port = bean->socks_port; - } else { - ext_socks_port = MkPort(); - } - } else { - ext_mapping_port = MkPort(); - ext_socks_port = MkPort(); - } - } - if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true; - if (thisExternalStat == 1) { - // mapping - status->inbounds += QJsonObject{ - {"type", "direct"}, - {"tag", tagOut + "-mapping"}, - {"listen", "127.0.0.1"}, - {"listen_port", ext_mapping_port}, - {"override_address", ent->bean->serverAddress}, - {"override_port", ent->bean->serverPort}, - }; - // no chain rule and not outbound, so need to set to direct - if (isFirstProfile) { - status->routingRules += QJsonObject{ - {"inbound", QJsonArray{tagOut + "-mapping"}}, - {"outbound", "direct"}, - }; - } - } - - // Outbound - - QJsonObject outbound; - auto stream = GetStreamSettings(ent->bean.get()); - - if (thisExternalStat > 0) { - auto extR = ent->bean->BuildExternal(ext_mapping_port, ext_socks_port, thisExternalStat); - if (extR.program.isEmpty()) { - status->result->error = QObject::tr("Core not found: %1").arg(ent->bean->DisplayCoreType()); - return {}; - } - if (!extR.error.isEmpty()) { // rejected - status->result->error = extR.error; - return {}; - } - extR.tag = ent->bean->DisplayType(); - status->result->extRs.emplace_back(std::make_shared(extR)); - - // SOCKS OUTBOUND - outbound["type"] = "socks"; - outbound["server"] = "127.0.0.1"; - outbound["server_port"] = ext_socks_port; - } else { - const auto coreR = ent->bean->BuildCoreObjSingBox(); - if (coreR.outbound.isEmpty()) { - status->result->error = "unsupported outbound"; - return {}; - } - if (!coreR.error.isEmpty()) { // rejected - status->result->error = coreR.error; - return {}; - } - outbound = coreR.outbound; - } - - // outbound misc - outbound["tag"] = tagOut; - ent->traffic_data->id = ent->id; - ent->traffic_data->tag = tagOut.toStdString(); - status->result->outboundStats += ent->traffic_data; - - // mux common - auto needMux = ent->type == "vmess" || ent->type == "trojan" || ent->type == "vless"; - needMux &= dataStore->mux_concurrency > 0; - - if (stream != nullptr) { - if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) { - needMux = false; - } - if (stream->multiplex_status == 0) { - if (!dataStore->mux_default_on) needMux = false; - } else if (stream->multiplex_status == 1) { - needMux = true; - } else if (stream->multiplex_status == 2) { - needMux = false; - } - } - if (ent->type == "vless" && outbound["flow"] != "") { - needMux = false; - } - - // common - // apply domain_strategy - outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy; - // apply mux - if (!muxApplied && needMux) { - auto muxObj = QJsonObject{ - {"enabled", true}, - {"protocol", dataStore->mux_protocol}, - {"padding", dataStore->mux_padding}, - {"max_streams", dataStore->mux_concurrency}, - }; - outbound["multiplex"] = muxObj; - muxApplied = true; - } - - // apply custom outbound settings - MergeJson(outbound, QString2QJsonObject(ent->bean->custom_outbound)); - - // Bypass Lookup for the first profile - auto serverAddress = ent->bean->serverAddress; - - auto customBean = dynamic_cast(ent->bean.get()); - if (customBean != nullptr && customBean->core == "internal") { - auto server = QString2QJsonObject(customBean->config_simple)["server"].toString(); - if (!server.isEmpty()) serverAddress = server; - } - - if (!IsIpAddress(serverAddress)) { - status->domainListDNSDirect += "full:" + serverAddress; - } - - status->outbounds += outbound; - pastTag = tagOut; - pastExternalStat = thisExternalStat; - index++; - } - - return chainTagOut; - } - - // SingBox - - void BuildConfigSingBox(const std::shared_ptr &status) { - // Log - status->result->coreConfig["log"] = QJsonObject{{"level", dataStore->log_level}}; - - // Inbounds - - // mixed-in - if (IsValidPort(dataStore->inbound_socks_port) && !status->forTest) { - QJsonObject inboundObj; - inboundObj["tag"] = "mixed-in"; - inboundObj["type"] = "mixed"; - inboundObj["listen"] = dataStore->inbound_address; - inboundObj["listen_port"] = dataStore->inbound_socks_port; - if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) { - inboundObj["sniff"] = true; - inboundObj["sniff_override_destination"] = dataStore->routing->sniffing_mode == SniffingMode::FOR_DESTINATION; - } - if (dataStore->inbound_auth->NeedAuth()) { - inboundObj["users"] = QJsonArray{ - QJsonObject{ - {"username", dataStore->inbound_auth->username}, - {"password", dataStore->inbound_auth->password}, - }, - }; - } - inboundObj["domain_strategy"] = dataStore->routing->domain_strategy; - status->inbounds += inboundObj; - } - - // tun-in - if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) { - QJsonObject inboundObj; - inboundObj["tag"] = "tun-in"; - inboundObj["type"] = "tun"; - inboundObj["interface_name"] = genTunName(); - inboundObj["auto_route"] = true; - inboundObj["endpoint_independent_nat"] = true; - inboundObj["mtu"] = dataStore->vpn_mtu; - inboundObj["stack"] = Preset::SingBox::VpnImplementation.value(dataStore->vpn_implementation); - inboundObj["strict_route"] = dataStore->vpn_strict_route; - inboundObj["inet4_address"] = "172.19.0.1/28"; - if (dataStore->vpn_ipv6) inboundObj["inet6_address"] = "fdfe:dcba:9876::1/126"; - if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) { - inboundObj["sniff"] = true; - inboundObj["sniff_override_destination"] = dataStore->routing->sniffing_mode == SniffingMode::FOR_DESTINATION; - } - inboundObj["domain_strategy"] = dataStore->routing->domain_strategy; - status->inbounds += inboundObj; - } - - // Outbounds - auto tagProxy = BuildChain(0, status); - if (!status->result->error.isEmpty()) return; - - // direct & bypass & block - status->outbounds += QJsonObject{ - {"type", "direct"}, - {"tag", "direct"}, - }; - status->outbounds += QJsonObject{ - {"type", "direct"}, - {"tag", "bypass"}, - }; - status->outbounds += QJsonObject{ - {"type", "block"}, - {"tag", "block"}, - }; - if (!status->forTest) { - status->outbounds += QJsonObject{ - {"type", "dns"}, - {"tag", "dns-out"}, - }; - } - - // custom inbound - if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray()) - - status->result->coreConfig.insert("inbounds", status->inbounds); - status->result->coreConfig.insert("outbounds", status->outbounds); - - // user rule - if (!status->forTest) { - DOMAIN_USER_RULE - IP_USER_RULE - } - - // sing-box common rule object - auto make_rule = [&](const QStringList &list, bool isIP = false) { - QJsonObject rule; - // - QJsonArray ip_cidr; - QJsonArray geoip; - // - QJsonArray domain_keyword; - QJsonArray domain_subdomain; - QJsonArray domain_regexp; - QJsonArray domain_full; - QJsonArray geosite; - for (auto item: list) { - if (isIP) { - if (item.startsWith("geoip:")) { - geoip += item.replace("geoip:", ""); - } else { - ip_cidr += item; - } - } else { - // https://www.v2fly.org/config/dns.html#dnsobject - if (item.startsWith("geosite:")) { - geosite += item.replace("geosite:", ""); - } else if (item.startsWith("full:")) { - domain_full += item.replace("full:", "").toLower(); - } else if (item.startsWith("domain:")) { - domain_subdomain += item.replace("domain:", "").toLower(); - } else if (item.startsWith("regexp:")) { - domain_regexp += item.replace("regexp:", "").toLower(); - } else if (item.startsWith("keyword:")) { - domain_keyword += item.replace("keyword:", "").toLower(); - } else { - domain_subdomain += item.toLower(); - } - } - } - if (isIP) { - if (ip_cidr.isEmpty() && geoip.isEmpty()) return rule; - rule["ip_cidr"] = ip_cidr; - rule["geoip"] = geoip; - } else { - if (domain_keyword.isEmpty() && domain_subdomain.isEmpty() && domain_regexp.isEmpty() && domain_full.isEmpty() && geosite.isEmpty()) { - return rule; - } - rule["domain"] = domain_full; - rule["domain_suffix"] = domain_subdomain; // v2ray Subdomain => sing-box suffix - rule["domain_keyword"] = domain_keyword; - rule["domain_regex"] = domain_regexp; - rule["geosite"] = geosite; - } - return rule; - }; - - // final add DNS - QJsonObject dns; - QJsonArray dnsServers; - QJsonArray dnsRules; - - // Remote - if (!status->forTest) - dnsServers += QJsonObject{ - {"tag", "dns-remote"}, - {"address_resolver", "dns-local"}, - {"strategy", dataStore->routing->remote_dns_strategy}, - {"address", dataStore->routing->remote_dns}, - {"detour", tagProxy}, - }; - - // Direct - QJsonObject directObj{ - {"tag", "dns-direct"}, - {"address_resolver", "dns-local"}, - {"strategy", dataStore->routing->direct_dns_strategy}, - {"address", dataStore->routing->direct_dns}, - {"detour", "direct"}, - }; - if (dataStore->routing->dns_final_out == "bypass") { - dnsServers.prepend(directObj); - } else { - dnsServers.append(directObj); - } - dnsRules.append(QJsonObject{ - {"outbound", "any"}, - {"server", "dns-direct"}, - }); - - // block - if (!status->forTest) - dnsServers += QJsonObject{ - {"tag", "dns-block"}, - {"address", "rcode://success"}, - }; - - // Fakedns - if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) { - dnsServers += QJsonObject{ - {"tag", "dns-fake"}, - {"address", "fakeip"}, - }; - dns["fakeip"] = QJsonObject{ - {"enabled", true}, - {"inet4_range", "198.18.0.0/15"}, - {"inet6_range", "fc00::/18"}, - }; - } - - // Underlying 100% Working DNS ? - dnsServers += QJsonObject{ - {"tag", "dns-local"}, - {"address", BOX_UNDERLYING_DNS}, - {"detour", "direct"}, - }; - - // sing-box dns rule object - auto add_rule_dns = [&](const QStringList &list, const QString &server) { - auto rule = make_rule(list, false); - if (rule.isEmpty()) return; - rule["server"] = server; - dnsRules += rule; - }; - add_rule_dns(status->domainListDNSRemote, "dns-remote"); - add_rule_dns(status->domainListDNSDirect, "dns-direct"); - - // built-in rules - if (!status->forTest) { - dnsRules += QJsonObject{ - {"query_type", QJsonArray{32, 33}}, - {"server", "dns-block"}, - }; - dnsRules += QJsonObject{ - {"domain_suffix", ".lan"}, - {"server", "dns-block"}, - }; - } - - // fakedns rule - if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) { - dnsRules += QJsonObject{ - {"inbound", "tun-in"}, - {"server", "dns-fake"}, - }; - } - - dns["servers"] = dnsServers; - dns["rules"] = dnsRules; - dns["independent_cache"] = true; - - if (dataStore->routing->use_dns_object) { - dns = QString2QJsonObject(dataStore->routing->dns_object); - } - status->result->coreConfig.insert("dns", dns); - - // Routing - - // dns hijack - if (!status->forTest) { - status->routingRules += QJsonObject{ - {"protocol", "dns"}, - {"outbound", "dns-out"}, - }; - } - - // sing-box routing rule object - auto add_rule_route = [&](const QStringList &list, bool isIP, const QString &out) { - auto rule = make_rule(list, isIP); - if (rule.isEmpty()) return; - rule["outbound"] = out; - status->routingRules += rule; - }; - - // final add user rule - add_rule_route(status->domainListBlock, false, "block"); - add_rule_route(status->domainListRemote, false, tagProxy); - add_rule_route(status->domainListDirect, false, "bypass"); - add_rule_route(status->ipListBlock, true, "block"); - add_rule_route(status->ipListRemote, true, tagProxy); - add_rule_route(status->ipListDirect, true, "bypass"); - - // built-in rules - status->routingRules += QJsonObject{ - {"network", "udp"}, - {"port", QJsonArray{135, 137, 138, 139, 5353}}, - {"outbound", "block"}, - }; - status->routingRules += QJsonObject{ - {"ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}}, - {"outbound", "block"}, - }; - status->routingRules += QJsonObject{ - {"source_ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}}, - {"outbound", "block"}, - }; - - // tun user rule - if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) { - auto match_out = dataStore->vpn_rule_white ? "proxy" : "bypass"; - - QString process_name_rule = dataStore->vpn_rule_process.trimmed(); - if (!process_name_rule.isEmpty()) { - auto arr = SplitLinesSkipSharp(process_name_rule); - QJsonObject rule{{"outbound", match_out}, - {"process_name", QList2QJsonArray(arr)}}; - status->routingRules += rule; - } - - QString cidr_rule = dataStore->vpn_rule_cidr.trimmed(); - if (!cidr_rule.isEmpty()) { - auto arr = SplitLinesSkipSharp(cidr_rule); - QJsonObject rule{{"outbound", match_out}, - {"ip_cidr", QList2QJsonArray(arr)}}; - status->routingRules += rule; - } - - auto autoBypassExternalProcessPaths = getAutoBypassExternalProcessPaths(status->result); - if (!autoBypassExternalProcessPaths.isEmpty()) { - QJsonObject rule{{"outbound", "bypass"}, - {"process_name", QList2QJsonArray(autoBypassExternalProcessPaths)}}; - status->routingRules += rule; - } - } - - // geopath - auto geoip = FindCoreAsset("geoip.db"); - auto geosite = FindCoreAsset("geosite.db"); - if (geoip.isEmpty()) status->result->error = +"geoip.db not found"; - if (geosite.isEmpty()) status->result->error = +"geosite.db not found"; - - // final add routing rule - auto routingRules = QString2QJsonObject(dataStore->routing->custom)["rules"].toArray(); - if (status->forTest) routingRules = {}; - if (!status->forTest) QJSONARRAY_ADD(routingRules, QString2QJsonObject(dataStore->custom_route_global)["rules"].toArray()) - QJSONARRAY_ADD(routingRules, status->routingRules) - auto routeObj = QJsonObject{ - {"rules", routingRules}, - {"auto_detect_interface", dataStore->spmode_vpn}, // TODO force enable? - { - "geoip", - QJsonObject{ - {"path", geoip}, - }, - }, - { - "geosite", - QJsonObject{ - {"path", geosite}, - }, - }}; - if (!status->forTest) routeObj["final"] = dataStore->routing->def_outbound; - if (status->forExport) { - routeObj.remove("geoip"); - routeObj.remove("geosite"); - routeObj.remove("auto_detect_interface"); - } - status->result->coreConfig.insert("route", routeObj); - - // experimental - QJsonObject experimentalObj; - - if (!status->forTest && dataStore->core_box_clash_api > 0) { - QJsonObject clash_api = { - {"external_controller", "127.0.0.1:" + Int2String(dataStore->core_box_clash_api)}, - {"secret", dataStore->core_box_clash_api_secret}, - {"external_ui", "dashboard"}, - }; - experimentalObj["clash_api"] = clash_api; - } - - if (!experimentalObj.isEmpty()) status->result->coreConfig.insert("experimental", experimentalObj); - } - - QString WriteVPNSingBoxConfig() { - // tun user rule - auto match_out = dataStore->vpn_rule_white ? "neko-socks" : "direct"; - auto no_match_out = dataStore->vpn_rule_white ? "direct" : "neko-socks"; - - QString process_name_rule = dataStore->vpn_rule_process.trimmed(); - if (!process_name_rule.isEmpty()) { - auto arr = SplitLinesSkipSharp(process_name_rule); - QJsonObject rule{{"outbound", match_out}, - {"process_name", QList2QJsonArray(arr)}}; - process_name_rule = "," + QJsonObject2QString(rule, false); - } - - QString cidr_rule = dataStore->vpn_rule_cidr.trimmed(); - if (!cidr_rule.isEmpty()) { - auto arr = SplitLinesSkipSharp(cidr_rule); - QJsonObject rule{{"outbound", match_out}, - {"ip_cidr", QList2QJsonArray(arr)}}; - cidr_rule = "," + QJsonObject2QString(rule, false); - } - - // TODO bypass ext core process path? - - // auth - QString socks_user_pass; - if (dataStore->inbound_auth->NeedAuth()) { - socks_user_pass = R"( "username": "%1", "password": "%2", )"; - socks_user_pass = socks_user_pass.arg(dataStore->inbound_auth->username, dataStore->inbound_auth->password); - } - // gen config - auto configFn = ":/neko/vpn/sing-box-vpn.json"; - if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json"; - auto config = ReadFileText(configFn) - .replace("//%IPV6_ADDRESS%", dataStore->vpn_ipv6 ? R"("inet6_address": "fdfe:dcba:9876::1/126",)" : "") - .replace("//%SOCKS_USER_PASS%", socks_user_pass) - .replace("//%PROCESS_NAME_RULE%", process_name_rule) - .replace("//%CIDR_RULE%", cidr_rule) - .replace("%MTU%", Int2String(dataStore->vpn_mtu)) - .replace("%STACK%", Preset::SingBox::VpnImplementation.value(dataStore->vpn_implementation)) - .replace("%TUN_NAME%", genTunName()) - .replace("%STRICT_ROUTE%", dataStore->vpn_strict_route ? "true" : "false") - .replace("%FINAL_OUT%", no_match_out) - .replace("%DNS_ADDRESS%", BOX_UNDERLYING_DNS) - .replace("%FAKE_DNS_INBOUND%", dataStore->fake_dns ? "tun-in" : "empty") - .replace("%PORT%", Int2String(dataStore->inbound_socks_port)); - // write config - QFile file; - file.setFileName(QFileInfo(configFn).fileName()); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); - file.write(config.toUtf8()); - file.close(); - return QFileInfo(file).absoluteFilePath(); - } - - QString WriteVPNLinuxScript(const QString &configPath) { -#ifdef Q_OS_WIN - return {}; -#endif - // gen script - auto scriptFn = ":/neko/vpn/vpn-run-root.sh"; - if (QFile::exists("vpn/vpn-run-root.sh")) scriptFn = "vpn/vpn-run-root.sh"; - auto script = ReadFileText(scriptFn) - .replace("./nekobox_core", QApplication::applicationDirPath() + "/nekobox_core") - .replace("$CONFIG_PATH", configPath); - // write script - QFile file2; - file2.setFileName(QFileInfo(scriptFn).fileName()); - file2.open(QIODevice::ReadWrite | QIODevice::Truncate); - file2.write(script.toUtf8()); - file2.close(); - return QFileInfo(file2).absoluteFilePath(); - } - -} // namespace NekoGui diff --git a/db/ConfigBuilder.hpp b/db/ConfigBuilder.hpp deleted file mode 100644 index 3c150c7..0000000 --- a/db/ConfigBuilder.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "ProxyEntity.hpp" -#include "sys/ExternalProcess.hpp" - -namespace NekoGui { - class BuildConfigResult { - public: - QString error; - QJsonObject coreConfig; - - QList> outboundStats; // all, but not including "bypass" "block" - std::shared_ptr outboundStat; // main - QStringList ignoreConnTag; - - std::list> extRs; - }; - - class BuildConfigStatus { - public: - std::shared_ptr result; - std::shared_ptr ent; - bool forTest; - bool forExport; - - // priv - QList globalProfiles; - - // xxList is V2Ray format string list - - QStringList domainListDNSRemote; - QStringList domainListDNSDirect; - QStringList domainListRemote; - QStringList domainListDirect; - QStringList ipListRemote; - QStringList ipListDirect; - QStringList domainListBlock; - QStringList ipListBlock; - - // config format - - QJsonArray routingRules; - QJsonArray inbounds; - QJsonArray outbounds; - }; - - std::shared_ptr BuildConfig(const std::shared_ptr &ent, bool forTest, bool forExport); - - void BuildConfigSingBox(const std::shared_ptr &status); - - QString BuildChain(int chainId, const std::shared_ptr &status); - - QString BuildChainInternal(int chainId, const QList> &ents, - const std::shared_ptr &status); - - QString WriteVPNSingBoxConfig(); - - QString WriteVPNLinuxScript(const QString &configPath); -} // namespace NekoGui diff --git a/db/Database.cpp b/db/Database.cpp deleted file mode 100644 index 5736438..0000000 --- a/db/Database.cpp +++ /dev/null @@ -1,394 +0,0 @@ -#include "Database.hpp" - -#include "fmt/includes.h" - -#include -#include -#include - -namespace NekoGui { - - ProfileManager *profileManager = new ProfileManager(); - - ProfileManager::ProfileManager() : JsonStore("groups/pm.json") { - _add(new configItem("groups", &groupsTabOrder, itemType::integerList)); - } - - QList filterIntJsonFile(const QString &path) { - QList result; - QDir dr(path); - auto entryList = dr.entryList(QDir::Files); - for (auto e: entryList) { - e = e.toLower(); - if (!e.endsWith(".json", Qt::CaseInsensitive)) continue; - e = e.remove(".json", Qt::CaseInsensitive); - bool ok; - auto id = e.toInt(&ok); - if (ok) { - result << id; - } - } - std::sort(result.begin(), result.end()); - return result; - } - - void ProfileManager::LoadManager() { - JsonStore::Load(); - // - profiles = {}; - groups = {}; - profilesIdOrder = filterIntJsonFile("profiles"); - groupsIdOrder = filterIntJsonFile("groups"); - // Load Proxys - QList delProfile; - for (auto id: profilesIdOrder) { - auto ent = LoadProxyEntity(QStringLiteral("profiles/%1.json").arg(id)); - // Corrupted profile? - if (ent == nullptr || ent->bean == nullptr || ent->bean->version == -114514) { - delProfile << id; - continue; - } - profiles[id] = ent; - } - // Clear Corrupted profile - for (auto id: delProfile) { - DeleteProfile(id); - } - // Load Groups - auto loadedOrder = groupsTabOrder; - groupsTabOrder = {}; - for (auto id: groupsIdOrder) { - auto ent = LoadGroup(QStringLiteral("groups/%1.json").arg(id)); - // Corrupted group? - if (ent->id != id) { - continue; - } - // Ensure order contains every group - if (!loadedOrder.contains(id)) { - loadedOrder << id; - } - groups[id] = ent; - } - // Ensure groups contains order - for (auto id: loadedOrder) { - if (groups.count(id)) { - groupsTabOrder << id; - } - } - // First setup - if (groups.empty()) { - auto defaultGroup = NekoGui::ProfileManager::NewGroup(); - defaultGroup->name = QObject::tr("Default"); - NekoGui::profileManager->AddGroup(defaultGroup); - } - // - if (dataStore->flag_reorder) { - { - // remove all (contains orphan) - for (const auto &profile: profiles) { - QFile::remove(profile.second->fn); - } - } - std::map gidOld2New; - { - int i = 0; - int ii = 0; - QList newProfilesIdOrder; - std::map> newProfiles; - for (auto gid: groupsTabOrder) { - auto group = GetGroup(gid); - gidOld2New[gid] = ii++; - for (auto const &profile: group->ProfilesWithOrder()) { - auto oldId = profile->id; - auto newId = i++; - profile->id = newId; - profile->gid = gidOld2New[gid]; - profile->fn = QStringLiteral("profiles/%1.json").arg(newId); - profile->Save(); - newProfiles[newId] = profile; - newProfilesIdOrder << newId; - } - group->order = {}; - group->Save(); - } - profiles = newProfiles; - profilesIdOrder = newProfilesIdOrder; - } - { - QList newGroupsIdOrder; - std::map> newGroups; - for (auto oldGid: groupsTabOrder) { - auto newId = gidOld2New[oldGid]; - auto group = groups[oldGid]; - QFile::remove(group->fn); - group->id = newId; - group->fn = QStringLiteral("groups/%1.json").arg(newId); - group->Save(); - newGroups[newId] = group; - newGroupsIdOrder << newId; - } - groups = newGroups; - groupsIdOrder = newGroupsIdOrder; - groupsTabOrder = newGroupsIdOrder; - } - MessageBoxInfo(software_name, "Profiles and groups reorder complete."); - } - } - - void ProfileManager::SaveManager() { - JsonStore::Save(); - } - - std::shared_ptr ProfileManager::LoadProxyEntity(const QString &jsonPath) { - // Load type - ProxyEntity ent0(nullptr, nullptr); - ent0.fn = jsonPath; - auto validJson = ent0.Load(); - auto type = ent0.type; - - // Load content - std::shared_ptr ent; - bool validType = validJson; - - if (validType) { - ent = NewProxyEntity(type); - validType = ent->bean->version != -114514; - } - - if (validType) { - ent->load_control_must = true; - ent->fn = jsonPath; - ent->Load(); - } - return ent; - } - - // 新建的不给 fn 和 id - - std::shared_ptr ProfileManager::NewProxyEntity(const QString &type) { - NekoGui_fmt::AbstractBean *bean; - - if (type == "socks") { - bean = new NekoGui_fmt::SocksHttpBean(NekoGui_fmt::SocksHttpBean::type_Socks5); - } else if (type == "http") { - bean = new NekoGui_fmt::SocksHttpBean(NekoGui_fmt::SocksHttpBean::type_HTTP); - } else if (type == "shadowsocks") { - bean = new NekoGui_fmt::ShadowSocksBean(); - } else if (type == "chain") { - bean = new NekoGui_fmt::ChainBean(); - } else if (type == "vmess") { - bean = new NekoGui_fmt::VMessBean(); - } else if (type == "trojan") { - bean = new NekoGui_fmt::TrojanVLESSBean(NekoGui_fmt::TrojanVLESSBean::proxy_Trojan); - } else if (type == "vless") { - bean = new NekoGui_fmt::TrojanVLESSBean(NekoGui_fmt::TrojanVLESSBean::proxy_VLESS); - } else if (type == "naive") { - bean = new NekoGui_fmt::NaiveBean(); - } else if (type == "hysteria2") { - bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria2); - } else if (type == "tuic") { - bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_TUIC); - } else if (type == "custom") { - bean = new NekoGui_fmt::CustomBean(); - } else { - bean = new NekoGui_fmt::AbstractBean(-114514); - } - - auto ent = std::make_shared(bean, type); - return ent; - } - - std::shared_ptr ProfileManager::NewGroup() { - auto ent = std::make_shared(); - return ent; - } - - // ProxyEntity - - ProxyEntity::ProxyEntity(NekoGui_fmt::AbstractBean *bean, const QString &type_) { - if (type_ != nullptr) this->type = type_; - - _add(new configItem("type", &type, itemType::string)); - _add(new configItem("id", &id, itemType::integer)); - _add(new configItem("gid", &gid, itemType::integer)); - _add(new configItem("yc", &latency, itemType::integer)); - _add(new configItem("report", &full_test_report, itemType::string)); - - // 可以不关联 bean,只加载 ProxyEntity 的信息 - if (bean != nullptr) { - this->bean = std::shared_ptr(bean); - // 有虚函数就要在这里 dynamic_cast - _add(new configItem("bean", dynamic_cast(bean), itemType::jsonStore)); - _add(new configItem("traffic", dynamic_cast(traffic_data.get()), itemType::jsonStore)); - } - }; - - QString ProxyEntity::DisplayLatency() const { - if (latency < 0) { - return QObject::tr("Unavailable"); - } else if (latency > 0) { - return UNICODE_LRO + QStringLiteral("%1 ms").arg(latency); - } else { - return ""; - } - } - - QColor ProxyEntity::DisplayLatencyColor() const { - if (latency < 0) { - return Qt::red; - } else if (latency > 0) { - auto greenMs = dataStore->test_latency_url.startsWith("https://") ? 200 : 100; - if (latency < greenMs) { - return Qt::darkGreen; - } else { - return Qt::darkYellow; - } - } else { - return {}; - } - } - - // Profile - - int ProfileManager::NewProfileID() const { - if (profiles.empty()) { - return 0; - } else { - return profilesIdOrder.last() + 1; - } - } - - bool ProfileManager::AddProfile(const std::shared_ptr &ent, int gid) { - if (ent->id >= 0) { - return false; - } - - ent->gid = gid < 0 ? dataStore->current_group : gid; - ent->id = NewProfileID(); - profiles[ent->id] = ent; - profilesIdOrder.push_back(ent->id); - - ent->fn = QStringLiteral("profiles/%1.json").arg(ent->id); - ent->Save(); - return true; - } - - void ProfileManager::DeleteProfile(int id) { - if (id < 0) return; - if (dataStore->started_id == id) return; - profiles.erase(id); - profilesIdOrder.removeAll(id); - QFile(QStringLiteral("profiles/%1.json").arg(id)).remove(); - } - - void ProfileManager::MoveProfile(const std::shared_ptr &ent, int gid) { - if (gid == ent->gid || gid < 0) return; - auto oldGroup = GetGroup(ent->gid); - if (oldGroup != nullptr && !oldGroup->order.isEmpty()) { - oldGroup->order.removeAll(ent->id); - oldGroup->Save(); - } - auto newGroup = GetGroup(gid); - if (newGroup != nullptr && !newGroup->order.isEmpty()) { - newGroup->order.push_back(ent->id); - newGroup->Save(); - } - ent->gid = gid; - ent->Save(); - } - - std::shared_ptr ProfileManager::GetProfile(int id) { - return profiles.count(id) ? profiles[id] : nullptr; - } - - // Group - - Group::Group() { - _add(new configItem("id", &id, itemType::integer)); - _add(new configItem("front_proxy_id", &front_proxy_id, itemType::integer)); - _add(new configItem("archive", &archive, itemType::boolean)); - _add(new configItem("skip_auto_update", &skip_auto_update, itemType::boolean)); - _add(new configItem("name", &name, itemType::string)); - _add(new configItem("order", &order, itemType::integerList)); - _add(new configItem("url", &url, itemType::string)); - _add(new configItem("info", &info, itemType::string)); - _add(new configItem("lastup", &sub_last_update, itemType::integer64)); - _add(new configItem("manually_column_width", &manually_column_width, itemType::boolean)); - _add(new configItem("column_width", &column_width, itemType::integerList)); - } - - std::shared_ptr ProfileManager::LoadGroup(const QString &jsonPath) { - auto ent = std::make_shared(); - ent->fn = jsonPath; - ent->Load(); - return ent; - } - - int ProfileManager::NewGroupID() const { - if (groups.empty()) { - return 0; - } else { - return groupsIdOrder.last() + 1; - } - } - - bool ProfileManager::AddGroup(const std::shared_ptr &ent) { - if (ent->id >= 0) { - return false; - } - - ent->id = NewGroupID(); - groups[ent->id] = ent; - groupsIdOrder.push_back(ent->id); - groupsTabOrder.push_back(ent->id); - - ent->fn = QStringLiteral("groups/%1.json").arg(ent->id); - ent->Save(); - return true; - } - - void ProfileManager::DeleteGroup(int gid) { - if (groups.size() <= 1) return; - QList toDelete; - for (const auto &[id, profile]: profiles) { - if (profile->gid == gid) toDelete += id; // map访问中,不能操作 - } - for (const auto &id: toDelete) { - DeleteProfile(id); - } - groups.erase(gid); - groupsIdOrder.removeAll(gid); - groupsTabOrder.removeAll(gid); - QFile(QStringLiteral("groups/%1.json").arg(gid)).remove(); - } - - std::shared_ptr ProfileManager::GetGroup(int id) { - return groups.count(id) ? groups[id] : nullptr; - } - - std::shared_ptr ProfileManager::CurrentGroup() { - return GetGroup(dataStore->current_group); - } - - QList> Group::Profiles() const { - QList> ret; - for (const auto &[_, profile]: profileManager->profiles) { - if (id == profile->gid) ret += profile; - } - return ret; - } - - QList> Group::ProfilesWithOrder() const { - if (order.isEmpty()) { - return Profiles(); - } else { - QList> ret; - for (auto _id: order) { - auto ent = profileManager->GetProfile(_id); - if (ent != nullptr) ret += ent; - } - return ret; - } - } - -} // namespace NekoGui diff --git a/db/Database.hpp b/db/Database.hpp deleted file mode 100644 index dc7272f..0000000 --- a/db/Database.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "main/NekoGui.hpp" -#include "ProxyEntity.hpp" -#include "Group.hpp" - -namespace NekoGui { - class ProfileManager : private JsonStore { - public: - // JsonStore - - // order -> id - QList groupsTabOrder; - - // Manager - - std::map> profiles; - std::map> groups; - - ProfileManager(); - - // LoadManager Reset and loads profiles & groups - void LoadManager(); - - void SaveManager(); - - [[nodiscard]] static std::shared_ptr NewProxyEntity(const QString &type); - - [[nodiscard]] static std::shared_ptr NewGroup(); - - bool AddProfile(const std::shared_ptr &ent, int gid = -1); - - void DeleteProfile(int id); - - void MoveProfile(const std::shared_ptr &ent, int gid); - - std::shared_ptr GetProfile(int id); - - bool AddGroup(const std::shared_ptr &ent); - - void DeleteGroup(int gid); - - std::shared_ptr GetGroup(int id); - - std::shared_ptr CurrentGroup(); - - private: - // sort by id - QList profilesIdOrder; - QList groupsIdOrder; - - [[nodiscard]] int NewProfileID() const; - - [[nodiscard]] int NewGroupID() const; - - static std::shared_ptr LoadProxyEntity(const QString &jsonPath); - - static std::shared_ptr LoadGroup(const QString &jsonPath); - }; - - extern ProfileManager *profileManager; -} // namespace NekoGui diff --git a/db/Group.hpp b/db/Group.hpp deleted file mode 100644 index e55c680..0000000 --- a/db/Group.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "main/NekoGui.hpp" -#include "ProxyEntity.hpp" - -namespace NekoGui { - class Group : public JsonStore { - public: - int id = -1; - bool archive = false; - bool skip_auto_update = false; - QString name = ""; - QString url = ""; - QString info = ""; - qint64 sub_last_update = 0; - int front_proxy_id = -1; - - // list ui - bool manually_column_width = false; - QList column_width; - QList order; - - Group(); - - // 按 id 顺序 - [[nodiscard]] QList> Profiles() const; - - // 按 显示 顺序 - [[nodiscard]] QList> ProfilesWithOrder() const; - }; -} // namespace NekoGui diff --git a/db/ProfileFilter.cpp b/db/ProfileFilter.cpp deleted file mode 100644 index ea062d3..0000000 --- a/db/ProfileFilter.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "ProfileFilter.hpp" - -namespace NekoGui { - - QString ProfileFilter_ent_key(const std::shared_ptr &ent, bool by_address) { - by_address &= ent->type != "custom"; - return by_address ? (ent->bean->DisplayAddress() + ent->bean->DisplayType()) - : QJsonObject2QString(ent->bean->ToJson({"c_cfg", "c_out"}), true) + ent->bean->DisplayType(); - } - - void ProfileFilter::Uniq(const QList> &in, - QList> &out, - bool by_address, bool keep_last) { - QMap> hashMap; - - for (const auto &ent: in) { - QString key = ProfileFilter_ent_key(ent, by_address); - if (hashMap.contains(key)) { - if (keep_last) { - out.removeAll(hashMap[key]); - hashMap[key] = ent; - out += ent; - } - } else { - hashMap[key] = ent; - out += ent; - } - } - } - - void ProfileFilter::Common(const QList> &src, - const QList> &dst, - QList> &outSrc, - QList> &outDst, - bool by_address) { - QMap> hashMap; - - for (const auto &ent: src) { - QString key = ProfileFilter_ent_key(ent, by_address); - hashMap[key] = ent; - } - for (const auto &ent: dst) { - QString key = ProfileFilter_ent_key(ent, by_address); - if (hashMap.contains(key)) { - outDst += ent; - outSrc += hashMap[key]; - } - } - } - - void ProfileFilter::OnlyInSrc(const QList> &src, - const QList> &dst, - QList> &out, - bool by_address) { - QMap hashMap; - - for (const auto &ent: dst) { - QString key = ProfileFilter_ent_key(ent, by_address); - hashMap[key] = true; - } - for (const auto &ent: src) { - QString key = ProfileFilter_ent_key(ent, by_address); - if (!hashMap.contains(key)) out += ent; - } - } - - void ProfileFilter::OnlyInSrc_ByPointer(const QList> &src, - const QList> &dst, - QList> &out) { - for (const auto &ent: src) { - if (!dst.contains(ent)) out += ent; - } - } - -} // namespace NekoGui \ No newline at end of file diff --git a/db/ProfileFilter.hpp b/db/ProfileFilter.hpp deleted file mode 100644 index b2d35b1..0000000 --- a/db/ProfileFilter.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ProxyEntity.hpp" - -namespace NekoGui { - class ProfileFilter { - public: - static void Uniq( - const QList> &in, - QList> &out, - bool by_address = false, // def by bean - bool keep_last = false // def keep first - ); - - static void Common( - const QList> &src, - const QList> &dst, - QList> &outSrc, - QList> &outDst, - bool by_address = false // def by bean - ); - - static void OnlyInSrc( - const QList> &src, - const QList> &dst, - QList> &out, - bool by_address = false // def by bean - ); - - static void OnlyInSrc_ByPointer( - const QList> &src, - const QList> &dst, - QList> &out); - }; -} // namespace NekoGui diff --git a/db/ProxyEntity.hpp b/db/ProxyEntity.hpp deleted file mode 100644 index f0cded9..0000000 --- a/db/ProxyEntity.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "main/NekoGui.hpp" -#include "db/traffic/TrafficData.hpp" -#include "fmt/AbstractBean.hpp" - -namespace NekoGui_fmt { - class SocksHttpBean; - - class ShadowSocksBean; - - class VMessBean; - - class TrojanVLESSBean; - - class NaiveBean; - - class QUICBean; - - class CustomBean; - - class ChainBean; -}; // namespace NekoGui_fmt - -namespace NekoGui { - class ProxyEntity : public JsonStore { - public: - QString type; - - int id = -1; - int gid = 0; - int latency = 0; - std::shared_ptr bean; - std::shared_ptr traffic_data = std::make_shared(""); - - QString full_test_report; - - ProxyEntity(NekoGui_fmt::AbstractBean *bean, const QString &type_); - - [[nodiscard]] QString DisplayLatency() const; - - [[nodiscard]] QColor DisplayLatencyColor() const; - - [[nodiscard]] NekoGui_fmt::ChainBean *ChainBean() const { - return (NekoGui_fmt::ChainBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::SocksHttpBean *SocksHTTPBean() const { - return (NekoGui_fmt::SocksHttpBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::ShadowSocksBean *ShadowSocksBean() const { - return (NekoGui_fmt::ShadowSocksBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::VMessBean *VMessBean() const { - return (NekoGui_fmt::VMessBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::TrojanVLESSBean *TrojanVLESSBean() const { - return (NekoGui_fmt::TrojanVLESSBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::NaiveBean *NaiveBean() const { - return (NekoGui_fmt::NaiveBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::QUICBean *QUICBean() const { - return (NekoGui_fmt::QUICBean *) bean.get(); - }; - - [[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const { - return (NekoGui_fmt::CustomBean *) bean.get(); - }; - }; -} // namespace NekoGui diff --git a/db/traffic/TrafficData.hpp b/db/traffic/TrafficData.hpp deleted file mode 100644 index 8ac2e09..0000000 --- a/db/traffic/TrafficData.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "main/NekoGui.hpp" - -namespace NekoGui_traffic { - class TrafficData : public JsonStore { - public: - int id = -1; // ent id - std::string tag; - - long long downlink = 0; - long long uplink = 0; - long long downlink_rate = 0; - long long uplink_rate = 0; - - long long last_update; - - explicit TrafficData(std::string tag) { - this->tag = std::move(tag); - _add(new configItem("dl", &downlink, itemType::integer64)); - _add(new configItem("ul", &uplink, itemType::integer64)); - }; - - void Reset() { - downlink = 0; - uplink = 0; - downlink_rate = 0; - uplink_rate = 0; - } - - [[nodiscard]] QString DisplaySpeed() const { - return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate)); - } - - [[nodiscard]] QString DisplayTraffic() const { - if (downlink + uplink == 0) return ""; - return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink)); - } - }; -} // namespace NekoGui_traffic diff --git a/db/traffic/TrafficLooper.cpp b/db/traffic/TrafficLooper.cpp deleted file mode 100644 index 04f10cf..0000000 --- a/db/traffic/TrafficLooper.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "TrafficLooper.hpp" - -#include "rpc/gRPC.h" -#include "ui/mainwindow_interface.h" - -#include -#include -#include -#include -#include - -namespace NekoGui_traffic { - - TrafficLooper *trafficLooper = new TrafficLooper; - QElapsedTimer elapsedTimer; - - TrafficData *TrafficLooper::update_stats(TrafficData *item) { -#ifndef NKR_NO_GRPC - // last update - auto now = elapsedTimer.elapsed(); - auto interval = now - item->last_update; - item->last_update = now; - if (interval <= 0) return nullptr; - - // query - auto uplink = NekoGui_rpc::defaultClient->QueryStats(item->tag, "uplink"); - auto downlink = NekoGui_rpc::defaultClient->QueryStats(item->tag, "downlink"); - - // add diff - item->downlink += downlink; - item->uplink += uplink; - item->downlink_rate = downlink * 1000 / interval; - item->uplink_rate = uplink * 1000 / interval; - - // return diff - auto ret = new TrafficData(item->tag); - ret->downlink = downlink; - ret->uplink = uplink; - ret->downlink_rate = item->downlink_rate; - ret->uplink_rate = item->uplink_rate; - return ret; -#endif - return nullptr; - } - - QJsonArray TrafficLooper::get_connection_list() { -#ifndef NKR_NO_GRPC - auto str = NekoGui_rpc::defaultClient->ListConnections(); - QJsonDocument jsonDocument = QJsonDocument::fromJson(str.c_str()); - return jsonDocument.array(); -#else - return QJsonArray{}; -#endif - } - - void TrafficLooper::UpdateAll() { - std::map updated; // tag to diff - for (const auto &item: this->items) { - auto data = item.get(); - auto diff = updated[data->tag]; - // 避免重复查询一个 outbound tag - if (diff == nullptr) { - diff = update_stats(data); - updated[data->tag] = diff; - } else { - data->uplink += diff->uplink; - data->downlink += diff->downlink; - data->uplink_rate = diff->uplink_rate; - data->downlink_rate = diff->downlink_rate; - } - } - updated[bypass->tag] = update_stats(bypass); - // - for (const auto &pair: updated) { - delete pair.second; - } - } - - void TrafficLooper::Loop() { - elapsedTimer.start(); - while (true) { - auto sleep_ms = NekoGui::dataStore->traffic_loop_interval; - if (sleep_ms < 500 || sleep_ms > 5000) sleep_ms = 1000; - QThread::msleep(sleep_ms); - if (NekoGui::dataStore->traffic_loop_interval == 0) continue; // user disabled - - // profile start and stop - if (!loop_enabled) { - // 停止 - if (looping) { - looping = false; - runOnUiThread([=] { - auto m = GetMainWindow(); - m->refresh_status("STOP"); - }); - } - continue; - } else { - // 开始 - if (!looping) { - looping = true; - } - } - - // do update - loop_mutex.lock(); - - UpdateAll(); - - // do conn list update - QJsonArray conn_list; - if (NekoGui::dataStore->connection_statistics) { - conn_list = get_connection_list(); - } - - loop_mutex.unlock(); - - // post to UI - runOnUiThread([=] { - auto m = GetMainWindow(); - if (proxy != nullptr) { - m->refresh_status(QObject::tr("Proxy: %1\nDirect: %2").arg(proxy->DisplaySpeed(), bypass->DisplaySpeed())); - } - for (const auto &item: items) { - if (item->id < 0) continue; - m->refresh_proxy_list(item->id); - } - if (NekoGui::dataStore->connection_statistics) { - m->refresh_connection_list(conn_list); - } - }); - } - } - -} // namespace NekoGui_traffic diff --git a/db/traffic/TrafficLooper.hpp b/db/traffic/TrafficLooper.hpp deleted file mode 100644 index f5b64fa..0000000 --- a/db/traffic/TrafficLooper.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "TrafficData.hpp" - -namespace NekoGui_traffic { - class TrafficLooper { - public: - bool loop_enabled = false; - bool looping = false; - QMutex loop_mutex; - - QList> items; - TrafficData *proxy = nullptr; - - void UpdateAll(); - - void Loop(); - - private: - TrafficData *bypass = new TrafficData("bypass"); - - [[nodiscard]] static TrafficData *update_stats(TrafficData *item); - - [[nodiscard]] static QJsonArray get_connection_list(); - }; - - extern TrafficLooper *trafficLooper; -} // namespace NekoGui_traffic diff --git a/docs/Build_Core.md b/docs/Build_Core.md deleted file mode 100644 index 5f1d02c..0000000 --- a/docs/Build_Core.md +++ /dev/null @@ -1,24 +0,0 @@ -## 构建 nekobox_core - -### 目录结构 - -``` - | nekoray - | go/cmd/* - | sing-box-extra - | sing-box - | ...... -``` - -### 常规构建 - -1. `bash libs/get_source.sh` (自动下载目录结构,自动 checkout commit) -2. `GOOS=windows GOARCH=amd64 bash libs/build_go.sh` - -具体支持的 GOOS 和 GOARCH 请看 `libs/build_go.sh` - -非官方构建无需编译 `updater` `launcher` - -### sing-box tags - -具体使用的 tags 请看 `libs/build_go.sh` diff --git a/docs/Build_Linux.md b/docs/Build_Linux.md deleted file mode 100644 index 966cb77..0000000 --- a/docs/Build_Linux.md +++ /dev/null @@ -1,76 +0,0 @@ -在 Linux 下编译 Nekoray - -## git clone 源码 - -``` -git clone https://github.com/MatsuriDayo/nekoray.git --recursive -``` - -## 简单编译法 - -条件: - -1. C++ 依赖:`protobuf yaml-cpp zxing-cpp` 已用包管理器安装,并符合版本要求。 -2. 已安装 `qtbase` `qtsvg` `qttools` `qtx11extras` -3. 已安装 Qt `5.12.x` 或 `5.15.x` -4. 系统为 `x86-64-linux-gnu` - -```shell -mkdir build -cd build -cmake -GNinja .. -ninja -``` - -编译完成后得到 `nekobox` - -解压 Release 的压缩包,替换其中的 `nekobox`,删除 `launcher` 即可使用。 - -## 复杂编译法 - -### CMake 参数 - -| CMake 参数 | 默认值 | 含义 | -|-------------------|-------------------|-----------------------| -| QT_VERSION_MAJOR | 5 | QT版本 | -| NKR_NO_EXTERNAL | | 不包含外部 C/C++ 依赖 (以下所有) | -| NKR_NO_YAML | | 不包含 yaml-cpp | -| NKR_NO_QHOTKEY | | 不包含 qhotkey | -| NKR_NO_ZXING | | 不包含 zxing | -| NKR_NO_GRPC | | 不包含 gRPC | -| NKR_PACKAGE | | 编译 package 版本 (aur) | -| NKR_LIBS | ./libs/deps/built | 依赖搜索目录 | -| NKR_DISABLE_LIBS | | 禁用 NKR_LIBS | - -1. `NKR_LIBS` 的值会被追加到 `CMAKE_PREFIX_PATH` -2. `NKR_PACKAGE` 打开后,`NKR_LIBS` 的默认值为 `./libs/deps/package` ,具体依赖请看 `build_deps_all.sh` -3. `NKR_PACKAGE` 打开后,应用将使用 appdata 目录存放配置,自动更新等功能将被禁用。 - -### C++ 部分 - -当您的发行版没有上面几个 C++ 依赖包,或者版本不符合要求时,可以参考 `build_deps_all.sh` 编译脚本自行编译。 - -条件: 已安装 Qt `5.12.x` 或 `5.15.x` - -#### 编译安装 C/C++ 依赖 - -(这一步可能要挂梯) - -```shell -./libs/build_deps_all.sh -``` - -#### 编译本体 - -```shell -mkdir build -cd build -cmake -GNinja .. -ninja -``` - -编译完成后得到 `nekobox` - -### Go 部分编译 - -请看 [Build_Core.md](./Build_Core.md) diff --git a/docs/Build_Windows.md b/docs/Build_Windows.md deleted file mode 100644 index b8a1469..0000000 --- a/docs/Build_Windows.md +++ /dev/null @@ -1,64 +0,0 @@ -在 Windows 下编译 Nekoray - -### git clone 源码 - -``` -git clone https://github.com/MatsuriDayo/nekoray.git --recursive -``` - -### 安装 Visual Studio - -从微软官网安装,可以使用 2019 和 2022 版本,安装 Win32 C++ 开发环境。 - -安装好后可以在「开始」菜单找到 `x64 Native Tools Command Prompt` - -本文之后的命令均在该 cmd 内执行。`cmake` `ninja` 等工具使用 VS 自带的即可。 - -### 下载 Qt SDK - -目前 Windows Release 使用的版本是 Qt 6.5.x - -下载解压后,将 bin 目录添加到环境变量。 - -#### Release 编译用到的 Qt 包下载 (MSVC2019 x86_64) - -https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.5.0-Windows-x86_64-VS2022-17.5.5-20230507.7z - -#### 官方签名版 Qt 5.15.2 (可选,已知有内存泄漏的BUG) - -在此下载 `qtbase` `qtsvg` `qttools` 的包并解压到同一个目录。 - -https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5152/qt.qt5.5152.win64_msvc2019_64/ - -### C++ 部分编译 - -#### 编译安装 C/C++ 依赖 - -(这一步可能要挂梯) - -```shell -bash ./libs/build_deps_all.sh -``` - -目前只有 bash 脚本,没有批处理或 powershell,如果 Windows 没有带 bash 建议自行安装。 - -CMake 参数等细节与 Linux 大同小异,有问题可以参照 Build_Linux 文档。 - -#### 编译本体 - -请根据你的 QT Sdk 的位置替换命令 - -```shell -mkdir build -cd build -cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=D:/path/to/qt/5.15.2/msvc2019_64 .. -ninja -``` - -编译完成后得到 `nekobox.exe` - -最后运行 `windeployqt nekobox.exe` 自动复制所需 DLL 等文件到当前目录 - -### Go 部分编译 - -请看 [Build_Core.md](./Build_Core.md) diff --git a/docs/RunFlags.md b/docs/RunFlags.md deleted file mode 100644 index eda46da..0000000 --- a/docs/RunFlags.md +++ /dev/null @@ -1,5 +0,0 @@ -# 运行参数 - -- `-many` 无视同目录正在运行的实例,强行开启新的实例。 -- `-appdata` 开启后配置文件会指定目录,未指定目录则使用共享目录,无法多开和自动升级。 -- `-flag_reorder` 进行重新整理配置文件的顺序,并删除损坏和孤立的配置。 diff --git a/docs/Run_Linux.md b/docs/Run_Linux.md deleted file mode 100644 index ae0aaa1..0000000 --- a/docs/Run_Linux.md +++ /dev/null @@ -1,81 +0,0 @@ -## Linux 安装 - -### Debian 系发行版 - -使用 Debian 系发行版时,推荐使用 .deb 包安装: - -```shell -sudo apt install ./nekoray-*-debian-x64.deb -``` - -安装完成后,桌面快捷方式启动自带参数 `-appdata`,如果想要直接启动并使用之前的配置,注意附带本参数。 - -### Arch 系发行版 - -使用 Arch 系发行版时,推荐从 ```aur``` 或 ```archlinuxcn``` 安装: - -#### AUR -##### 最新稳定版 - -```shell -[yay/paru] -S nekoray -``` - -##### 最新 Git 版 (开发版) - -```shell -[yay/paru] -S nekoray-git -``` - -#### archlinuxcn - -##### 最新稳定版 - -```shell -sudo pacman -S nekoray -``` - -##### 最新 Git 版 (开发版) - -```shell -sudo pacman -S nekoray-git -``` - -### 其他发行版 - -下载 .zip 文件,解压到合适的路径,开箱即用。 - -或下载 .AppImage,并使用 `chmod +x nekoray-*-AppImage-x64.AppImage` 给予可执行权限。 - -具体使用方法见下文。 - -## Linux 运行 - -**使用 Linux 系统相信您已具备基本的排错能力, -本项目不提供特定发行版/架构的支持,预编译文件不能满足您的需求时,请自行编译/适配。** - -已知部分 Linux 发行版无法使用、非 x86_64 暂无适配,可以尝试自行编译。 - -目前 Release 便携包解压后,有两种使用方法: - -1. System: 若要使用系统的 Qt5 运行库,请执行 `./nekoray` -2. Bundle: 若要使用预编译的 Qt 运行库,请执行 `./launcher` - -### Bundle - -要求:已安装主流的发行版和 xcb 桌面环境。 - -运行: `./launcher` 或 部分系统可双击打开 - -launcher 参数 - -* `./launcher -- -appdata` ( `--` 后的参数传递给主程序 ) -* `-debug` Debug mode - -Ubuntu 22.04: `sudo apt install libxcb-xinerama0` - -### System - -要求:已安装主流的发行版和 xcb 桌面环境,已安装 Qt5.12 ~ Qt5.15 环境。 - -运行: `./nekoray` 或 部分系统可双击打开。如果无法运行,建议使用 Bundle 版。 diff --git a/docs/readme.md b/docs/readme.md deleted file mode 100644 index c6c68fe..0000000 --- a/docs/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# 技术文档 - -# Technical documentation - -1. Build GUI: `Build_*.md` -2. Build Core: `Build_Core.md` diff --git a/fmt/AbstractBean.cpp b/fmt/AbstractBean.cpp deleted file mode 100644 index d030604..0000000 --- a/fmt/AbstractBean.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "includes.h" - -#include -#include -#include - -namespace NekoGui_fmt { - AbstractBean::AbstractBean(int version) { - this->version = version; - _add(new configItem("_v", &this->version, itemType::integer)); - _add(new configItem("name", &name, itemType::string)); - _add(new configItem("addr", &serverAddress, itemType::string)); - _add(new configItem("port", &serverPort, itemType::integer)); - _add(new configItem("c_cfg", &custom_config, itemType::string)); - _add(new configItem("c_out", &custom_outbound, itemType::string)); - } - - QString AbstractBean::ToNekorayShareLink(const QString &type) { - auto b = ToJson(); - QUrl url; - url.setScheme("nekoray"); - url.setHost(type); - url.setFragment(QJsonObject2QString(b, true) - .toUtf8() - .toBase64(QByteArray::Base64UrlEncoding)); - return url.toString(); - } - - QString AbstractBean::DisplayAddress() { - return ::DisplayAddress(serverAddress, serverPort); - } - - QString AbstractBean::DisplayName() { - if (name.isEmpty()) { - return DisplayAddress(); - } - return name; - } - - QString AbstractBean::DisplayTypeAndName() { - return QStringLiteral("[%1] %2").arg(DisplayType(), DisplayName()); - } - - void AbstractBean::ResolveDomainToIP(const std::function &onFinished) { - bool noResolve = false; - if (dynamic_cast(this) != nullptr) noResolve = true; - if (dynamic_cast(this) != nullptr) noResolve = true; - if (dynamic_cast(this) != nullptr) noResolve = true; - if (IsIpAddress(serverAddress)) noResolve = true; - if (noResolve) { - onFinished(); - return; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) // TODO older QT - QHostInfo::lookupHost(serverAddress, QApplication::instance(), [=](const QHostInfo &host) { - auto addr = host.addresses(); - if (!addr.isEmpty()) { - auto domain = serverAddress; - auto stream = GetStreamSettings(this); - - // replace serverAddress - serverAddress = addr.first().toString(); - - // replace ws tls - if (stream != nullptr) { - if (stream->security == "tls" && stream->sni.isEmpty()) { - stream->sni = domain; - } - if (stream->network == "ws" && stream->host.isEmpty()) { - stream->host = domain; - } - } - } - onFinished(); - }); -#endif - } -} // namespace NekoGui_fmt diff --git a/fmt/AbstractBean.hpp b/fmt/AbstractBean.hpp deleted file mode 100644 index 0a5c098..0000000 --- a/fmt/AbstractBean.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -#include "main/NekoGui.hpp" - -namespace NekoGui_fmt { - struct CoreObjOutboundBuildResult { - public: - QJsonObject outbound; - QString error; - }; - - struct ExternalBuildResult { - public: - QString program; - QStringList env; - QStringList arguments; - // - QString tag; - // - QString error; - QString config_export; - }; - - class AbstractBean : public JsonStore { - public: - int version; - - QString name = ""; - QString serverAddress = "127.0.0.1"; - int serverPort = 1080; - - QString custom_config = ""; - QString custom_outbound = ""; - - explicit AbstractBean(int version); - - // - - QString ToNekorayShareLink(const QString &type); - - void ResolveDomainToIP(const std::function &onFinished); - - // - - [[nodiscard]] virtual QString DisplayAddress(); - - [[nodiscard]] virtual QString DisplayName(); - - virtual QString DisplayCoreType() { return software_core_name; }; - - virtual QString DisplayType() { return {}; }; - - virtual QString DisplayTypeAndName(); - - // - - virtual int NeedExternal(bool isFirstProfile) { return 0; }; - - virtual CoreObjOutboundBuildResult BuildCoreObjSingBox() { return {}; }; - - virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) { return {}; }; - - virtual QString ToShareLink() { return {}; }; - }; - -} // namespace NekoGui_fmt diff --git a/fmt/Bean2CoreObj_box.cpp b/fmt/Bean2CoreObj_box.cpp deleted file mode 100644 index 72b6e1e..0000000 --- a/fmt/Bean2CoreObj_box.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "db/ProxyEntity.hpp" -#include "fmt/includes.h" - -namespace NekoGui_fmt { - void V2rayStreamSettings::BuildStreamSettingsSingBox(QJsonObject *outbound) { - // https://sing-box.sagernet.org/configuration/shared/v2ray-transport - - if (network != "tcp") { - QJsonObject transport{{"type", network}}; - if (network == "ws") { - if (!host.isEmpty()) transport["headers"] = QJsonObject{{"Host", host}}; - // ws path & ed - auto pathWithoutEd = SubStrBefore(path, "?ed="); - if (!pathWithoutEd.isEmpty()) transport["path"] = pathWithoutEd; - if (pathWithoutEd != path) { - auto ed = SubStrAfter(path, "?ed=").toInt(); - if (ed > 0) { - transport["max_early_data"] = ed; - transport["early_data_header_name"] = "Sec-WebSocket-Protocol"; - } - } - if (ws_early_data_length > 0) { - transport["max_early_data"] = ws_early_data_length; - transport["early_data_header_name"] = ws_early_data_name; - } - } else if (network == "http") { - if (!path.isEmpty()) transport["path"] = path; - if (!host.isEmpty()) transport["host"] = QList2QJsonArray(host.split(",")); - } else if (network == "grpc") { - if (!path.isEmpty()) transport["service_name"] = path; - } else if (network == "httpupgrade") { - if (!path.isEmpty()) transport["path"] = path; - if (!host.isEmpty()) transport["host"] = host; - } - outbound->insert("transport", transport); - } else if (header_type == "http") { - // TCP + headerType - QJsonObject transport{ - {"type", "http"}, - {"method", "GET"}, - {"path", path}, - {"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}}, - }; - outbound->insert("transport", transport); - } - - // 对应字段 tls - if (security == "tls") { - QJsonObject tls{{"enabled", true}}; - if (allow_insecure || NekoGui::dataStore->skip_cert) tls["insecure"] = true; - if (!sni.trimmed().isEmpty()) tls["server_name"] = sni; - if (!certificate.trimmed().isEmpty()) { - tls["certificate"] = certificate.trimmed(); - } - if (!alpn.trimmed().isEmpty()) { - tls["alpn"] = QList2QJsonArray(alpn.split(",")); - } - QString fp = utlsFingerprint; - if (!reality_pbk.trimmed().isEmpty()) { - tls["reality"] = QJsonObject{ - {"enabled", true}, - {"public_key", reality_pbk}, - {"short_id", reality_sid.split(",")[0]}, - }; - if (fp.isEmpty()) fp = "random"; - } - if (!fp.isEmpty()) { - tls["utls"] = QJsonObject{ - {"enabled", true}, - {"fingerprint", fp}, - }; - } - outbound->insert("tls", tls); - } - - if (outbound->value("type").toString() == "vmess" || outbound->value("type").toString() == "vless") { - outbound->insert("packet_encoding", packet_encoding); - } - } - - CoreObjOutboundBuildResult SocksHttpBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound; - outbound["type"] = socks_http_type == type_HTTP ? "http" : "socks"; - if (socks_http_type == type_Socks4) outbound["version"] = "4"; - outbound["server"] = serverAddress; - outbound["server_port"] = serverPort; - - if (!username.isEmpty() && !password.isEmpty()) { - outbound["username"] = username; - outbound["password"] = password; - } - - stream->BuildStreamSettingsSingBox(&outbound); - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult ShadowSocksBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound{{"type", "shadowsocks"}}; - outbound["server"] = serverAddress; - outbound["server_port"] = serverPort; - outbound["method"] = method; - outbound["password"] = password; - - if (uot != 0) { - QJsonObject udp_over_tcp{ - {"enabled", true}, - {"version", uot}, - }; - outbound["udp_over_tcp"] = udp_over_tcp; - } else { - outbound["udp_over_tcp"] = false; - } - - if (!plugin.trimmed().isEmpty()) { - outbound["plugin"] = SubStrBefore(plugin, ";"); - outbound["plugin_opts"] = SubStrAfter(plugin, ";"); - } - - stream->BuildStreamSettingsSingBox(&outbound); - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult VMessBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound{ - {"type", "vmess"}, - {"server", serverAddress}, - {"server_port", serverPort}, - {"uuid", uuid.trimmed()}, - {"alter_id", aid}, - {"security", security}, - }; - - stream->BuildStreamSettingsSingBox(&outbound); - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult TrojanVLESSBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound{ - {"type", proxy_type == proxy_VLESS ? "vless" : "trojan"}, - {"server", serverAddress}, - {"server_port", serverPort}, - }; - - QJsonObject settings; - if (proxy_type == proxy_VLESS) { - if (flow.right(7) == "-udp443") { - // 检查末尾是否包含"-udp443",如果是,则删去 - flow.chop(7); - } else if (flow == "none") { - // 不使用 flow - flow = ""; - } - outbound["uuid"] = password.trimmed(); - outbound["flow"] = flow; - } else { - outbound["password"] = password; - } - - stream->BuildStreamSettingsSingBox(&outbound); - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult QUICBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - QJsonObject coreTlsObj{ - {"enabled", true}, - {"disable_sni", disableSni}, - {"insecure", allowInsecure}, - {"certificate", caText.trimmed()}, - {"server_name", sni}, - }; - if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QList2QJsonArray(alpn.split(",")); - if (proxy_type == proxy_Hysteria2) coreTlsObj["alpn"] = "h3"; - - QJsonObject outbound{ - {"server", serverAddress}, - {"server_port", serverPort}, - {"tls", coreTlsObj}, - }; - - if (proxy_type == proxy_Hysteria2) { - outbound["type"] = "hysteria2"; - outbound["password"] = password; - outbound["up_mbps"] = uploadMbps; - outbound["down_mbps"] = downloadMbps; - - if (!hopPort.trimmed().isEmpty()) { - outbound["hop_ports"] = hopPort; - outbound["hop_interval"] = hopInterval; - } - if (!obfsPassword.isEmpty()) { - outbound["obfs"] = QJsonObject{ - {"type", "salamander"}, - {"password", obfsPassword}, - }; - } - } else if (proxy_type == proxy_TUIC) { - outbound["type"] = "tuic"; - outbound["uuid"] = uuid; - outbound["password"] = password; - outbound["congestion_control"] = congestionControl; - if (uos) { - outbound["udp_over_stream"] = true; - } else { - outbound["udp_relay_mode"] = udpRelayMode; - } - outbound["zero_rtt_handshake"] = zeroRttHandshake; - if (!heartbeat.trimmed().isEmpty()) outbound["heartbeat"] = heartbeat; - } - - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult CustomBean::BuildCoreObjSingBox() { - CoreObjOutboundBuildResult result; - - if (core == "internal") { - result.outbound = QString2QJsonObject(config_simple); - } - - return result; - } -} // namespace NekoGui_fmt diff --git a/fmt/Bean2External.cpp b/fmt/Bean2External.cpp deleted file mode 100644 index c58a651..0000000 --- a/fmt/Bean2External.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include "db/ProxyEntity.hpp" -#include "fmt/includes.h" - -#include -#include -#include -#include - -#define WriteTempFile(fn, data) \ - QDir dir; \ - if (!dir.exists("temp")) dir.mkdir("temp"); \ - QFile f(QStringLiteral("temp/") + fn); \ - bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); \ - if (ok) { \ - f.write(data); \ - } else { \ - result.error = f.errorString(); \ - } \ - f.close(); \ - auto TempFile = QFileInfo(f).absoluteFilePath(); - -namespace NekoGui_fmt { - // -1: Cannot use this config - // 0: Internal - // 1: Mapping External - // 2: Direct External - - int NaiveBean::NeedExternal(bool isFirstProfile) { - if (isFirstProfile) { - if (NekoGui::dataStore->spmode_vpn) { - return 1; - } - return 2; - } - return 1; - } - - int QUICBean::NeedExternal(bool isFirstProfile) { - auto extCore = [=] { - if (isFirstProfile) { - if (NekoGui::dataStore->spmode_vpn && hopPort.trimmed().isEmpty()) { - return 1; - } - return 2; - } else { - if (!hopPort.trimmed().isEmpty()) { - return -1; - } - } - return 1; - }; - - if (!forceExternal) { - // sing-box support - return 0; - } else { - // external core support - return extCore(); - } - } - - int CustomBean::NeedExternal(bool isFirstProfile) { - if (core == "internal" || core == "internal-full") return 0; - return 1; - } - - ExternalBuildResult NaiveBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { - ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("naive")}; - - auto is_direct = external_stat == 2; - auto domain_address = sni.isEmpty() ? serverAddress : sni; - auto connect_address = is_direct ? serverAddress : "127.0.0.1"; - auto connect_port = is_direct ? serverPort : mapping_port; - domain_address = WrapIPV6Host(domain_address); - connect_address = WrapIPV6Host(connect_address); - - auto proxy_url = QUrl(); - proxy_url.setScheme(protocol); - proxy_url.setUserName(username); - proxy_url.setPassword(password); - proxy_url.setPort(connect_port); - proxy_url.setHost(domain_address); - - if (!disable_log) result.arguments += "--log"; - result.arguments += "--listen=socks://127.0.0.1:" + Int2String(socks_port); - result.arguments += "--proxy=" + proxy_url.toString(QUrl::FullyEncoded); - if (domain_address != connect_address) - result.arguments += "--host-resolver-rules=MAP " + domain_address + " " + connect_address; - if (insecure_concurrency > 0) result.arguments += "--insecure-concurrency=" + Int2String(insecure_concurrency); - if (!extra_headers.trimmed().isEmpty()) result.arguments += "--extra-headers=" + extra_headers; - if (!certificate.trimmed().isEmpty()) { - WriteTempFile("naive_" + GetRandomString(10) + ".crt", certificate.toUtf8()); - result.env += "SSL_CERT_FILE=" + TempFile; - } - - auto config_export = QStringList{result.program}; - config_export += result.arguments; - result.config_export = QStringList2Command(config_export); - - return result; - } - - ExternalBuildResult QUICBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { - if (proxy_type == proxy_TUIC) { - ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("tuic")}; - - QJsonObject relay; - - relay["uuid"] = uuid; - relay["password"] = password; - relay["udp_relay_mode"] = udpRelayMode; - relay["congestion_control"] = congestionControl; - relay["zero_rtt_handshake"] = zeroRttHandshake; - relay["disable_sni"] = disableSni; - if (!heartbeat.trimmed().isEmpty()) relay["heartbeat"] = heartbeat; - if (!alpn.trimmed().isEmpty()) relay["alpn"] = QList2QJsonArray(alpn.split(",")); - - if (!caText.trimmed().isEmpty()) { - WriteTempFile("tuic_" + GetRandomString(10) + ".crt", caText.toUtf8()); - QJsonArray certificate; - certificate.append(TempFile); - relay["certificates"] = certificate; - } - - // The most confused part of TUIC...... - if (serverAddress == sni) { - relay["server"] = serverAddress + ":" + Int2String(serverPort); - } else { - relay["server"] = sni + ":" + Int2String(serverPort); - relay["ip"] = serverAddress; - } - - QJsonObject local{ - {"server", "127.0.0.1:" + Int2String(socks_port)}, - }; - - QJsonObject config{ - {"relay", relay}, - {"local", local}, - }; - - // - - result.config_export = QJsonObject2QString(config, false); - WriteTempFile("tuic_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); - result.arguments = QStringList{"-c", TempFile}; - - return result; - } else if (proxy_type == proxy_Hysteria2) { - ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria2")}; - - QJsonObject config; - - auto server = serverAddress; - if (!hopPort.trimmed().isEmpty()) { - server = WrapIPV6Host(server) + ":" + hopPort; - } else { - server = WrapIPV6Host(server) + ":" + Int2String(serverPort); - } - - QJsonObject transport; - transport["type"] = "udp"; - transport["udp"] = QJsonObject{ - {"hopInterval", QString::number(hopInterval) + "s"}, - }; - config["transport"] = transport; - - config["server"] = server; - config["socks5"] = QJsonObject{ - {"listen", "127.0.0.1:" + Int2String(socks_port)}, - {"disableUDP", false}, - }; - config["auth"] = password; - - QJsonObject bandwidth; - if (uploadMbps > 0) bandwidth["up"] = Int2String(uploadMbps) + " mbps"; - if (downloadMbps > 0) bandwidth["down"] = Int2String(downloadMbps) + " mbps"; - config["bandwidth"] = bandwidth; - - QJsonObject quic; - if (streamReceiveWindow > 0) quic["initStreamReceiveWindow"] = streamReceiveWindow; - if (connectionReceiveWindow > 0) quic["initConnReceiveWindow"] = connectionReceiveWindow; - if (disableMtuDiscovery) quic["disablePathMTUDiscovery"] = true; - config["quic"] = quic; - - config["fastOpen"] = true; - config["lazy"] = true; - - if (!obfsPassword.isEmpty()) { - QJsonObject obfs; - obfs["type"] = "salamander"; - obfs["salamander"] = QJsonObject{ - {"password", obfsPassword}, - }; - - config["obfs"] = obfs; - } - - QJsonObject tls; - auto sniGen = sni; - if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress; - tls["sni"] = sniGen; - if (allowInsecure) tls["insecure"] = true; - if (!caText.trimmed().isEmpty()) { - WriteTempFile("hysteria2_" + GetRandomString(10) + ".crt", caText.toUtf8()); - QJsonArray certificate; - certificate.append(TempFile); - tls["certificates"] = certificate; - } - config["tls"] = tls; - - result.config_export = QJsonObject2QString(config, false); - WriteTempFile("hysteria2_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); - result.arguments = QStringList{"-c", TempFile}; - - return result; - } - ExternalBuildResult e; - e.error = "unknown type"; - return e; - } - - ExternalBuildResult CustomBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { - ExternalBuildResult result{NekoGui::dataStore->extraCore->Get(core)}; - - result.arguments = command; // TODO split? - - for (int i = 0; i < result.arguments.length(); i++) { - auto arg = result.arguments[i]; - arg = arg.replace("%mapping_port%", Int2String(mapping_port)); - arg = arg.replace("%socks_port%", Int2String(socks_port)); - arg = arg.replace("%server_addr%", serverAddress); - arg = arg.replace("%server_port%", Int2String(serverPort)); - result.arguments[i] = arg; - } - - if (!config_simple.trimmed().isEmpty()) { - auto config = config_simple; - config = config.replace("%mapping_port%", Int2String(mapping_port)); - config = config.replace("%socks_port%", Int2String(socks_port)); - config = config.replace("%server_addr%", serverAddress); - config = config.replace("%server_port%", Int2String(serverPort)); - - // suffix - QString suffix; - if (!config_suffix.isEmpty()) { - suffix = "." + config_suffix; - } else if (!QString2QJsonObject(config).isEmpty()) { - // trojan-go: unsupported config format: xxx.tmp. use .yaml or .json instead. - suffix = ".json"; - } - - // write config - WriteTempFile("custom_" + GetRandomString(10) + suffix, config.toUtf8()); - for (int i = 0; i < result.arguments.count(); i++) { - result.arguments[i] = result.arguments[i].replace("%config%", TempFile); - } - - result.config_export = config; - } - - return result; - } - -} // namespace NekoGui_fmt diff --git a/fmt/Bean2Link.cpp b/fmt/Bean2Link.cpp deleted file mode 100644 index f194259..0000000 --- a/fmt/Bean2Link.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "db/ProxyEntity.hpp" -#include "fmt/includes.h" - -#include - -namespace NekoGui_fmt { - QString SocksHttpBean::ToShareLink() { - QUrl url; - if (socks_http_type == type_HTTP) { // http - if (stream->security == "tls") { - url.setScheme("https"); - } else { - url.setScheme("http"); - } - } else { - url.setScheme(QStringLiteral("socks%1").arg(socks_http_type)); - } - if (!name.isEmpty()) url.setFragment(name); - if (!username.isEmpty()) url.setUserName(username); - if (!password.isEmpty()) url.setPassword(password); - url.setHost(serverAddress); - url.setPort(serverPort); - return url.toString(QUrl::FullyEncoded); - } - - QString TrojanVLESSBean::ToShareLink() { - QUrl url; - QUrlQuery query; - url.setScheme(proxy_type == proxy_VLESS ? "vless" : "trojan"); - url.setUserName(password); - url.setHost(serverAddress); - url.setPort(serverPort); - if (!name.isEmpty()) url.setFragment(name); - - // security - auto security = stream->security; - if (security == "tls" && !stream->reality_pbk.trimmed().isEmpty()) security = "reality"; - query.addQueryItem("security", security); - - if (!stream->sni.isEmpty()) query.addQueryItem("sni", stream->sni); - if (!stream->alpn.isEmpty()) query.addQueryItem("alpn", stream->alpn); - if (stream->allow_insecure) query.addQueryItem("allowInsecure", "1"); - if (!stream->utlsFingerprint.isEmpty()) query.addQueryItem("fp", stream->utlsFingerprint); - - if (security == "reality") { - query.addQueryItem("pbk", stream->reality_pbk); - if (!stream->reality_sid.isEmpty()) query.addQueryItem("sid", stream->reality_sid); - if (!stream->reality_spx.isEmpty()) query.addQueryItem("spx", stream->reality_spx); - } - - // type - query.addQueryItem("type", stream->network); - - if (stream->network == "ws" || stream->network == "http" || stream->network == "httpupgrade") { - if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path); - if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host); - } else if (stream->network == "grpc") { - if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path); - } else if (stream->network == "tcp") { - if (stream->header_type == "http") { - if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path); - query.addQueryItem("headerType", "http"); - query.addQueryItem("host", stream->host); - } - } - - // protocol - if (proxy_type == proxy_VLESS) { - if (!flow.isEmpty()) { - query.addQueryItem("flow", flow); - } - query.addQueryItem("encryption", "none"); - } - - url.setQuery(query); - return url.toString(QUrl::FullyEncoded); - } - - const char* fixShadowsocksUserNameEncodeMagic = "fixShadowsocksUserNameEncodeMagic-holder-for-QUrl"; - - QString ShadowSocksBean::ToShareLink() { - QUrl url; - url.setScheme("ss"); - if (method.startsWith("2022-")) { - url.setUserName(fixShadowsocksUserNameEncodeMagic); - } else { - auto method_password = method + ":" + password; - url.setUserName(method_password.toUtf8().toBase64(QByteArray::Base64Option::Base64UrlEncoding)); - } - url.setHost(serverAddress); - url.setPort(serverPort); - if (!name.isEmpty()) url.setFragment(name); - QUrlQuery q; - if (!plugin.isEmpty()) q.addQueryItem("plugin", plugin); - if (!q.isEmpty()) url.setQuery(q); - // - auto link = url.toString(QUrl::FullyEncoded); - link = link.replace(fixShadowsocksUserNameEncodeMagic, method + ":" + QUrl::toPercentEncoding(password)); - return link; - } - - QString VMessBean::ToShareLink() { - if (NekoGui::dataStore->old_share_link_format) { - // v2rayN format - QJsonObject N{ - {"v", "2"}, - {"ps", name}, - {"add", serverAddress}, - {"port", Int2String(serverPort)}, - {"id", uuid}, - {"aid", Int2String(aid)}, - {"net", stream->network}, - {"host", stream->host}, - {"path", stream->path}, - {"type", stream->header_type}, - {"scy", security}, - {"tls", stream->security == "tls" ? "tls" : ""}, - {"sni", stream->sni}, - }; - return "vmess://" + QJsonObject2QString(N, true).toUtf8().toBase64(); - } else { - // ducksoft format - QUrl url; - QUrlQuery query; - url.setScheme("vmess"); - url.setUserName(uuid); - url.setHost(serverAddress); - url.setPort(serverPort); - if (!name.isEmpty()) url.setFragment(name); - - query.addQueryItem("encryption", security); - - // security - auto security = stream->security; - if (security == "tls" && !stream->reality_pbk.trimmed().isEmpty()) security = "reality"; - query.addQueryItem("security", security); - - if (!stream->sni.isEmpty()) query.addQueryItem("sni", stream->sni); - if (stream->allow_insecure) query.addQueryItem("allowInsecure", "1"); - if (stream->utlsFingerprint.isEmpty()) { - query.addQueryItem("fp", NekoGui::dataStore->utlsFingerprint); - } else { - query.addQueryItem("fp", stream->utlsFingerprint); - } - - if (security == "reality") { - query.addQueryItem("pbk", stream->reality_pbk); - if (!stream->reality_sid.isEmpty()) query.addQueryItem("sid", stream->reality_sid); - if (!stream->reality_spx.isEmpty()) query.addQueryItem("spx", stream->reality_spx); - } - - // type - query.addQueryItem("type", stream->network); - - if (stream->network == "ws" || stream->network == "http" || stream->network == "httpupgrade") { - if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path); - if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host); - } else if (stream->network == "grpc") { - if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path); - } else if (stream->network == "tcp") { - if (stream->header_type == "http") { - query.addQueryItem("headerType", "http"); - query.addQueryItem("host", stream->host); - } - } - - url.setQuery(query); - return url.toString(QUrl::FullyEncoded); - } - } - - QString NaiveBean::ToShareLink() { - QUrl url; - url.setScheme("naive+" + protocol); - url.setUserName(username); - url.setPassword(password); - url.setHost(serverAddress); - url.setPort(serverPort); - if (!name.isEmpty()) url.setFragment(name); - return url.toString(QUrl::FullyEncoded); - } - - QString QUICBean::ToShareLink() { - QUrl url; - if (proxy_type == proxy_TUIC) { - url.setScheme("tuic"); - url.setUserName(uuid); - url.setPassword(password); - url.setHost(serverAddress); - url.setPort(serverPort); - - QUrlQuery q; - if (!congestionControl.isEmpty()) q.addQueryItem("congestion_control", congestionControl); - if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn); - if (!sni.isEmpty()) q.addQueryItem("sni", sni); - if (!udpRelayMode.isEmpty()) q.addQueryItem("udp_relay_mode", udpRelayMode); - if (allowInsecure) q.addQueryItem("allow_insecure", "1"); - if (disableSni) q.addQueryItem("disable_sni", "1"); - if (!q.isEmpty()) url.setQuery(q); - if (!name.isEmpty()) url.setFragment(name); - } else if (proxy_type == proxy_Hysteria2) { - url.setScheme("hy2"); - url.setHost(serverAddress); - url.setPort(serverPort); - if (password.contains(":")) { - url.setUserName(SubStrBefore(password, ":")); - url.setPassword(SubStrAfter(password, ":")); - } else { - url.setUserName(password); - } - QUrlQuery q; - if (!obfsPassword.isEmpty()) { - q.addQueryItem("obfs", "salamander"); - q.addQueryItem("obfs-password", obfsPassword); - } - if (!hopPort.trimmed().isEmpty()) q.addQueryItem("mport", hopPort); - if (allowInsecure) q.addQueryItem("insecure", "1"); - if (!sni.isEmpty()) q.addQueryItem("sni", sni); - if (!q.isEmpty()) url.setQuery(q); - if (!name.isEmpty()) url.setFragment(name); - } - return url.toString(QUrl::FullyEncoded); - } - -} // namespace NekoGui_fmt diff --git a/fmt/ChainBean.hpp b/fmt/ChainBean.hpp deleted file mode 100644 index 27f14ab..0000000 --- a/fmt/ChainBean.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "main/NekoGui.hpp" - -namespace NekoGui_fmt { - class ChainBean : public AbstractBean { - public: - QList list; // in to out - - ChainBean() : AbstractBean(0) { - _add(new configItem("list", &list, itemType::integerList)); - }; - - QString DisplayType() override { return QObject::tr("Chain Proxy"); }; - - QString DisplayAddress() override { return ""; }; - }; -} // namespace NekoGui_fmt diff --git a/fmt/CustomBean.hpp b/fmt/CustomBean.hpp deleted file mode 100644 index 926e50d..0000000 --- a/fmt/CustomBean.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" - -namespace NekoGui_fmt { - class CustomBean : public AbstractBean { - public: - QString core; - QList command; - QString config_suffix; - QString config_simple; - int mapping_port = 0; - int socks_port = 0; - - CustomBean() : AbstractBean(0) { - _add(new configItem("core", &core, itemType::string)); - _add(new configItem("cmd", &command, itemType::stringList)); - _add(new configItem("cs", &config_simple, itemType::string)); - _add(new configItem("cs_suffix", &config_suffix, itemType::string)); - _add(new configItem("mapping_port", &mapping_port, itemType::integer)); - _add(new configItem("socks_port", &socks_port, itemType::integer)); - }; - - QString DisplayType() override { - if (core == "internal") { - auto obj = QString2QJsonObject(config_simple); - return obj["type"].toString(); - } else if (core == "internal-full") { - return software_core_name + " config"; - } - return core; - }; - - QString DisplayCoreType() override { return NeedExternal(true) == 0 ? software_core_name : core; }; - - QString DisplayAddress() override { - if (core == "internal") { - auto obj = QString2QJsonObject(config_simple); - return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt()); - } else if (core == "internal-full") { - return {}; - } - return AbstractBean::DisplayAddress(); - }; - - int NeedExternal(bool isFirstProfile) override; - - ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - }; -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/Link2Bean.cpp b/fmt/Link2Bean.cpp deleted file mode 100644 index f5d9e0d..0000000 --- a/fmt/Link2Bean.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include "db/ProxyEntity.hpp" -#include "fmt/includes.h" - -#include - -namespace NekoGui_fmt { - -#define DECODE_V2RAY_N_1 \ - QString linkN = DecodeB64IfValid(SubStrBefore(SubStrAfter(link, "://"), "#"), QByteArray::Base64Option::Base64UrlEncoding); \ - if (linkN.isEmpty()) return false; \ - auto hasRemarks = link.contains("#"); \ - if (hasRemarks) linkN += "#" + SubStrAfter(link, "#"); \ - auto url = QUrl("https://" + linkN); - - bool SocksHttpBean::TryParseLink(const QString &link) { - auto url = QUrl(link); - if (!url.isValid()) return false; - auto query = GetQuery(url); - - if (link.startsWith("socks4")) socks_http_type = type_Socks4; - if (link.startsWith("http")) socks_http_type = type_HTTP; - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - username = url.userName(); - password = url.password(); - if (serverPort == -1) serverPort = socks_http_type == type_HTTP ? 443 : 1080; - - // v2rayN fmt - if (password.isEmpty() && !username.isEmpty()) { - QString n = DecodeB64IfValid(username); - if (!n.isEmpty()) { - username = SubStrBefore(n, ":"); - password = SubStrAfter(n, ":"); - } - } - - stream->security = GetQueryValue(query, "security", ""); - stream->sni = GetQueryValue(query, "sni"); - if (link.startsWith("https")) stream->security = "tls"; - - return !serverAddress.isEmpty(); - } - - bool TrojanVLESSBean::TryParseLink(const QString &link) { - auto url = QUrl(link); - if (!url.isValid()) return false; - auto query = GetQuery(url); - - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - password = url.userName(); - if (serverPort == -1) serverPort = 443; - - // security - - auto type = GetQueryValue(query, "type", "tcp"); - if (type == "h2") { - type = "http"; - } - stream->network = type; - - if (proxy_type == proxy_Trojan) { - stream->security = GetQueryValue(query, "security", "tls").replace("reality", "tls").replace("none", ""); - } else { - stream->security = GetQueryValue(query, "security", "").replace("reality", "tls").replace("none", ""); - } - auto sni1 = GetQueryValue(query, "sni"); - auto sni2 = GetQueryValue(query, "peer"); - if (!sni1.isEmpty()) stream->sni = sni1; - if (!sni2.isEmpty()) stream->sni = sni2; - stream->alpn = GetQueryValue(query, "alpn"); - if (!query.queryItemValue("allowInsecure").isEmpty()) stream->allow_insecure = true; - stream->reality_pbk = GetQueryValue(query, "pbk", ""); - stream->reality_sid = GetQueryValue(query, "sid", ""); - stream->reality_spx = GetQueryValue(query, "spx", ""); - stream->utlsFingerprint = GetQueryValue(query, "fp", ""); - if (stream->utlsFingerprint.isEmpty()) { - stream->utlsFingerprint = NekoGui::dataStore->utlsFingerprint; - } - - // type - if (stream->network == "ws") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", ""); - } else if (stream->network == "http") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", "").replace("|", ","); - } else if (stream->network == "httpupgrade") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", ""); - } else if (stream->network == "grpc") { - stream->path = GetQueryValue(query, "serviceName", ""); - } else if (stream->network == "tcp") { - if (GetQueryValue(query, "headerType") == "http") { - stream->header_type = "http"; - stream->host = GetQueryValue(query, "host", ""); - stream->path = GetQueryValue(query, "path", ""); - } - } - - // protocol - if (proxy_type == proxy_VLESS) { - flow = GetQueryValue(query, "flow", ""); - } - - return !(password.isEmpty() || serverAddress.isEmpty()); - } - - bool ShadowSocksBean::TryParseLink(const QString &link) { - if (SubStrBefore(link, "#").contains("@")) { - // SS - auto url = QUrl(link); - if (!url.isValid()) return false; - - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - - if (url.password().isEmpty()) { - // traditional format - auto method_password = DecodeB64IfValid(url.userName(), QByteArray::Base64Option::Base64UrlEncoding); - if (method_password.isEmpty()) return false; - method = SubStrBefore(method_password, ":"); - password = SubStrAfter(method_password, ":"); - } else { - // 2022 format - method = url.userName(); - password = url.password(); - } - - auto query = GetQuery(url); - plugin = query.queryItemValue("plugin").replace("simple-obfs;", "obfs-local;"); - } else { - // v2rayN - DECODE_V2RAY_N_1 - - if (hasRemarks) name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - method = url.userName(); - password = url.password(); - } - return !(serverAddress.isEmpty() || method.isEmpty() || password.isEmpty()); - } - - bool VMessBean::TryParseLink(const QString &link) { - // V2RayN Format - auto linkN = DecodeB64IfValid(SubStrAfter(link, "vmess://")); - if (!linkN.isEmpty()) { - auto objN = QString2QJsonObject(linkN); - if (objN.isEmpty()) return false; - // REQUIRED - uuid = objN["id"].toString(); - serverAddress = objN["add"].toString(); - serverPort = objN["port"].toVariant().toInt(); - // OPTIONAL - name = objN["ps"].toString(); - aid = objN["aid"].toVariant().toInt(); - stream->host = objN["host"].toString(); - stream->path = objN["path"].toString(); - stream->sni = objN["sni"].toString(); - stream->header_type = objN["type"].toString(); - auto net = objN["net"].toString(); - if (!net.isEmpty()) { - if (net == "h2") { - net = "http"; - } - stream->network = net; - } - auto scy = objN["scy"].toString(); - if (!scy.isEmpty()) security = scy; - // TLS (XTLS?) - stream->security = objN["tls"].toString(); - // TODO quic & kcp - return true; - } else { - // https://github.com/XTLS/Xray-core/discussions/716 - auto url = QUrl(link); - if (!url.isValid()) return false; - auto query = GetQuery(url); - - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - uuid = url.userName(); - if (serverPort == -1) serverPort = 443; - - aid = 0; // “此分享标准仅针对 VMess AEAD 和 VLESS。” - security = GetQueryValue(query, "encryption", "auto"); - - // security - auto type = GetQueryValue(query, "type", "tcp"); - if (type == "h2") { - type = "http"; - } - stream->network = type; - stream->security = GetQueryValue(query, "security", "tls").replace("reality", "tls"); - auto sni1 = GetQueryValue(query, "sni"); - auto sni2 = GetQueryValue(query, "peer"); - if (!sni1.isEmpty()) stream->sni = sni1; - if (!sni2.isEmpty()) stream->sni = sni2; - if (!query.queryItemValue("allowInsecure").isEmpty()) stream->allow_insecure = true; - stream->reality_pbk = GetQueryValue(query, "pbk", ""); - stream->reality_sid = GetQueryValue(query, "sid", ""); - stream->reality_spx = GetQueryValue(query, "spx", ""); - stream->utlsFingerprint = GetQueryValue(query, "fp", ""); - if (stream->utlsFingerprint.isEmpty()) { - stream->utlsFingerprint = NekoGui::dataStore->utlsFingerprint; - } - - // type - if (stream->network == "ws") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", ""); - } else if (stream->network == "http") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", "").replace("|", ","); - } else if (stream->network == "httpupgrade") { - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", ""); - } else if (stream->network == "grpc") { - stream->path = GetQueryValue(query, "serviceName", ""); - } else if (stream->network == "tcp") { - if (GetQueryValue(query, "headerType") == "http") { - stream->header_type = "http"; - stream->path = GetQueryValue(query, "path", ""); - stream->host = GetQueryValue(query, "host", ""); - } - } - return !(uuid.isEmpty() || serverAddress.isEmpty()); - } - - return false; - } - - bool NaiveBean::TryParseLink(const QString &link) { - auto url = QUrl(link); - if (!url.isValid()) return false; - - protocol = url.scheme().replace("naive+", ""); - if (protocol != "https" && protocol != "quic") return false; - - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - username = url.userName(); - password = url.password(); - - return !(username.isEmpty() || password.isEmpty() || serverAddress.isEmpty()); - } - - bool QUICBean::TryParseLink(const QString &link) { - auto url = QUrl(link); - auto query = QUrlQuery(url.query()); - if (url.host().isEmpty() || url.port() == -1) return false; - - if (url.scheme() == "tuic") { - // by daeuniverse - // https://github.com/daeuniverse/dae/discussions/182 - - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - if (serverPort == -1) serverPort = 443; - serverPort = url.port(); - - uuid = url.userName(); - password = url.password(); - - congestionControl = query.queryItemValue("congestion_control"); - alpn = query.queryItemValue("alpn"); - sni = query.queryItemValue("sni"); - udpRelayMode = query.queryItemValue("udp_relay_mode"); - allowInsecure = query.queryItemValue("allow_insecure") == "1"; - disableSni = query.queryItemValue("disable_sni") == "1"; - } else if (QStringList{"hy2", "hysteria2"}.contains(url.scheme())) { - name = url.fragment(QUrl::FullyDecoded); - serverAddress = url.host(); - serverPort = url.port(); - hopPort = query.queryItemValue("mport"); - obfsPassword = query.queryItemValue("obfs-password"); - allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure")); - - if (url.password().isEmpty()) { - password = url.userName(); - } else { - password = url.userName() + ":" + url.password(); - } - - sni = query.queryItemValue("sni"); - } - - return true; - } - -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/NaiveBean.hpp b/fmt/NaiveBean.hpp deleted file mode 100644 index 85e9b35..0000000 --- a/fmt/NaiveBean.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" - -namespace NekoGui_fmt { - class NaiveBean : public AbstractBean { - public: - QString username = ""; - QString password = ""; - QString protocol = "https"; - QString extra_headers = ""; - QString sni = ""; - QString certificate = ""; - int insecure_concurrency = 0; - - bool disable_log = false; - - NaiveBean() : AbstractBean(0) { - _add(new configItem("username", &username, itemType::string)); - _add(new configItem("password", &password, itemType::string)); - _add(new configItem("protocol", &protocol, itemType::string)); - _add(new configItem("extra_headers", &extra_headers, itemType::string)); - _add(new configItem("sni", &sni, itemType::string)); - _add(new configItem("certificate", &certificate, itemType::string)); - _add(new configItem("insecure_concurrency", &insecure_concurrency, itemType::integer)); - _add(new configItem("disable_log", &disable_log, itemType::boolean)); - }; - - QString DisplayCoreType() override { return "Naive"; }; - - QString DisplayType() override { return "Naive"; }; - - int NeedExternal(bool isFirstProfile) override; - - ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/Preset.hpp b/fmt/Preset.hpp deleted file mode 100644 index 050a32e..0000000 --- a/fmt/Preset.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace Preset { - namespace SingBox { - inline QStringList VpnImplementation = {"gvisor", "system", "mixed"}; - inline QStringList DomainStrategy = {"", "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6"}; - inline QStringList UtlsFingerPrint = {"", "chrome", "firefox", "edge", "safari", "360", "qq", "ios", "android", "random", "randomized"}; - inline QStringList ShadowsocksMethods = {"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20"}; - inline QStringList Flows = {"xtls-rprx-vision"}; - } // namespace SingBox - - namespace Windows { - inline QStringList system_proxy_format{"{ip}:{http_port}", - "socks={ip}:{socks_port}", - "http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}", - "http=http://{ip}:{http_port};https=http://{ip}:{http_port}"}; - } // namespace Windows -} // namespace Preset diff --git a/fmt/QUICBean.hpp b/fmt/QUICBean.hpp deleted file mode 100644 index 9a18c63..0000000 --- a/fmt/QUICBean.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" - -namespace NekoGui_fmt { - class QUICBean : public AbstractBean { - public: - // static constexpr int proxy_Hysteria = 0; - static constexpr int proxy_TUIC = 1; - static constexpr int proxy_Hysteria2 = 3; - int proxy_type = proxy_Hysteria2; - - bool forceExternal = false; - - // Hysteria 2 - - QString obfsPassword = ""; - - int uploadMbps = 0; - int downloadMbps = 0; - - qint64 streamReceiveWindow = 0; - qint64 connectionReceiveWindow = 0; - bool disableMtuDiscovery = false; - - int hopInterval = 10; - QString hopPort = ""; - - // TUIC - - QString uuid = ""; - QString congestionControl = "bbr"; - QString udpRelayMode = "native"; - bool zeroRttHandshake = false; - QString heartbeat = "10s"; - bool uos = false; - - // HY2&TUIC - - QString password = ""; - - // TLS - - bool allowInsecure = false; - QString sni = ""; - QString alpn = ""; - QString caText = ""; - bool disableSni = false; - - explicit QUICBean(int _proxy_type) : AbstractBean(0) { - proxy_type = _proxy_type; - if (proxy_type == proxy_Hysteria2) { - _add(new configItem("obfsPassword", &obfsPassword, itemType::string)); - _add(new configItem("uploadMbps", &uploadMbps, itemType::integer)); - _add(new configItem("downloadMbps", &downloadMbps, itemType::integer)); - _add(new configItem("streamReceiveWindow", &streamReceiveWindow, itemType::integer64)); - _add(new configItem("connectionReceiveWindow", &connectionReceiveWindow, itemType::integer64)); - _add(new configItem("disableMtuDiscovery", &disableMtuDiscovery, itemType::boolean)); - _add(new configItem("hopInterval", &hopInterval, itemType::integer)); - _add(new configItem("hopPort", &hopPort, itemType::string)); - _add(new configItem("password", &password, itemType::string)); - } else if (proxy_type == proxy_TUIC) { - _add(new configItem("uuid", &uuid, itemType::string)); - _add(new configItem("password", &password, itemType::string)); - _add(new configItem("congestionControl", &congestionControl, itemType::string)); - _add(new configItem("udpRelayMode", &udpRelayMode, itemType::string)); - _add(new configItem("zeroRttHandshake", &zeroRttHandshake, itemType::boolean)); - _add(new configItem("heartbeat", &heartbeat, itemType::string)); - _add(new configItem("uos", &uos, itemType::boolean)); - } - _add(new configItem("forceExternal", &forceExternal, itemType::boolean)); - // TLS - _add(new configItem("allowInsecure", &allowInsecure, itemType::boolean)); - _add(new configItem("sni", &sni, itemType::string)); - _add(new configItem("alpn", &alpn, itemType::string)); - _add(new configItem("caText", &caText, itemType::string)); - _add(new configItem("disableSni", &disableSni, itemType::boolean)); - }; - - QString DisplayAddress() override { - if (!hopPort.trimmed().isEmpty()) return WrapIPV6Host(serverAddress) + ":" + hopPort; - return ::DisplayAddress(serverAddress, serverPort); - } - - QString DisplayCoreType() override { - if (NeedExternal(true) == 0) { - return software_core_name; - } else if (proxy_type == proxy_TUIC) { - return "tuic"; - } else { - return "hysteria2"; - } - } - - QString DisplayType() override { - if (proxy_type == proxy_TUIC) { - return "TUIC"; - } else { - return "Hysteria2"; - } - }; - - int NeedExternal(bool isFirstProfile) override; - - ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/ShadowSocksBean.hpp b/fmt/ShadowSocksBean.hpp deleted file mode 100644 index becb684..0000000 --- a/fmt/ShadowSocksBean.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" -#include "fmt/V2RayStreamSettings.hpp" - -namespace NekoGui_fmt { - class ShadowSocksBean : public AbstractBean { - public: - QString method = "aes-128-gcm"; - QString password = ""; - QString plugin = ""; - int uot = 0; - - std::shared_ptr stream = std::make_shared(); - - ShadowSocksBean() : AbstractBean(0) { - _add(new configItem("method", &method, itemType::string)); - _add(new configItem("pass", &password, itemType::string)); - _add(new configItem("plugin", &plugin, itemType::string)); - _add(new configItem("uot", &uot, itemType::integer)); - _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); - }; - - QString DisplayType() override { return "Shadowsocks"; }; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt diff --git a/fmt/SocksHttpBean.hpp b/fmt/SocksHttpBean.hpp deleted file mode 100644 index 930a5f8..0000000 --- a/fmt/SocksHttpBean.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" -#include "fmt/V2RayStreamSettings.hpp" - -namespace NekoGui_fmt { - class SocksHttpBean : public AbstractBean { - public: - static constexpr int type_HTTP = -80; - static constexpr int type_Socks4 = 4; - static constexpr int type_Socks5 = 5; - - int socks_http_type = type_Socks5; - QString username = ""; - QString password = ""; - - std::shared_ptr stream = std::make_shared(); - - explicit SocksHttpBean(int _socks_http_type) : AbstractBean(0) { - this->socks_http_type = _socks_http_type; - _add(new configItem("v", &socks_http_type, itemType::integer)); - _add(new configItem("username", &username, itemType::string)); - _add(new configItem("password", &password, itemType::string)); - _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); - }; - - QString DisplayType() override { return socks_http_type == type_HTTP ? "HTTP" : "Socks"; }; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt diff --git a/fmt/TrojanVLESSBean.hpp b/fmt/TrojanVLESSBean.hpp deleted file mode 100644 index 77070a6..0000000 --- a/fmt/TrojanVLESSBean.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" -#include "fmt/V2RayStreamSettings.hpp" - -namespace NekoGui_fmt { - class TrojanVLESSBean : public AbstractBean { - public: - static constexpr int proxy_Trojan = 0; - static constexpr int proxy_VLESS = 1; - int proxy_type = proxy_Trojan; - - QString password = ""; - QString flow = ""; - - std::shared_ptr stream = std::make_shared(); - - explicit TrojanVLESSBean(int _proxy_type) : AbstractBean(0) { - proxy_type = _proxy_type; - _add(new configItem("pass", &password, itemType::string)); - _add(new configItem("flow", &flow, itemType::string)); - _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); - }; - - QString DisplayType() override { return proxy_type == proxy_VLESS ? "VLESS" : "Trojan"; }; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/V2RayStreamSettings.hpp b/fmt/V2RayStreamSettings.hpp deleted file mode 100644 index 9a6de9f..0000000 --- a/fmt/V2RayStreamSettings.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "AbstractBean.hpp" - -namespace NekoGui_fmt { - class V2rayStreamSettings : public JsonStore { - public: - QString network = "tcp"; - QString security = ""; - QString packet_encoding = ""; - // ws/http/grpc/tcp-http/httpupgrade - QString path = ""; - QString host = ""; - // kcp/quic/tcp-http - QString header_type = ""; - // tls - QString sni = ""; - QString alpn = ""; - QString certificate = ""; - QString utlsFingerprint = ""; - bool allow_insecure = false; - // ws early data - QString ws_early_data_name = ""; - int ws_early_data_length = 0; - // reality - QString reality_pbk = ""; - QString reality_sid = ""; - QString reality_spx = ""; - // multiplex - int multiplex_status = 0; - - V2rayStreamSettings() : JsonStore() { - _add(new configItem("net", &network, itemType::string)); - _add(new configItem("sec", &security, itemType::string)); - _add(new configItem("pac_enc", &packet_encoding, itemType::string)); - _add(new configItem("path", &path, itemType::string)); - _add(new configItem("host", &host, itemType::string)); - _add(new configItem("sni", &sni, itemType::string)); - _add(new configItem("alpn", &alpn, itemType::string)); - _add(new configItem("cert", &certificate, itemType::string)); - _add(new configItem("insecure", &allow_insecure, itemType::boolean)); - _add(new configItem("h_type", &header_type, itemType::string)); - _add(new configItem("ed_name", &ws_early_data_name, itemType::string)); - _add(new configItem("ed_len", &ws_early_data_length, itemType::integer)); - _add(new configItem("utls", &utlsFingerprint, itemType::string)); - _add(new configItem("pbk", &reality_pbk, itemType::string)); - _add(new configItem("sid", &reality_sid, itemType::string)); - _add(new configItem("spx", &reality_spx, itemType::string)); - _add(new configItem("mux_s", &multiplex_status, itemType::integer)); - } - - void BuildStreamSettingsSingBox(QJsonObject *outbound); - }; - - inline V2rayStreamSettings *GetStreamSettings(AbstractBean *bean) { - if (bean == nullptr) return nullptr; - auto stream_item = bean->_get("stream"); - if (stream_item != nullptr) { - auto stream_store = (JsonStore *) stream_item->ptr; - auto stream = (NekoGui_fmt::V2rayStreamSettings *) stream_store; - return stream; - } - return nullptr; - } -} // namespace NekoGui_fmt diff --git a/fmt/VMessBean.hpp b/fmt/VMessBean.hpp deleted file mode 100644 index 2bd81a4..0000000 --- a/fmt/VMessBean.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" -#include "fmt/V2RayStreamSettings.hpp" - -namespace NekoGui_fmt { - class VMessBean : public AbstractBean { - public: - QString uuid = ""; - int aid = 0; - QString security = "auto"; - - std::shared_ptr stream = std::make_shared(); - - VMessBean() : AbstractBean(0) { - _add(new configItem("id", &uuid, itemType::string)); - _add(new configItem("aid", &aid, itemType::integer)); - _add(new configItem("sec", &security, itemType::string)); - _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); - }; - - QString DisplayType() override { return "VMess"; }; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt diff --git a/fmt/includes.h b/fmt/includes.h deleted file mode 100644 index cd3fe3d..0000000 --- a/fmt/includes.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "SocksHttpBean.hpp" -#include "ShadowSocksBean.hpp" -#include "ChainBean.hpp" -#include "VMessBean.hpp" -#include "TrojanVLESSBean.hpp" -#include "NaiveBean.hpp" -#include "QUICBean.hpp" -#include "CustomBean.hpp" diff --git a/go/.gitignore b/go/.gitignore deleted file mode 100644 index 96af7a5..0000000 --- a/go/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.log -*.pem -*.json -*.exe -*.dat -/cmd/nekoray_core/nekoray_core -/cmd/nekobox_core/nekobox_core -*.db diff --git a/go/cmd/nekobox_core/core_box.go b/go/cmd/nekobox_core/core_box.go deleted file mode 100644 index 4e5d253..0000000 --- a/go/cmd/nekobox_core/core_box.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "context" - "net" - "net/http" - - "github.com/matsuridayo/libneko/neko_common" - "github.com/matsuridayo/libneko/neko_log" - box "github.com/sagernet/sing-box" - "github.com/sagernet/sing-box/boxapi" - boxmain "github.com/sagernet/sing-box/cmd/sing-box" -) - -var instance *box.Box -var instance_cancel context.CancelFunc - -func setupCore() { - boxmain.SetDisableColor(true) - // - neko_log.SetupLog(50*1024, "./neko.log") - // - neko_common.GetCurrentInstance = func() interface{} { - return instance - } - neko_common.DialContext = func(ctx context.Context, specifiedInstance interface{}, network, addr string) (net.Conn, error) { - if i, ok := specifiedInstance.(*box.Box); ok { - return boxapi.DialContext(ctx, i, network, addr) - } - if instance != nil { - return boxapi.DialContext(ctx, instance, network, addr) - } - return neko_common.DialContextSystem(ctx, network, addr) - } - neko_common.DialUDP = func(ctx context.Context, specifiedInstance interface{}) (net.PacketConn, error) { - if i, ok := specifiedInstance.(*box.Box); ok { - return boxapi.DialUDP(ctx, i) - } - if instance != nil { - return boxapi.DialUDP(ctx, instance) - } - return neko_common.DialUDPSystem(ctx) - } - neko_common.CreateProxyHttpClient = func(specifiedInstance interface{}) *http.Client { - if i, ok := specifiedInstance.(*box.Box); ok { - return boxapi.CreateProxyHttpClient(i) - } - return boxapi.CreateProxyHttpClient(instance) - } -} diff --git a/go/cmd/nekobox_core/go.mod b/go/cmd/nekobox_core/go.mod deleted file mode 100644 index 6af5b62..0000000 --- a/go/cmd/nekobox_core/go.mod +++ /dev/null @@ -1,105 +0,0 @@ -module nekobox_core - -go 1.19 - -require ( - github.com/matsuridayo/libneko v1.0.0 // replaced - github.com/sagernet/sing-box v1.0.0 // replaced - // github.com/sagernet/sing-dns v1.0.0 // indirect; replaced - grpc_server v1.0.0 -) - -require ( - berty.tech/go-libtor v1.0.385 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.6 // indirect - github.com/caddyserver/certmagic v0.20.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/cretz/bine v0.2.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gaukas/godicttls v0.0.4 // indirect - github.com/go-chi/chi/v5 v5.0.12 // indirect - github.com/go-chi/cors v1.2.1 // indirect - github.com/go-chi/render v1.0.3 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/gobwas/httphead v0.1.0 // indirect - github.com/gobwas/pool v0.2.1 // indirect - github.com/gofrs/uuid/v5 v5.2.0 // indirect - github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect - github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/libdns/alidns v1.0.3 // indirect - github.com/libdns/cloudflare v0.1.1 // indirect - github.com/libdns/libdns v0.2.2 // indirect - github.com/logrusorgru/aurora v2.0.3+incompatible // indirect - github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d // indirect - github.com/mholt/acmez v1.2.0 // indirect - github.com/miekg/dns v1.1.59 // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect - github.com/ooni/go-libtor v1.1.8 // indirect - github.com/oschwald/maxminddb-golang v1.12.0 // indirect - github.com/pierrec/lz4/v4 v4.1.14 // indirect - github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.4.1 // indirect - github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect - github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect - github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect - github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect - github.com/sagernet/quic-go v0.47.0-beta.2 // indirect - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect - github.com/sagernet/sing v0.4.3 // indirect - github.com/sagernet/sing-dns v0.2.3 // indirect - github.com/sagernet/sing-mux v0.2.0 // indirect - github.com/sagernet/sing-quic v0.2.2 // indirect - github.com/sagernet/sing-shadowsocks v0.2.7 // indirect - github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect - github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.3.3 // indirect - github.com/sagernet/sing-vmess v0.1.12 // indirect - github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect - github.com/sagernet/utls v1.5.4 // indirect - github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect - github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect - github.com/spf13/cobra v1.8.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect - github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect - github.com/zeebo/blake3 v0.2.3 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.33.0 // indirect - lukechampine.com/blake3 v1.3.0 // indirect -) - -replace grpc_server => ../../grpc_server - -replace github.com/matsuridayo/libneko => ../../../../libneko - -replace github.com/sagernet/sing-box => ../../../../sing-box - -replace github.com/sagernet/sing-quic => ../../../../sing-quic - -// replace github.com/sagernet/sing => ../../../../sing - -// replace github.com/sagernet/sing-dns => ../../../../sing-dns diff --git a/go/cmd/nekobox_core/go.sum b/go/cmd/nekobox_core/go.sum deleted file mode 100644 index 4f99f25..0000000 --- a/go/cmd/nekobox_core/go.sum +++ /dev/null @@ -1,298 +0,0 @@ -berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= -berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= -cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= -github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= -github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= -github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= -github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= -github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= -github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= -github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= -github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= -github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= -github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= -github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU= -github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= -github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= -github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= -github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= -github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U= -github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= -github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= -github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= -github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= -github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= -github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= -github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= -github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= -github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I= -github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0= -github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk= -github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/quic-go v0.47.0-beta.2 h1:1tCGWFOSaXIeuQaHrwOMJIYvlupjTcaVInGQw5ArULU= -github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYigzQ5lQu3Nh4wNh8Q= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= -github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8= -github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= -github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k= -github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg= -github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= -github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= -github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= -github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= -github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= -github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= -github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= -github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.3.3 h1:LZnQNmfGcNG2KPTPkLgc+Lo7k606QJVkPp2DnjriwUk= -github.com/sagernet/sing-tun v0.3.3/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ= -github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg= -github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I= -github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= -github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= -github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= -github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs= -github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc= -github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= -github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= -github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= -github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= -github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= -github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/go/cmd/nekobox_core/grpc_box.go b/go/cmd/nekobox_core/grpc_box.go deleted file mode 100644 index aabc0f5..0000000 --- a/go/cmd/nekobox_core/grpc_box.go +++ /dev/null @@ -1,152 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - - "grpc_server" - "grpc_server/gen" - - "github.com/matsuridayo/libneko/neko_common" - "github.com/matsuridayo/libneko/neko_log" - "github.com/matsuridayo/libneko/speedtest" - box "github.com/sagernet/sing-box" - "github.com/sagernet/sing-box/boxapi" - boxmain "github.com/sagernet/sing-box/cmd/sing-box" - - "log" - - "github.com/sagernet/sing-box/option" -) - -type server struct { - grpc_server.BaseServer -} - -func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.ErrorResp, _ error) { - var err error - - defer func() { - out = &gen.ErrorResp{} - if err != nil { - out.Error = err.Error() - instance = nil - } - }() - - if neko_common.Debug { - log.Println("Start:", in.CoreConfig) - } - - if instance != nil { - err = errors.New("instance already started") - return - } - - instance, instance_cancel, err = boxmain.Create([]byte(in.CoreConfig)) - - if instance != nil { - // Logger - instance.SetLogWritter(neko_log.LogWriter) - // V2ray Service - if in.StatsOutbounds != nil { - instance.Router().SetV2RayServer(boxapi.NewSbV2rayServer(option.V2RayStatsServiceOptions{ - Enabled: true, - Outbounds: in.StatsOutbounds, - })) - } - } - - return -} - -func (s *server) Stop(ctx context.Context, in *gen.EmptyReq) (out *gen.ErrorResp, _ error) { - var err error - - defer func() { - out = &gen.ErrorResp{} - if err != nil { - out.Error = err.Error() - } - }() - - if instance == nil { - return - } - - instance_cancel() - instance.Close() - - instance = nil - - return -} - -func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp, _ error) { - var err error - out = &gen.TestResp{Ms: 0} - - defer func() { - if err != nil { - out.Error = err.Error() - } - }() - - if in.Mode == gen.TestMode_UrlTest { - var i *box.Box - var cancel context.CancelFunc - if in.Config != nil { - // Test instance - i, cancel, err = boxmain.Create([]byte(in.Config.CoreConfig)) - if i != nil { - defer i.Close() - defer cancel() - } - if err != nil { - return - } - } else { - // Test running instance - i = instance - if i == nil { - return - } - } - // Latency - out.Ms, err = speedtest.UrlTest(boxapi.CreateProxyHttpClient(i), in.Url, in.Timeout, speedtest.UrlTestStandard_RTT) - } else if in.Mode == gen.TestMode_TcpPing { - out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout) - } else if in.Mode == gen.TestMode_FullTest { - i, cancel, err := boxmain.Create([]byte(in.Config.CoreConfig)) - if i != nil { - defer i.Close() - defer cancel() - } - if err != nil { - return - } - return grpc_server.DoFullTest(ctx, in, i) - } - - return -} - -func (s *server) QueryStats(ctx context.Context, in *gen.QueryStatsReq) (out *gen.QueryStatsResp, _ error) { - out = &gen.QueryStatsResp{} - - if instance != nil { - if ss, ok := instance.Router().V2RayServer().(*boxapi.SbV2rayServer); ok { - out.Traffic = ss.QueryStats(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", in.Tag, in.Direct)) - } - } - - return -} - -func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.ListConnectionsResp, error) { - out := &gen.ListConnectionsResp{ - // TODO upstream api - } - return out, nil -} diff --git a/go/cmd/nekobox_core/main.go b/go/cmd/nekobox_core/main.go deleted file mode 100644 index d234952..0000000 --- a/go/cmd/nekobox_core/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "os" - _ "unsafe" - - "grpc_server" - - "github.com/matsuridayo/libneko/neko_common" - boxmain "github.com/sagernet/sing-box/cmd/sing-box" - "github.com/sagernet/sing-box/constant" -) - -func main() { - fmt.Println("sing-box:", constant.Version, "NekoBox:", neko_common.Version_neko) - fmt.Println() - - // nekobox_core - if len(os.Args) > 1 && os.Args[1] == "nekobox" { - neko_common.RunMode = neko_common.RunMode_NekoBox_Core - grpc_server.RunCore(setupCore, &server{}) - return - } - - // sing-box - boxmain.Main() -} diff --git a/go/cmd/updater/.gitignore b/go/cmd/updater/.gitignore deleted file mode 100644 index 7b3c510..0000000 --- a/go/cmd/updater/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/updater -/launcher diff --git a/go/cmd/updater/go.mod b/go/cmd/updater/go.mod deleted file mode 100644 index 8a8b280..0000000 --- a/go/cmd/updater/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module updater - -go 1.18 - -require github.com/codeclysm/extract v2.2.0+incompatible - -require ( - github.com/h2non/filetype v1.1.3 // indirect - github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect - github.com/stretchr/testify v1.7.1 // indirect -) diff --git a/go/cmd/updater/go.sum b/go/cmd/updater/go.sum deleted file mode 100644 index 4949ed5..0000000 --- a/go/cmd/updater/go.sum +++ /dev/null @@ -1,19 +0,0 @@ -github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI= -github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= -github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY= -github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go/cmd/updater/launcher.go b/go/cmd/updater/launcher.go deleted file mode 100644 index ed9b0e0..0000000 --- a/go/cmd/updater/launcher.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !linux - -package main - -import ( - "log" - "runtime" -) - -func Launcher() { - log.Fatalln("launcher is not for your platform", runtime.GOOS) -} diff --git a/go/cmd/updater/launcher_linux.go b/go/cmd/updater/launcher_linux.go deleted file mode 100644 index 334708e..0000000 --- a/go/cmd/updater/launcher_linux.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "flag" - "log" - "os" - "os/exec" - "path/filepath" -) - -var local_qt_theme bool - -func Launcher() { - log.Println("Running as launcher") - wd, _ := filepath.Abs(".") - - _debug := flag.Bool("debug", false, "Debug mode") - flag.Parse() - - cmd := exec.Command("./nekobox", flag.Args()...) - - ld_env := "LD_LIBRARY_PATH=" + filepath.Join(wd, "./usr/lib") - qt_plugin_env := "QT_PLUGIN_PATH=" + filepath.Join(wd, "./usr/plugins") - - // Qt 5.12 abi is usually compatible with system Qt 5.15 - // But use package Qt 5.12 by default. - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "NKR_FROM_LAUNCHER=1") - cmd.Env = append(cmd.Env, ld_env, qt_plugin_env) - log.Println(ld_env, qt_plugin_env, cmd) - - if *_debug { - cmd.Env = append(cmd.Env, "QT_DEBUG_PLUGINS=1") - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - cmd.Run() - } else { - cmd.Start() - } -} diff --git a/go/cmd/updater/main.go b/go/cmd/updater/main.go deleted file mode 100644 index 1e6ca63..0000000 --- a/go/cmd/updater/main.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "time" -) - -func main() { - // update & launcher - exe, err := os.Executable() - if err != nil { - panic(err.Error()) - } - - wd := filepath.Dir(exe) - os.Chdir(wd) - exe = filepath.Base(os.Args[0]) - log.Println("exe:", exe, "exe dir:", wd) - - if strings.HasPrefix(strings.ToLower(exe), "updater") { - if runtime.GOOS == "windows" { - if strings.HasPrefix(strings.ToLower(exe), "updater.old") { - // 2. "updater.old" update files - time.Sleep(time.Second) - Updater() - // 3. start - exec.Command("./nekobox.exe").Start() - } else { - // 1. main prog quit and run "updater.exe" - Copy("./updater.exe", "./updater.old") - exec.Command("./updater.old", os.Args[1:]...).Start() - } - } else { - // 1. update files - Updater() - // 2. start - if os.Getenv("NKR_FROM_LAUNCHER") == "1" { - Launcher() - } else { - exec.Command("./nekobox").Start() - } - } - return - } else if strings.HasPrefix(strings.ToLower(exe), "launcher") { - Launcher() - return - } - log.Fatalf("wrong name") -} - -func Copy(src string, dst string) { - // Read all content of src to data - data, _ := ioutil.ReadFile(src) - // Write data to dst - ioutil.WriteFile(dst, data, 0644) -} diff --git a/go/cmd/updater/msgbox.go b/go/cmd/updater/msgbox.go deleted file mode 100644 index f94ba8f..0000000 --- a/go/cmd/updater/msgbox.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !windows - -package main - -func MessageBoxPlain(title, caption string) int { - return 0 -} diff --git a/go/cmd/updater/msgbox_windows.go b/go/cmd/updater/msgbox_windows.go deleted file mode 100644 index 216f489..0000000 --- a/go/cmd/updater/msgbox_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "syscall" - "unsafe" -) - -// MessageBoxPlain of Win32 API. -func MessageBoxPlain(title, caption string) int { - const ( - NULL = 0 - MB_OK = 0 - ) - return MessageBox(NULL, caption, title, MB_OK) -} - -// MessageBox of Win32 API. -func MessageBox(hwnd uintptr, caption, title string, flags uint) int { - ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call( - uintptr(hwnd), - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), - uintptr(flags)) - - return int(ret) -} diff --git a/go/cmd/updater/updater.go b/go/cmd/updater/updater.go deleted file mode 100644 index c71c775..0000000 --- a/go/cmd/updater/updater.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "context" - "log" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/codeclysm/extract" -) - -func Updater() { - pre_cleanup := func() { - if runtime.GOOS == "linux" { - os.RemoveAll("./usr") - } - os.RemoveAll("./nekoray_update") - } - - // find update package - var updatePackagePath string - if len(os.Args) == 2 && Exist(os.Args[1]) { - updatePackagePath = os.Args[1] - } else if Exist("./nekoray.zip") { - updatePackagePath = "./nekoray.zip" - } else if Exist("./nekoray.tar.gz") { - updatePackagePath = "./nekoray.tar.gz" - } else { - log.Fatalln("no update") - } - log.Println("updating from", updatePackagePath) - - // extract update package - if strings.HasSuffix(updatePackagePath, ".zip") { - pre_cleanup() - f, err := os.Open(updatePackagePath) - if err != nil { - log.Fatalln(err.Error()) - } - err = extract.Zip(context.Background(), f, "./nekoray_update", nil) - if err != nil { - log.Fatalln(err.Error()) - } - f.Close() - } else if strings.HasSuffix(updatePackagePath, ".tar.gz") { - pre_cleanup() - f, err := os.Open(updatePackagePath) - if err != nil { - log.Fatalln(err.Error()) - } - err = extract.Gz(context.Background(), f, "./nekoray_update", nil) - if err != nil { - log.Fatalln(err.Error()) - } - f.Close() - } - - // remove old file - removeAll("./*.dll") - removeAll("./*.dmp") - - // update move - err := Mv("./nekoray_update/nekoray", "./") - if err != nil { - MessageBoxPlain("NekoGui Updater", "Update failed. Please close the running instance and run the updater again.\n\n"+err.Error()) - log.Fatalln(err.Error()) - } - - os.RemoveAll("./nekoray_update") - os.RemoveAll("./nekoray.zip") - os.RemoveAll("./nekoray.tar.gz") - - // nekoray -> nekobox - os.Remove("./nekoray.exe") - os.Remove("./nekoray.png") - os.Remove("./nekoray_core.exe") -} - -func Exist(path string) bool { - _, err := os.Stat(path) - return err == nil -} - -func FindExist(paths []string) string { - for _, path := range paths { - if Exist(path) { - return path - } - } - return "" -} - -func Mv(src, dst string) error { - s, err := os.Stat(src) - if err != nil { - return err - } - if s.IsDir() { - es, err := os.ReadDir(src) - if err != nil { - return err - } - for _, e := range es { - err = Mv(filepath.Join(src, e.Name()), filepath.Join(dst, e.Name())) - if err != nil { - return err - } - } - } else { - err = os.MkdirAll(filepath.Dir(dst), 0755) - if err != nil { - return err - } - err = os.Rename(src, dst) - if err != nil { - return err - } - } - return nil -} - -func removeAll(glob string) { - files, _ := filepath.Glob(glob) - for _, f := range files { - os.Remove(f) - } -} diff --git a/go/grpc_server/auth/auth.go b/go/grpc_server/auth/auth.go deleted file mode 100644 index 53f65f8..0000000 --- a/go/grpc_server/auth/auth.go +++ /dev/null @@ -1,54 +0,0 @@ -package auth - -import ( - "context" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -// Authenticator exposes a function for authenticating requests. -type Authenticator struct { - Token string -} - -// Authenticate checks that a token exists and is valid. It stores the user -// metadata in the returned context and removes the token from the context. -func (a Authenticator) Authenticate(ctx context.Context) (newCtx context.Context, err error) { - auth, err := extractHeader(ctx, "nekoray_auth") - if err != nil { - return ctx, err - } - - if auth != a.Token { - return ctx, status.Error(codes.Unauthenticated, "invalid token") - } - - return purgeHeader(ctx, "nekoray_auth"), nil -} - -func extractHeader(ctx context.Context, header string) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", status.Error(codes.Unauthenticated, "no headers in request") - } - - authHeaders, ok := md[header] - if !ok { - return "", status.Error(codes.Unauthenticated, "no header in request") - } - - if len(authHeaders) != 1 { - return "", status.Error(codes.Unauthenticated, "more than 1 header in request") - } - - return authHeaders[0], nil -} - -func purgeHeader(ctx context.Context, header string) context.Context { - md, _ := metadata.FromIncomingContext(ctx) - mdCopy := md.Copy() - mdCopy[header] = nil - return metadata.NewIncomingContext(ctx, mdCopy) -} diff --git a/go/grpc_server/fulltest.go b/go/grpc_server/fulltest.go deleted file mode 100644 index f888ece..0000000 --- a/go/grpc_server/fulltest.go +++ /dev/null @@ -1,180 +0,0 @@ -package grpc_server - -import ( - "context" - "encoding/hex" - "fmt" - "grpc_server/gen" - "io" - "log" - "math" - "net" - "net/http" - "strings" - "time" - - "github.com/matsuridayo/libneko/neko_common" - "github.com/matsuridayo/libneko/speedtest" -) - -const ( - KiB = 1024 - MiB = 1024 * KiB -) - -func getBetweenStr(str, start, end string) string { - n := strings.Index(str, start) - if n == -1 { - n = 0 - } - str = string([]byte(str)[n:]) - m := strings.Index(str, end) - if m == -1 { - m = len(str) - } - str = string([]byte(str)[:m]) - return str[len(start):] -} - -func DoFullTest(ctx context.Context, in *gen.TestReq, instance interface{}) (out *gen.TestResp, _ error) { - out = &gen.TestResp{} - httpClient := neko_common.CreateProxyHttpClient(instance) - - // Latency - var latency string - if in.FullLatency { - t, _ := speedtest.UrlTest(httpClient, in.Url, in.Timeout, speedtest.UrlTestStandard_RTT) - out.Ms = t - if t > 0 { - latency = fmt.Sprint(t, "ms") - } else { - latency = "Error" - } - } - - // UDP Latency - var udpLatency string - if in.FullUdpLatency { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) - result := make(chan string) - - go func() { - var startTime = time.Now() - pc, err := neko_common.DialContext(ctx, instance, "udp", "8.8.8.8:53") - if err == nil { - defer pc.Close() - dnsPacket, _ := hex.DecodeString("0000010000010000000000000377777706676f6f676c6503636f6d0000010001") - _, err = pc.Write(dnsPacket) - if err == nil { - var buf [1400]byte - _, err = pc.Read(buf[:]) - } - } - if err == nil { - var endTime = time.Now() - result <- fmt.Sprint(endTime.Sub(startTime).Abs().Milliseconds(), "ms") - } else { - log.Println("UDP Latency test error:", err) - result <- "Error" - } - close(result) - }() - - select { - case <-ctx.Done(): - udpLatency = "Timeout" - case r := <-result: - udpLatency = r - } - cancel() - } - - // 入口 IP - var in_ip string - if in.FullInOut { - _in_ip, err := net.ResolveIPAddr("ip", in.InAddress) - if err == nil { - in_ip = _in_ip.String() - } else { - in_ip = err.Error() - } - } - - // 出口 IP - var out_ip string - if in.FullInOut { - resp, err := httpClient.Get("https://www.cloudflare.com/cdn-cgi/trace") - if err == nil { - b, _ := io.ReadAll(resp.Body) - out_ip = getBetweenStr(string(b), "ip=", "\n") - resp.Body.Close() - } else { - out_ip = "Error" - } - } - - // 下载 - var speed string - if in.FullSpeed { - if in.FullSpeedTimeout <= 0 { - in.FullSpeedTimeout = 30 - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(in.FullSpeedTimeout)) - result := make(chan string) - var bodyClose io.Closer - - go func() { - req, _ := http.NewRequestWithContext(ctx, "GET", in.FullSpeedUrl, nil) - resp, err := httpClient.Do(req) - if err == nil && resp != nil && resp.Body != nil { - bodyClose = resp.Body - defer resp.Body.Close() - - timeStart := time.Now() - n, _ := io.Copy(io.Discard, resp.Body) - timeEnd := time.Now() - - duration := math.Max(timeEnd.Sub(timeStart).Seconds(), 0.000001) - resultSpeed := (float64(n) / duration) / MiB - result <- fmt.Sprintf("%.2fMiB/s", resultSpeed) - } else { - result <- "Error" - } - close(result) - }() - - select { - case <-ctx.Done(): - speed = "Timeout" - case s := <-result: - speed = s - } - - cancel() - if bodyClose != nil { - bodyClose.Close() - } - } - - fr := make([]string, 0) - if latency != "" { - fr = append(fr, fmt.Sprintf("Latency: %s", latency)) - } - if udpLatency != "" { - fr = append(fr, fmt.Sprintf("UDPLatency: %s", udpLatency)) - } - if speed != "" { - fr = append(fr, fmt.Sprintf("Speed: %s", speed)) - } - if in_ip != "" { - fr = append(fr, fmt.Sprintf("In: %s", in_ip)) - } - if out_ip != "" { - fr = append(fr, fmt.Sprintf("Out: %s", out_ip)) - } - - out.FullReport = strings.Join(fr, " / ") - - return -} diff --git a/go/grpc_server/gen/libcore.pb.go b/go/grpc_server/gen/libcore.pb.go deleted file mode 100644 index c9b5282..0000000 --- a/go/grpc_server/gen/libcore.pb.go +++ /dev/null @@ -1,1146 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v4.23.3 -// source: libcore.proto - -package gen - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type TestMode int32 - -const ( - TestMode_TcpPing TestMode = 0 - TestMode_UrlTest TestMode = 1 - TestMode_FullTest TestMode = 2 -) - -// Enum value maps for TestMode. -var ( - TestMode_name = map[int32]string{ - 0: "TcpPing", - 1: "UrlTest", - 2: "FullTest", - } - TestMode_value = map[string]int32{ - "TcpPing": 0, - "UrlTest": 1, - "FullTest": 2, - } -) - -func (x TestMode) Enum() *TestMode { - p := new(TestMode) - *p = x - return p -} - -func (x TestMode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (TestMode) Descriptor() protoreflect.EnumDescriptor { - return file_libcore_proto_enumTypes[0].Descriptor() -} - -func (TestMode) Type() protoreflect.EnumType { - return &file_libcore_proto_enumTypes[0] -} - -func (x TestMode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use TestMode.Descriptor instead. -func (TestMode) EnumDescriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{0} -} - -type UpdateAction int32 - -const ( - UpdateAction_Check UpdateAction = 0 - UpdateAction_Download UpdateAction = 1 -) - -// Enum value maps for UpdateAction. -var ( - UpdateAction_name = map[int32]string{ - 0: "Check", - 1: "Download", - } - UpdateAction_value = map[string]int32{ - "Check": 0, - "Download": 1, - } -) - -func (x UpdateAction) Enum() *UpdateAction { - p := new(UpdateAction) - *p = x - return p -} - -func (x UpdateAction) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (UpdateAction) Descriptor() protoreflect.EnumDescriptor { - return file_libcore_proto_enumTypes[1].Descriptor() -} - -func (UpdateAction) Type() protoreflect.EnumType { - return &file_libcore_proto_enumTypes[1] -} - -func (x UpdateAction) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use UpdateAction.Descriptor instead. -func (UpdateAction) EnumDescriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{1} -} - -type EmptyReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *EmptyReq) Reset() { - *x = EmptyReq{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EmptyReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EmptyReq) ProtoMessage() {} - -func (x *EmptyReq) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EmptyReq.ProtoReflect.Descriptor instead. -func (*EmptyReq) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{0} -} - -type EmptyResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *EmptyResp) Reset() { - *x = EmptyResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EmptyResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EmptyResp) ProtoMessage() {} - -func (x *EmptyResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EmptyResp.ProtoReflect.Descriptor instead. -func (*EmptyResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{1} -} - -type ErrorResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` -} - -func (x *ErrorResp) Reset() { - *x = ErrorResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ErrorResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ErrorResp) ProtoMessage() {} - -func (x *ErrorResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ErrorResp.ProtoReflect.Descriptor instead. -func (*ErrorResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{2} -} - -func (x *ErrorResp) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -type LoadConfigReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CoreConfig string `protobuf:"bytes,1,opt,name=core_config,json=coreConfig,proto3" json:"core_config,omitempty"` - EnableNekorayConnections bool `protobuf:"varint,2,opt,name=enable_nekoray_connections,json=enableNekorayConnections,proto3" json:"enable_nekoray_connections,omitempty"` - StatsOutbounds []string `protobuf:"bytes,3,rep,name=stats_outbounds,json=statsOutbounds,proto3" json:"stats_outbounds,omitempty"` -} - -func (x *LoadConfigReq) Reset() { - *x = LoadConfigReq{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LoadConfigReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LoadConfigReq) ProtoMessage() {} - -func (x *LoadConfigReq) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LoadConfigReq.ProtoReflect.Descriptor instead. -func (*LoadConfigReq) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{3} -} - -func (x *LoadConfigReq) GetCoreConfig() string { - if x != nil { - return x.CoreConfig - } - return "" -} - -func (x *LoadConfigReq) GetEnableNekorayConnections() bool { - if x != nil { - return x.EnableNekorayConnections - } - return false -} - -func (x *LoadConfigReq) GetStatsOutbounds() []string { - if x != nil { - return x.StatsOutbounds - } - return nil -} - -type TestReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Mode TestMode `protobuf:"varint,1,opt,name=mode,proto3,enum=libcore.TestMode" json:"mode,omitempty"` - Timeout int32 `protobuf:"varint,6,opt,name=timeout,proto3" json:"timeout,omitempty"` - // TcpPing - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - // UrlTest - Config *LoadConfigReq `protobuf:"bytes,3,opt,name=config,proto3" json:"config,omitempty"` - Inbound string `protobuf:"bytes,4,opt,name=inbound,proto3" json:"inbound,omitempty"` - Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` - // FullTest - InAddress string `protobuf:"bytes,7,opt,name=in_address,json=inAddress,proto3" json:"in_address,omitempty"` - FullLatency bool `protobuf:"varint,8,opt,name=full_latency,json=fullLatency,proto3" json:"full_latency,omitempty"` - FullSpeed bool `protobuf:"varint,9,opt,name=full_speed,json=fullSpeed,proto3" json:"full_speed,omitempty"` - FullSpeedUrl string `protobuf:"bytes,13,opt,name=full_speed_url,json=fullSpeedUrl,proto3" json:"full_speed_url,omitempty"` - FullSpeedTimeout int32 `protobuf:"varint,14,opt,name=full_speed_timeout,json=fullSpeedTimeout,proto3" json:"full_speed_timeout,omitempty"` - FullInOut bool `protobuf:"varint,10,opt,name=full_in_out,json=fullInOut,proto3" json:"full_in_out,omitempty"` - FullUdpLatency bool `protobuf:"varint,12,opt,name=full_udp_latency,json=fullUdpLatency,proto3" json:"full_udp_latency,omitempty"` - // Deprecated: Do not use. - FullNat bool `protobuf:"varint,11,opt,name=full_nat,json=fullNat,proto3" json:"full_nat,omitempty"` -} - -func (x *TestReq) Reset() { - *x = TestReq{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TestReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TestReq) ProtoMessage() {} - -func (x *TestReq) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TestReq.ProtoReflect.Descriptor instead. -func (*TestReq) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{4} -} - -func (x *TestReq) GetMode() TestMode { - if x != nil { - return x.Mode - } - return TestMode_TcpPing -} - -func (x *TestReq) GetTimeout() int32 { - if x != nil { - return x.Timeout - } - return 0 -} - -func (x *TestReq) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *TestReq) GetConfig() *LoadConfigReq { - if x != nil { - return x.Config - } - return nil -} - -func (x *TestReq) GetInbound() string { - if x != nil { - return x.Inbound - } - return "" -} - -func (x *TestReq) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *TestReq) GetInAddress() string { - if x != nil { - return x.InAddress - } - return "" -} - -func (x *TestReq) GetFullLatency() bool { - if x != nil { - return x.FullLatency - } - return false -} - -func (x *TestReq) GetFullSpeed() bool { - if x != nil { - return x.FullSpeed - } - return false -} - -func (x *TestReq) GetFullSpeedUrl() string { - if x != nil { - return x.FullSpeedUrl - } - return "" -} - -func (x *TestReq) GetFullSpeedTimeout() int32 { - if x != nil { - return x.FullSpeedTimeout - } - return 0 -} - -func (x *TestReq) GetFullInOut() bool { - if x != nil { - return x.FullInOut - } - return false -} - -func (x *TestReq) GetFullUdpLatency() bool { - if x != nil { - return x.FullUdpLatency - } - return false -} - -// Deprecated: Do not use. -func (x *TestReq) GetFullNat() bool { - if x != nil { - return x.FullNat - } - return false -} - -type TestResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` - Ms int32 `protobuf:"varint,2,opt,name=ms,proto3" json:"ms,omitempty"` - FullReport string `protobuf:"bytes,3,opt,name=full_report,json=fullReport,proto3" json:"full_report,omitempty"` -} - -func (x *TestResp) Reset() { - *x = TestResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TestResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TestResp) ProtoMessage() {} - -func (x *TestResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TestResp.ProtoReflect.Descriptor instead. -func (*TestResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{5} -} - -func (x *TestResp) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -func (x *TestResp) GetMs() int32 { - if x != nil { - return x.Ms - } - return 0 -} - -func (x *TestResp) GetFullReport() string { - if x != nil { - return x.FullReport - } - return "" -} - -type QueryStatsReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` - Direct string `protobuf:"bytes,2,opt,name=direct,proto3" json:"direct,omitempty"` -} - -func (x *QueryStatsReq) Reset() { - *x = QueryStatsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *QueryStatsReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*QueryStatsReq) ProtoMessage() {} - -func (x *QueryStatsReq) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use QueryStatsReq.ProtoReflect.Descriptor instead. -func (*QueryStatsReq) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{6} -} - -func (x *QueryStatsReq) GetTag() string { - if x != nil { - return x.Tag - } - return "" -} - -func (x *QueryStatsReq) GetDirect() string { - if x != nil { - return x.Direct - } - return "" -} - -type QueryStatsResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Traffic int64 `protobuf:"varint,1,opt,name=traffic,proto3" json:"traffic,omitempty"` -} - -func (x *QueryStatsResp) Reset() { - *x = QueryStatsResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *QueryStatsResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*QueryStatsResp) ProtoMessage() {} - -func (x *QueryStatsResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use QueryStatsResp.ProtoReflect.Descriptor instead. -func (*QueryStatsResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{7} -} - -func (x *QueryStatsResp) GetTraffic() int64 { - if x != nil { - return x.Traffic - } - return 0 -} - -type UpdateReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Action UpdateAction `protobuf:"varint,1,opt,name=action,proto3,enum=libcore.UpdateAction" json:"action,omitempty"` - CheckPreRelease bool `protobuf:"varint,2,opt,name=check_pre_release,json=checkPreRelease,proto3" json:"check_pre_release,omitempty"` -} - -func (x *UpdateReq) Reset() { - *x = UpdateReq{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateReq) ProtoMessage() {} - -func (x *UpdateReq) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateReq.ProtoReflect.Descriptor instead. -func (*UpdateReq) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{8} -} - -func (x *UpdateReq) GetAction() UpdateAction { - if x != nil { - return x.Action - } - return UpdateAction_Check -} - -func (x *UpdateReq) GetCheckPreRelease() bool { - if x != nil { - return x.CheckPreRelease - } - return false -} - -type UpdateResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` - AssetsName string `protobuf:"bytes,2,opt,name=assets_name,json=assetsName,proto3" json:"assets_name,omitempty"` - DownloadUrl string `protobuf:"bytes,3,opt,name=download_url,json=downloadUrl,proto3" json:"download_url,omitempty"` - ReleaseUrl string `protobuf:"bytes,4,opt,name=release_url,json=releaseUrl,proto3" json:"release_url,omitempty"` - ReleaseNote string `protobuf:"bytes,5,opt,name=release_note,json=releaseNote,proto3" json:"release_note,omitempty"` - IsPreRelease bool `protobuf:"varint,6,opt,name=is_pre_release,json=isPreRelease,proto3" json:"is_pre_release,omitempty"` -} - -func (x *UpdateResp) Reset() { - *x = UpdateResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateResp) ProtoMessage() {} - -func (x *UpdateResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateResp.ProtoReflect.Descriptor instead. -func (*UpdateResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{9} -} - -func (x *UpdateResp) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -func (x *UpdateResp) GetAssetsName() string { - if x != nil { - return x.AssetsName - } - return "" -} - -func (x *UpdateResp) GetDownloadUrl() string { - if x != nil { - return x.DownloadUrl - } - return "" -} - -func (x *UpdateResp) GetReleaseUrl() string { - if x != nil { - return x.ReleaseUrl - } - return "" -} - -func (x *UpdateResp) GetReleaseNote() string { - if x != nil { - return x.ReleaseNote - } - return "" -} - -func (x *UpdateResp) GetIsPreRelease() bool { - if x != nil { - return x.IsPreRelease - } - return false -} - -type ListConnectionsResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - NekorayConnectionsJson string `protobuf:"bytes,1,opt,name=nekoray_connections_json,json=nekorayConnectionsJson,proto3" json:"nekoray_connections_json,omitempty"` -} - -func (x *ListConnectionsResp) Reset() { - *x = ListConnectionsResp{} - if protoimpl.UnsafeEnabled { - mi := &file_libcore_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListConnectionsResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListConnectionsResp) ProtoMessage() {} - -func (x *ListConnectionsResp) ProtoReflect() protoreflect.Message { - mi := &file_libcore_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListConnectionsResp.ProtoReflect.Descriptor instead. -func (*ListConnectionsResp) Descriptor() ([]byte, []int) { - return file_libcore_proto_rawDescGZIP(), []int{10} -} - -func (x *ListConnectionsResp) GetNekorayConnectionsJson() string { - if x != nil { - return x.NekorayConnectionsJson - } - return "" -} - -var File_libcore_proto protoreflect.FileDescriptor - -var file_libcore_proto_rawDesc = []byte{ - 0x0a, 0x0d, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x07, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x0a, 0x0a, 0x08, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x52, 0x65, 0x71, 0x22, 0x0b, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x22, 0x21, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, - 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x22, 0x97, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x72, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x1a, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x6e, 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x6f, - 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, - 0x73, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0xde, - 0x03, 0x0a, 0x07, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x12, 0x25, 0x0a, 0x04, 0x6d, 0x6f, - 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x52, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x4c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x65, - 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x70, 0x65, - 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, - 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, - 0x53, 0x70, 0x65, 0x65, 0x64, 0x55, 0x72, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x75, 0x6c, 0x6c, - 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x70, 0x65, 0x65, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x69, - 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x75, 0x6c, - 0x6c, 0x49, 0x6e, 0x4f, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, - 0x64, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0e, 0x66, 0x75, 0x6c, 0x6c, 0x55, 0x64, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, - 0x12, 0x1d, 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x6e, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x66, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x74, 0x22, - 0x51, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x6d, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x22, 0x39, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x22, 0x2a, 0x0a, - 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x2d, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x70, - 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x65, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, - 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, - 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x72, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x24, - 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x50, 0x72, 0x65, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x22, 0x4f, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x38, 0x0a, 0x18, 0x6e, - 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x6e, - 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x2a, 0x32, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, - 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x63, 0x70, 0x50, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x72, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x46, - 0x75, 0x6c, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x10, 0x02, 0x2a, 0x27, 0x0a, 0x0c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, - 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x0e, 0x4c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x45, 0x78, 0x69, 0x74, 0x12, 0x11, 0x2e, - 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, - 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x05, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4c, - 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, - 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, - 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x6c, 0x69, - 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, - 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x12, 0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x6c, 0x69, 0x62, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, 0x11, 0x5a, 0x0f, 0x67, 0x72, 0x70, - 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_libcore_proto_rawDescOnce sync.Once - file_libcore_proto_rawDescData = file_libcore_proto_rawDesc -) - -func file_libcore_proto_rawDescGZIP() []byte { - file_libcore_proto_rawDescOnce.Do(func() { - file_libcore_proto_rawDescData = protoimpl.X.CompressGZIP(file_libcore_proto_rawDescData) - }) - return file_libcore_proto_rawDescData -} - -var file_libcore_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_libcore_proto_msgTypes = make([]protoimpl.MessageInfo, 11) -var file_libcore_proto_goTypes = []interface{}{ - (TestMode)(0), // 0: libcore.TestMode - (UpdateAction)(0), // 1: libcore.UpdateAction - (*EmptyReq)(nil), // 2: libcore.EmptyReq - (*EmptyResp)(nil), // 3: libcore.EmptyResp - (*ErrorResp)(nil), // 4: libcore.ErrorResp - (*LoadConfigReq)(nil), // 5: libcore.LoadConfigReq - (*TestReq)(nil), // 6: libcore.TestReq - (*TestResp)(nil), // 7: libcore.TestResp - (*QueryStatsReq)(nil), // 8: libcore.QueryStatsReq - (*QueryStatsResp)(nil), // 9: libcore.QueryStatsResp - (*UpdateReq)(nil), // 10: libcore.UpdateReq - (*UpdateResp)(nil), // 11: libcore.UpdateResp - (*ListConnectionsResp)(nil), // 12: libcore.ListConnectionsResp -} -var file_libcore_proto_depIdxs = []int32{ - 0, // 0: libcore.TestReq.mode:type_name -> libcore.TestMode - 5, // 1: libcore.TestReq.config:type_name -> libcore.LoadConfigReq - 1, // 2: libcore.UpdateReq.action:type_name -> libcore.UpdateAction - 2, // 3: libcore.LibcoreService.Exit:input_type -> libcore.EmptyReq - 10, // 4: libcore.LibcoreService.Update:input_type -> libcore.UpdateReq - 5, // 5: libcore.LibcoreService.Start:input_type -> libcore.LoadConfigReq - 2, // 6: libcore.LibcoreService.Stop:input_type -> libcore.EmptyReq - 6, // 7: libcore.LibcoreService.Test:input_type -> libcore.TestReq - 8, // 8: libcore.LibcoreService.QueryStats:input_type -> libcore.QueryStatsReq - 2, // 9: libcore.LibcoreService.ListConnections:input_type -> libcore.EmptyReq - 3, // 10: libcore.LibcoreService.Exit:output_type -> libcore.EmptyResp - 11, // 11: libcore.LibcoreService.Update:output_type -> libcore.UpdateResp - 4, // 12: libcore.LibcoreService.Start:output_type -> libcore.ErrorResp - 4, // 13: libcore.LibcoreService.Stop:output_type -> libcore.ErrorResp - 7, // 14: libcore.LibcoreService.Test:output_type -> libcore.TestResp - 9, // 15: libcore.LibcoreService.QueryStats:output_type -> libcore.QueryStatsResp - 12, // 16: libcore.LibcoreService.ListConnections:output_type -> libcore.ListConnectionsResp - 10, // [10:17] is the sub-list for method output_type - 3, // [3:10] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { file_libcore_proto_init() } -func file_libcore_proto_init() { - if File_libcore_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_libcore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EmptyReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EmptyResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ErrorResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoadConfigReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TestReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TestResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryStatsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryStatsResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_libcore_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListConnectionsResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_libcore_proto_rawDesc, - NumEnums: 2, - NumMessages: 11, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_libcore_proto_goTypes, - DependencyIndexes: file_libcore_proto_depIdxs, - EnumInfos: file_libcore_proto_enumTypes, - MessageInfos: file_libcore_proto_msgTypes, - }.Build() - File_libcore_proto = out.File - file_libcore_proto_rawDesc = nil - file_libcore_proto_goTypes = nil - file_libcore_proto_depIdxs = nil -} diff --git a/go/grpc_server/gen/libcore.proto b/go/grpc_server/gen/libcore.proto deleted file mode 100644 index 23abf1b..0000000 --- a/go/grpc_server/gen/libcore.proto +++ /dev/null @@ -1,94 +0,0 @@ -syntax = "proto3"; - -package libcore; -option go_package = "grpc_server/gen"; - -service LibcoreService { - rpc Exit(EmptyReq) returns (EmptyResp) {} - rpc Update(UpdateReq) returns (UpdateResp) {} - // - rpc Start(LoadConfigReq) returns (ErrorResp) {} - rpc Stop(EmptyReq) returns (ErrorResp) {} - rpc Test(TestReq) returns (TestResp) {} - rpc QueryStats(QueryStatsReq) returns (QueryStatsResp) {} - rpc ListConnections(EmptyReq) returns (ListConnectionsResp) {} -} - -message EmptyReq {} - -message EmptyResp {} - -message ErrorResp { - string error = 1; -} - -message LoadConfigReq { - string core_config = 1; - bool enable_nekoray_connections = 2; - repeated string stats_outbounds = 3; -} - -enum TestMode { - TcpPing = 0; - UrlTest = 1; - FullTest = 2; -} - -message TestReq { - TestMode mode = 1; - int32 timeout = 6; - // TcpPing - string address = 2; - // UrlTest - LoadConfigReq config = 3; - string inbound = 4; - string url = 5; - // FullTest - string in_address = 7; - bool full_latency = 8; - bool full_speed = 9; - string full_speed_url = 13; - int32 full_speed_timeout = 14; - bool full_in_out = 10; - bool full_udp_latency = 12; - // - bool full_nat = 11 [deprecated = true]; -} - -message TestResp { - string error = 1; - int32 ms = 2; - string full_report = 3; -} - -message QueryStatsReq{ - string tag = 1; - string direct = 2; -} - -message QueryStatsResp{ - int64 traffic = 1; -} - -enum UpdateAction { - Check = 0; - Download = 1; -} - -message UpdateReq { - UpdateAction action = 1; - bool check_pre_release = 2; -} - -message UpdateResp { - string error = 1; - string assets_name = 2; - string download_url = 3; - string release_url = 4; - string release_note = 5; - bool is_pre_release = 6; -} - -message ListConnectionsResp { - string nekoray_connections_json = 1; -} diff --git a/go/grpc_server/gen/libcore_grpc.pb.go b/go/grpc_server/gen/libcore_grpc.pb.go deleted file mode 100644 index 663854b..0000000 --- a/go/grpc_server/gen/libcore_grpc.pb.go +++ /dev/null @@ -1,321 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v4.23.3 -// source: libcore.proto - -package gen - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// LibcoreServiceClient is the client API for LibcoreService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type LibcoreServiceClient interface { - Exit(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*EmptyResp, error) - Update(ctx context.Context, in *UpdateReq, opts ...grpc.CallOption) (*UpdateResp, error) - Start(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error) - Stop(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ErrorResp, error) - Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*TestResp, error) - QueryStats(ctx context.Context, in *QueryStatsReq, opts ...grpc.CallOption) (*QueryStatsResp, error) - ListConnections(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ListConnectionsResp, error) -} - -type libcoreServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewLibcoreServiceClient(cc grpc.ClientConnInterface) LibcoreServiceClient { - return &libcoreServiceClient{cc} -} - -func (c *libcoreServiceClient) Exit(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*EmptyResp, error) { - out := new(EmptyResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/Exit", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) Update(ctx context.Context, in *UpdateReq, opts ...grpc.CallOption) (*UpdateResp, error) { - out := new(UpdateResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/Update", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) Start(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error) { - out := new(ErrorResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/Start", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) Stop(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ErrorResp, error) { - out := new(ErrorResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/Stop", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*TestResp, error) { - out := new(TestResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/Test", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) QueryStats(ctx context.Context, in *QueryStatsReq, opts ...grpc.CallOption) (*QueryStatsResp, error) { - out := new(QueryStatsResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/QueryStats", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *libcoreServiceClient) ListConnections(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ListConnectionsResp, error) { - out := new(ListConnectionsResp) - err := c.cc.Invoke(ctx, "/libcore.LibcoreService/ListConnections", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// LibcoreServiceServer is the server API for LibcoreService service. -// All implementations must embed UnimplementedLibcoreServiceServer -// for forward compatibility -type LibcoreServiceServer interface { - Exit(context.Context, *EmptyReq) (*EmptyResp, error) - Update(context.Context, *UpdateReq) (*UpdateResp, error) - Start(context.Context, *LoadConfigReq) (*ErrorResp, error) - Stop(context.Context, *EmptyReq) (*ErrorResp, error) - Test(context.Context, *TestReq) (*TestResp, error) - QueryStats(context.Context, *QueryStatsReq) (*QueryStatsResp, error) - ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error) - mustEmbedUnimplementedLibcoreServiceServer() -} - -// UnimplementedLibcoreServiceServer must be embedded to have forward compatible implementations. -type UnimplementedLibcoreServiceServer struct { -} - -func (UnimplementedLibcoreServiceServer) Exit(context.Context, *EmptyReq) (*EmptyResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Exit not implemented") -} -func (UnimplementedLibcoreServiceServer) Update(context.Context, *UpdateReq) (*UpdateResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") -} -func (UnimplementedLibcoreServiceServer) Start(context.Context, *LoadConfigReq) (*ErrorResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") -} -func (UnimplementedLibcoreServiceServer) Stop(context.Context, *EmptyReq) (*ErrorResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") -} -func (UnimplementedLibcoreServiceServer) Test(context.Context, *TestReq) (*TestResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Test not implemented") -} -func (UnimplementedLibcoreServiceServer) QueryStats(context.Context, *QueryStatsReq) (*QueryStatsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented") -} -func (UnimplementedLibcoreServiceServer) ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListConnections not implemented") -} -func (UnimplementedLibcoreServiceServer) mustEmbedUnimplementedLibcoreServiceServer() {} - -// UnsafeLibcoreServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to LibcoreServiceServer will -// result in compilation errors. -type UnsafeLibcoreServiceServer interface { - mustEmbedUnimplementedLibcoreServiceServer() -} - -func RegisterLibcoreServiceServer(s grpc.ServiceRegistrar, srv LibcoreServiceServer) { - s.RegisterService(&LibcoreService_ServiceDesc, srv) -} - -func _LibcoreService_Exit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EmptyReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).Exit(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/Exit", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).Exit(ctx, req.(*EmptyReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).Update(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/Update", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).Update(ctx, req.(*UpdateReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LoadConfigReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).Start(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/Start", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).Start(ctx, req.(*LoadConfigReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EmptyReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).Stop(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/Stop", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).Stop(ctx, req.(*EmptyReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_Test_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TestReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).Test(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/Test", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).Test(ctx, req.(*TestReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryStatsReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).QueryStats(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/QueryStats", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).QueryStats(ctx, req.(*QueryStatsReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _LibcoreService_ListConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EmptyReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(LibcoreServiceServer).ListConnections(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/libcore.LibcoreService/ListConnections", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(LibcoreServiceServer).ListConnections(ctx, req.(*EmptyReq)) - } - return interceptor(ctx, in, info, handler) -} - -// LibcoreService_ServiceDesc is the grpc.ServiceDesc for LibcoreService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var LibcoreService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "libcore.LibcoreService", - HandlerType: (*LibcoreServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Exit", - Handler: _LibcoreService_Exit_Handler, - }, - { - MethodName: "Update", - Handler: _LibcoreService_Update_Handler, - }, - { - MethodName: "Start", - Handler: _LibcoreService_Start_Handler, - }, - { - MethodName: "Stop", - Handler: _LibcoreService_Stop_Handler, - }, - { - MethodName: "Test", - Handler: _LibcoreService_Test_Handler, - }, - { - MethodName: "QueryStats", - Handler: _LibcoreService_QueryStats_Handler, - }, - { - MethodName: "ListConnections", - Handler: _LibcoreService_ListConnections_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "libcore.proto", -} diff --git a/go/grpc_server/gen/update_proto.sh b/go/grpc_server/gen/update_proto.sh deleted file mode 100644 index 709ed22..0000000 --- a/go/grpc_server/gen/update_proto.sh +++ /dev/null @@ -1,3 +0,0 @@ -protoc -I . --go_out=. --go_opt paths=source_relative --go-grpc_out=. --go-grpc_opt paths=source_relative libcore.proto - -# protoc -I . --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` libcore.proto diff --git a/go/grpc_server/go.mod b/go/grpc_server/go.mod deleted file mode 100644 index 2e7a85e..0000000 --- a/go/grpc_server/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module grpc_server - -go 1.19 - -require ( - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/matsuridayo/libneko v1.0.0 // replaced - google.golang.org/grpc v1.49.0 - google.golang.org/protobuf v1.28.1 -) - -require ( - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect - golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect -) - -replace github.com/matsuridayo/libneko v1.0.0 => ../../../libneko diff --git a/go/grpc_server/go.sum b/go/grpc_server/go.sum deleted file mode 100644 index 6140d88..0000000 --- a/go/grpc_server/go.sum +++ /dev/null @@ -1,178 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb h1:ZrsicilzPCS/Xr8qtBZZLpy4P9TYXAfl49ctG1/5tgw= -google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/go/grpc_server/grpc.go b/go/grpc_server/grpc.go deleted file mode 100644 index 210f780..0000000 --- a/go/grpc_server/grpc.go +++ /dev/null @@ -1,103 +0,0 @@ -package grpc_server - -import ( - "bufio" - "context" - "flag" - "fmt" - "grpc_server/auth" - "grpc_server/gen" - "log" - "net" - "os" - "runtime" - "strconv" - "strings" - "syscall" - "time" - - "github.com/matsuridayo/libneko/neko_common" - - grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" - "google.golang.org/grpc" -) - -type BaseServer struct { - gen.LibcoreServiceServer -} - -func (s *BaseServer) Exit(ctx context.Context, in *gen.EmptyReq) (out *gen.EmptyResp, _ error) { - out = &gen.EmptyResp{} - - // Connection closed - os.Exit(0) - return -} - -func RunCore(setupCore func(), server gen.LibcoreServiceServer) { - _token := flag.String("token", "", "") - _port := flag.Int("port", 19810, "") - _debug := flag.Bool("debug", false, "") - flag.CommandLine.Parse(os.Args[2:]) - - neko_common.Debug = *_debug - - go func() { - parent, err := os.FindProcess(os.Getppid()) - if err != nil { - log.Fatalln("find parent:", err) - } - if runtime.GOOS == "windows" { - state, err := parent.Wait() - log.Fatalln("parent exited:", state, err) - } else { - for { - time.Sleep(time.Second * 10) - err = parent.Signal(syscall.Signal(0)) - if err != nil { - log.Fatalln("parent exited:", err) - } - } - } - }() - - // Libcore - setupCore() - - // GRPC - lis, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(*_port)) - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - - token := *_token - if token == "" { - os.Stderr.WriteString("Please set a token: ") - s := bufio.NewScanner(os.Stdin) - if s.Scan() { - token = strings.TrimSpace(s.Text()) - } - } - if token == "" { - fmt.Println("You must set a token") - os.Exit(0) - } - os.Stderr.WriteString("token is set\n") - - auther := auth.Authenticator{ - Token: token, - } - - s := grpc.NewServer( - grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(auther.Authenticate)), - grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(auther.Authenticate)), - ) - gen.RegisterLibcoreServiceServer(s, server) - - name := "nekobox_core" - - log.Printf("%s grpc server listening at %v\n", name, lis.Addr()) - if err := s.Serve(lis); err != nil { - log.Fatalf("failed to serve: %v", err) - } -} diff --git a/go/grpc_server/update.go b/go/grpc_server/update.go deleted file mode 100644 index 2e7224e..0000000 --- a/go/grpc_server/update.go +++ /dev/null @@ -1,116 +0,0 @@ -package grpc_server - -import ( - "context" - "encoding/json" - "grpc_server/gen" - "io" - "net/http" - "os" - "runtime" - "strings" - "time" - - "github.com/matsuridayo/libneko/neko_common" -) - -var update_download_url string - -func (s *BaseServer) Update(ctx context.Context, in *gen.UpdateReq) (*gen.UpdateResp, error) { - ret := &gen.UpdateResp{} - - client := neko_common.CreateProxyHttpClient(neko_common.GetCurrentInstance()) - - if in.Action == gen.UpdateAction_Check { // Check update - ctx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - - req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/repos/MatsuriDayo/nekoray/releases", nil) - resp, err := client.Do(req) - if err != nil { - ret.Error = err.Error() - return ret, nil - } - defer resp.Body.Close() - - v := []struct { - HtmlUrl string `json:"html_url"` - Assets []struct { - Name string `json:"name"` - BrowserDownloadUrl string `json:"browser_download_url"` - } `json:"assets"` - Prerelease bool `json:"prerelease"` - Body string `json:"body"` - }{} - err = json.NewDecoder(resp.Body).Decode(&v) - if err != nil { - ret.Error = err.Error() - return ret, nil - } - - nowVer := strings.TrimLeft(neko_common.Version_neko, "nekoray-") - - var search string - if runtime.GOOS == "windows" && runtime.GOARCH == "amd64" { - search = "windows64" - } else if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" { - search = "linux64" - } else if runtime.GOOS == "darwin" { - search = "macos-" + runtime.GOARCH - } else { - ret.Error = "Not official support platform" - return ret, nil - } - - for _, release := range v { - if len(release.Assets) > 0 { - for _, asset := range release.Assets { - if strings.Contains(asset.Name, nowVer) { - return ret, nil // No update - } - if strings.Contains(asset.Name, search) { - if release.Prerelease && !in.CheckPreRelease { - continue - } - update_download_url = asset.BrowserDownloadUrl - ret.AssetsName = asset.Name - ret.DownloadUrl = asset.BrowserDownloadUrl - ret.ReleaseUrl = release.HtmlUrl - ret.ReleaseNote = release.Body - ret.IsPreRelease = release.Prerelease - return ret, nil // update - } - } - } - } - } else { // Download update - if update_download_url == "" { - ret.Error = "?" - return ret, nil - } - - req, _ := http.NewRequestWithContext(ctx, "GET", update_download_url, nil) - resp, err := client.Do(req) - if err != nil { - ret.Error = err.Error() - return ret, nil - } - defer resp.Body.Close() - - f, err := os.OpenFile("../nekoray.zip", os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0644) - if err != nil { - ret.Error = err.Error() - return ret, nil - } - defer f.Close() - - _, err = io.Copy(f, resp.Body) - if err != nil { - ret.Error = err.Error() - return ret, nil - } - f.Sync() - } - - return ret, nil -} diff --git a/libs/.gitignore b/libs/.gitignore deleted file mode 100644 index ecbf89d..0000000 --- a/libs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/deps* -downloaded \ No newline at end of file diff --git a/libs/build_deps_all.sh b/libs/build_deps_all.sh deleted file mode 100755 index a17b803..0000000 --- a/libs/build_deps_all.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -set -e - -cd libs - -# 参数 -if [ -z $cmake ]; then - cmake="cmake" -fi -if [ -z $deps ]; then - deps="deps" -fi - -# libs/deps/... -mkdir -p $deps -cd $deps -if [ -z $NKR_PACKAGE ]; then - INSTALL_PREFIX=$PWD/built -else - INSTALL_PREFIX=$PWD/package -fi -rm -rf $INSTALL_PREFIX -mkdir -p $INSTALL_PREFIX - -#### clean #### -clean() { - rm -rf dl.zip yaml-* zxing-* protobuf -} - -#### ZXing v2.0.0 #### -curl -L -o dl.zip https://github.com/nu-book/zxing-cpp/archive/refs/tags/v2.0.0.zip -unzip dl.zip - -cd zxing-* -mkdir -p build -cd build - -$cmake .. -GNinja -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=OFF -DBUILD_BLACKBOX_TESTS=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -ninja && ninja install - -cd ../.. - -#### yaml-cpp #### -curl -L -o dl.zip https://github.com/jbeder/yaml-cpp/archive/refs/tags/yaml-cpp-0.7.0.zip -unzip dl.zip - -cd yaml-* -mkdir -p build -cd build - -$cmake .. -GNinja -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -ninja && ninja install - -cd ../.. - -#### protobuf #### -git clone --recurse-submodules -b v21.4 --depth 1 --shallow-submodules https://github.com/protocolbuffers/protobuf - -#备注:交叉编译要在 host 也安装 protobuf 并且版本一致,编译安装,同参数,安装到 /usr/local - -mkdir -p protobuf/build -cd protobuf/build - -$cmake .. -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -Dprotobuf_MSVC_STATIC_RUNTIME=OFF \ - -Dprotobuf_BUILD_TESTS=OFF \ - -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -ninja && ninja install - -cd ../.. - -#### -clean diff --git a/libs/build_go.sh b/libs/build_go.sh deleted file mode 100755 index 966ae18..0000000 --- a/libs/build_go.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -set -e - -source libs/env_deploy.sh -[ "$GOOS" == "windows" ] && [ "$GOARCH" == "amd64" ] && DEST=$DEPLOYMENT/windows64 || true -[ "$GOOS" == "windows" ] && [ "$GOARCH" == "arm64" ] && DEST=$DEPLOYMENT/windows-arm64 || true -[ "$GOOS" == "linux" ] && [ "$GOARCH" == "amd64" ] && DEST=$DEPLOYMENT/linux64 || true -[ "$GOOS" == "linux" ] && [ "$GOARCH" == "arm64" ] && DEST=$DEPLOYMENT/linux-arm64 || true -if [ -z $DEST ]; then - echo "Please set GOOS GOARCH" - exit 1 -fi -rm -rf $DEST -mkdir -p $DEST - -export CGO_ENABLED=0 - -#### Go: updater #### -pushd go/cmd/updater -[ "$GOOS" == "darwin" ] || go build -o $DEST -trimpath -ldflags "-w -s" -[ "$GOOS" == "linux" ] && mv $DEST/updater $DEST/launcher || true -popd - -#### Go: nekobox_core #### -pushd go/cmd/nekobox_core -go build -v -o $DEST -trimpath -ldflags "-w -s -X github.com/matsuridayo/libneko/neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech" -popd diff --git a/libs/build_public_res.sh b/libs/build_public_res.sh deleted file mode 100755 index 0fdbf19..0000000 --- a/libs/build_public_res.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -source libs/env_deploy.sh -DEST=$DEPLOYMENT/public_res -rm -rf $DEST -mkdir -p $DEST - -#### Download geodata #### -curl -fLso $DEST/geoip.dat "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -curl -fLso $DEST/geosite.dat "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" -curl -fLso $DEST/geoip.db "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db" -curl -fLso $DEST/geosite.db "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db" - -#### copy res/public #### -cp res/public/* $DEST diff --git a/libs/deploy_linux64.sh b/libs/deploy_linux64.sh deleted file mode 100755 index 76d0b29..0000000 --- a/libs/deploy_linux64.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -set -e - -source libs/env_deploy.sh -DEST=$DEPLOYMENT/linux64 -rm -rf $DEST -mkdir -p $DEST - -#### copy binary #### -cp $BUILD/nekobox $DEST - -#### Download: prebuilt runtime #### -curl -Lso usr.zip https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/20230202-5.12.8-ubuntu20.04-linux64.zip -unzip usr.zip -mv usr $DEST - - -#### copy so #### -# 5.11 looks buggy on new systems... -exit - -USR_LIB=/usr/lib/x86_64-linux-gnu -mkdir usr -pushd usr -mkdir lib -pushd lib -cp $USR_LIB/libQt5Core.so.5 . -cp $USR_LIB/libQt5DBus.so.5 . -cp $USR_LIB/libQt5Gui.so.5 . -cp $USR_LIB/libQt5Network.so.5 . -cp $USR_LIB/libQt5Svg.so.5 . -cp $USR_LIB/libQt5Widgets.so.5 . -cp $USR_LIB/libQt5X11Extras.so.5 . -cp $USR_LIB/libQt5XcbQpa.so.5 . -cp $USR_LIB/libdouble-conversion.so.? . -cp $USR_LIB/libxcb-util.so.? . -cp $USR_LIB/libicuuc.so.?? . -cp $USR_LIB/libicui18n.so.?? . -cp $USR_LIB/libicudata.so.?? . -popd -mkdir plugins -pushd plugins -cp -r $USR_LIB/qt5/plugins/bearer . -cp -r $USR_LIB/qt5/plugins/iconengines . -cp -r $USR_LIB/qt5/plugins/imageformats . -cp -r $USR_LIB/qt5/plugins/platforminputcontexts . -cp -r $USR_LIB/qt5/plugins/platforms . -cp -r $USR_LIB/qt5/plugins/xcbglintegrations . -popd -popd -mv usr $DEST diff --git a/libs/deploy_windows64.sh b/libs/deploy_windows64.sh deleted file mode 100755 index a513aad..0000000 --- a/libs/deploy_windows64.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -e - -source libs/env_deploy.sh -DEST=$DEPLOYMENT/windows64 -rm -rf $DEST -mkdir -p $DEST - -#### copy exe #### -cp $BUILD/nekobox.exe $DEST - -#### deploy qt & DLL runtime #### -pushd $DEST -windeployqt nekobox.exe --no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --verbose 2 -rm -rf translations -rm -rf libEGL.dll libGLESv2.dll Qt6Pdf.dll - -if [ "$DL_QT_VER" != "5.15" ]; then - cp $SRC_ROOT/qtsdk/Qt/bin/libcrypto-3-x64.dll . - cp $SRC_ROOT/qtsdk/Qt/bin/libssl-3-x64.dll . -fi - -popd - -#### prepare deployment #### -cp $BUILD/*.pdb $DEPLOYMENT diff --git a/libs/download_qtsdk_win.sh b/libs/download_qtsdk_win.sh deleted file mode 100644 index f683210..0000000 --- a/libs/download_qtsdk_win.sh +++ /dev/null @@ -1,10 +0,0 @@ -mkdir qtsdk -cd qtsdk -if [ "$DL_QT_VER" == "5.15" ]; then - curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt5.15.7-Windows-x86_64-VS2019-16.11.20-20221103.7z -else - curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.7.2-Windows-x86_64-VS2022-17.10.3-20240621.7z -fi -7z x *.7z -rm *.7z -mv Qt* Qt diff --git a/libs/env_deploy.sh b/libs/env_deploy.sh deleted file mode 100644 index 9682533..0000000 --- a/libs/env_deploy.sh +++ /dev/null @@ -1,4 +0,0 @@ -SRC_ROOT="$PWD" -DEPLOYMENT="$SRC_ROOT/deployment" -BUILD="$SRC_ROOT/build" -version_standalone="nekoray-"$(cat nekoray_version.txt) # 下次改 diff --git a/libs/env_qtsdk.sh b/libs/env_qtsdk.sh deleted file mode 100644 index 0cf6eaa..0000000 --- a/libs/env_qtsdk.sh +++ /dev/null @@ -1,8 +0,0 @@ -echo "Setting Qt Sdk Dir to" "$1" -export Qt5_DIR="$1" -export Qt6_DIR=$Qt5_DIR -export PATH=$PATH:$Qt5_DIR/bin -export LD_LIBRARY_PATH=$Qt5_DIR/lib -export PKG_CONFIG_PATH=$Qt5_DIR/lib/pkgconfig -export QT_PLUGIN_PATH=$Qt5_DIR/plugins -export QML2_IMPORT_PATH=$Qt5_DIR/lib/qml diff --git a/libs/format_cpp.sh b/libs/format_cpp.sh deleted file mode 100755 index 1251ad5..0000000 --- a/libs/format_cpp.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -git ls-files | grep -E "\.cpp|\.h" | grep -v "3rdparty" | xargs -n1 clang-format -i diff --git a/libs/get_source.sh b/libs/get_source.sh deleted file mode 100755 index b416bd7..0000000 --- a/libs/get_source.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -set -e - -source libs/env_deploy.sh -ENV_NEKORAY=1 -source libs/get_source_env.sh -pushd .. - -#### - -if [ ! -d "sing-box" ]; then - git clone --no-checkout https://github.com/MatsuriDayo/sing-box.git -fi -pushd sing-box -git checkout "$COMMIT_SING_BOX" - -popd - -#### - -if [ ! -d "sing-quic" ]; then - git clone --no-checkout https://github.com/MatsuriDayo/sing-quic.git -fi -pushd sing-quic -git checkout "$COMMIT_SING_QUIC" - -popd - -#### - -if [ ! -d "libneko" ]; then - git clone --no-checkout https://github.com/MatsuriDayo/libneko.git -fi -pushd libneko -git checkout "$COMMIT_LIBNEKO" - -popd - -#### - -popd diff --git a/libs/get_source_env.sh b/libs/get_source_env.sh deleted file mode 100644 index 1f35b7a..0000000 --- a/libs/get_source_env.sh +++ /dev/null @@ -1,3 +0,0 @@ -export COMMIT_SING_BOX="06557f6cef23160668122a17a818b378b5a216b5" -export COMMIT_SING_QUIC="b49ce60d9b3622d5238fee96bfd3c5f6e3915b42" -export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b" diff --git a/libs/package_appimage.sh b/libs/package_appimage.sh deleted file mode 100644 index c6c3882..0000000 --- a/libs/package_appimage.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -sudo apt-get install fuse -y - -cp -r linux64 nekobox.AppDir - -# The file for Appimage - -rm nekobox.AppDir/launcher - -cat >nekobox.AppDir/nekobox.desktop <<-EOF -[Desktop Entry] -Name=nekobox -Exec=echo "nekobox started" -Icon=nekobox -Type=Application -Categories=Network -EOF - -cat >nekobox.AppDir/AppRun <<-EOF -#!/bin/bash -echo "PATH: \${PATH}" -echo "nekobox runing on: \$APPDIR" -LD_LIBRARY_PATH=\${APPDIR}/usr/lib QT_PLUGIN_PATH=\${APPDIR}/usr/plugins \${APPDIR}/nekobox -appdata "\$@" -EOF - -chmod +x nekobox.AppDir/AppRun - -# build - -curl -fLSO https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage -chmod +x appimagetool-x86_64.AppImage -./appimagetool-x86_64.AppImage nekobox.AppDir - -# clean - -rm appimagetool-x86_64.AppImage -rm -rf nekobox.AppDir diff --git a/libs/package_debian.sh b/libs/package_debian.sh deleted file mode 100644 index 491aa7d..0000000 --- a/libs/package_debian.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -version="$1" - -mkdir -p nekoray/DEBIAN -mkdir -p nekoray/opt -cp -r linux64 nekoray/opt/ -mv nekoray/opt/linux64 nekoray/opt/nekoray -rm -rf nekoray/opt/nekoray/usr -rm nekoray/opt/nekoray/launcher - -# basic -cat >nekoray/DEBIAN/control <<-EOF -Package: nekoray -Version: $version -Architecture: amd64 -Maintainer: MatsuriDayo nekoha_matsuri@protonmail.com -Depends: libxcb-xinerama0, libqt5core5a, libqt5gui5, libqt5network5, libqt5widgets5, libqt5svg5, libqt5x11extras5, desktop-file-utils -Description: Qt based cross-platform GUI proxy configuration manager (backend: v2ray / sing-box) -EOF - -cat >nekoray/DEBIAN/postinst <<-EOF -if [ ! -s /usr/share/applications/nekoray.desktop ]; then - cat >/usr/share/applications/nekoray.desktop<<-END -[Desktop Entry] -Name=nekoray -Comment=Qt based cross-platform GUI proxy configuration manager (backend: sing-box) -Exec=sh -c "PATH=/opt/nekoray:\$PATH /opt/nekoray/nekobox -appdata" -Icon=/opt/nekoray/nekobox.png -Terminal=false -Type=Application -Categories=Network;Application; -END -fi - -setcap cap_net_admin=ep /opt/nekoray/nekobox_core - -update-desktop-database -EOF - -sudo chmod 0755 nekoray/DEBIAN/postinst - -# desktop && PATH - -sudo dpkg-deb -Zxz --build nekoray diff --git a/main/Const.hpp b/main/Const.hpp deleted file mode 100644 index 38d328c..0000000 --- a/main/Const.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -namespace NekoGui { - namespace DomainMatcher { - enum DomainMatcher { - DEFAULT, - MPH, - }; - } - - namespace SniffingMode { - enum SniffingMode { - DISABLE, - FOR_ROUTING, - FOR_DESTINATION, - }; - } - - namespace CoreType { - enum CoreType { - V2RAY, // DO NOT USE - SING_BOX, - }; - } -} // namespace NekoGui diff --git a/main/GuiUtils.hpp b/main/GuiUtils.hpp deleted file mode 100644 index 792d171..0000000 --- a/main/GuiUtils.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -// Dialogs - -#define Dialog_DialogBasicSettings "DialogBasicSettings" -#define Dialog_DialogEditProfile "DialogEditProfile" -#define Dialog_DialogManageGroups "DialogManageGroups" -#define Dialog_DialogManageRoutes "DialogManageRoutes" - -// Utils - -#define QRegExpValidator_Number new QRegularExpressionValidator(QRegularExpression("^[0-9]+$"), this) - -// NekoRay Save&Load - -#define P_C_LOAD_STRING(a) CACHE.a = bean->a; -#define P_C_SAVE_STRING(a) bean->a = CACHE.a; -#define D_C_LOAD_STRING(a) CACHE.a = NekoGui::dataStore->a; -#define D_C_SAVE_STRING(a) NekoGui::dataStore->a = CACHE.a; - -#define P_LOAD_STRING(a) ui->a->setText(bean->a); -#define P_LOAD_STRING_PLAIN(a) ui->a->setPlainText(bean->a); -#define P_SAVE_STRING(a) bean->a = ui->a->text(); -#define P_SAVE_STRING_PLAIN(a) bean->a = ui->a->toPlainText(); - -#define D_LOAD_STRING(a) ui->a->setText(NekoGui::dataStore->a); -#define D_LOAD_STRING_PLAIN(a) ui->a->setPlainText(NekoGui::dataStore->a); -#define D_SAVE_STRING(a) NekoGui::dataStore->a = ui->a->text(); -#define D_SAVE_STRING_PLAIN(a) NekoGui::dataStore->a = ui->a->toPlainText(); - -#define P_LOAD_INT(a) \ - ui->a->setText(Int2String(bean->a)); \ - ui->a->setValidator(QRegExpValidator_Number); -#define P_SAVE_INT(a) bean->a = ui->a->text().toInt(); - -#define D_LOAD_INT(a) \ - ui->a->setText(Int2String(NekoGui::dataStore->a)); \ - ui->a->setValidator(QRegExpValidator_Number); -#define D_SAVE_INT(a) NekoGui::dataStore->a = ui->a->text().toInt(); - -#define P_LOAD_COMBO_STRING(a) ui->a->setCurrentText(bean->a); -#define P_SAVE_COMBO_STRING(a) bean->a = ui->a->currentText(); - -#define D_LOAD_COMBO_STRING(a) ui->a->setCurrentText(NekoGui::dataStore->a); -#define D_SAVE_COMBO_STRING(a) NekoGui::dataStore->a = ui->a->currentText(); - -#define P_LOAD_COMBO_INT(a) ui->a->setCurrentIndex(bean->a); -#define P_SAVE_COMBO_INT(a) bean->a = ui->a->currentIndex(); - -#define D_LOAD_BOOL(a) ui->a->setChecked(NekoGui::dataStore->a); -#define D_SAVE_BOOL(a) NekoGui::dataStore->a = ui->a->isChecked(); - -#define P_LOAD_BOOL(a) ui->a->setChecked(bean->a); -#define P_SAVE_BOOL(a) bean->a = ui->a->isChecked(); - -#define D_LOAD_INT_ENABLE(i, e) \ - if (NekoGui::dataStore->i > 0) { \ - ui->e->setChecked(true); \ - ui->i->setText(Int2String(NekoGui::dataStore->i)); \ - } else { \ - ui->e->setChecked(false); \ - ui->i->setText(Int2String(-NekoGui::dataStore->i)); \ - } \ - ui->i->setValidator(QRegExpValidator_Number); -#define D_SAVE_INT_ENABLE(i, e) \ - if (ui->e->isChecked()) { \ - NekoGui::dataStore->i = ui->i->text().toInt(); \ - } else { \ - NekoGui::dataStore->i = -ui->i->text().toInt(); \ - } - -#define C_EDIT_JSON_ALLOW_EMPTY(a) \ - auto editor = new JsonEditor(QString2QJsonObject(CACHE.a), this); \ - auto result = editor->OpenEditor(); \ - CACHE.a = QJsonObject2QString(result, true); \ - if (result.isEmpty()) CACHE.a = ""; \ - editor->deleteLater(); - -// - -#define ADD_ASTERISK(parent) \ - for (auto label: parent->findChildren()) { \ - auto text = label->text(); \ - if (!label->toolTip().isEmpty() && !text.endsWith("*")) { \ - label->setText(text + "*"); \ - } \ - } \ - for (auto checkBox: parent->findChildren()) { \ - auto text = checkBox->text(); \ - if (!checkBox->toolTip().isEmpty() && !text.endsWith("*")) { \ - checkBox->setText(text + "*"); \ - } \ - } diff --git a/main/HTTPRequestHelper.cpp b/main/HTTPRequestHelper.cpp deleted file mode 100644 index 62c8cd2..0000000 --- a/main/HTTPRequestHelper.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "HTTPRequestHelper.hpp" - -#include -#include -#include -#include -#include - -#include "main/NekoGui.hpp" - -namespace NekoGui_network { - - NekoHTTPResponse NetworkRequestHelper::HttpGet(const QUrl &url) { - QNetworkRequest request; - QNetworkAccessManager accessManager; - request.setUrl(url); - // Set proxy - if (NekoGui::dataStore->sub_use_proxy) { - QNetworkProxy p; - // Note: sing-box mixed socks5 protocol error - p.setType(QNetworkProxy::HttpProxy); - p.setHostName("127.0.0.1"); - p.setPort(NekoGui::dataStore->inbound_socks_port); - if (NekoGui::dataStore->inbound_auth->NeedAuth()) { - p.setUser(NekoGui::dataStore->inbound_auth->username); - p.setPassword(NekoGui::dataStore->inbound_auth->password); - } - accessManager.setProxy(p); - if (NekoGui::dataStore->started_id < 0) { - return NekoHTTPResponse{QObject::tr("Request with proxy but no profile started.")}; - } - } - if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy) { - auto cap = accessManager.proxy().capabilities(); - accessManager.proxy().setCapabilities(cap | QNetworkProxy::HostNameLookupCapability); - } - // Set attribute -#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) - request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); -#endif - request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, NekoGui::dataStore->GetUserAgent()); - if (NekoGui::dataStore->sub_insecure) { - QSslConfiguration c; - c.setPeerVerifyMode(QSslSocket::PeerVerifyMode::VerifyNone); - request.setSslConfiguration(c); - } - // - auto _reply = accessManager.get(request); - connect(_reply, &QNetworkReply::sslErrors, _reply, [](const QList &errors) { - QStringList error_str; - for (const auto &err: errors) { - error_str << err.errorString(); - } - MW_show_log(QStringLiteral("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : "")); - }); - // Wait for response - auto abortTimer = new QTimer; - abortTimer->setSingleShot(true); - abortTimer->setInterval(10000); - QObject::connect(abortTimer, &QTimer::timeout, _reply, &QNetworkReply::abort); - abortTimer->start(); - { - QEventLoop loop; - QObject::connect(_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - loop.exec(); - } - if (abortTimer != nullptr) { - abortTimer->stop(); - abortTimer->deleteLater(); - } - // - auto result = NekoHTTPResponse{_reply->error() == QNetworkReply::NetworkError::NoError ? "" : _reply->errorString(), - _reply->readAll(), _reply->rawHeaderPairs()}; - _reply->deleteLater(); - return result; - } - - QString NetworkRequestHelper::GetHeader(const QList> &header, const QString &name) { - for (const auto &p: header) { - if (QString(p.first).toLower() == name.toLower()) return p.second; - } - return ""; - } - -} // namespace NekoGui_network diff --git a/main/HTTPRequestHelper.hpp b/main/HTTPRequestHelper.hpp deleted file mode 100644 index 76760f0..0000000 --- a/main/HTTPRequestHelper.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace NekoGui_network { - struct NekoHTTPResponse { - QString error; - QByteArray data; - QList> header; - }; - - class NetworkRequestHelper : QObject { - Q_OBJECT - - explicit NetworkRequestHelper(QObject *parent) : QObject(parent){}; - - ~NetworkRequestHelper() override = default; - ; - - public: - static NekoHTTPResponse HttpGet(const QUrl &url); - - static QString GetHeader(const QList> &header, const QString &name); - }; -} // namespace NekoGui_network - -using namespace NekoGui_network; diff --git a/main/NekoGui.cpp b/main/NekoGui.cpp deleted file mode 100644 index e84c337..0000000 --- a/main/NekoGui.cpp +++ /dev/null @@ -1,462 +0,0 @@ -#include "NekoGui.hpp" -#include "fmt/Preset.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include "sys/windows/guihelper.h" -#else -#ifdef Q_OS_LINUX -#include -#endif -#include -#endif - -namespace NekoGui_ConfigItem { - - // 添加关联 - void JsonStore::_add(configItem *item) { - _map.insert(item->name, std::shared_ptr(item)); - } - - QString JsonStore::_name(void *p) { - for (const auto &_item: _map) { - if (_item->ptr == p) return _item->name; - } - return {}; - } - - std::shared_ptr JsonStore::_get(const QString &name) { - // 直接 [] 会设置一个 nullptr ,所以先判断是否存在 - if (_map.contains(name)) { - return _map[name]; - } - return nullptr; - } - - void JsonStore::_setValue(const QString &name, void *p) { - auto item = _get(name); - if (item == nullptr) return; - - switch (item->type) { - case itemType::string: - *(QString *) item->ptr = *(QString *) p; - break; - case itemType::boolean: - *(bool *) item->ptr = *(bool *) p; - break; - case itemType::integer: - *(int *) item->ptr = *(int *) p; - break; - case itemType::integer64: - *(long long *) item->ptr = *(long long *) p; - break; - // others... - case stringList: - case integerList: - case jsonStore: - break; - } - } - - QJsonObject JsonStore::ToJson(const QStringList &without) { - QJsonObject object; - for (const auto &_item: _map) { - auto item = _item.get(); - if (without.contains(item->name)) continue; - switch (item->type) { - case itemType::string: - // Allow Empty - if (!((QString *) item->ptr)->isEmpty()) { - object.insert(item->name, *(QString *) item->ptr); - } - break; - case itemType::integer: - object.insert(item->name, *(int *) item->ptr); - break; - case itemType::integer64: - object.insert(item->name, *(long long *) item->ptr); - break; - case itemType::boolean: - object.insert(item->name, *(bool *) item->ptr); - break; - case itemType::stringList: - object.insert(item->name, QList2QJsonArray(*(QList *) item->ptr)); - break; - case itemType::integerList: - object.insert(item->name, QList2QJsonArray(*(QList *) item->ptr)); - break; - case itemType::jsonStore: - // _add 时应关联对应 JsonStore 的指针 - object.insert(item->name, ((JsonStore *) item->ptr)->ToJson()); - break; - } - } - return object; - } - - QByteArray JsonStore::ToJsonBytes() { - QJsonDocument document; - document.setObject(ToJson()); - return document.toJson(save_control_compact ? QJsonDocument::Compact : QJsonDocument::Indented); - } - - void JsonStore::FromJson(QJsonObject object) { - for (const auto &key: object.keys()) { - if (_map.count(key) == 0) { - continue; - } - - auto value = object[key]; - auto item = _map[key].get(); - - if (item == nullptr) - continue; // 故意忽略 - - // 根据类型修改ptr的内容 - switch (item->type) { - case itemType::string: - if (value.type() != QJsonValue::String) { - continue; - } - *(QString *) item->ptr = value.toString(); - break; - case itemType::integer: - if (value.type() != QJsonValue::Double) { - continue; - } - *(int *) item->ptr = value.toInt(); - break; - case itemType::integer64: - if (value.type() != QJsonValue::Double) { - continue; - } - *(long long *) item->ptr = value.toDouble(); - break; - case itemType::boolean: - if (value.type() != QJsonValue::Bool) { - continue; - } - *(bool *) item->ptr = value.toBool(); - break; - case itemType::stringList: - if (value.type() != QJsonValue::Array) { - continue; - } - *(QList *) item->ptr = QJsonArray2QListString(value.toArray()); - break; - case itemType::integerList: - if (value.type() != QJsonValue::Array) { - continue; - } - *(QList *) item->ptr = QJsonArray2QListInt(value.toArray()); - break; - case itemType::jsonStore: - if (value.type() != QJsonValue::Object) { - continue; - } - ((JsonStore *) item->ptr)->FromJson(value.toObject()); - break; - } - } - - if (callback_after_load != nullptr) callback_after_load(); - } - - void JsonStore::FromJsonBytes(const QByteArray &data) { - QJsonParseError error{}; - auto document = QJsonDocument::fromJson(data, &error); - - if (error.error != error.NoError) { - qDebug() << "QJsonParseError" << error.errorString(); - return; - } - - FromJson(document.object()); - } - - bool JsonStore::Save() { - if (callback_before_save != nullptr) callback_before_save(); - if (save_control_no_save) return false; - - auto save_content = ToJsonBytes(); - auto changed = last_save_content != save_content; - last_save_content = save_content; - - QFile file; - file.setFileName(fn); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); - file.write(save_content); - file.close(); - - return changed; - } - - bool JsonStore::Load() { - QFile file; - file.setFileName(fn); - - if (!file.exists() && !load_control_must) { - return false; - } - - bool ok = file.open(QIODevice::ReadOnly); - if (!ok) { - MessageBoxWarning("error", "can not open config " + fn + "\n" + file.errorString()); - } else { - last_save_content = file.readAll(); - FromJsonBytes(last_save_content); - } - - file.close(); - return ok; - } - -} // namespace NekoGui_ConfigItem - -namespace NekoGui { - - DataStore *dataStore = new DataStore(); - - // datastore - - DataStore::DataStore() : JsonStore() { - _add(new configItem("extraCore", dynamic_cast(extraCore), itemType::jsonStore)); - _add(new configItem("inbound_auth", dynamic_cast(inbound_auth), itemType::jsonStore)); - - _add(new configItem("user_agent2", &user_agent, itemType::string)); - _add(new configItem("test_url", &test_latency_url, itemType::string)); - _add(new configItem("test_url_dl", &test_download_url, itemType::string)); - _add(new configItem("test_dl_timeout", &test_download_timeout, itemType::integer)); - _add(new configItem("current_group", ¤t_group, itemType::integer)); - _add(new configItem("inbound_address", &inbound_address, itemType::string)); - _add(new configItem("inbound_socks_port", &inbound_socks_port, itemType::integer)); - _add(new configItem("log_level", &log_level, itemType::string)); - _add(new configItem("mux_protocol", &mux_protocol, itemType::string)); - _add(new configItem("mux_concurrency", &mux_concurrency, itemType::integer)); - _add(new configItem("mux_padding", &mux_padding, itemType::boolean)); - _add(new configItem("mux_default_on", &mux_default_on, itemType::boolean)); - _add(new configItem("traffic_loop_interval", &traffic_loop_interval, itemType::integer)); - _add(new configItem("test_concurrent", &test_concurrent, itemType::integer)); - _add(new configItem("theme", &theme, itemType::string)); - _add(new configItem("custom_inbound", &custom_inbound, itemType::string)); - _add(new configItem("custom_route", &custom_route_global, itemType::string)); - _add(new configItem("sub_use_proxy", &sub_use_proxy, itemType::boolean)); - _add(new configItem("remember_id", &remember_id, itemType::integer)); - _add(new configItem("remember_enable", &remember_enable, itemType::boolean)); - _add(new configItem("language", &language, itemType::integer)); - _add(new configItem("spmode2", &remember_spmode, itemType::stringList)); - _add(new configItem("skip_cert", &skip_cert, itemType::boolean)); - _add(new configItem("hk_mw", &hotkey_mainwindow, itemType::string)); - _add(new configItem("hk_group", &hotkey_group, itemType::string)); - _add(new configItem("hk_route", &hotkey_route, itemType::string)); - _add(new configItem("hk_spmenu", &hotkey_system_proxy_menu, itemType::string)); - _add(new configItem("fakedns", &fake_dns, itemType::boolean)); - _add(new configItem("active_routing", &active_routing, itemType::string)); - _add(new configItem("mw_size", &mw_size, itemType::string)); - _add(new configItem("conn_stat", &connection_statistics, itemType::boolean)); - _add(new configItem("vpn_impl", &vpn_implementation, itemType::integer)); - _add(new configItem("vpn_mtu", &vpn_mtu, itemType::integer)); - _add(new configItem("vpn_ipv6", &vpn_ipv6, itemType::boolean)); - _add(new configItem("vpn_hide_console", &vpn_hide_console, itemType::boolean)); - _add(new configItem("vpn_strict_route", &vpn_strict_route, itemType::boolean)); - _add(new configItem("vpn_bypass_process", &vpn_rule_process, itemType::string)); - _add(new configItem("vpn_bypass_cidr", &vpn_rule_cidr, itemType::string)); - _add(new configItem("vpn_rule_white", &vpn_rule_white, itemType::boolean)); - _add(new configItem("check_include_pre", &check_include_pre, itemType::boolean)); - _add(new configItem("sp_format", &system_proxy_format, itemType::string)); - _add(new configItem("sub_clear", &sub_clear, itemType::boolean)); - _add(new configItem("sub_insecure", &sub_insecure, itemType::boolean)); - _add(new configItem("sub_auto_update", &sub_auto_update, itemType::integer)); - _add(new configItem("log_ignore", &log_ignore, itemType::stringList)); - _add(new configItem("start_minimal", &start_minimal, itemType::boolean)); - _add(new configItem("max_log_line", &max_log_line, itemType::integer)); - _add(new configItem("splitter_state", &splitter_state, itemType::string)); - _add(new configItem("utlsFingerprint", &utlsFingerprint, itemType::string)); - _add(new configItem("core_box_clash_api", &core_box_clash_api, itemType::integer)); - _add(new configItem("core_box_clash_api_secret", &core_box_clash_api_secret, itemType::string)); - _add(new configItem("core_box_underlying_dns", &core_box_underlying_dns, itemType::string)); - _add(new configItem("vpn_internal_tun", &vpn_internal_tun, itemType::boolean)); - } - - void DataStore::UpdateStartedId(int id) { - started_id = id; - if (remember_enable) { - remember_id = id; - Save(); - } else if (remember_id >= 0) { - remember_id = -1919; - Save(); - } - } - - QString DataStore::GetUserAgent(bool isDefault) const { - if (user_agent.isEmpty()) { - isDefault = true; - } - if (isDefault) { - QString version = SubStrBefore(NKR_VERSION, "-"); - if (!version.contains(".")) version = "2.0"; - return "NekoBox/PC/" + version + " (Prefer ClashMeta Format)"; - } - return user_agent; - } - - // preset routing - Routing::Routing(int preset) : JsonStore() { - if (preset == 1) { - direct_ip = - "geoip:cn\n" - "geoip:private"; - direct_domain = "geosite:cn"; - proxy_ip = ""; - proxy_domain = ""; - block_ip = ""; - block_domain = - "geosite:category-ads-all\n" - "domain:appcenter.ms\n" - "domain:firebase.io\n" - "domain:crashlytics.com\n"; - } - if (!Preset::SingBox::DomainStrategy.contains(domain_strategy)) domain_strategy = ""; - if (!Preset::SingBox::DomainStrategy.contains(outbound_domain_strategy)) outbound_domain_strategy = ""; - _add(new configItem("direct_ip", &this->direct_ip, itemType::string)); - _add(new configItem("direct_domain", &this->direct_domain, itemType::string)); - _add(new configItem("proxy_ip", &this->proxy_ip, itemType::string)); - _add(new configItem("proxy_domain", &this->proxy_domain, itemType::string)); - _add(new configItem("block_ip", &this->block_ip, itemType::string)); - _add(new configItem("block_domain", &this->block_domain, itemType::string)); - _add(new configItem("def_outbound", &this->def_outbound, itemType::string)); - _add(new configItem("custom", &this->custom, itemType::string)); - // - _add(new configItem("remote_dns", &this->remote_dns, itemType::string)); - _add(new configItem("remote_dns_strategy", &this->remote_dns_strategy, itemType::string)); - _add(new configItem("direct_dns", &this->direct_dns, itemType::string)); - _add(new configItem("direct_dns_strategy", &this->direct_dns_strategy, itemType::string)); - _add(new configItem("domain_strategy", &this->domain_strategy, itemType::string)); - _add(new configItem("outbound_domain_strategy", &this->outbound_domain_strategy, itemType::string)); - _add(new configItem("dns_routing", &this->dns_routing, itemType::boolean)); - _add(new configItem("sniffing_mode", &this->sniffing_mode, itemType::integer)); - _add(new configItem("use_dns_object", &this->use_dns_object, itemType::boolean)); - _add(new configItem("dns_object", &this->dns_object, itemType::string)); - _add(new configItem("dns_final_out", &this->dns_final_out, itemType::string)); - } - - QString Routing::DisplayRouting() const { - return QStringLiteral("[Proxy] %1\n[Proxy] %2\n[Direct] %3\n[Direct] %4\n[Block] %5\n[Block] %6\n[Default Outbound] %7\n[DNS] %8") - .arg(SplitLinesSkipSharp(proxy_domain).join(","), 10) - .arg(SplitLinesSkipSharp(proxy_ip).join(","), 10) - .arg(SplitLinesSkipSharp(direct_domain).join(","), 10) - .arg(SplitLinesSkipSharp(direct_ip).join(","), 10) - .arg(SplitLinesSkipSharp(block_domain).join(","), 10) - .arg(SplitLinesSkipSharp(block_ip).join(","), 10) - .arg(def_outbound) - .arg(use_dns_object ? "DNS Object" : "Simple DNS"); - } - - QStringList Routing::List() { - QDir dr(ROUTES_PREFIX); - return dr.entryList(QDir::Files); - } - - bool Routing::SetToActive(const QString &name) { - NekoGui::dataStore->routing = std::make_unique(); - NekoGui::dataStore->routing->load_control_must = true; - NekoGui::dataStore->routing->fn = ROUTES_PREFIX + name; - auto ok = NekoGui::dataStore->routing->Load(); - if (ok) { - NekoGui::dataStore->active_routing = name; - NekoGui::dataStore->Save(); - } - return ok; - } - - // NO default extra core - - ExtraCore::ExtraCore() : JsonStore() { - _add(new configItem("core_map", &this->core_map, itemType::string)); - } - - QString ExtraCore::Get(const QString &id) const { - auto obj = QString2QJsonObject(core_map); - for (const auto &c: obj.keys()) { - if (c == id) return obj[id].toString(); - } - return ""; - } - - void ExtraCore::Set(const QString &id, const QString &path) { - auto obj = QString2QJsonObject(core_map); - obj[id] = path; - core_map = QJsonObject2QString(obj, true); - } - - void ExtraCore::Delete(const QString &id) { - auto obj = QString2QJsonObject(core_map); - obj.remove(id); - core_map = QJsonObject2QString(obj, true); - } - - InboundAuthorization::InboundAuthorization() : JsonStore() { - _add(new configItem("user", &this->username, itemType::string)); - _add(new configItem("pass", &this->password, itemType::string)); - } - - bool InboundAuthorization::NeedAuth() const { - return !username.trimmed().isEmpty() && !password.trimmed().isEmpty(); - } - - // System Utils - - QString FindCoreAsset(const QString &name) { - QStringList search{}; - search << QApplication::applicationDirPath(); - search << "/usr/share/sing-geoip"; - search << "/usr/share/sing-geosite"; - search << "/usr/share/sing-box"; - search << "/usr/lib/nekobox"; - search << "/usr/share/nekobox"; - for (const auto &dir: search) { - if (dir.isEmpty()) continue; - QFileInfo asset(dir + "/" + name); - if (asset.exists()) { - return asset.absoluteFilePath(); - } - } - return {}; - } - - QString FindNekoBoxCoreRealPath() { - auto fn = QApplication::applicationDirPath() + "/nekobox_core"; - auto fi = QFileInfo(fn); - if (fi.isSymLink()) return fi.symLinkTarget(); - return fn; - } - - short isAdminCache = -1; - - // IsAdmin 主要判断:有无权限启动 Tun - bool IsAdmin() { - if (isAdminCache >= 0) return isAdminCache; - - bool admin = false; -#ifdef Q_OS_WIN - admin = Windows_IsInAdmin(); -#else -#ifdef Q_OS_LINUX - admin |= Linux_GetCapString(FindNekoBoxCoreRealPath()).contains("cap_net_admin"); -#endif - admin |= geteuid() == 0; -#endif - - isAdminCache = admin; - return admin; - }; - -} // namespace NekoGui diff --git a/main/NekoGui.hpp b/main/NekoGui.hpp deleted file mode 100644 index a54a32e..0000000 --- a/main/NekoGui.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "Const.hpp" -#include "NekoGui_Utils.hpp" -#include "NekoGui_ConfigItem.hpp" -#include "NekoGui_DataStore.hpp" - -// Switch core support - -namespace NekoGui { - inline int coreType = CoreType::SING_BOX; - - QString FindCoreAsset(const QString &name); - - QString FindNekoBoxCoreRealPath(); - - bool IsAdmin(); -} // namespace NekoGui - -#define ROUTES_PREFIX_NAME QStringLiteral("routes_box") -#define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/") diff --git a/main/NekoGui_ConfigItem.hpp b/main/NekoGui_ConfigItem.hpp deleted file mode 100644 index 152ac3e..0000000 --- a/main/NekoGui_ConfigItem.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// DO NOT INCLUDE THIS - -namespace NekoGui_ConfigItem { - // config 工具 - enum itemType { - string, - integer, - integer64, - boolean, - stringList, - integerList, - jsonStore, - }; - - class configItem { - public: - QString name; - void *ptr; - itemType type; - - configItem(QString n, void *p, itemType t) { - name = std::move(n); - ptr = p; - type = t; - } - }; - - // 可格式化对象 - class JsonStore { - public: - QMap> _map; - - std::function callback_after_load = nullptr; - std::function callback_before_save = nullptr; - - QString fn; - bool load_control_must = false; // must load from file - bool save_control_compact = false; - bool save_control_no_save = false; - QByteArray last_save_content; - - JsonStore() = default; - - explicit JsonStore(QString fileName) { - fn = std::move(fileName); - } - - void _add(configItem *item); - - QString _name(void *p); - - std::shared_ptr _get(const QString &name); - - void _setValue(const QString &name, void *p); - - QJsonObject ToJson(const QStringList &without = {}); - - QByteArray ToJsonBytes(); - - void FromJson(QJsonObject object); - - void FromJsonBytes(const QByteArray &data); - - bool Save(); - - bool Load(); - }; -} // namespace NekoGui_ConfigItem - -using namespace NekoGui_ConfigItem; diff --git a/main/NekoGui_DataStore.hpp b/main/NekoGui_DataStore.hpp deleted file mode 100644 index ec0ff24..0000000 --- a/main/NekoGui_DataStore.hpp +++ /dev/null @@ -1,182 +0,0 @@ -// DO NOT INCLUDE THIS - -namespace NekoGui { - - class Routing : public JsonStore { - public: - QString direct_ip; - QString direct_domain; - QString proxy_ip; - QString proxy_domain; - QString block_ip; - QString block_domain; - QString def_outbound = "proxy"; - QString custom = "{\"rules\": []}"; - - // DNS - QString remote_dns = "https://dns.google/dns-query"; - QString remote_dns_strategy = ""; - QString direct_dns = "https://doh.pub/dns-query"; - QString direct_dns_strategy = ""; - bool dns_routing = true; - bool use_dns_object = false; - QString dns_object = ""; - QString dns_final_out = "proxy"; - - // Misc - QString domain_strategy = "AsIs"; - QString outbound_domain_strategy = "AsIs"; - int sniffing_mode = SniffingMode::FOR_ROUTING; - - explicit Routing(int preset = 0); - - [[nodiscard]] QString DisplayRouting() const; - - static QStringList List(); - - static bool SetToActive(const QString &name); - }; - - class ExtraCore : public JsonStore { - public: - QString core_map; - - explicit ExtraCore(); - - [[nodiscard]] QString Get(const QString &id) const; - - void Set(const QString &id, const QString &path); - - void Delete(const QString &id); - }; - - class InboundAuthorization : public JsonStore { - public: - QString username; - QString password; - - InboundAuthorization(); - - [[nodiscard]] bool NeedAuth() const; - }; - - class DataStore : public JsonStore { - public: - // Running - - QString core_token; - int core_port = 19810; - int started_id = -1919; - bool core_running = false; - bool prepare_exit = false; - bool spmode_vpn = false; - bool spmode_system_proxy = false; - bool need_keep_vpn_off = false; - QString appdataDir = ""; - QStringList ignoreConnTag = {}; - - std::unique_ptr routing; - int imported_count = 0; - bool refreshing_group_list = false; - bool refreshing_group = false; - int resolve_count = 0; - - // Flags - QStringList argv = {}; - bool flag_use_appdata = false; - bool flag_many = false; - bool flag_tray = false; - bool flag_debug = false; - bool flag_restart_tun_on = false; - bool flag_reorder = false; - - // Saved - - // Misc - QString log_level = "info"; - QString test_latency_url = "http://cp.cloudflare.com/"; - QString test_download_url = "http://cachefly.cachefly.net/10mb.test"; - int test_download_timeout = 30; - int test_concurrent = 5; - bool old_share_link_format = true; - int traffic_loop_interval = 1000; - bool connection_statistics = false; - int current_group = 0; // group id - QString mux_protocol = "h2mux"; - bool mux_padding = false; - int mux_concurrency = 8; - bool mux_default_on = false; - QString theme = "0"; - int language = 0; - QString mw_size = ""; - bool check_include_pre = false; - QString system_proxy_format = ""; - QStringList log_ignore = {}; - bool start_minimal = false; - int max_log_line = 200; - QString splitter_state = ""; - - // Subscription - QString user_agent = ""; // set at main.cpp - bool sub_use_proxy = false; - bool sub_clear = false; - bool sub_insecure = false; - int sub_auto_update = -30; - - // Security - bool skip_cert = false; - QString utlsFingerprint = ""; - - // Remember - QStringList remember_spmode = {}; - int remember_id = -1919; - bool remember_enable = false; - - // Socks & HTTP Inbound - QString inbound_address = "127.0.0.1"; - int inbound_socks_port = 2080; // or Mixed - InboundAuthorization *inbound_auth = new InboundAuthorization; - QString custom_inbound = "{\"inbounds\": []}"; - - // Routing - QString custom_route_global = "{\"rules\": []}"; - QString active_routing = "Default"; - - // VPN - bool fake_dns = false; - bool vpn_internal_tun = true; - int vpn_implementation = 0; - int vpn_mtu = 9000; - bool vpn_ipv6 = false; - bool vpn_hide_console = true; - bool vpn_strict_route = false; - bool vpn_rule_white = false; - QString vpn_rule_process = ""; - QString vpn_rule_cidr = ""; - - // Hotkey - QString hotkey_mainwindow = ""; - QString hotkey_group = ""; - QString hotkey_route = ""; - QString hotkey_system_proxy_menu = ""; - - // Core - int core_box_clash_api = -9090; - QString core_box_clash_api_secret = ""; - QString core_box_underlying_dns = ""; - - // Other Core - ExtraCore *extraCore = new ExtraCore; - - // Methods - - DataStore(); - - void UpdateStartedId(int id); - - QString GetUserAgent(bool isDefault = false) const; - }; - - extern DataStore *dataStore; - -} // namespace NekoGui diff --git a/main/NekoGui_Utils.cpp b/main/NekoGui_Utils.cpp deleted file mode 100644 index 92a0672..0000000 --- a/main/NekoGui_Utils.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include "NekoGui_Utils.hpp" - -#include "3rdparty/base64.h" -#include "3rdparty/QThreadCreateThread.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include "sys/windows/guihelper.h" -#endif - -QStringList SplitLines(const QString &_string) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - return _string.split(QRegularExpression("[\r\n]"), Qt::SplitBehaviorFlags::SkipEmptyParts); -#else - return _string.split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); -#endif -} - -QStringList SplitLinesSkipSharp(const QString &_string, int maxLine) { - auto lines = SplitLines(_string); - QStringList newLines; - int i = 0; - for (const auto &line: lines) { - if (line.trimmed().startsWith("#")) continue; - newLines << line; - if (maxLine > 0 && ++i >= maxLine) break; - } - return newLines; -} - -QByteArray DecodeB64IfValid(const QString &input, QByteArray::Base64Options options) { - Qt515Base64::Base64Options newOptions = Qt515Base64::Base64Option::AbortOnBase64DecodingErrors; - if (options.testFlag(QByteArray::Base64UrlEncoding)) newOptions |= Qt515Base64::Base64Option::Base64UrlEncoding; - if (options.testFlag(QByteArray::OmitTrailingEquals)) newOptions |= Qt515Base64::Base64Option::OmitTrailingEquals; - auto result = Qt515Base64::QByteArray_fromBase64Encoding(input.toUtf8(), newOptions); - if (result) { - return result.decoded; - } - return {}; -} - -QString QStringList2Command(const QStringList &list) { - QStringList new_list; - for (auto str: list) { - auto q = "\"" + str.replace("\"", "\\\"") + "\""; - new_list << q; - } - return new_list.join(" "); -} - -QString GetQueryValue(const QUrlQuery &q, const QString &key, const QString &def) { - auto a = q.queryItemValue(key); - if (a.isEmpty()) { - return def; - } - return a; -} - -QString GetRandomString(int randomStringLength) { - std::random_device rd; - std::mt19937 mt(rd()); - - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - - std::uniform_int_distribution dist(0, possibleCharacters.length() - 1); - - QString randomString; - for (int i = 0; i < randomStringLength; ++i) { - QChar nextChar = possibleCharacters.at(dist(mt)); - randomString.append(nextChar); - } - return randomString; -} - -quint64 GetRandomUint64() { - std::random_device rd; - std::mt19937 mt(rd()); - std::uniform_int_distribution dist; - return dist(mt); -} - -// QString >> QJson -QJsonObject QString2QJsonObject(const QString &jsonString) { - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8()); - QJsonObject jsonObject = jsonDocument.object(); - return jsonObject; -} - -// QJson >> QString -QString QJsonObject2QString(const QJsonObject &jsonObject, bool compact) { - return QJsonDocument(jsonObject).toJson(compact ? QJsonDocument::Compact : QJsonDocument::Indented); -} - -template -QJsonArray QList2QJsonArray(const QList &list) { - QVariantList list2; - for (auto &item: list) - list2.append(item); - return QJsonArray::fromVariantList(list2); -} - -template QJsonArray QList2QJsonArray(const QList &list); -template QJsonArray QList2QJsonArray(const QList &list); - -QList QJsonArray2QListInt(const QJsonArray &arr) { - QList list2; - for (auto item: arr) - list2.append(item.toInt()); - return list2; -} - -QList QJsonArray2QListString(const QJsonArray &arr) { - QList list2; - for (auto item: arr) - list2.append(item.toString()); - return list2; -} - -QByteArray ReadFile(const QString &path) { - QFile file(path); - file.open(QFile::ReadOnly); - return file.readAll(); -} - -QString ReadFileText(const QString &path) { - QFile file(path); - file.open(QFile::ReadOnly | QFile::Text); - QTextStream stream(&file); - return stream.readAll(); -} - -int MkPort() { - QTcpServer s; - s.listen(); - auto port = s.serverPort(); - s.close(); - return port; -} - -QString ReadableSize(const qint64 &size) { - double sizeAsDouble = size; - static QStringList measures; - if (measures.isEmpty()) - measures << "B" - << "KiB" - << "MiB" - << "GiB" - << "TiB" - << "PiB" - << "EiB" - << "ZiB" - << "YiB"; - QStringListIterator it(measures); - QString measure(it.next()); - while (sizeAsDouble >= 1024.0 && it.hasNext()) { - measure = it.next(); - sizeAsDouble /= 1024.0; - } - return QString::fromLatin1("%1 %2").arg(sizeAsDouble, 0, 'f', 2).arg(measure); -} - -bool IsIpAddress(const QString &str) { - auto address = QHostAddress(str); - if (address.protocol() == QAbstractSocket::IPv4Protocol || address.protocol() == QAbstractSocket::IPv6Protocol) - return true; - return false; -} - -bool IsIpAddressV4(const QString &str) { - auto address = QHostAddress(str); - if (address.protocol() == QAbstractSocket::IPv4Protocol) - return true; - return false; -} - -bool IsIpAddressV6(const QString &str) { - auto address = QHostAddress(str); - if (address.protocol() == QAbstractSocket::IPv6Protocol) - return true; - return false; -} - -QString DisplayTime(long long time, int formatType) { - QDateTime t; - t.setMSecsSinceEpoch(time * 1000); - return QLocale().toString(t, QLocale::FormatType(formatType)); -} - -QWidget *GetMessageBoxParent() { - auto activeWindow = QApplication::activeWindow(); - if (activeWindow == nullptr && mainwindow != nullptr) { - if (mainwindow->isVisible()) return mainwindow; - return nullptr; - } - return activeWindow; -} - -int MessageBoxWarning(const QString &title, const QString &text) { - return QMessageBox::warning(GetMessageBoxParent(), title, text); -} - -int MessageBoxInfo(const QString &title, const QString &text) { - return QMessageBox::information(GetMessageBoxParent(), title, text); -} - -void ActivateWindow(QWidget *w) { - w->setWindowState(w->windowState() & ~Qt::WindowMinimized); - w->setVisible(true); -#ifdef Q_OS_WIN - Windows_QWidget_SetForegroundWindow(w); -#endif - w->raise(); - w->activateWindow(); -} - -void runOnUiThread(const std::function &callback, QObject *parent) { - // any thread - auto *timer = new QTimer(); - auto thread = dynamic_cast(parent); - if (thread == nullptr) { - timer->moveToThread(parent == nullptr ? mainwindow->thread() : parent->thread()); - } else { - timer->moveToThread(thread); - } - timer->setSingleShot(true); - QObject::connect(timer, &QTimer::timeout, [=]() { - // main thread - callback(); - timer->deleteLater(); - }); - QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0)); -} - -void runOnNewThread(const std::function &callback) { - createQThread(callback)->start(); -} - -void setTimeout(const std::function &callback, QObject *obj, int timeout) { - auto t = new QTimer; - QObject::connect(t, &QTimer::timeout, obj, [=] { - callback(); - t->deleteLater(); - }); - t->setSingleShot(true); - t->setInterval(timeout); - t->start(); -} diff --git a/main/NekoGui_Utils.hpp b/main/NekoGui_Utils.hpp deleted file mode 100644 index 5c21d34..0000000 --- a/main/NekoGui_Utils.hpp +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT INCLUDE THIS - -#include -#include -#include -#include -#include - -// - -inline QString software_name = "NekoBox"; -inline QString software_core_name = "sing-box"; - -// Main Functions - -inline std::function MF_release_runguard; - -// MainWindow functions -class QWidget; -inline QWidget *mainwindow; -inline std::function MW_show_log; -inline std::function MW_show_log_ext; -inline std::function MW_show_log_ext_vt100; -inline std::function MW_dialog_message; - -// Dispatchers - -class QThread; -inline QThread *DS_cores; - -// Timers - -class QTimer; -inline QTimer *TM_auto_update_subsctiption; -inline std::function TM_auto_update_subsctiption_Reset_Minute; - -// String - -#define FIRST_OR_SECOND(a, b) a.isEmpty() ? b : a - -inline const QString UNICODE_LRO = QString::fromUtf8(QByteArray::fromHex("E280AD")); - -#define Int2String(num) QString::number(num) - -inline QString SubStrBefore(QString str, const QString &sub) { - if (!str.contains(sub)) return str; - return str.left(str.indexOf(sub)); -} - -inline QString SubStrAfter(QString str, const QString &sub) { - if (!str.contains(sub)) return str; - return str.right(str.length() - str.indexOf(sub) - sub.length()); -} - -QString QStringList2Command(const QStringList &list); - -QStringList SplitLines(const QString &_string); - -QStringList SplitLinesSkipSharp(const QString &_string, int maxLine = 0); - -// Base64 - -QByteArray DecodeB64IfValid(const QString &input, QByteArray::Base64Options options = QByteArray::Base64Option::Base64Encoding); - -// URL - -class QUrlQuery; - -#define GetQuery(url) QUrlQuery((url).query(QUrl::ComponentFormattingOption::FullyDecoded)); - -QString GetQueryValue(const QUrlQuery &q, const QString &key, const QString &def = ""); - -QString GetRandomString(int randomStringLength); - -quint64 GetRandomUint64(); - -// JSON - -class QJsonObject; -class QJsonArray; - -QJsonObject QString2QJsonObject(const QString &jsonString); - -QString QJsonObject2QString(const QJsonObject &jsonObject, bool compact); - -template -QJsonArray QList2QJsonArray(const QList &list); - -QList QJsonArray2QListInt(const QJsonArray &arr); - -#define QJSONARRAY_ADD(arr, add) \ - for (const auto &a: (add)) { \ - (arr) += a; \ - } -#define QJSONOBJECT_COPY(src, dst, key) \ - if (src.contains(key)) dst[key] = src[key]; -#define QJSONOBJECT_COPY2(src, dst, src_key, dst_key) \ - if (src.contains(src_key)) dst[dst_key] = src[src_key]; - -QList QJsonArray2QListString(const QJsonArray &arr); - -// Files - -QByteArray ReadFile(const QString &path); - -QString ReadFileText(const QString &path); - -// Validators - -bool IsIpAddress(const QString &str); - -bool IsIpAddressV4(const QString &str); - -bool IsIpAddressV6(const QString &str); - -// [2001:4860:4860::8888] -> 2001:4860:4860::8888 -inline QString UnwrapIPV6Host(QString &str) { - return str.replace("[", "").replace("]", ""); -} - -// [2001:4860:4860::8888] or 2001:4860:4860::8888 -> [2001:4860:4860::8888] -inline QString WrapIPV6Host(QString &str) { - if (!IsIpAddressV6(str)) return str; - return "[" + UnwrapIPV6Host(str) + "]"; -} - -inline QString DisplayAddress(QString serverAddress, int serverPort) { - if (serverAddress.isEmpty() && serverPort == 0) return {}; - return WrapIPV6Host(serverAddress) + ":" + Int2String(serverPort); -}; - -// Format & Misc - -int MkPort(); - -QString DisplayTime(long long time, int formatType = 0); - -QString ReadableSize(const qint64 &size); - -inline bool InRange(unsigned x, unsigned low, unsigned high) { - return (low <= x && x <= high); -} - -inline bool IsValidPort(int port) { - return InRange(port, 1, 65535); -} - -// UI - -QWidget *GetMessageBoxParent(); - -int MessageBoxWarning(const QString &title, const QString &text); - -int MessageBoxInfo(const QString &title, const QString &text); - -void ActivateWindow(QWidget *w); - -// - -void runOnUiThread(const std::function &callback, QObject *parent = nullptr); - -void runOnNewThread(const std::function &callback); - -template -inline void connectOnce(EMITTER *emitter, SIGNAL signal, RECEIVER *receiver, ReceiverFunc f, - Qt::ConnectionType connectionType = Qt::AutoConnection) { - auto connection = std::make_shared(); - auto onTriggered = [connection, f](auto... arguments) { - std::invoke(f, arguments...); - QObject::disconnect(*connection); - }; - - *connection = QObject::connect(emitter, signal, receiver, onTriggered, connectionType); -} - -void setTimeout(const std::function &callback, QObject *obj, int timeout = 0); diff --git a/main/main.cpp b/main/main.cpp deleted file mode 100644 index b14f04f..0000000 --- a/main/main.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "3rdparty/RunGuard.hpp" -#include "main/NekoGui.hpp" - -#include "ui/mainwindow_interface.h" - -#ifdef Q_OS_WIN -#include "sys/windows/MiniDump.h" -#endif - -void signal_handler(int signum) { - if (qApp) { - GetMainWindow()->on_commitDataRequest(); - qApp->exit(); - } -} - -QTranslator* trans = nullptr; -QTranslator* trans_qt = nullptr; - -void loadTranslate(const QString& locale) { - if (trans != nullptr) { - trans->deleteLater(); - } - if (trans_qt != nullptr) { - trans_qt->deleteLater(); - } - // - trans = new QTranslator; - trans_qt = new QTranslator; - QLocale::setDefault(QLocale(locale)); - // - if (trans->load(":/translations/" + locale + ".qm")) { - QCoreApplication::installTranslator(trans); - } - if (trans_qt->load(QApplication::applicationDirPath() + "/qtbase_" + locale + ".qm")) { - QCoreApplication::installTranslator(trans_qt); - } -} - -#define LOCAL_SERVER_PREFIX "nekoraylocalserver-" - -int main(int argc, char* argv[]) { - // Core dump -#ifdef Q_OS_WIN - Windows_SetCrashHandler(); -#endif - - // pre-init QApplication -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) - QApplication::setAttribute(Qt::AA_DontUseNativeDialogs); -#endif - QApplication::setQuitOnLastWindowClosed(false); - auto preQApp = new QApplication(argc, argv); - - // Clean - QDir::setCurrent(QApplication::applicationDirPath()); - if (QFile::exists("updater.old")) { - QFile::remove("updater.old"); - } -#ifndef Q_OS_WIN - if (!QFile::exists("updater")) { - QFile::link("launcher", "updater"); - } -#endif - - // Flags - NekoGui::dataStore->argv = QApplication::arguments(); - if (NekoGui::dataStore->argv.contains("-many")) NekoGui::dataStore->flag_many = true; - if (NekoGui::dataStore->argv.contains("-appdata")) { - NekoGui::dataStore->flag_use_appdata = true; - int appdataIndex = NekoGui::dataStore->argv.indexOf("-appdata"); - if (NekoGui::dataStore->argv.size() > appdataIndex + 1 && !NekoGui::dataStore->argv.at(appdataIndex + 1).startsWith("-")) { - NekoGui::dataStore->appdataDir = NekoGui::dataStore->argv.at(appdataIndex + 1); - } - } - if (NekoGui::dataStore->argv.contains("-tray")) NekoGui::dataStore->flag_tray = true; - if (NekoGui::dataStore->argv.contains("-debug")) NekoGui::dataStore->flag_debug = true; - if (NekoGui::dataStore->argv.contains("-flag_restart_tun_on")) NekoGui::dataStore->flag_restart_tun_on = true; - if (NekoGui::dataStore->argv.contains("-flag_reorder")) NekoGui::dataStore->flag_reorder = true; -#ifdef NKR_CPP_USE_APPDATA - NekoGui::dataStore->flag_use_appdata = true; // Example: Package & MacOS -#endif -#ifdef NKR_CPP_DEBUG - NekoGui::dataStore->flag_debug = true; -#endif - - // dirs & clean - auto wd = QDir(QApplication::applicationDirPath()); - if (NekoGui::dataStore->flag_use_appdata) { - QApplication::setApplicationName("nekoray"); - if (!NekoGui::dataStore->appdataDir.isEmpty()) { - wd.setPath(NekoGui::dataStore->appdataDir); - } else { - wd.setPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); - } - } - if (!wd.exists()) wd.mkpath(wd.absolutePath()); - if (!wd.exists("config")) wd.mkdir("config"); - QDir::setCurrent(wd.absoluteFilePath("config")); - QDir("temp").removeRecursively(); - - // init QApplication - delete preQApp; - QApplication a(argc, argv); - - // dispatchers - DS_cores = new QThread; - DS_cores->start(); - - // RunGuard - RunGuard guard("nekoray" + wd.absolutePath()); - quint64 guard_data_in = GetRandomUint64(); - quint64 guard_data_out = 0; - if (!NekoGui::dataStore->flag_many && !guard.tryToRun(&guard_data_in)) { - // Some Good System - if (guard.isAnotherRunning(&guard_data_out)) { - // Wake up a running instance - QLocalSocket socket; - socket.connectToServer(LOCAL_SERVER_PREFIX + Int2String(guard_data_out)); - qDebug() << socket.fullServerName(); - if (!socket.waitForConnected(500)) { - qDebug() << "Failed to wake a running instance."; - return 0; - } - qDebug() << "connected to local server, try to raise another program"; - return 0; - } - // Some Bad System - QMessageBox::warning(nullptr, "NekoGui", "RunGuard disallow to run, use -many to force start."); - return 0; - } - MF_release_runguard = [&] { guard.release(); }; - -// icons -#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) - QIcon::setFallbackSearchPaths(QStringList{ - ":/neko", - ":/icon", - }); -#endif - - // icon for no theme - if (QIcon::themeName().isEmpty()) { - QIcon::setThemeName("breeze"); - } - - // Dir - QDir dir; - bool dir_success = true; - if (!dir.exists("profiles")) { - dir_success &= dir.mkdir("profiles"); - } - if (!dir.exists("groups")) { - dir_success &= dir.mkdir("groups"); - } - if (!dir.exists(ROUTES_PREFIX_NAME)) { - dir_success &= dir.mkdir(ROUTES_PREFIX_NAME); - } - if (!dir_success) { - QMessageBox::warning(nullptr, "Error", "No permission to write " + dir.absolutePath()); - return 1; - } - - // Load dataStore - switch (NekoGui::coreType) { - case NekoGui::CoreType::SING_BOX: - NekoGui::dataStore->fn = "groups/nekobox.json"; - break; - default: - MessageBoxWarning("Error", "Unknown coreType."); - return 0; - } - auto isLoaded = NekoGui::dataStore->Load(); - if (!isLoaded) { - NekoGui::dataStore->Save(); - } - - // Datastore & Flags - if (NekoGui::dataStore->start_minimal) NekoGui::dataStore->flag_tray = true; - - // load routing - NekoGui::dataStore->routing = std::make_unique(); - NekoGui::dataStore->routing->fn = ROUTES_PREFIX + NekoGui::dataStore->active_routing; - isLoaded = NekoGui::dataStore->routing->Load(); - if (!isLoaded) { - NekoGui::dataStore->routing->Save(); - } - - // Translate - QString locale; - switch (NekoGui::dataStore->language) { - case 1: // English - break; - case 2: - locale = "zh_CN"; - break; - case 3: - locale = "fa_IR"; // farsi(iran) - break; - case 4: - locale = "ru_RU"; // Russian - break; - default: - locale = QLocale().name(); - } - QGuiApplication::tr("QT_LAYOUT_DIRECTION"); - loadTranslate(locale); - - // Signals - signal(SIGTERM, signal_handler); - signal(SIGINT, signal_handler); - - // QLocalServer - QLocalServer server; - auto server_name = LOCAL_SERVER_PREFIX + Int2String(guard_data_in); - QLocalServer::removeServer(server_name); - server.listen(server_name); - QObject::connect(&server, &QLocalServer::newConnection, &a, [&] { - auto socket = server.nextPendingConnection(); - qDebug() << "nextPendingConnection:" << server_name << socket; - socket->deleteLater(); - // raise main window - MW_dialog_message("", "Raise"); - }); - - UI_InitMainWindow(); - return QApplication::exec(); -} diff --git a/nekoray_version.txt b/nekoray_version.txt deleted file mode 100644 index 866d240..0000000 --- a/nekoray_version.txt +++ /dev/null @@ -1 +0,0 @@ -4.0.1-2024-12-12 diff --git a/res/dashboard-notice.html b/res/dashboard-notice.html deleted file mode 100644 index cfea1c7..0000000 --- a/res/dashboard-notice.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - -

-

Please put your clash dashboard files to "./config/dashboard" dir.

-

For example, you can download from the following URL.

-

- Download Yacd-meta - or - Use online -

-

- - - \ No newline at end of file diff --git a/res/icon/dialog-question.svg b/res/icon/dialog-question.svg deleted file mode 100644 index 7ff4bc4..0000000 --- a/res/icon/dialog-question.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/icon/internet-web-browser.svg b/res/icon/internet-web-browser.svg deleted file mode 100644 index 138a1a3..0000000 --- a/res/icon/internet-web-browser.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/cancel.svg b/res/icon/material/cancel.svg deleted file mode 100644 index 3327c05..0000000 --- a/res/icon/material/cancel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/delete.svg b/res/icon/material/delete.svg deleted file mode 100644 index 21c80c2..0000000 --- a/res/icon/material/delete.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/history.svg b/res/icon/material/history.svg deleted file mode 100644 index 20c490c..0000000 --- a/res/icon/material/history.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/lock-open-outline.svg b/res/icon/material/lock-open-outline.svg deleted file mode 100644 index 4e0291d..0000000 --- a/res/icon/material/lock-open-outline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/lock-outline.svg b/res/icon/material/lock-outline.svg deleted file mode 100644 index 4dee801..0000000 --- a/res/icon/material/lock-outline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/swap-horizontal.svg b/res/icon/material/swap-horizontal.svg deleted file mode 100644 index 3f30649..0000000 --- a/res/icon/material/swap-horizontal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/material/swap-vertical.svg b/res/icon/material/swap-vertical.svg deleted file mode 100644 index 41d46a9..0000000 --- a/res/icon/material/swap-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/icon/network-server.svg b/res/icon/network-server.svg deleted file mode 100644 index 0806722..0000000 --- a/res/icon/network-server.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - diff --git a/res/icon/preferences.svg b/res/icon/preferences.svg deleted file mode 100644 index 875b939..0000000 --- a/res/icon/preferences.svg +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/icon/system-run.svg b/res/icon/system-run.svg deleted file mode 100644 index 45494f4..0000000 --- a/res/icon/system-run.svg +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/icon/system-software-update.svg b/res/icon/system-software-update.svg deleted file mode 100644 index e8567fb..0000000 --- a/res/icon/system-software-update.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/neko.css b/res/neko.css deleted file mode 100644 index 512e281..0000000 --- a/res/neko.css +++ /dev/null @@ -1,3 +0,0 @@ -QMessageBox { - messagebox-text-interaction-flags: 5; -} diff --git a/res/neko.qrc b/res/neko.qrc deleted file mode 100644 index 10e97e1..0000000 --- a/res/neko.qrc +++ /dev/null @@ -1,24 +0,0 @@ - - - icon/internet-web-browser.svg - icon/system-run.svg - icon/preferences.svg - icon/network-server.svg - icon/dialog-question.svg - icon/system-software-update.svg - icon/material/lock-open-outline.svg - icon/material/lock-outline.svg - icon/material/cancel.svg - icon/material/history.svg - icon/material/swap-vertical.svg - icon/material/delete.svg - icon/material/swap-horizontal.svg - - - public/nekobox.png - neko.css - vpn/vpn-run-root.sh - vpn/sing-box-vpn.json - dashboard-notice.html - - diff --git a/res/nekobox.ico b/res/nekobox.ico deleted file mode 100644 index 451e66fc1f62c155dceb42dc0d7aa49aac4ba774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67646 zcmd>n1$>ot`~IOj6i}2@lu`jvly2$nhSAc^sEv&n>kUS4FxK70*w|PnIcmMp9V#Mm zuK#sEXJ-u5_xHY{qWlk^o9Fa-p6_*ce5X>C;=hd>Rs6rPs(Hmys`peXRdW)bk|&kt z<*wYTRYj$c=JCJ#|4Kals-!Y1RcRi|k}4EERg=fsq`IVrBw3EHOO-C$t90241IksY zKBhvQy0a?1UvF`>4;tvc+wc>E+KpQpf7q(6n(y~5TcuW?(v@rWE>)>&S5=vEUvW)8<99bDeMG89lHU{vYLVU{)gV

-ZN(*NH#E>vlPDC;6?BnMuiUwGxypUZ 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 Kwkv+u^8WzzuBwOt diff --git a/res/public/nekobox.png b/res/public/nekobox.png deleted file mode 100644 index 4ba56b0b475ac2e98794f9fa85636d95dc853145..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64105 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≈d4w!!ip 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%0Ar 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 h%93J+rxn{9!e9u1Zj10sAzh(1p - - qss/flatgray.css - qss/lightblue.css - qss/flatgray/add_bottom.png - qss/flatgray/add_left.png - qss/flatgray/add_right.png - qss/flatgray/add_top.png - qss/flatgray/arrow_bottom.png - qss/flatgray/arrow_left.png - qss/flatgray/arrow_right.png - qss/flatgray/arrow_top.png - qss/flatgray/branch_close.png - qss/flatgray/branch_open.png - qss/flatgray/calendar_nextmonth.png - qss/flatgray/calendar_prevmonth.png - qss/flatgray/checkbox_checked.png - qss/flatgray/checkbox_checked_disable.png - qss/flatgray/checkbox_parcial.png - qss/flatgray/checkbox_parcial_disable.png - qss/flatgray/checkbox_unchecked.png - qss/flatgray/checkbox_unchecked_disable.png - qss/flatgray/menu_checked.png - qss/flatgray/radiobutton_checked.png - qss/flatgray/radiobutton_checked_disable.png - qss/flatgray/radiobutton_unchecked.png - qss/flatgray/radiobutton_unchecked_disable.png - qss/lightblue/add_bottom.png - qss/lightblue/add_left.png - qss/lightblue/add_right.png - qss/lightblue/add_top.png - qss/lightblue/arrow_bottom.png - qss/lightblue/arrow_left.png - qss/lightblue/arrow_right.png - qss/lightblue/arrow_top.png - qss/lightblue/branch_close.png - qss/lightblue/branch_open.png - qss/lightblue/calendar_nextmonth.png - qss/lightblue/calendar_prevmonth.png - qss/lightblue/checkbox_checked.png - qss/lightblue/checkbox_checked_disable.png - qss/lightblue/checkbox_parcial.png - qss/lightblue/checkbox_parcial_disable.png - qss/lightblue/checkbox_unchecked.png - qss/lightblue/checkbox_unchecked_disable.png - qss/lightblue/menu_checked.png - qss/lightblue/radiobutton_checked.png - qss/lightblue/radiobutton_checked_disable.png - qss/lightblue/radiobutton_unchecked.png - qss/lightblue/radiobutton_unchecked_disable.png - qss/blacksoft.css - qss/blacksoft/add_bottom.png - qss/blacksoft/add_left.png - qss/blacksoft/add_right.png - qss/blacksoft/add_top.png - qss/blacksoft/arrow_bottom.png - qss/blacksoft/arrow_left.png - qss/blacksoft/arrow_right.png - qss/blacksoft/arrow_top.png - qss/blacksoft/branch_close.png - qss/blacksoft/branch_open.png - qss/blacksoft/calendar_nextmonth.png - qss/blacksoft/calendar_prevmonth.png - qss/blacksoft/checkbox_checked.png - qss/blacksoft/checkbox_checked_disable.png - qss/blacksoft/checkbox_parcial.png - qss/blacksoft/checkbox_parcial_disable.png - qss/blacksoft/checkbox_unchecked.png - qss/blacksoft/checkbox_unchecked_disable.png - qss/blacksoft/menu_checked.png - qss/blacksoft/radiobutton_checked.png - qss/blacksoft/radiobutton_checked_disable.png - qss/blacksoft/radiobutton_unchecked.png - qss/blacksoft/radiobutton_unchecked_disable.png - - diff --git a/res/theme/feiyangqingyun/qss/blacksoft.css b/res/theme/feiyangqingyun/qss/blacksoft.css deleted file mode 100644 index 09a607a..0000000 --- a/res/theme/feiyangqingyun/qss/blacksoft.css +++ /dev/null @@ -1,679 +0,0 @@ -QPalette{background:#444444;}*{outline:0px;color:#DCDCDC;} - -QGraphicsView{ -border:1px solid #242424; -qproperty-backgroundBrush:#444444; -} - -QWidget[form="true"],QLabel[frameShape="1"]{ -border:1px solid #242424; -border-radius:0px; -} - -QWidget[form="bottom"]{ -background:#484848; -} - -QWidget[form="bottom"] .QFrame{ -border:1px solid #DCDCDC; -} - -QWidget[form="bottom"] QLabel,QWidget[form="title"] QLabel{ -border-radius:0px; -color:#DCDCDC; -background:none; -border-style:none; -} - -QWidget[form="title"],QWidget[nav="left"],QWidget[nav="top"] QAbstractButton{ -border-style:none; -border-radius:0px; -padding:5px; -color:#DCDCDC; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -QWidget[nav="top"] QAbstractButton:hover,QWidget[nav="top"] QAbstractButton:pressed,QWidget[nav="top"] QAbstractButton:checked{ -border-style:solid; -border-width:0px 0px 2px 0px; -padding:4px 4px 2px 4px; -border-color:#AAAAAA; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #646464,stop:1 #525252); -} - -QWidget[nav="left"] QAbstractButton{ -border-radius:0px; -color:#DCDCDC; -background:none; -border-style:none; -} - -QWidget[nav="left"] QAbstractButton:hover{ -color:#FFFFFF; -background-color:#AAAAAA; -} - -QWidget[nav="left"] QAbstractButton:checked,QWidget[nav="left"] QAbstractButton:pressed{ -color:#DCDCDC; -border-style:solid; -border-width:0px 0px 0px 2px; -padding:4px 4px 4px 2px; -border-color:#AAAAAA; -background-color:#444444; -} - -QWidget[video="true"] QLabel{ -color:#DCDCDC; -border:1px solid #242424; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -QWidget[video="true"] QLabel:focus{ -border:1px solid #AAAAAA; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #646464,stop:1 #525252); -} - -QLineEdit:read-only{ -background-color:#484848; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -border:1px solid #242424; -border-radius:3px; -padding:2px; -background:none; -selection-background-color:#AAAAAA; -selection-color:#FFFFFF; -} - -QLineEdit:focus,QTextEdit:focus,QPlainTextEdit:focus,QSpinBox:focus,QDoubleSpinBox:focus,QComboBox:focus,QDateEdit:focus,QTimeEdit:focus,QDateTimeEdit:focus,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QSpinBox:hover,QDoubleSpinBox:hover,QComboBox:hover,QDateEdit:hover,QTimeEdit:hover,QDateTimeEdit:hover{ -border:1px solid #242424; -} - -QLineEdit[echoMode="2"]{ -lineedit-password-character:9679; -} - -.QFrame{ -border:1px solid #242424; -border-radius:3px; -} - -.QGroupBox{ -border:1px solid #242424; -border-radius:5px; -margin-top:3ex; -} - -.QGroupBox::title{ -subcontrol-origin:margin; -position:relative; -left:10px; -} - -.QPushButton,.QToolButton{ -border-style:none; -border:1px solid #242424; -color:#DCDCDC; -padding:5px; -min-height:15px; -border-radius:5px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -.QPushButton:hover,.QToolButton:hover{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #646464,stop:1 #525252); -} - -.QPushButton:pressed,.QToolButton:pressed{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -.QToolButton::menu-indicator{ -image:None; -} - -QToolButton#btnMenu,QPushButton#btnMenu_Min,QPushButton#btnMenu_Max,QPushButton#btnMenu_Close{ -border-radius:3px; -color:#DCDCDC; -padding:3px; -margin:0px; -background:none; -border-style:none; -} - -QToolButton#btnMenu:hover,QPushButton#btnMenu_Min:hover,QPushButton#btnMenu_Max:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(51,127,209,230); -} - -QPushButton#btnMenu_Close:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(238,0,0,128); -} - -QRadioButton::indicator{ -width:15px; -height:15px; -} - -QRadioButton::indicator::unchecked{ -image:url(:/qss/blacksoft/radiobutton_unchecked.png); -} - -QRadioButton::indicator::unchecked:disabled{ -image:url(:/qss/blacksoft/radiobutton_unchecked_disable.png); -} - -QRadioButton::indicator::checked{ -image:url(:/qss/blacksoft/radiobutton_checked.png); -} - -QRadioButton::indicator::checked:disabled{ -image:url(:/qss/blacksoft/radiobutton_checked_disable.png); -} - -QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -padding:0px 0px 0px 0px; -} - -QCheckBox::indicator,QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -width:13px; -height:13px; -} - -QCheckBox::indicator:unchecked,QGroupBox::indicator:unchecked,QTreeView::indicator:unchecked,QListView::indicator:unchecked,QTableView::indicator:unchecked{ -image:url(:/qss/blacksoft/checkbox_unchecked.png); -} - -QCheckBox::indicator:unchecked:disabled,QGroupBox::indicator:unchecked:disabled,QTreeView::indicator:unchecked:disabled,QListView::indicator:unchecked:disabled,QTableView::indicator:unchecked:disabled{ -image:url(:/qss/blacksoft/checkbox_unchecked_disable.png); -} - -QCheckBox::indicator:checked,QGroupBox::indicator:checked,QTreeView::indicator:checked,QListView::indicator:checked,QTableView::indicator:checked{ -image:url(:/qss/blacksoft/checkbox_checked.png); -} - -QCheckBox::indicator:checked:disabled,QGroupBox::indicator:checked:disabled,QTreeView::indicator:checked:disabled,QListView::indicator:checked:disabled,QTableView::indicator:checked:disabled{ -image:url(:/qss/blacksoft/checkbox_checked_disable.png); -} - -QCheckBox::indicator:indeterminate,QGroupBox::indicator:indeterminate,QTreeView::indicator:indeterminate,QListView::indicator:indeterminate,QTableView::indicator:indeterminate{ -image:url(:/qss/blacksoft/checkbox_parcial.png); -} - -QCheckBox::indicator:indeterminate:disabled,QGroupBox::indicator:indeterminate:disabled,QTreeView::indicator:indeterminate:disabled,QListView::indicator:indeterminate:disabled,QTableView::indicator:indeterminate:disabled{ -image:url(:/qss/blacksoft/checkbox_parcial_disable.png); -} - -QTimeEdit::up-button,QDateEdit::up-button,QDateTimeEdit::up-button,QDoubleSpinBox::up-button,QSpinBox::up-button{ -image:url(:/qss/blacksoft/add_top.png); -width:10px; -height:10px; -padding:2px 5px 0px 0px; -} - -QTimeEdit::down-button,QDateEdit::down-button,QDateTimeEdit::down-button,QDoubleSpinBox::down-button,QSpinBox::down-button{ -image:url(:/qss/blacksoft/add_bottom.png); -width:10px; -height:10px; -padding:0px 5px 2px 0px; -} - -QTimeEdit::up-button:pressed,QDateEdit::up-button:pressed,QDateTimeEdit::up-button:pressed,QDoubleSpinBox::up-button:pressed,QSpinBox::up-button:pressed{ -top:-2px; -} - -QTimeEdit::down-button:pressed,QDateEdit::down-button:pressed,QDateTimeEdit::down-button:pressed,QDoubleSpinBox::down-button:pressed,QSpinBox::down-button:pressed,QSpinBox::down-button:pressed{ -bottom:-2px; -} - -QComboBox::down-arrow,QDateEdit[calendarPopup="true"]::down-arrow,QTimeEdit[calendarPopup="true"]::down-arrow,QDateTimeEdit[calendarPopup="true"]::down-arrow{ -image:url(:/qss/blacksoft/add_bottom.png); -width:10px; -height:10px; -right:2px; -} - -QComboBox::drop-down,QDateEdit::drop-down,QTimeEdit::drop-down,QDateTimeEdit::drop-down{ -subcontrol-origin:padding; -subcontrol-position:top right; -width:15px; -border-left-width:0px; -border-left-style:solid; -border-top-right-radius:3px; -border-bottom-right-radius:3px; -border-left-color:#242424; -} - -QComboBox::drop-down:on{ -top:1px; -} - -QMenuBar::item{ -color:#DCDCDC; -background-color:#484848; -margin:0px; -padding:3px 10px; -} - -QMenu,QMenuBar,QMenu:disabled,QMenuBar:disabled{ -color:#DCDCDC; -background-color:#484848; -border:1px solid #242424; -margin:0px; -} - -QMenu::item{ -padding:3px 20px; -} - -QMenu::indicator{ -width:20px; -height:13px; -} - -QMenu::indicator::checked{ -image:url(:/qss/blacksoft/menu_checked.png); -} - -QMenu::right-arrow{ -image:url(:/qss/blacksoft/arrow_right.png); -width:13px; -height:13px; -padding:0px 3px 0px 0px; -} - -QMenu::item:selected,QMenuBar::item:selected{ -color:#DCDCDC; -border:0px solid #242424; -background:#646464; -} - -QMenu::separator{ -height:1px; -background:#242424; -} - -QProgressBar{ -min-height:10px; -background:#484848; -border-radius:5px; -text-align:center; -border:1px solid #484848; -} - -QProgressBar:chunk{ -border-radius:5px; -background-color:#242424; -} - -QSlider::groove:horizontal{ -height:8px; -border-radius:4px; -background:#484848; -} - -QSlider::add-page:horizontal{ -height:8px; -border-radius:4px; -background:#484848; -} - -QSlider::sub-page:horizontal{ -height:8px; -border-radius:4px; -background:#242424; -} - -QSlider::handle:horizontal{ -width:13px; -margin-top:-3px; -margin-bottom:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #444444,stop:0.8 #242424); -} - -QSlider::groove:vertical{ -width:8px; -border-radius:4px; -background:#484848; -} - -QSlider::add-page:vertical{ -width:8px; -border-radius:4px; -background:#242424; -} - -QSlider::sub-page:vertical{ -width:8px; -border-radius:4px; -background:#484848; -} - -QSlider::handle:vertical{ -height:14px; -margin-left:-3px; -margin-right:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #444444,stop:0.8 #242424); -} - -QScrollBar:horizontal{ -background:#484848; -padding:0px; -border-radius:6px; -max-height:12px; -} - -QScrollBar::handle:horizontal{ -background:#242424; -min-width:50px; -border-radius:6px; -} - -QScrollBar::handle:horizontal:hover{ -background:#AAAAAA; -} - -QScrollBar::handle:horizontal:pressed{ -background:#AAAAAA; -} - -QScrollBar::add-page:horizontal{ -background:none; -} - -QScrollBar::sub-page:horizontal{ -background:none; -} - -QScrollBar::add-line:horizontal{ -background:none; -} - -QScrollBar::sub-line:horizontal{ -background:none; -} - -QScrollBar:vertical{ -background:#484848; -padding:0px; -border-radius:6px; -max-width:12px; -} - -QScrollBar::handle:vertical{ -background:#242424; -min-height:50px; -border-radius:6px; -} - -QScrollBar::handle:vertical:hover{ -background:#AAAAAA; -} - -QScrollBar::handle:vertical:pressed{ -background:#AAAAAA; -} - -QScrollBar::add-page:vertical{ -background:none; -} - -QScrollBar::sub-page:vertical{ -background:none; -} - -QScrollBar::add-line:vertical{ -background:none; -} - -QScrollBar::sub-line:vertical{ -background:none; -} - -QScrollArea{ -border:0px; -} - -QTreeView,QListView,QTableView,QTabWidget::pane{ -border:1px solid #242424; -selection-background-color:#646464; -selection-color:#DCDCDC; -alternate-background-color:#525252; -gridline-color:#242424; -} - -QTreeView::branch:closed:has-children{ -margin:4px; -border-image:url(:/qss/blacksoft/branch_open.png); -} - -QTreeView::branch:open:has-children{ -margin:4px; -border-image:url(:/qss/blacksoft/branch_close.png); -} - -QTreeView,QListView,QTableView,QSplitter::handle,QTreeView::branch{ -background:#444444; -} - -QTableView::item:selected,QListView::item:selected,QTreeView::item:selected{ -color:#DCDCDC; -background:#383838; -} - -QTableView::item:hover,QListView::item:hover,QTreeView::item:hover,QHeaderView,QHeaderView::section,QTableCornerButton:section{ -color:#DCDCDC; -background:#525252; -} - -QTableView::item,QListView::item,QTreeView::item{ -padding:1px; -margin:0px; -border:0px; -} - -QHeaderView::section,QTableCornerButton:section{ -padding:3px; -margin:0px; -border:1px solid #242424; -border-left-width:0px; -border-right-width:1px; -border-top-width:0px; -border-bottom-width:1px; -} - -QTabBar::tab{ -border:1px solid #242424; -color:#DCDCDC; -margin:0px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #646464,stop:1 #525252); -} - -QTabBar::tab:selected{ -border-style:solid; -border-color:#AAAAAA; -background:#444444; -} - -QTabBar::tab:top,QTabBar::tab:bottom{ -padding:3px 8px 3px 8px; -} - -QTabBar::tab:left,QTabBar::tab:right{ -padding:8px 3px 8px 3px; -} - -QTabBar::tab:top:selected{ -border-width:2px 0px 0px 0px; -} - -QTabBar::tab:right:selected{ -border-width:0px 0px 0px 2px; -} - -QTabBar::tab:bottom:selected{ -border-width:0px 0px 2px 0px; -} - -QTabBar::tab:left:selected{ -border-width:0px 2px 0px 0px; -} - -QTabBar::tab:first:top:selected,QTabBar::tab:first:bottom:selected{ -border-left-width:1px; -border-left-color:#242424; -} - -QTabBar::tab:first:left:selected,QTabBar::tab:first:right:selected{ -border-top-width:1px; -border-top-color:#242424; -} - -QTabBar::tab:last:top:selected,QTabBar::tab:last:bottom:selected{ -border-right-width:1px; -border-right-color:#242424; -} - -QTabBar::tab:last:left:selected,QTabBar::tab:last:right:selected{ -border-bottom-width:1px; -border-bottom-color:#242424; -} - -QStatusBar::item{ -border:0px solid #484848; -border-radius:3px; -} - -QToolBox::tab,QGroupBox#gboxDevicePanel,QGroupBox#gboxDeviceTitle,QFrame#gboxDevicePanel,QFrame#gboxDeviceTitle{ -padding:3px; -border-radius:5px; -color:#DCDCDC; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -QToolTip{ -border:0px solid #DCDCDC; -padding:1px; -color:#DCDCDC; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -QToolBox::tab:selected{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #646464,stop:1 #525252); -} - -QPrintPreviewDialog QToolButton{ -border:0px solid #DCDCDC; -border-radius:0px; -margin:0px; -padding:3px; -background:none; -} - -QColorDialog QPushButton,QFileDialog QPushButton{ -min-width:80px; -} - -QToolButton#qt_calendar_prevmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/blacksoft/calendar_prevmonth.png); -} - -QToolButton#qt_calendar_nextmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/blacksoft/calendar_nextmonth.png); -} - -QToolButton#qt_calendar_prevmonth,QToolButton#qt_calendar_nextmonth,QToolButton#qt_calendar_monthbutton,QToolButton#qt_calendar_yearbutton{ -border:0px solid #DCDCDC; -border-radius:3px; -margin:3px 3px 3px 3px; -padding:3px; -background:none; -} - -QToolButton#qt_calendar_prevmonth:hover,QToolButton#qt_calendar_nextmonth:hover,QToolButton#qt_calendar_monthbutton:hover,QToolButton#qt_calendar_yearbutton:hover,QToolButton#qt_calendar_prevmonth:pressed,QToolButton#qt_calendar_nextmonth:pressed,QToolButton#qt_calendar_monthbutton:pressed,QToolButton#qt_calendar_yearbutton:pressed{ -border:1px solid #242424; -} - -QCalendarWidget QSpinBox#qt_calendar_yearedit{ -margin:2px; -} - -QCalendarWidget QToolButton::menu-indicator{ -image:None; -} - -QCalendarWidget QTableView{ -border-width:0px; -} - -QCalendarWidget QWidget#qt_calendar_navigationbar{ -border:1px solid #242424; -border-width:1px 1px 0px 1px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #484848,stop:1 #383838); -} - -QTableView[model="true"]::item{ -padding:0px; -margin:0px; -} - -QTableView QLineEdit,QTableView QComboBox,QTableView QSpinBox,QTableView QDoubleSpinBox,QTableView QDateEdit,QTableView QTimeEdit,QTableView QDateTimeEdit{ -border-width:0px; -border-radius:0px; -} - -QTableView QLineEdit:focus,QTableView QComboBox:focus,QTableView QSpinBox:focus,QTableView QDoubleSpinBox:focus,QTableView QDateEdit:focus,QTableView QTimeEdit:focus,QTableView QDateTimeEdit:focus{ -border-width:0px; -border-radius:0px; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -background:#444444; -} - -QTabWidget::pane:top{top:-1px;} -QTabWidget::pane:bottom{bottom:-1px;} -QTabWidget::pane:left{right:-1px;} -QTabWidget::pane:right{left:-1px;} - -QDialog,QDial,#QUIWidgetMain{ -background-color:#444444; -color:#DCDCDC; -} - -QDialogButtonBox>QPushButton{ -min-width:50px; -} - -QListView[noborder="true"],QTreeView[noborder="true"],QTabWidget[noborder="true"]::pane{ -border-width:0px; -} - -QToolBar>*,QStatusBar>*{ -margin:2px; -} - -*:disabled,QMenu::item:disabled,QTabBar:tab:disabled,QHeaderView::section:disabled{ -background:#444444; -border-color:#484848; -color:#242424; -} - -/*TextColor:#DCDCDC*/ -/*PanelColor:#444444*/ -/*BorderColor:#242424*/ -/*NormalColorStart:#484848*/ -/*NormalColorEnd:#383838*/ -/*DarkColorStart:#646464*/ -/*DarkColorEnd:#525252*/ -/*HighColor:#AAAAAA*/ \ No newline at end of file diff --git a/res/theme/feiyangqingyun/qss/blacksoft/add_bottom.png b/res/theme/feiyangqingyun/qss/blacksoft/add_bottom.png deleted file mode 100644 index b4a5f1494ac956b541c0d92818891026eb6d2c77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335 zcmV-V0kHmwP)Fyw;pPL{gHA$1n_Zjog@FP<|(=<)fG;JgC4SnC=ipV2yQP?nv$c-`P z-8ELO!(kZCMdX3=C#-~j@sx~}hm=Q5_BR^Vui zdGcL2^9{?ge6?-+Vy(Rbu1n#jX*vR@Xdo0t2I2k$LJ?#zE(i!&kx{r{AY?&CwA20{`t8J7fvDC90&G7y50yK%dKu!+oq+YJOKG8+yyzFV<%UDq^CbFkKa%=7$8 h2Arm8nx_4Oz5%Q)wpkE#fv zs|Xg>hMc4dT$5OdVB%WX2%d0BW)_A0%E0dairwtYLP|=?|Bl31B+Ifh5xE8)hheyA zn&xHKv3rg#0P;LP2X28Q;6zn7UDw_3I$qsmeBnX}H^Bb4&j(ZHa0C8=n;aLQ6%HJL zmN+m5TI0YF=z#+xpcf7dfSx#52=vCm0$>ar%ml{5!3?)gL>}t8z5_mDtecXO@)z<6 X;{S^MZkvK)00000NkvXXu0mjf8B?Pj diff --git a/res/theme/feiyangqingyun/qss/blacksoft/add_right.png b/res/theme/feiyangqingyun/qss/blacksoft/add_right.png deleted file mode 100644 index 4c79925194c478dfaaedc732e6d87a616e3123ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmV-m0iXVfP)1g!BZ0 z0O<_`XQToMjz|>{oRCT&I3U$PC`7ISgm2_MnV%C_4+!u9EV)Z-0e3ORhf@~U0PbRp zn|Yr1r!1?1ML5s`i*cX>y5K+qbi;u^&=m*1KzAJY0VO!_0m^Vt4wT{`6SyqPd~lWH y0M2rTyRLg}n&usNY1{U09LGFBcjXjO4V5a0000nNXK-2( zWEL<2r!7V%0~2uCB4iRU1E*ypqk#)>S{5=2SOBLbBmV>z#A!*$Szra67L5!7E8?^$ zWau!7$X!L8wkD!SX1)%6FnPRf+j9U{p?Trf0JfpwB%tfMPXNyVzUr8MwFmGN8qPk6 un0W(WQ(E-1aU4JTzCYBwVOf@CRqPvMCWRUzRkYav0000!J5O8>CuBWpwprB2(=%x1scMnU(bOrSju9LV6gr&sAdac8EI;iaa z5EQULXF|uF+BZv8k9E%eyEE2~*CeC{<{vfC4=b1*3 eqqo#wFn{`>F!4%w&K6(@F?hQAxvXG>T zjD_{W%WL)kT8SV`tgPI^&CUdceC0xtUy*EP0xc~q|2tA+k^O#u0o;qoO9IbW*k)td3Hz?9N1=llR1eScHc<27@*0he$kaRFN4zyWB917n~y z4h(@FI4}Zw;lKdsiGz(mZyam@*1$m_uoeyqfb%?`(#$ z5Fn$05R6m+ArPqoLJ(33gaD)(2${$=fbfO9ardOh39JXi-QUd2Q}5OS?&`X}J?3x? z;I68wZQHi*#~iDHSvb%FvvHsUhTuR048uV@Fcb%|z;GPI0VOzy0m^WY50v5{{p0?6 zFv@WNb;@$nH23p7?}28TriZ@oKSZulsq}C91PgeNv|n*ItN;K207*qoM6N<$f(vYy AX8-^I diff --git a/res/theme/feiyangqingyun/qss/blacksoft/arrow_top.png b/res/theme/feiyangqingyun/qss/blacksoft/arrow_top.png deleted file mode 100644 index d2c71e8208394014e6e1b2c7b4f0d1f1c8052e17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%3=E9? zo-U3d5v^}8FAO?jAaLxXen$*TF;nB}RWFkjDj9Wz<~_W})O_%s^ank`x$9o@q+66W ztxWiKg-?A}_9V6DoBQvcd$(SxVOi!ZAYyBn$t!rb|8Yfl(R<%c)qB<6F7FxmE}PT} zUNknIEwnpa|Kq*r?6`QMvGvR*I2< zpn9tE{`8stg_RG@804G|M>!qGZhh#*9s6>P4iyJL{LY?aA)=M zv6>*dy!2lE-o%#&a23_2rOH*i`n4hjgJfk8eK^*=j`4hzU=RZd(&6#Iw|x0 zRfLJ}=2y{sLPJfN49GRWm~+I0a;k$!P+9TTqP^)M5p-SV1kB7aY3keBWOekxKw;DS8+HH;&^RxvtwD zY{b4`yD<^e3GWtD`8Hj}l?pFu0AB~yYT@|vv01ixOs zjns_dghX_mEDk*;!!SH47K@JncBTkj5|Ql*{dWL0B6^t=A|e4ZU%0M&pX{mrNm{Me zArW0oSn?(!d!FY#%sL}eXK)l&P>U7RVgs^BW-8=urM7^^3gv&?g1nq0A(38y5Dw`L2w{*5KoQaXe*eUHJdSLTTI=(`V-eZ# zft&#vV=eQv=3^nGvMS}S$k=SNZS~5ZvKMP z_n_xAfE)9J`w56r>d5@qegckxt@*(pfuDfE+BmbB0QZt4c{+b#3e2+Xty1dIx8ikX z0^Cj0^o~+$;odWSP(+k5<`i(&;f77E_3afK^-|md&^1L-G)3e&aM}kBJOV@Dz!NYO z4mP7AB3Z+gs2zwSgPFRaFkWSyJK! z2Vq_kZ#eMZX1wCSL!&Os^3ZTNyb83m*3Xh8d99QZ54OJ}UjZ)n9p&5Sv#S6A002ov JPDHLkV1h5pC4~S0 diff --git a/res/theme/feiyangqingyun/qss/blacksoft/calendar_prevmonth.png b/res/theme/feiyangqingyun/qss/blacksoft/calendar_prevmonth.png deleted file mode 100644 index 46d4d625c9aba4d5b2e366139d6b98b913a57224..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667 zcmV;M0%ZM(P)x{Q;$Pt0IEZw*;_z;eKuV=bDQNQU$oo0ela_0fd%rvKium2$5q|H( z?~<4I9`HZ^ctq6&hr?lWGMQWja1;c=QQ3O63QT_^rM#<@8VMnWLWm!696yhuXsJxi z$~qXw@fs1m25|YrV+~+aDYX%XVZy0fNe9otW%buR&wENltY(z%;0)Z!d;_rT`@S@- zU#f#~96unUx0sCs0FQ{SapqtH|KMH$xK!lh^?LmaXC4;N5O+XCk9)n|A!jb*?;r!W z8U(>dbJ}r?fQ!Zf20$A)pdio|4k!S$i38>UO>jbppIYmO00tb0>$=lUr?W4F(1n18 zovSLjH*hA<5-!V*<2WnXx|v(3rIaf~v;{jj0JyHT_On3_NJNhSTv;gTbcEKro(*!q zTCL_T)-yY2b?^g@H<}kBIiOPN4S-G9Lms-W`!yTnfMFOWN~sOlK~9NitK04FWe?6> z2csxjYBUtNniedkN&J%>6I6jV!dA zv573aAGC!u+ELp;8lOoR$Zf}QZgUS!#-}>QI=MWZPT!42Bi81Fb?nTLMD!BHkT=KU z@eR&g?1Ot4hDnknPYjT(l)CybKz?dAo4cGjO5bi3KyEvZbFbBE{o>TGhoUqhmV4l?DFyuQz5fcJjXgMc^)mC7*7_U( zQnt83|KM#L07xkf5uG6LCkF$r;+Baa2LZ0bEhD8g%zW9kmK_+w#gY}6inG>gBARxs zr2rcMubKHo?0=@SSV=vxD=E4C0RaMnhopU0ZN-0h3165T$RZ4xyfeQgQLgvB& z;VRx^=1Xg>-k!D_4v6oAJ0bhHF{Yd(TOs?p&ZeHl8zK9=Yj4slBq94A1Z>*P1d%zW z6x+00oyc4xlx_RLe~@)uALN?vgf{Bb`~>i*4@_73=%};dfcwe9?z!v0--3ufmsSLF zC4>-X0OqA79WlxP++nE4|TMq(MWVCIXa fX`XAX7Xa`F3D;<%=aqnT00000NkvXXu0mjfN$mu% diff --git a/res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked_disable.png b/res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked_disable.png deleted file mode 100644 index f6aab4033a67e3b67955f59fa60a4a93ba51370f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmV;B0&o3^P)Lyh@AJ0$b`h#Qb#V|9yL1#BJVgh&$vY7T z2mb*laTWYAIvu#);3C{2IGvM|2t_z>%#tRhf|Cd>H)wmtybfpKdZusEHffgoj!j

xW9Rm}Jfn8t+_?haf@JqUsgW6T=Rw=GyEFgC_))@rq#3*L6+8@86VC_k7>qwJmP7S|>-N(M_%O$0BexU}j{N7P{>2yKIDXe` zHm}T2TMP&3``|*zJZ_9xz9e%Y^SaJv{T9!J%=4~2%V8lCGVejatfv_(vWO|gJjbm> zWKkoOdC!9rNY{056gA(mZPZ!%!@(qK1*qghnyk9u`U-G956HBRCkqSqTLF7zD=d9g z!27ZlmbR5r>KPE0ElH>yMbRte`+hf$^Pbr$|Dw@Q2kpVO+unV zm{&i7P49ce7Mnl9emmMeM<1MS|M0Sj?{UlL)(;xGTvAS1#+Su@XH#IPy}v%>>$0>P zeXcI&Zf%oIkvX1{=6@jW?JC*Z%QF48PANM3znl4n{?eG09Wqz-es<1WyQS<%ywU}2 zroDuSm$9xXvAZ9MpI^0V`+;p`C(YZ=rhV36IBX!(b%J>+|9^4gYF51 zE{-7)t#8j6dL0Q6Xn0tkro8RqimekOB5!4CPwSf6TK<7;Q}_oavqNUvs+b(t>~W~z z+$gGeI*ifz+OedGs$#)=()J1099nO4zQ1v1#~b16)&-(YD;QW7Xmn*ikh{?%5&e~Q zPP@Q8mGhs41uL1hC9VIrBIIk%q|}?r1p?KZk9{lJc_;LN#iYtFJ`S!qG zZp)_yQ`y(rWIT1$=8vkpaH=^BMM$bvdqc-wiH{2p6u&vzF>}=`-GHO>)_lD6{qNjq zsgeAL3piVDEEd)GoD^f?YdoJ}OYZNP%U(8jykTf}5YjXgpO|^Q)VwrSpP`Q7$qcir U<~IXh0fU0U)78&qol`;+0Ih9}NB{r; diff --git a/res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked.png b/res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked.png deleted file mode 100644 index 8a23968e063407ca349c15b5cf9d6f666d8565c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>k}@P)@Cq#?L`XJM1VIiPTNf;@u(G@2 zN)^sdun-GNI|~af#IvwC>@81Xb7-TWh(dP5CLv&-MmS9FjaN+36I3V}*0?XszEY1V^J$J5AG<){GI6+gj_lCCj1L>(!gh<~D#WOYqxd zGPxFpp-}*krs<}=Nhx)+3ON7>!_ag(okt{h@{Hu!Mx${V0MtCYXzkf=x7(kpkh2+& z$4>zqkvNM$#6%dj0*4hAbL8a73Ywl{2Bx@DB zA&%n>lJ@1i2bMSZ%4*cJEPGs~krTjt zyo9kWPi7Q0XLE)c|H_$&Rqmv0F(VqT{BzgbULd* zny20L^mKK6e0&*LWUr&|RCTu1YJD42Yr&J-Qq}oRr?Zl`9KgoL#?K^4ZkyRXFFu#1 z=~V!A&P|HQd2ew&2!beYu>)ASTz&*3{ci=X0cfdII&NlxxA|=pMcX45WNK>atC@Z9 zq8jYK5Qj<>McX2>bud3Ng8!lr%t|4cl|nEpg zC}vhV80y&ASWiU0d)es`N)2f?n{`!nUi1e*<2de`*?VtcuF+_Oqt%06uQz9AXT9hZ z?6%l|XY`GMQ>ywT48xIC(m8iUL>|%~`FRR#!!2);qTeteDwL5!uPf zt!8G|MdT8DldV)lmfP+2-Ju{r7>12BP1k@kIm!GlPb-zmLLA4x!Mnw+R_mjx&H}Fw zNo)tW@5_N#K@WA#T{g2jB65NLlqpBPusb|`kR(Z*^S=s?9)AG`9HCqlvGOqh0000< KMNUMnLSTX-T@Yjd diff --git a/res/theme/feiyangqingyun/qss/blacksoft/menu_checked.png b/res/theme/feiyangqingyun/qss/blacksoft/menu_checked.png deleted file mode 100644 index 4fca11f958a859debbc44b1f9601e1e8e69bcdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 501 zcmV;pQ5ZA!Ewf3Uh?LK6UTL~~pl09S09k4a&`W8Z*b~>Hgzhi3$X5i9fB5MWa;L^+? zYXdsq(u}2)hf1ke02!WE2nVoXj8SPLIItiNKualK(ne5V30y3tJXT75rX35P!6=Fj zthJXwWQ;lQ_xm3;;Et41Z@)V;fpHw4SZi;9wY19^Gwk(xNu9V%U00000NkvXXu0mjfgi6*S diff --git a/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked.png b/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked.png deleted file mode 100644 index 69e499fb1c3033b4fa560c5567af20dc1bda0d48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 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 diff --git a/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked_disable.png b/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked_disable.png deleted file mode 100644 index f729f172f2a2341bc246de76b7f568780635e4ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1365 zcmV-b1*-aqP)LYClmr|s~cAMRK z?&-tMzWB^Dv#%3<=>KJA&OP_u-<b!sHv{5egk+4aApYR*MNw;+S%E;jY)>SEMPPmZFC%G zx2i6mBd|-Ps(U=od!)O&yRVc_Nx*nK9``(N57076KOWEn`~v(@uq^?uD|}8i9S(;= zp|$Pp?Y&b5bX_-Ut=(S+&H_h)*CLU~SIJ~@D9EX?v2jsVRn-H)3qW%j$V2&j{@$QS z76HcN@df#OevgRU64a8ab^{wTnan;w%XB(XCKiir0Nw;HE8(*<5{W$S|FcEmx7KbF zkyQZ=Sm2B5>gr{gOlI#i-~enoo&MMua}98)gwOh1F1Oxqyd;8!t>gf(H~{YbNF?%X zso|We5{bn8TrT$su+CqmsvXwa+p^j0Z=<$}5fp3fGXZb_pUnjvKr)%^&*$?`5D-Zb ziHAa=M=!CBekl{Zj4@_)DwXP*E!;%8uDiloyBApGD@upM;ag9fIB^ChL{L>%2f$fX z-8$EB0I5{!2(TldC^|emJhng$KvPpwHE?%;^D)P9cFi?hK?=O5s%Lz51KdwxbPa&x zI1Q@0tf&Rm{i#&yT!jEhr_)^`@}u7_B5jF8V$m2N&`3(YlD+O1O0tbqWs`Z0| zgY{4VG{#&3)cBnGR5e{;K!D?nACaS~6_F)lfU4^HYl%p|F=nvRbk^D)RsG9nt`U*T z#{f&H*KwS`D~C3sa5$V7k&-D_Nk9>CJkOi)m0+$k3V6z2psGt9$C+Pg0MGM+7N}L# zJdDr5UWzl@h53BGsnURq>>&O?^oz*&Nm5v?7kp0hMPxQeosqmV=mZPAdImMOee zSi{IRQZ6aEpsH&!nM~kybh5XZdyFwVDILk#>Xb2NT`HCOxs*fMcayHJu5W;wf#h71 z0f&q+E6c&b6rR)0pFe-LwzhUhC=?n7Rsi8yg7pDUr__%U975G zRCNQzDbi#*3%oUzg*9Wq2{O@W^d=E`xbVo-Oz~p62xNe-Jf&1R1?!*}ZcH5>f{ X=*#LR(ps-<00000NkvXXu0mjf;(CsV diff --git a/res/theme/feiyangqingyun/qss/flatgray.css b/res/theme/feiyangqingyun/qss/flatgray.css deleted file mode 100644 index 883e90f..0000000 --- a/res/theme/feiyangqingyun/qss/flatgray.css +++ /dev/null @@ -1,679 +0,0 @@ -QPalette{background:#FFFFFF;}*{outline:0px;color:#57595B;} - -QGraphicsView{ -border:1px solid #B6B6B6; -qproperty-backgroundBrush:#FFFFFF; -} - -QWidget[form="true"],QLabel[frameShape="1"]{ -border:1px solid #B6B6B6; -border-radius:0px; -} - -QWidget[form="bottom"]{ -background:#E4E4E4; -} - -QWidget[form="bottom"] .QFrame{ -border:1px solid #57595B; -} - -QWidget[form="bottom"] QLabel,QWidget[form="title"] QLabel{ -border-radius:0px; -color:#57595B; -background:none; -border-style:none; -} - -QWidget[form="title"],QWidget[nav="left"],QWidget[nav="top"] QAbstractButton{ -border-style:none; -border-radius:0px; -padding:5px; -color:#57595B; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -QWidget[nav="top"] QAbstractButton:hover,QWidget[nav="top"] QAbstractButton:pressed,QWidget[nav="top"] QAbstractButton:checked{ -border-style:solid; -border-width:0px 0px 2px 0px; -padding:4px 4px 2px 4px; -border-color:#575959; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F6F6F6,stop:1 #F6F6F6); -} - -QWidget[nav="left"] QAbstractButton{ -border-radius:0px; -color:#57595B; -background:none; -border-style:none; -} - -QWidget[nav="left"] QAbstractButton:hover{ -color:#FFFFFF; -background-color:#575959; -} - -QWidget[nav="left"] QAbstractButton:checked,QWidget[nav="left"] QAbstractButton:pressed{ -color:#57595B; -border-style:solid; -border-width:0px 0px 0px 2px; -padding:4px 4px 4px 2px; -border-color:#575959; -background-color:#FFFFFF; -} - -QWidget[video="true"] QLabel{ -color:#57595B; -border:1px solid #B6B6B6; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -QWidget[video="true"] QLabel:focus{ -border:1px solid #575959; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F6F6F6,stop:1 #F6F6F6); -} - -QLineEdit:read-only{ -background-color:#E4E4E4; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -border:1px solid #B6B6B6; -border-radius:3px; -padding:2px; -background:none; -selection-background-color:#575959; -selection-color:#FFFFFF; -} - -QLineEdit:focus,QTextEdit:focus,QPlainTextEdit:focus,QSpinBox:focus,QDoubleSpinBox:focus,QComboBox:focus,QDateEdit:focus,QTimeEdit:focus,QDateTimeEdit:focus,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QSpinBox:hover,QDoubleSpinBox:hover,QComboBox:hover,QDateEdit:hover,QTimeEdit:hover,QDateTimeEdit:hover{ -border:1px solid #B6B6B6; -} - -QLineEdit[echoMode="2"]{ -lineedit-password-character:9679; -} - -.QFrame{ -border:1px solid #B6B6B6; -border-radius:3px; -} - -.QGroupBox{ -border:1px solid #B6B6B6; -border-radius:5px; -margin-top:3ex; -} - -.QGroupBox::title{ -subcontrol-origin:margin; -position:relative; -left:10px; -} - -.QPushButton,.QToolButton{ -border-style:none; -border:1px solid #B6B6B6; -color:#57595B; -padding:5px; -min-height:15px; -border-radius:5px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -.QPushButton:hover,.QToolButton:hover{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F6F6F6,stop:1 #F6F6F6); -} - -.QPushButton:pressed,.QToolButton:pressed{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -.QToolButton::menu-indicator{ -image:None; -} - -QToolButton#btnMenu,QPushButton#btnMenu_Min,QPushButton#btnMenu_Max,QPushButton#btnMenu_Close{ -border-radius:3px; -color:#57595B; -padding:3px; -margin:0px; -background:none; -border-style:none; -} - -QToolButton#btnMenu:hover,QPushButton#btnMenu_Min:hover,QPushButton#btnMenu_Max:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(51,127,209,230); -} - -QPushButton#btnMenu_Close:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(238,0,0,128); -} - -QRadioButton::indicator{ -width:15px; -height:15px; -} - -QRadioButton::indicator::unchecked{ -image:url(:/qss/flatgray/radiobutton_unchecked.png); -} - -QRadioButton::indicator::unchecked:disabled{ -image:url(:/qss/flatgray/radiobutton_unchecked_disable.png); -} - -QRadioButton::indicator::checked{ -image:url(:/qss/flatgray/radiobutton_checked.png); -} - -QRadioButton::indicator::checked:disabled{ -image:url(:/qss/flatgray/radiobutton_checked_disable.png); -} - -QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -padding:0px 0px 0px 0px; -} - -QCheckBox::indicator,QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -width:13px; -height:13px; -} - -QCheckBox::indicator:unchecked,QGroupBox::indicator:unchecked,QTreeView::indicator:unchecked,QListView::indicator:unchecked,QTableView::indicator:unchecked{ -image:url(:/qss/flatgray/checkbox_unchecked.png); -} - -QCheckBox::indicator:unchecked:disabled,QGroupBox::indicator:unchecked:disabled,QTreeView::indicator:unchecked:disabled,QListView::indicator:unchecked:disabled,QTableView::indicator:unchecked:disabled{ -image:url(:/qss/flatgray/checkbox_unchecked_disable.png); -} - -QCheckBox::indicator:checked,QGroupBox::indicator:checked,QTreeView::indicator:checked,QListView::indicator:checked,QTableView::indicator:checked{ -image:url(:/qss/flatgray/checkbox_checked.png); -} - -QCheckBox::indicator:checked:disabled,QGroupBox::indicator:checked:disabled,QTreeView::indicator:checked:disabled,QListView::indicator:checked:disabled,QTableView::indicator:checked:disabled{ -image:url(:/qss/flatgray/checkbox_checked_disable.png); -} - -QCheckBox::indicator:indeterminate,QGroupBox::indicator:indeterminate,QTreeView::indicator:indeterminate,QListView::indicator:indeterminate,QTableView::indicator:indeterminate{ -image:url(:/qss/flatgray/checkbox_parcial.png); -} - -QCheckBox::indicator:indeterminate:disabled,QGroupBox::indicator:indeterminate:disabled,QTreeView::indicator:indeterminate:disabled,QListView::indicator:indeterminate:disabled,QTableView::indicator:indeterminate:disabled{ -image:url(:/qss/flatgray/checkbox_parcial_disable.png); -} - -QTimeEdit::up-button,QDateEdit::up-button,QDateTimeEdit::up-button,QDoubleSpinBox::up-button,QSpinBox::up-button{ -image:url(:/qss/flatgray/add_top.png); -width:10px; -height:10px; -padding:2px 5px 0px 0px; -} - -QTimeEdit::down-button,QDateEdit::down-button,QDateTimeEdit::down-button,QDoubleSpinBox::down-button,QSpinBox::down-button{ -image:url(:/qss/flatgray/add_bottom.png); -width:10px; -height:10px; -padding:0px 5px 2px 0px; -} - -QTimeEdit::up-button:pressed,QDateEdit::up-button:pressed,QDateTimeEdit::up-button:pressed,QDoubleSpinBox::up-button:pressed,QSpinBox::up-button:pressed{ -top:-2px; -} - -QTimeEdit::down-button:pressed,QDateEdit::down-button:pressed,QDateTimeEdit::down-button:pressed,QDoubleSpinBox::down-button:pressed,QSpinBox::down-button:pressed,QSpinBox::down-button:pressed{ -bottom:-2px; -} - -QComboBox::down-arrow,QDateEdit[calendarPopup="true"]::down-arrow,QTimeEdit[calendarPopup="true"]::down-arrow,QDateTimeEdit[calendarPopup="true"]::down-arrow{ -image:url(:/qss/flatgray/add_bottom.png); -width:10px; -height:10px; -right:2px; -} - -QComboBox::drop-down,QDateEdit::drop-down,QTimeEdit::drop-down,QDateTimeEdit::drop-down{ -subcontrol-origin:padding; -subcontrol-position:top right; -width:15px; -border-left-width:0px; -border-left-style:solid; -border-top-right-radius:3px; -border-bottom-right-radius:3px; -border-left-color:#B6B6B6; -} - -QComboBox::drop-down:on{ -top:1px; -} - -QMenuBar::item{ -color:#57595B; -background-color:#E4E4E4; -margin:0px; -padding:3px 10px; -} - -QMenu,QMenuBar,QMenu:disabled,QMenuBar:disabled{ -color:#57595B; -background-color:#E4E4E4; -border:1px solid #B6B6B6; -margin:0px; -} - -QMenu::item{ -padding:3px 20px; -} - -QMenu::indicator{ -width:20px; -height:13px; -} - -QMenu::indicator::checked{ -image:url(:/qss/flatgray/menu_checked.png); -} - -QMenu::right-arrow{ -image:url(:/qss/flatgray/arrow_right.png); -width:13px; -height:13px; -padding:0px 3px 0px 0px; -} - -QMenu::item:selected,QMenuBar::item:selected{ -color:#57595B; -border:0px solid #B6B6B6; -background:#F6F6F6; -} - -QMenu::separator{ -height:1px; -background:#B6B6B6; -} - -QProgressBar{ -min-height:10px; -background:#E4E4E4; -border-radius:5px; -text-align:center; -border:1px solid #E4E4E4; -} - -QProgressBar:chunk{ -border-radius:5px; -background-color:#B6B6B6; -} - -QSlider::groove:horizontal{ -height:8px; -border-radius:4px; -background:#E4E4E4; -} - -QSlider::add-page:horizontal{ -height:8px; -border-radius:4px; -background:#E4E4E4; -} - -QSlider::sub-page:horizontal{ -height:8px; -border-radius:4px; -background:#B6B6B6; -} - -QSlider::handle:horizontal{ -width:13px; -margin-top:-3px; -margin-bottom:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #FFFFFF,stop:0.8 #B6B6B6); -} - -QSlider::groove:vertical{ -width:8px; -border-radius:4px; -background:#E4E4E4; -} - -QSlider::add-page:vertical{ -width:8px; -border-radius:4px; -background:#B6B6B6; -} - -QSlider::sub-page:vertical{ -width:8px; -border-radius:4px; -background:#E4E4E4; -} - -QSlider::handle:vertical{ -height:14px; -margin-left:-3px; -margin-right:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #FFFFFF,stop:0.8 #B6B6B6); -} - -QScrollBar:horizontal{ -background:#E4E4E4; -padding:0px; -border-radius:6px; -max-height:12px; -} - -QScrollBar::handle:horizontal{ -background:#B6B6B6; -min-width:50px; -border-radius:6px; -} - -QScrollBar::handle:horizontal:hover{ -background:#575959; -} - -QScrollBar::handle:horizontal:pressed{ -background:#575959; -} - -QScrollBar::add-page:horizontal{ -background:none; -} - -QScrollBar::sub-page:horizontal{ -background:none; -} - -QScrollBar::add-line:horizontal{ -background:none; -} - -QScrollBar::sub-line:horizontal{ -background:none; -} - -QScrollBar:vertical{ -background:#E4E4E4; -padding:0px; -border-radius:6px; -max-width:12px; -} - -QScrollBar::handle:vertical{ -background:#B6B6B6; -min-height:50px; -border-radius:6px; -} - -QScrollBar::handle:vertical:hover{ -background:#575959; -} - -QScrollBar::handle:vertical:pressed{ -background:#575959; -} - -QScrollBar::add-page:vertical{ -background:none; -} - -QScrollBar::sub-page:vertical{ -background:none; -} - -QScrollBar::add-line:vertical{ -background:none; -} - -QScrollBar::sub-line:vertical{ -background:none; -} - -QScrollArea{ -border:0px; -} - -QTreeView,QListView,QTableView,QTabWidget::pane{ -border:1px solid #B6B6B6; -selection-background-color:#F6F6F6; -selection-color:#57595B; -alternate-background-color:#F6F6F6; -gridline-color:#B6B6B6; -} - -QTreeView::branch:closed:has-children{ -margin:4px; -border-image:url(:/qss/flatgray/branch_open.png); -} - -QTreeView::branch:open:has-children{ -margin:4px; -border-image:url(:/qss/flatgray/branch_close.png); -} - -QTreeView,QListView,QTableView,QSplitter::handle,QTreeView::branch{ -background:#FFFFFF; -} - -QTableView::item:selected,QListView::item:selected,QTreeView::item:selected{ -color:#57595B; -background:#E4E4E4; -} - -QTableView::item:hover,QListView::item:hover,QTreeView::item:hover,QHeaderView,QHeaderView::section,QTableCornerButton:section{ -color:#57595B; -background:#F6F6F6; -} - -QTableView::item,QListView::item,QTreeView::item{ -padding:1px; -margin:0px; -border:0px; -} - -QHeaderView::section,QTableCornerButton:section{ -padding:3px; -margin:0px; -border:1px solid #B6B6B6; -border-left-width:0px; -border-right-width:1px; -border-top-width:0px; -border-bottom-width:1px; -} - -QTabBar::tab{ -border:1px solid #B6B6B6; -color:#57595B; -margin:0px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F6F6F6,stop:1 #F6F6F6); -} - -QTabBar::tab:selected{ -border-style:solid; -border-color:#575959; -background:#FFFFFF; -} - -QTabBar::tab:top,QTabBar::tab:bottom{ -padding:3px 8px 3px 8px; -} - -QTabBar::tab:left,QTabBar::tab:right{ -padding:8px 3px 8px 3px; -} - -QTabBar::tab:top:selected{ -border-width:2px 0px 0px 0px; -} - -QTabBar::tab:right:selected{ -border-width:0px 0px 0px 2px; -} - -QTabBar::tab:bottom:selected{ -border-width:0px 0px 2px 0px; -} - -QTabBar::tab:left:selected{ -border-width:0px 2px 0px 0px; -} - -QTabBar::tab:first:top:selected,QTabBar::tab:first:bottom:selected{ -border-left-width:1px; -border-left-color:#B6B6B6; -} - -QTabBar::tab:first:left:selected,QTabBar::tab:first:right:selected{ -border-top-width:1px; -border-top-color:#B6B6B6; -} - -QTabBar::tab:last:top:selected,QTabBar::tab:last:bottom:selected{ -border-right-width:1px; -border-right-color:#B6B6B6; -} - -QTabBar::tab:last:left:selected,QTabBar::tab:last:right:selected{ -border-bottom-width:1px; -border-bottom-color:#B6B6B6; -} - -QStatusBar::item{ -border:0px solid #E4E4E4; -border-radius:3px; -} - -QToolBox::tab,QGroupBox#gboxDevicePanel,QGroupBox#gboxDeviceTitle,QFrame#gboxDevicePanel,QFrame#gboxDeviceTitle{ -padding:3px; -border-radius:5px; -color:#57595B; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -QToolTip{ -border:0px solid #57595B; -padding:1px; -color:#57595B; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -QToolBox::tab:selected{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F6F6F6,stop:1 #F6F6F6); -} - -QPrintPreviewDialog QToolButton{ -border:0px solid #57595B; -border-radius:0px; -margin:0px; -padding:3px; -background:none; -} - -QColorDialog QPushButton,QFileDialog QPushButton{ -min-width:80px; -} - -QToolButton#qt_calendar_prevmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/flatgray/calendar_prevmonth.png); -} - -QToolButton#qt_calendar_nextmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/flatgray/calendar_nextmonth.png); -} - -QToolButton#qt_calendar_prevmonth,QToolButton#qt_calendar_nextmonth,QToolButton#qt_calendar_monthbutton,QToolButton#qt_calendar_yearbutton{ -border:0px solid #57595B; -border-radius:3px; -margin:3px 3px 3px 3px; -padding:3px; -background:none; -} - -QToolButton#qt_calendar_prevmonth:hover,QToolButton#qt_calendar_nextmonth:hover,QToolButton#qt_calendar_monthbutton:hover,QToolButton#qt_calendar_yearbutton:hover,QToolButton#qt_calendar_prevmonth:pressed,QToolButton#qt_calendar_nextmonth:pressed,QToolButton#qt_calendar_monthbutton:pressed,QToolButton#qt_calendar_yearbutton:pressed{ -border:1px solid #B6B6B6; -} - -QCalendarWidget QSpinBox#qt_calendar_yearedit{ -margin:2px; -} - -QCalendarWidget QToolButton::menu-indicator{ -image:None; -} - -QCalendarWidget QTableView{ -border-width:0px; -} - -QCalendarWidget QWidget#qt_calendar_navigationbar{ -border:1px solid #B6B6B6; -border-width:1px 1px 0px 1px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #E4E4E4,stop:1 #E4E4E4); -} - -QTableView[model="true"]::item{ -padding:0px; -margin:0px; -} - -QTableView QLineEdit,QTableView QComboBox,QTableView QSpinBox,QTableView QDoubleSpinBox,QTableView QDateEdit,QTableView QTimeEdit,QTableView QDateTimeEdit{ -border-width:0px; -border-radius:0px; -} - -QTableView QLineEdit:focus,QTableView QComboBox:focus,QTableView QSpinBox:focus,QTableView QDoubleSpinBox:focus,QTableView QDateEdit:focus,QTableView QTimeEdit:focus,QTableView QDateTimeEdit:focus{ -border-width:0px; -border-radius:0px; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -background:#FFFFFF; -} - -QTabWidget::pane:top{top:-1px;} -QTabWidget::pane:bottom{bottom:-1px;} -QTabWidget::pane:left{right:-1px;} -QTabWidget::pane:right{left:-1px;} - -QDialog,QDial,#QUIWidgetMain{ -background-color:#FFFFFF; -color:#57595B; -} - -QDialogButtonBox>QPushButton{ -min-width:50px; -} - -QListView[noborder="true"],QTreeView[noborder="true"],QTabWidget[noborder="true"]::pane{ -border-width:0px; -} - -QToolBar>*,QStatusBar>*{ -margin:2px; -} - -*:disabled,QMenu::item:disabled,QTabBar:tab:disabled,QHeaderView::section:disabled{ -background:#FFFFFF; -border-color:#E4E4E4; -color:#B6B6B6; -} - -/*TextColor:#57595B*/ -/*PanelColor:#FFFFFF*/ -/*BorderColor:#B6B6B6*/ -/*NormalColorStart:#E4E4E4*/ -/*NormalColorEnd:#E4E4E4*/ -/*DarkColorStart:#F6F6F6*/ -/*DarkColorEnd:#F6F6F6*/ -/*HighColor:#575959*/ \ No newline at end of file diff --git a/res/theme/feiyangqingyun/qss/flatgray/add_bottom.png b/res/theme/feiyangqingyun/qss/flatgray/add_bottom.png deleted file mode 100644 index 868e68710ff1bf5a9d02044f9404f9a913849805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmV-W0k8gvP)ly0>K-Zgo6Yi{EJM+K_U>G$SgR>0E9otY&ggS1Q>>)Zkpz~s;Z+g i<}(LQ(=<)f?w||cl8Oc-KhUxO0000&qqgOM%||O^opYIQ+gAV?0_ih734|OGyH>!hry2i34Mx zH4Y4c3LF>#RX8vJDsiw9sK&txU=AE40(0RY0oeEbag6bn3^+&tj^p?Nyb3I{Ee1AC z^BzLD23`c7A0<75&beI(;fxH(Y^xEhAxNj+z&RQgMO0!j<7!$!&7 Q{Qv*}07*qoM6N<$g3{cXX#fBK diff --git a/res/theme/feiyangqingyun/qss/flatgray/add_right.png b/res/theme/feiyangqingyun/qss/flatgray/add_right.png deleted file mode 100644 index be8dd1acec168ba524cbd56ec0c067db8155781a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmV-s0h#`ZP)LS*?IqfGGF)LJ@}O~P%4%Fc9ya{j^jmDRZnJi8e`n_ zegCS0eOIL{*L8hwX4k-_nY~QYbR}~Almv`1o*j(m%d$MHkx9T`k|T40AVzusL5TDM zf(Ype1Od_;2+l|a5FC*zAUGkFKyW~+fslz@0|;Np+Zbcg;{?_N0=%19Og&l)xD6rP z9doz_a2rCn8HVBGm}50C3kOe<~2JAv^~rZn!y&ccS1mq$8WHt_`_<^yMdbf(Pl=ToXRk51b6o zKN_lk{r}VLazbMPtGcQq8*_On_|?Zm39qz$ zH=cEBcf81vUdJ?R+0V3Rj0bF|XI-4yVRgIU^F)c{K8C)Np54zX|7G3&rv984=x*KQ Z`V-SCY9*@s7Xd?p!PC{xWt~$(695EChtU84 diff --git a/res/theme/feiyangqingyun/qss/flatgray/arrow_bottom.png b/res/theme/feiyangqingyun/qss/flatgray/arrow_bottom.png deleted file mode 100644 index 37307b75041d36df89dff1f3ef9f49e8d0722815..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%3=E9S zo-U3d5v^~pS$nY>O0+#R7v$^c?H0JQnYVhQq+T}mD5xl@NtjTauz96Jh2xuj#fLZNZ`*fx-+S|( z6OqL-4T;>=b6e8%3nbdi4gBAR?4Na@BA%bg!C~3@@4*v(yRDe7CK$PM_UY~mty?b` z^gTXaRQ~*6#BScSn_T7P)q9y#BsqL12JWzLyuc#a+s)(@nj)kp#SuE;K7^ zdg8zt=#2wMpaKU@Kot%gfJz*U1gdc`0+<5_iNIVqNC4J#y=`!j0|yDfw%uwCo)wtB z3J54VE`UNd6`0cP4uUidaysw731mL?#Cw^a94M7ae>+QAuIsz2D(q$$=e;p~ zY1=myY+seM9J)u~hRE1@7WHIuEpi>+OkBU%PX?D3?NyCT1paOaWEK$g$ZQ~JkuE^c zA>DwWLAnCLAL$MRU!(*Gen=S*e2`KgggUz;PnTAfN+|i$;0_J#btU(hC@Xiu diff --git a/res/theme/feiyangqingyun/qss/flatgray/branch_close.png b/res/theme/feiyangqingyun/qss/flatgray/branch_close.png deleted file mode 100644 index f5b6d34625ad9470f379c703553e6021608d3cbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%K*61! zE{-7)t#8j6avd^|IQsGX(X7J4()Z64;ugjoIJ>f${R3Cty~QF67t9vTU(o(RqN{uD ztR2U$_;QNg%RlX{P~Xte(eYw_r?t=Ilocjs3~X}NTNuBE-k6uW`Atr|#&3b+g$w3I zRP`}4s2)(!dH?xVYwZiyKyior(P6zkcU)_$uBq_+2|LX6^=Rxprt-|H)&~#tPpGL* z5qT%Iqq#1GJG0c!%ixra6J<(U>M#4r$GOU mb{oU7@B5FE9Ttn_uk#6&*j+33@dYjb0000%pStyx1_=4QR zO-qq3qJM(ZT?B^?&Mq!#AqI31^j~lgM3U=bK*Yhpf-Xg>z4tg&6S=KvJolXVg1_;c z=RWz6aL&UG{LeogvC+i${iC^DZhsud>#bJnvyGnLlL_eieqMR`72pPt1J+4cYBpD& zSkZlJYL3el?jT(Mq0a$?xpKAAj@s>4Hh{|Hp<1oxDKEc*=xe+57#A&#`FedWJI@e! zd}89PKz+dT){sU(1!Efa?NMe0X$DM^*TB0WN;4yk4oRoe*(lFcpNr-)P~4-uD2VaF zF+RmnyZyFQnSLtBMI0iHfdDT%oj0zLnSkIPnHdOfkye1<4!L6%hheC?-R=pkwGlp! znbpEwU~Gu;4_fr%{k65nLjmW5#Y=*|2OKfM8D=1|_bmlMa15jYoE#Z#I|G02-Xuws zngS+9O6zBP;w&tjPj48FI!76oL|;uW{z*VU9p16>I~YI77Yz^7i+$kM)_V;bBPI2d zXsvITPER${8@*E;MbXFdbmgFk%wp+f>KAagN@q@mVHhUq%``-UASlJCbEcoBL?*S? zHyQ4t9*Elwm?^%$G^xDg5$LK799RUpz=5?FcQ-h&0Cb6i%>OL7#X$z(C~#mnNiHle zA5vc2aE${)U~zM^49pBsnh6JPXK`j67y>^P3K8&PK#3I`xH%-&aA5zMv5EsL7qwp$ pFMma#Ou~a^bM+aZE!h5!d<9TqM+;NWIKBV?002ovPDHLkV1gUqAq@Zk diff --git a/res/theme/feiyangqingyun/qss/flatgray/calendar_prevmonth.png b/res/theme/feiyangqingyun/qss/flatgray/calendar_prevmonth.png deleted file mode 100644 index 8a17d0f4e4fe94e90b2f2078c92ded3f8604dc07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 740 zcmVnY+iKO~GDF?%rH- zMf`4e9MALN-WT21s&*Taqt)^d zH^A+Nfp4r*TIBPegVt~94`$3U6QJjfZ~#C+Tw(5!=@FBXVH-!pPrx|P{A>DB(PCwp zK2pG7IIOyGZq82S*JOGHo`b=0?yx8pKS(2Lu2{fdhO3qrw3`fKlRrCcuEW z4F+A;6fQ|Yh@50HxsuIhU0=X}xUk|Gfx0V6L^uHGQJ_8?86WSiz3>1{^V&rMKZ*di z6Yh;~Imz0S2k4S}1Hhqh7*^-XUhvI?8Dh50IJX0JwF&jU-;6W9I-oKvYP= znnv`s_UPI2TbAXd`upZdQ4;`$(1wFmgE$OK7GD(J)n5FyxnUT(64UPjn1H{H?FxW< z46MrPRJfmt#i9j(n*t?~C0kR6Ur$eemqqSP!&9Baa351$zHsX7nK$L7rOmbj_XE<` zxj0Is?}K|($Urv>Qb>o>CkF>!l@}Mc+76UL5{}0lcd+-w(9H7k!mptHqz>*v2-~gN zcfj-*{#KlGU0s_gnO@R23qlCnbt<<4Af?jM)kgs2$Bm7ICDWtj>$mMWd3ug0b`*pG z?46U0_m#MI`R&W6KO+G)L6&S?y)>K4{gAC4_D+Qi(>$+0$uO|MReMGV5z3OcZ}S^B WE<_{l;^y+Z$@MBe0IcD17sXpNC<1319%8Si6bQ6W2HZfqN4*J!@?nK&-Xx7;}QM;u<#{ z9gbSJ9Z@wmE1C;(7*U`wi89s82IRKJ$(uCs3{!eoZt)&fNpqt z2-<53mkCV6l|)vG%LICuH)*&6WaYR_U~J;mq6cS?wcs*=GU0VH6BHpQ$U$8=0P(k; zWt0ycdZK$l5cKEy!76c(TL)z=Ryitjl6B#ruuw0DtYTs;@g`XeS=BgO@?AV1vdUR| zj%J}8vg$>^oZU=5vW_jq0_|2hvaS`%g8kq$()YJF>e}z*6zXzOxDdEo6=*g~chq&_ z;AXS1e6Bn2w*mR&T9FkI3C*bGy#*b*#>_^ce?IvB-t>--hkp#a3YV@?|0DYKYW>b` z6h&i6K6|*o-@5(t*9(OQ&KBybBQg5!d@u-;ndmQ$Rxdid=;6bnzWS&r<)JBY$s z_y;WQL{J+G8wCrS11+!A#$*VVLJ$tD#4?3od54E=l#M!Lp#c-KJDX%TYjJNoJK2Jjp>R3+}Oc12_}48yl#j9CL-0Y|D< zP1Lv6t}EaX{~r$Elv3(}0?t+~$tX}jt!hO^$5hn&_SSE0UyBkH(5I}3)12)J&E(`b%Zj=wX3}7m5l-9Z@A`hpHl@0Xb ze3c2zjWfoqIp^+78_NUyAR;$`*MKbpN5*Ew2ka$Daxo0UtvHT1fUO+J!f-xdFG-Rs ztyXIvAc~@49LKjj$U<>hz%kq?53+Dv8t@x%e-h_GmV!$It~%!qXHA+w{wNEV2CRw5 z({8tWJ}0tS77jp+F)n57lTzwhqtV!z!*{t9rs2uNB7Hwz2)T>)RJR%G;10qv?4X*uV%m9_RB(61UZ zrQUEjd?EmyPUnn>yaY~Hj2iW(Uawybf?!VpG@H%$wOZ|*h&%(53Ss-e?KqAbK@jW# Z`~s1oc)XT$aOVI3002ovPDHLkV1iM65RCu; diff --git a/res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial.png b/res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial.png deleted file mode 100644 index 97376f3abaa68fd367b45cd948f157e36026d4c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%3=E9y zo-U3d5v^};Ec9|N6gmF!z2)pXD-Ovy9Bz7VC?M~^UT>fyHSH+7LBtzvqp-ZY%o6XS zC3Kn!B4=1!C@Q?39O#%R;rB!Oocg~@=Kov#4?ld_xH6Xe%ZU>Y7CKjlO?xWjz~~nw zwZQDOEW5&n?|D}KnqL`JmR`xfDCuTie#d`XR@d6cdD&H*|8@Bs%DR@>h9}sUpGq^k zaEG(L(_uZwmo5Ut@^IFjXJa>Zc1*b?GFN*;=(`#BdESYCecXF)`Q@v#jf^6m_5D&S z`+n)RY-s4#Y3tu+*ywGWKTn^BZ?o*HwB8lIVx_TRpbDwB5ra`q&A zXPzjkekW4IrKC-G-NQfg9;nx3%u0(6eSSbovh&1Wi@x^K*m4FYfn*ydy~d?;cIwY_ zN!h<@m361#JBI5Air!r{kM+t7Z#4cF&zImE>MwfZjO|PT5se~!wKW;<4#wMLJayFO zkE*x{E${H}pHohMj7%rgAOBlEp>zqnHb!yaZuU-K{TUmsBe P1_XnrtDnm{r-UW|6gYnM^t-5em${H(@=}?G11zU@z6>@#`@Q$9 zo6h?UFQ%_I{``)om7?}SC9&TtqJ7tfI|{7Y`EkvzZ(6>V z4NeOu%(kg1iFEP4>y!BV^|x!R_rEhUUkEFFG`VM1cH{}hAN%C5?EO^RGBr=FAf+g4 z6~ozWpL_LP`SQPFqL~#M&dcpouDo2Ko5*mne|P2e)sr?q zpFWK*MR)syn0d}Bx8?*q^;KD+RwEdywew@x2LRk|(HCE}K=E?elsafWeo?ao(+cYQBmeQ|fuyC>i8$mFwzHa5=<^^lvPo?@r# zC^>K4^}Svtmqq73dcJzKec2~V_KW?QoPW7PHLtIFdi!I}3nj6b@@p%0t}Ad#$X^w9 q$Unr~<+i`g`QKAs?bca*BLC8&#k<;6*_ z(+PntIB_fb$FEjGNN$|GxO~_US1ya zj=2?n*3b1ACw4!sxNzp&5^fdUDW{)K-+LhSUgo)-?FZUZp3Etp&KR>YWEF$^rS`qw z*RQ_%DowwU;c3y&)id|{zdxJ9QI^;7KWb~#-6>nPRqso0s9(+SY%Ok-d!FH}PujKZ z)$jIv-{0{5HuLkoZz0pJye#4KvYIFPm#rW`YM~A= ziqg{jJ&znn^z4sb8+LwQip+8GM)m{yQocl9Qc_tC6kC1Y(p)*yWOE*Ch1yHj4Oe+* ze6Qo!q4d(`f3?2t&~WqS&E!e9Ci#^ye_lWH=H#nauXZYMm?>=To_+S2 z`}@BdA70tt+@^B(>?4KGtd|cNlqf4Ji|_evt~!^sZ5PjPrjj|u-?O&fx~;c=b;kRz Z?54?k9A`+BoB}2+22WQ%mvv4FO#opgI12y( diff --git a/res/theme/feiyangqingyun/qss/flatgray/menu_checked.png b/res/theme/feiyangqingyun/qss/flatgray/menu_checked.png deleted file mode 100644 index 6a1c7294bc0651f85199c5d7b34e6ab718675c38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$9MP){-P@(K~F!$PlGMLWUv5E~0Y2uC7k z?+XY%fiGbpvToR0Y))(hZ3KH+PY~U+(L(SIWM^z#-E|W)$xb31`86TKKba39nfwRJ zlqpjt*T`p0?5(a=T*bDAuNt-aI}8t^>fEAX0A3KxiIxLk3K$cdmFb!3+rQ0b^L=gM zz!%)Uz_+g9FW@XiAqxe@;5vy!7WNJ%;JWjOl7)J`dJfMjk}EJhPFl*9V$GzRe1Zfl32aLy=A#LM&fWGffwQ)`a#*%Ith0Fs7U0<;h zsU+h7^kiE*M?nz0u2p&Apz8^G-rBh1wvQ3Mn@-}2T6}`hyl_200MGa59h^VFsM%yZ zz8nqf)8YDnkO^rzRcai>tb_KE*>Lc|pTr(AI}Q@st-+Cl;lQ9D%#Iu!2Zjx6dZZm3 zr0A&4k@j$qvgJ=k+QmVJJxM&$J`OT&n);FX;2`6+^YY^2t};7wC|VBzr>^Mr`rcm4 gvaL*+GXF)M0T62ge$3|rApigX07*qoM6N<$f*dLJ+5i9m diff --git a/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked.png b/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked.png deleted file mode 100644 index 513a41e342995511d72dc2992c375750b915aa2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1513 zcmV zm@#Vp6^!d3{HT#UABjYM`Nsggfk2JO@J0}=1VbyR5p$XZb!0}gPvh~n+kXq7&*yuJ zN!tscrWgknG0q9f?21OCLxohI6j0Z7t+J~69cH{XEwJ%kO@cgdG!i*7c~u9k+pwX` zoi+~xsD5N{`C?ocxZSrUkw}*9xT_L$XfWA(+AadYU~uMx5xGVPybh*HC&*N?Z@sD?C19gJ z5Fwz}rn4auyek$CpL2k>$m*OTbX~ks+pyx8CNs|fRH3M{a7q2r>)n?wT^om;{}KoUJR=gv)cK8%$D-j6 ziUpe}UDvg_p6Y!7o>%x79!TC=lRwlV-~6YEO=>vmd=zj1kVquVfn?uy2CgXllvP$$ z`SVM1fPp~3LjtREl&iqP5%*nz2{w}Bd7KuczHTeT^0R9Q4(m7RCI)F|fFM^Uux56Qr zO7?x_1m`o1;FEWLO9;3=f~4J$v|F$^G`DhI!s`zxM&zNSnLenBBC+emv1b7QUaz;5 zK=qa$m^z)J(ByA^k6CO}&RZh40QNNcnp^DJ$K&yPU`km1%BRNxYTAs2st#PFXgsQH zbZDKO{UIm4<@c|*e*72<_kpna#t5l7X$(ud4j)BdS!Hr2m+D1_$c~M#Z zAmhmax`-Ai>fEy%UT-K=L4c=FKoFj_GxhiP-&B=n$efb{w9%lQp(LGtqF^`xP6u13 zISx1hGF!I6e?tNQBy-AQQ4c$SYz_l(uTUopR?ag3FbqSvK!p@$4p1FfQ1`@u`7i?@l{wcF(0%3e)+qhlh)Vs8dmKaZ^+C zb9N3+w6>;WN88_oq!2R_45S!H0sMt@!5@o;w>z!UIiBiQ0F+uji*aV`Ik)_!;FChq z#Ely)Xd8gSEi)w2eY+5g87(+gZV|{l5uzU zrM|lQx&;7PZV!k^U$V6RPIuSE%hQEp=8Z}tp)DG2|Hc*&0BTmO_(98LUIpm!2_`|n z%DVcx1@m?N%+<4JGsS@U{r>VLOPB2fuvTRs{q@{=?KoFlsXppV{uA%xMz* z@$a)|e{T1~J*HqVICCV!Z!@sYeoyA%w7bNsI=S24EHoO%DuUQE*$oy0mx*v5#6OQW z&?10e0Mk6D_8^!p$n}v(M49v*?`E0;f#s0cU+^_s(c)nj@n$|thos*ComT$*UsKgzU+6ksTN`b& P00000NkvXXu0mjfzunN5 diff --git a/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked_disable.png b/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked_disable.png deleted file mode 100644 index 8d16af56015b8e8c3ba8c2c715a74540bebb346c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1628 zcmV-i2BZ0jP)TcV$Bzh!aI&h;sd+_zH?BzJJ&h2}q4udvE5jsBF+q5a$%ZZA@?*x=rhvb|&jR ze?K%0?d`c~`XczjKjcHsbDrn<-RGWr-hltOWU7wYY}T4Rd-la5as$aLMPxC6Q!3`) zN$v*Fqm=6I>FN2|wr%~d1Co?Np|DtM{Um^!07UC_?FX<$L|#axQV0GOK-YCIBl#Z5 zD*&j;BpD?Qpg?j%YisK-GXj{+W@C+wjW3dX5I|^(V15k(c&4|v_oazmeNDi8KEEIo z3grPTo*}R?BH39km+x$EZ|@806A0)y&LWbX04|syj|boofNx122LKV758!eD^D)72 z5RsjcNaUvG=H_E{18&{Ab)iyfTMal3V7G`o+uz@xTeD_OIUvqlrPNxIkBi9pH8^*a zN~JbaCC35ga=FHEINT{B%L9TPAh{u(P8URkHIi0k+qP`jFf*Ffg#r z|FGlAZ&}tx5m{!4p+zJci^VQ&ZEbbyg{uJSbUOXM=XsX^*kS4;BCF%^_-cRq7{JO@ zve5vp0ld}U-+xykkto&+=2s?@IT(w@k^nw2n4kZ*pzFGqX|1;#0QYIFZ)|I8+do~n(Q;k4No(x_ znCmm`RZ6W$CXE|ym*i%Wo0U>K;_>)z zj^o@z(m0ig2uZ#H;IPl6MC6tcKLr4#Qt4~}mm1=1%VaV~O$oButku-i^g4j&05ll7 z%pv)n>$*<{L8elv{Q$l)m@X<53UjM~TI&S>8hxEkh=^+n3;^+Xd@X?WsJ&8zNNy?= z3OAUHl`3vGn9lM%@2n~y$&yLD*L|2F#h!(29O{5Q#E>? zw~ORX1HdxNvK|duPp6`6Hfx2$;gf$Qw-JXb{C1ke%yXFPfutI^ffwG6`p)xRAdQJ;uB zKWo;k*C#qe<@5P@;c(a{c^}OCMxe&AqSnpAZ6x2V5flJCl6yqta}oJ&v_WgVL_{(e zo+S7bk;!CC%j+0TYRbsx^YcQX(9;0! z#rQ4KL^>iOuhjElO&M@Rwr#7WOP5|l@(uvY0i2KVAJbm|_KC=606KbldiG59p89`H aNB;nCuyXHkR2mWh0000DIO}r0oIPN^FfLCS;6F zgeZoC3*BKCX-zqZ@uC+JER24YJqv82>ex=_&6 z2$8ij@8_Z0vb*zU|Fpa6!S8AI_wRl7&Ad1Be!zcRw9-Uf*EPT2U++M)fvFLMO#s#= z+`oZw6NDc%lEbEH{_>9j2122F;cz#Iwt}JMl*Ak-L4(eM_Jw8j-YE)TM@PpqOxh6u z_4y3o663NU=TIaPxt~jTRzO|XwW{ix_nGnLs=!i5wF&am)24YUduRsj+_$f+ByRTr zs3{m+vKUwH-kCWNjYe&+5Kq8hFjy`jA^??T9|v<_j05-+0Enmzkc|MX&mni>kMgHF zzd3d+o-^R$%q>`1;BH9J$$PO|7rbU591gF0v>>+&fw#a^l?ie#HnUe%M+vwq7%~ZH z@F;el1Romc@4uV@-lafC=rsX82%-{o1Pu*DP903Q`dn}wd%B3|Rdo^mZZ3BF?Q?^J zGx@*)!1%S1iH5e;Gn#as2dF|3vT<`u)6KEb(a99-$iEv$xs!N4UgGwKwNReD%G580Uk*Ffj*;xT30By)>RdOsgJzi8g02;{= zrTvlP2NHljqB=#)gJRy^CC$#xPOIz=*_A-rKA5I^MIlO4wn^{?WOls<{|yNMkj!%~ zkNUP3T!vvN7pRcJ1`>dRq1=cK!%%+F6jC;z1{87{NB}x(*NiEXS+o@oZBfMZg4+n& zG|fpM0ciE~+yk>~Q4mz`=E7<5olETs*YMKC<3&O=qzK%!d-t9fi@*i|sPxyo4xlvM z&Wwvo*N@-Dg3qgpu%P#f0jKM_M&JW32f=1LKIj6p`uoQj?Ap}{Xy`D)`wI=Y!C!k= zb>?A-^Oj{jZ~+1UNt{#_W9IH%hM{^^R;0y0%5`KVSTGnY-`uq2D1aR*f8yx*(bUwGOl4NcFpLHV;!Drw5ObUafA;(O^W+*Q{C7UnS3UW6YDP`i6+islmCYSS+^t zRk8vwo6R-_gTW3FSz0C8QB_@^OeVWTL~Bf1Ryv(F&CShgfe(P|{Cre(+r^6)*LnYJ zMftU3g-tl38rK4nn z54Z(B>+9=#E*6UwMhoUuDwR4BjYi|Z_dezbuqm6(-rx(Us$#8u1(@NDRMl?_g~F@V z*4kJUi^U4XV(~@%D>*M54nIG1pu7eh$608t-Q@##*jl@yy}kX&MB#?YahzsrtpiN= zm<}0Zmc`@oQ!u=OD}3NA@a|;80kpQZ?h}!(eN1y**DY^Q1F(Dd?kTF;>h)CB1Fq|C zpKLe)5m8nB6gcHE84-D0RizAAEEaD8=KJK?nM$S3)B_M8kw_c?e(^Eg+11rGy$oor zoeea3oGy!qQ!h9uS8&1% zY#|T`T&^b^fa|&^fs3BusEAw-wJ-9sSFT+7r*32=MIw;_uAUaYss$Jbg+k+Q2_{Q} zfX97oHwS~k#(Dz`3=H^fPzww|3D6jGmdY|u>+kQst=@pDIvem7qM)kRZW3$lA>f?H zsnHnITyH>QObeAut=AZHtPB_k1Wo`aeBvL=<#G-6f^!__CRKgdJFcn+B9X}HGGII& z{~P$(C&yxA%p>&zwAQu*bG_ptvMUygU0Z`C{lL|3Yp5c?o7rsEyND;O(tF?QR6gEQ zKyL6y+333NK2_c8O(Y^q!r}01gO~SYD3i&AgTdeyV4jaTU%I1*1Ex}`OTb4!(Gy@) zbz?rCe|j?DR8^Xqnl_4vU%oTOn2(3H6T`~e(9qxj-}wYafY0;!{7Vz5WICNTj^k`r z)lJ^HOWNAn+PZ(}K*eMuw44_BCKL*7XlZFVKNh%-j*eSJWGm2G1!R9_@H`4qi2Mj_@9yqC gINtBn|7#-p7ZZ^7mcSw#Pyhe`07*qoM6N<$f@6P{@&Et; diff --git a/res/theme/feiyangqingyun/qss/lightblue.css b/res/theme/feiyangqingyun/qss/lightblue.css deleted file mode 100644 index 590189b..0000000 --- a/res/theme/feiyangqingyun/qss/lightblue.css +++ /dev/null @@ -1,679 +0,0 @@ -QPalette{background:#EAF7FF;}*{outline:0px;color:#386487;} - -QGraphicsView{ -border:1px solid #C0DCF2; -qproperty-backgroundBrush:#EAF7FF; -} - -QWidget[form="true"],QLabel[frameShape="1"],QGraphicsView{ -border:1px solid #C0DCF2; -border-radius:0px; -} - -QWidget[form="bottom"]{ -background:#DEF0FE; -} - -QWidget[form="bottom"] .QFrame{ -border:1px solid #386487; -} - -QWidget[form="bottom"] QLabel,QWidget[form="title"] QLabel{ -border-radius:0px; -color:#386487; -background:none; -border-style:none; -} - -QWidget[form="title"],QWidget[nav="left"],QWidget[nav="top"] QAbstractButton{ -border-style:none; -border-radius:0px; -padding:5px; -color:#386487; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -QWidget[nav="top"] QAbstractButton:hover,QWidget[nav="top"] QAbstractButton:pressed,QWidget[nav="top"] QAbstractButton:checked{ -border-style:solid; -border-width:0px 0px 2px 0px; -padding:4px 4px 2px 4px; -border-color:#386488; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F2F9FF,stop:1 #DAEFFF); -} - -QWidget[nav="left"] QAbstractButton{ -border-radius:0px; -color:#386487; -background:none; -border-style:none; -} - -QWidget[nav="left"] QAbstractButton:hover{ -color:#FFFFFF; -background-color:#386488; -} - -QWidget[nav="left"] QAbstractButton:checked,QWidget[nav="left"] QAbstractButton:pressed{ -color:#386487; -border-style:solid; -border-width:0px 0px 0px 2px; -padding:4px 4px 4px 2px; -border-color:#386488; -background-color:#EAF7FF; -} - -QWidget[video="true"] QLabel{ -color:#386487; -border:1px solid #C0DCF2; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -QWidget[video="true"] QLabel:focus{ -border:1px solid #386488; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F2F9FF,stop:1 #DAEFFF); -} - -QLineEdit:read-only{ -background-color:#DEF0FE; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -border:1px solid #C0DCF2; -border-radius:3px; -padding:2px; -background:none; -selection-background-color:#386488; -selection-color:#FFFFFF; -} - -QLineEdit:focus,QTextEdit:focus,QPlainTextEdit:focus,QSpinBox:focus,QDoubleSpinBox:focus,QComboBox:focus,QDateEdit:focus,QTimeEdit:focus,QDateTimeEdit:focus,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QSpinBox:hover,QDoubleSpinBox:hover,QComboBox:hover,QDateEdit:hover,QTimeEdit:hover,QDateTimeEdit:hover{ -border:1px solid #C0DCF2; -} - -QLineEdit[echoMode="2"]{ -lineedit-password-character:9679; -} - -.QFrame{ -border:1px solid #C0DCF2; -border-radius:3px; -} - -.QGroupBox{ -border:1px solid #C0DCF2; -border-radius:5px; -margin-top:3ex; -} - -.QGroupBox::title{ -subcontrol-origin:margin; -position:relative; -left:10px; -} - -.QPushButton,.QToolButton{ -border-style:none; -border:1px solid #C0DCF2; -color:#386487; -padding:5px; -min-height:15px; -border-radius:5px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -.QPushButton:hover,.QToolButton:hover{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F2F9FF,stop:1 #DAEFFF); -} - -.QPushButton:pressed,.QToolButton:pressed{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -.QToolButton::menu-indicator{ -image:None; -} - -QToolButton#btnMenu,QPushButton#btnMenu_Min,QPushButton#btnMenu_Max,QPushButton#btnMenu_Close{ -border-radius:3px; -color:#386487; -padding:3px; -margin:0px; -background:none; -border-style:none; -} - -QToolButton#btnMenu:hover,QPushButton#btnMenu_Min:hover,QPushButton#btnMenu_Max:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(51,127,209,230); -} - -QPushButton#btnMenu_Close:hover{ -color:#FFFFFF; -margin:1px 1px 2px 1px; -background-color:rgba(238,0,0,128); -} - -QRadioButton::indicator{ -width:15px; -height:15px; -} - -QRadioButton::indicator::unchecked{ -image:url(:/qss/lightblue/radiobutton_unchecked.png); -} - -QRadioButton::indicator::unchecked:disabled{ -image:url(:/qss/lightblue/radiobutton_unchecked_disable.png); -} - -QRadioButton::indicator::checked{ -image:url(:/qss/lightblue/radiobutton_checked.png); -} - -QRadioButton::indicator::checked:disabled{ -image:url(:/qss/lightblue/radiobutton_checked_disable.png); -} - -QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -padding:0px 0px 0px 0px; -} - -QCheckBox::indicator,QGroupBox::indicator,QTreeView::indicator,QListView::indicator,QTableView::indicator{ -width:13px; -height:13px; -} - -QCheckBox::indicator:unchecked,QGroupBox::indicator:unchecked,QTreeView::indicator:unchecked,QListView::indicator:unchecked,QTableView::indicator:unchecked{ -image:url(:/qss/lightblue/checkbox_unchecked.png); -} - -QCheckBox::indicator:unchecked:disabled,QGroupBox::indicator:unchecked:disabled,QTreeView::indicator:unchecked:disabled,QListView::indicator:unchecked:disabled,QTableView::indicator:unchecked:disabled{ -image:url(:/qss/lightblue/checkbox_unchecked_disable.png); -} - -QCheckBox::indicator:checked,QGroupBox::indicator:checked,QTreeView::indicator:checked,QListView::indicator:checked,QTableView::indicator:checked{ -image:url(:/qss/lightblue/checkbox_checked.png); -} - -QCheckBox::indicator:checked:disabled,QGroupBox::indicator:checked:disabled,QTreeView::indicator:checked:disabled,QListView::indicator:checked:disabled,QTableView::indicator:checked:disabled{ -image:url(:/qss/lightblue/checkbox_checked_disable.png); -} - -QCheckBox::indicator:indeterminate,QGroupBox::indicator:indeterminate,QTreeView::indicator:indeterminate,QListView::indicator:indeterminate,QTableView::indicator:indeterminate{ -image:url(:/qss/lightblue/checkbox_parcial.png); -} - -QCheckBox::indicator:indeterminate:disabled,QGroupBox::indicator:indeterminate:disabled,QTreeView::indicator:indeterminate:disabled,QListView::indicator:indeterminate:disabled,QTableView::indicator:indeterminate:disabled{ -image:url(:/qss/lightblue/checkbox_parcial_disable.png); -} - -QTimeEdit::up-button,QDateEdit::up-button,QDateTimeEdit::up-button,QDoubleSpinBox::up-button,QSpinBox::up-button{ -image:url(:/qss/lightblue/add_top.png); -width:10px; -height:10px; -padding:2px 5px 0px 0px; -} - -QTimeEdit::down-button,QDateEdit::down-button,QDateTimeEdit::down-button,QDoubleSpinBox::down-button,QSpinBox::down-button{ -image:url(:/qss/lightblue/add_bottom.png); -width:10px; -height:10px; -padding:0px 5px 2px 0px; -} - -QTimeEdit::up-button:pressed,QDateEdit::up-button:pressed,QDateTimeEdit::up-button:pressed,QDoubleSpinBox::up-button:pressed,QSpinBox::up-button:pressed{ -top:-2px; -} - -QTimeEdit::down-button:pressed,QDateEdit::down-button:pressed,QDateTimeEdit::down-button:pressed,QDoubleSpinBox::down-button:pressed,QSpinBox::down-button:pressed,QSpinBox::down-button:pressed{ -bottom:-2px; -} - -QComboBox::down-arrow,QDateEdit[calendarPopup="true"]::down-arrow,QTimeEdit[calendarPopup="true"]::down-arrow,QDateTimeEdit[calendarPopup="true"]::down-arrow{ -image:url(:/qss/lightblue/add_bottom.png); -width:10px; -height:10px; -right:2px; -} - -QComboBox::drop-down,QDateEdit::drop-down,QTimeEdit::drop-down,QDateTimeEdit::drop-down{ -subcontrol-origin:padding; -subcontrol-position:top right; -width:15px; -border-left-width:0px; -border-left-style:solid; -border-top-right-radius:3px; -border-bottom-right-radius:3px; -border-left-color:#C0DCF2; -} - -QComboBox::drop-down:on{ -top:1px; -} - -QMenuBar::item{ -color:#386487; -background-color:#DEF0FE; -margin:0px; -padding:3px 10px; -} - -QMenu,QMenuBar,QMenu:disabled,QMenuBar:disabled{ -color:#386487; -background-color:#DEF0FE; -border:1px solid #C0DCF2; -margin:0px; -} - -QMenu::item{ -padding:3px 20px; -} - -QMenu::indicator{ -width:20px; -height:13px; -} - -QMenu::indicator::checked{ -image:url(:/qss/lightblue/menu_checked.png); -} - -QMenu::right-arrow{ -image:url(:/qss/lightblue/arrow_right.png); -width:13px; -height:13px; -padding:0px 3px 0px 0px; -} - -QMenu::item:selected,QMenuBar::item:selected{ -color:#386487; -border:0px solid #C0DCF2; -background:#F2F9FF; -} - -QMenu::separator{ -height:1px; -background:#C0DCF2; -} - -QProgressBar{ -min-height:10px; -background:#DEF0FE; -border-radius:5px; -text-align:center; -border:1px solid #DEF0FE; -} - -QProgressBar:chunk{ -border-radius:5px; -background-color:#C0DCF2; -} - -QSlider::groove:horizontal{ -height:8px; -border-radius:4px; -background:#DEF0FE; -} - -QSlider::add-page:horizontal{ -height:8px; -border-radius:4px; -background:#DEF0FE; -} - -QSlider::sub-page:horizontal{ -height:8px; -border-radius:4px; -background:#C0DCF2; -} - -QSlider::handle:horizontal{ -width:13px; -margin-top:-3px; -margin-bottom:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #EAF7FF,stop:0.8 #C0DCF2); -} - -QSlider::groove:vertical{ -width:8px; -border-radius:4px; -background:#DEF0FE; -} - -QSlider::add-page:vertical{ -width:8px; -border-radius:4px; -background:#C0DCF2; -} - -QSlider::sub-page:vertical{ -width:8px; -border-radius:4px; -background:#DEF0FE; -} - -QSlider::handle:vertical{ -height:14px; -margin-left:-3px; -margin-right:-3px; -border-radius:6px; -background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #EAF7FF,stop:0.8 #C0DCF2); -} - -QScrollBar:horizontal{ -background:#DEF0FE; -padding:0px; -border-radius:6px; -max-height:12px; -} - -QScrollBar::handle:horizontal{ -background:#C0DCF2; -min-width:50px; -border-radius:6px; -} - -QScrollBar::handle:horizontal:hover{ -background:#386488; -} - -QScrollBar::handle:horizontal:pressed{ -background:#386488; -} - -QScrollBar::add-page:horizontal{ -background:none; -} - -QScrollBar::sub-page:horizontal{ -background:none; -} - -QScrollBar::add-line:horizontal{ -background:none; -} - -QScrollBar::sub-line:horizontal{ -background:none; -} - -QScrollBar:vertical{ -background:#DEF0FE; -padding:0px; -border-radius:6px; -max-width:12px; -} - -QScrollBar::handle:vertical{ -background:#C0DCF2; -min-height:50px; -border-radius:6px; -} - -QScrollBar::handle:vertical:hover{ -background:#386488; -} - -QScrollBar::handle:vertical:pressed{ -background:#386488; -} - -QScrollBar::add-page:vertical{ -background:none; -} - -QScrollBar::sub-page:vertical{ -background:none; -} - -QScrollBar::add-line:vertical{ -background:none; -} - -QScrollBar::sub-line:vertical{ -background:none; -} - -QScrollArea{ -border:0px; -} - -QTreeView,QListView,QTableView,QTabWidget::pane{ -border:1px solid #C0DCF2; -selection-background-color:#F2F9FF; -selection-color:#386487; -alternate-background-color:#DAEFFF; -gridline-color:#C0DCF2; -} - -QTreeView::branch:closed:has-children{ -margin:4px; -border-image:url(:/qss/lightblue/branch_open.png); -} - -QTreeView::branch:open:has-children{ -margin:4px; -border-image:url(:/qss/lightblue/branch_close.png); -} - -QTreeView,QListView,QTableView,QSplitter::handle,QTreeView::branch{ -background:#EAF7FF; -} - -QTableView::item:selected,QListView::item:selected,QTreeView::item:selected{ -color:#386487; -background:#C0DEF6; -} - -QTableView::item:hover,QListView::item:hover,QTreeView::item:hover,QHeaderView,QHeaderView::section,QTableCornerButton:section{ -color:#386487; -background:#DAEFFF; -} - -QTableView::item,QListView::item,QTreeView::item{ -padding:1px; -margin:0px; -border:0px; -} - -QHeaderView::section,QTableCornerButton:section{ -padding:3px; -margin:0px; -border:1px solid #C0DCF2; -border-left-width:0px; -border-right-width:1px; -border-top-width:0px; -border-bottom-width:1px; -} - -QTabBar::tab{ -border:1px solid #C0DCF2; -color:#386487; -margin:0px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F2F9FF,stop:1 #DAEFFF); -} - -QTabBar::tab:selected{ -border-style:solid; -border-color:#386488; -background:#EAF7FF; -} - -QTabBar::tab:top,QTabBar::tab:bottom{ -padding:3px 8px 3px 8px; -} - -QTabBar::tab:left,QTabBar::tab:right{ -padding:8px 3px 8px 3px; -} - -QTabBar::tab:top:selected{ -border-width:2px 0px 0px 0px; -} - -QTabBar::tab:right:selected{ -border-width:0px 0px 0px 2px; -} - -QTabBar::tab:bottom:selected{ -border-width:0px 0px 2px 0px; -} - -QTabBar::tab:left:selected{ -border-width:0px 2px 0px 0px; -} - -QTabBar::tab:first:top:selected,QTabBar::tab:first:bottom:selected{ -border-left-width:1px; -border-left-color:#C0DCF2; -} - -QTabBar::tab:first:left:selected,QTabBar::tab:first:right:selected{ -border-top-width:1px; -border-top-color:#C0DCF2; -} - -QTabBar::tab:last:top:selected,QTabBar::tab:last:bottom:selected{ -border-right-width:1px; -border-right-color:#C0DCF2; -} - -QTabBar::tab:last:left:selected,QTabBar::tab:last:right:selected{ -border-bottom-width:1px; -border-bottom-color:#C0DCF2; -} - -QStatusBar::item{ -border:0px solid #DEF0FE; -border-radius:3px; -} - -QToolBox::tab,QGroupBox#gboxDevicePanel,QGroupBox#gboxDeviceTitle,QFrame#gboxDevicePanel,QFrame#gboxDeviceTitle{ -padding:3px; -border-radius:5px; -color:#386487; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -QToolTip{ -border:0px solid #386487; -padding:1px; -color:#386487; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -QToolBox::tab:selected{ -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #F2F9FF,stop:1 #DAEFFF); -} - -QPrintPreviewDialog QToolButton{ -border:0px solid #386487; -border-radius:0px; -margin:0px; -padding:3px; -background:none; -} - -QColorDialog QPushButton,QFileDialog QPushButton{ -min-width:80px; -} - -QToolButton#qt_calendar_prevmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/lightblue/calendar_prevmonth.png); -} - -QToolButton#qt_calendar_nextmonth{ -icon-size:0px; -min-width:20px; -image:url(:/qss/lightblue/calendar_nextmonth.png); -} - -QToolButton#qt_calendar_prevmonth,QToolButton#qt_calendar_nextmonth,QToolButton#qt_calendar_monthbutton,QToolButton#qt_calendar_yearbutton{ -border:0px solid #386487; -border-radius:3px; -margin:3px 3px 3px 3px; -padding:3px; -background:none; -} - -QToolButton#qt_calendar_prevmonth:hover,QToolButton#qt_calendar_nextmonth:hover,QToolButton#qt_calendar_monthbutton:hover,QToolButton#qt_calendar_yearbutton:hover,QToolButton#qt_calendar_prevmonth:pressed,QToolButton#qt_calendar_nextmonth:pressed,QToolButton#qt_calendar_monthbutton:pressed,QToolButton#qt_calendar_yearbutton:pressed{ -border:1px solid #C0DCF2; -} - -QCalendarWidget QSpinBox#qt_calendar_yearedit{ -margin:2px; -} - -QCalendarWidget QToolButton::menu-indicator{ -image:None; -} - -QCalendarWidget QTableView{ -border-width:0px; -} - -QCalendarWidget QWidget#qt_calendar_navigationbar{ -border:1px solid #C0DCF2; -border-width:1px 1px 0px 1px; -background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #DEF0FE,stop:1 #C0DEF6); -} - -QTableView[model="true"]::item{ -padding:0px; -margin:0px; -} - -QTableView QLineEdit,QTableView QComboBox,QTableView QSpinBox,QTableView QDoubleSpinBox,QTableView QDateEdit,QTableView QTimeEdit,QTableView QDateTimeEdit{ -border-width:0px; -border-radius:0px; -} - -QTableView QLineEdit:focus,QTableView QComboBox:focus,QTableView QSpinBox:focus,QTableView QDoubleSpinBox:focus,QTableView QDateEdit:focus,QTableView QTimeEdit:focus,QTableView QDateTimeEdit:focus{ -border-width:0px; -border-radius:0px; -} - -QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox,QDoubleSpinBox,QComboBox,QDateEdit,QTimeEdit,QDateTimeEdit{ -background:#EAF7FF; -} - -QTabWidget::pane:top{top:-1px;} -QTabWidget::pane:bottom{bottom:-1px;} -QTabWidget::pane:left{right:-1px;} -QTabWidget::pane:right{left:-1px;} - -QDialog,QDial,#QUIWidgetMain{ -background-color:#EAF7FF; -color:#386487; -} - -QDialogButtonBox>QPushButton{ -min-width:50px; -} - -QListView[noborder="true"],QTreeView[noborder="true"],QTabWidget[noborder="true"]::pane{ -border-width:0px; -} - -QToolBar>*,QStatusBar>*{ -margin:2px; -} - -*:disabled,QMenu::item:disabled,QTabBar:tab:disabled,QHeaderView::section:disabled{ -background:#EAF7FF; -border-color:#DEF0FE; -color:#C0DCF2; -} - -/*TextColor:#386487*/ -/*PanelColor:#EAF7FF*/ -/*BorderColor:#C0DCF2*/ -/*NormalColorStart:#DEF0FE*/ -/*NormalColorEnd:#C0DEF6*/ -/*DarkColorStart:#F2F9FF*/ -/*DarkColorEnd:#DAEFFF*/ -/*HighColor:#386488*/ \ No newline at end of file diff --git a/res/theme/feiyangqingyun/qss/lightblue/add_bottom.png b/res/theme/feiyangqingyun/qss/lightblue/add_bottom.png deleted file mode 100644 index 99eadb9d4be435c331abbb1e72e6849868be17ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 348 zcmV-i0i*tjP)$5BqXFo>|ij2edYaColnk;_~qq+Wm%SGS=NrkwV1EoCjxiCabd$s zTDfdS!{@GL)ir#0TK6lsr92McR4cu9Ejxi1%jR30p`oMLDf5$sc@B@_JcdC2U8JI*U?TO6hnI9zyk=`k={7)1VT1s z5Do%>kc;SVw!4l)4& u7B70Z{rWf;^tGvv9&_L<%d#wM5BdfXK749cAL0D~0000rWvbZ5${9EpVU&w8DV`&=Lndfz~+a0rbE@CeRB98NmDJW{V^j9yrJVu2t_J z0xuSrwsZ#W4~NST;~7vJczz^n4<76do~js+a6o3CVlQN`|DXr5_f2ArRJn^Sk!sb9 z6;ibxv_Ps?)Y?dso?~*b;c0fX@ah(9*UW~O zCwq@7*s4~}^1`bd-~#;2J>A_uzn_a-zhwb24*tA)DcMAg%me;Uj;sp=F){)OLSz&W zM94@W2$0c02u3P^5QtO(Aqc4iLI6??gqp}EfB?N^;>z8#J}1x|5OZc`=DBx`01c*I zrR#NW+eZKoExEZz$|Uid-N;M@7RKP=qMC04*h)fm_hf z(B=l*AV@)6zy-n`5)?Q^exxWM_^4K)@W#9AUD3?jjy3bRSU}S>P17_@13*$H)nfng zb+#LG?@o**Wyx$@&2PWJ_s_MwjjP!ONr|z$^OCY;_L0qRAaaGx53dEhJUJ{zBEo+I zT5zT)$Y5X#&eRzh1a!oiIw8G*o;Xu5(hC@fGX)`?fgN!sZ=@4263*m>Yz0QfnVgYZ zfr)S?C!`ga7-wolHXbG^b#Hl{7zY43A$Jee^4#`8bqP3uTpL%n-6Y^6FA z%t~Ry6I5HX=HSsbR<^_S?c=@?8T|ncU|?u>T%#3ue=&L!cqnK3Q47ZUdf)(pn5D&! zeGHeSRNuA0&q2L0ozE9m5!OoKX3cm4VuFEC6zPTg6$nL;-Z(EH zWJLzyyn&Df8H@`8LJ~3x7Yu|bWHc@c2tmjsTr?28kjc0tAao+L;F5u0MP|c6$9F5V udhcYJ*Y{09Uan8acR6sHrfHh?5BdV>AE&9- z32870-U5ec2pU0O0fHbc&>a+ptYByFnLsi}ZZb2UnAy2^ASWm1f5%cRQuI&vgK=iG z9yUG~$G2Cbm*0+UDPaJ`VB8C4*I+w=&|S9I59*AU0*k@87vK`;e18krW}P`?z$#o! zBtS152!NhAa0Ytgz!9jxffG=L0|%fI2MvL095eu?z(FK16%HbRMgM#&zzGR(5CME& zdtCs}0!w9T1Me?JZ>y3c2u}*o&qVFP*>pHxmA01x8GVXVAshV%Qy?4PBxwZRNJ&rPiIjD9UPx(QT!H*Bc&syr{AFP_9nP&??pk1iad@}Ayiu>2 dlauq8@&%GBmpnMGK-2&L002ovPDHLkV1gC@lsy0d diff --git a/res/theme/feiyangqingyun/qss/lightblue/arrow_right.png b/res/theme/feiyangqingyun/qss/lightblue/arrow_right.png deleted file mode 100644 index 604def67731f59c53bb0ca0eb531b3a676044ac0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364 zcmV-y0h9iTP);#Z z5F)*RAVPWqL4foIf-_P91V^L_2u?^P5FC(dAS5E20Kx`21dDo3pgAC5?+`5Zswv>S ztiG@JIcx$rFRRsH_B8s~=U5F)!hsf;i~}9e1qT|S8xH(|t~l@oy5qnPD8YdbP=!0dn!!;0000< KMNUMnLSTXnW0C6s diff --git a/res/theme/feiyangqingyun/qss/lightblue/arrow_top.png b/res/theme/feiyangqingyun/qss/lightblue/arrow_top.png deleted file mode 100644 index 3c0223187fbcf0917c5444e4a853595ba3a7ac64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmV-v0ha!WP)dTM&Ib@nU&2zW6d{DTX)+kZ_4dzPK$vg2v&?>)g99WtH9d9?w53a3>V@b6$EF_h}1W+#kz7*8+de&b6N{8+a2A<%e-{Vg%WDwARqgasM zz!V%6jPwFp;;0~`Gtd!7c_W>Go;b=2X$=g-QO-y!AQO&qLZ$+lag;UkA}}KyWrZ{X zGsaP=NW;TgC6#!PF%AG?4Lz+tjXI_aXCJ?Rd@TWJndZ#J(1)gJcowjl_urMq8-VYu zRDaq<2P{m(iyz6|!?28EvCMBe7A5)pdfa3Cf0QIilK#0L-yd+r>*i6c00000NkvXX Hu0mjf*SD6p diff --git a/res/theme/feiyangqingyun/qss/lightblue/branch_close.png b/res/theme/feiyangqingyun/qss/lightblue/branch_close.png deleted file mode 100644 index 73492b3e8fa95df8e649a7791daa5a73ec2e77d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%K*9Z< zE{-7)t#8j6avd^|IQnsV(2CeyRc!2qWtX`tSk1-x6}p@c9JS_9%Ij-l7r1g%*rJ-L zn@=~%j(xhxE>Wk}@3%h*osd^hQd0VreoFRMb7gQ(4ugqJnFL4K*6@4x*MB*?kK>KA z`)dKWw0V#1WV!b0^t9aq=`;CjZkHV;h?H zFKUX>ytym+1z*3(d^OJ!!Pa&2^%xw|6kdN$s$o%++ph|=QAz71?}IJc8AbvwwLq^h Nc)I$ztaD0e0suy!ViN!W diff --git a/res/theme/feiyangqingyun/qss/lightblue/branch_open.png b/res/theme/feiyangqingyun/qss/lightblue/branch_open.png deleted file mode 100644 index 9abd65c4f9fe78e0088ded3d1e50005cd913b0af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0WtoGP)f^JY6#kzg94Yf zAfyNq8gy%o3W>Jbf~H6a%zpg^O%1Iz_)4CmF&uOAyb6t@xpg)wo+gtyTQp8^>3&_jc9 z5vo_0uaA?3=7wVjXOc+^ns+^xYj_YAD^_r=wb4ZFB^+j8&^s>|gh_YF_h--uO7WK< z!*-V(g859M$Ux3JBL`I?K6e*e9+M@zx*drGpO`S)Es=pC0;a!NQUj0$@YW@&qQNJR zhnqLRmZKs1lf+h2OH4THxPb7^tmYq%68F8%@Yfk^gAtTs1f>{3DgRqg6PMArE!ski zazN3mWQItvWPe@(ybqw>rd?#>o!f(rXUPnM!SL7l0(CxMZa7OY82|tP07*qoM6N<$ Ef?(&ut^fc4 diff --git a/res/theme/feiyangqingyun/qss/lightblue/calendar_nextmonth.png b/res/theme/feiyangqingyun/qss/lightblue/calendar_nextmonth.png deleted file mode 100644 index 36a453bc0d77f120e646e5d0bdce44ec58981865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670 zcmV;P0%84$P) zwzdW2Xocsqf{g7xS?sc>t;PyyjMO7!e$(azX$6Fpm#Vyp(U=%%1)Lr|_f}L3z>-_1?p5$Oa7{*caq;0O65 zFbspX+05p&Nn71Afoi9UUIOY}Aio2)7m5ds1Xl&!V1u(vMPwP+);qK*W0vm&Emfm^ z%`cJVJ(xBhw;(iC*Hundkg+|5Vn?)Ky{J7Ti^%cl;aUQM&=@^hOTcAO#-oR2U@DMz zqP(xJsT?CPFqvDr7A=%_-K)oU7jmr+)&re5Uf?ls=O=SZAz^5u#Rx=YXV2i)K-ppY zHi2f)e&Q|ainyNulVxx3K=X>xTfjCKIB*E`fdfZCUpR08^ofInK;Jk>0IUWMEdMNY zoEU5{=nY_(PaIfYbt?gR6}HD{O@sr#cX47ISOU|A8Bv~AXmNrAOW?$nvx})fx`Agt zao~Q&#;5aDd5+$jyf%eUCuY1@5oNC+n~;2mk?owEzGB07*qoM6N<$ Ef{{%rzW@LL diff --git a/res/theme/feiyangqingyun/qss/lightblue/calendar_prevmonth.png b/res/theme/feiyangqingyun/qss/lightblue/calendar_prevmonth.png deleted file mode 100644 index cce673fe979082d1bc214b934535162d4e682673..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 758 zcmV6&p#fryI}bIO-V1;wxCj3dhu9ZYPFuN!0^!Mah=!$0uKVv zIN{oIsVe7FvLag3Wf!h5HhL07Ndf}DhZq47kIyXyL(%DCA+3qjWwzl!69D|H>k}~S z4|qoaE;L&$PjLg>%6uk{NOpYWt=0N1{et0GVgR5=Xp930KAl*XNDpCPIF=aTz!-py zO<(4CC#jV;B7KB_mT;QBN7<5}ypU$F%2G!C!;bOHyM1UiKS zOaPt40Zo7wao<2R$T?+1ASzR}Tz73AvaFi|TEy8E!wjtXKj;t+0J?NWw-29@gY^Le z@IWkagcI(d^SF9;erHlo{lNfa;6vcp+*#9#5QrGue>{d4XhTOqO|r%s3u!GFicWicZi#>}+HkOH5REvl&yK~P)d&9CeD^8Y zvnk-cLclQ0ZOi5f{HhMNAga>|FX!%EEC9rt0tJy7UE%$+5941%k*m}2R3|Xq+sQ?T zy7wG?`tkMiO51_!0cq-7>?G3k!QCmOrJDsoWTa~IOt`tOi zw0!+mETpwSC_3f!xwZnd55_e)<5R0Avg2~u5g^bInb8$~Y$kd6t7vVTcPg9?4kr3E o#!dssF~4Urd+UNNpS*ROpK_KsEA==<=>Px#07*qoM6N<$f}sgjAOHXW diff --git a/res/theme/feiyangqingyun/qss/lightblue/checkbox_checked.png b/res/theme/feiyangqingyun/qss/lightblue/checkbox_checked.png deleted file mode 100644 index 524d243aaecc59a6452ebba3455f303b12d3aa73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 636 zcmV-?0)zdDP)l8I|3s+I}F1b1!kGqH6el*_r4d$<+-+50D- zoa8*`Igju+rE+Uc2=+l9lWe~IGU->Jk6!-(5P;Iw?z*V#ChPhiMSV@BUJN=lPl}ax zL9T-L0_mh-<56Lly%=p66I|`wH5`B_IX|7P8&sYqjhT^kleEB=d*Gr;1gOFngj0n} z+q+BOy(*mayN!K-Vx?WcQsS1ng5x-VL8s0(MTce@pabTvY`RX;e1ER|bpuI4+T zg*s{!{VvFpxImLxxTAK7gZs(C;-x-odtS00zhP^W4Lg=W6t0l#!ULDC&#I+svx}vUjlU1N;WV W+didecXk&50000Pt7)xvD@y%a0W zIR%?QDr!|vRm*Xfm4X-ie>i}QE!HyyT#Q=cn&>43oQT?C=#+|D;MFm(PY#-0giVki zUOyf;&;eeiTZ#EXCb{aMmw|7l1>*!B;Pw-bb2(`NEM}7>2Uaj3#|8Wc*Ubw#4xlTp z+hR6Z0-pDK7BIR`xI_skElEXaLC-W~pF;5gH;Vgw45v}Iq-XA|q` zUlS{6z3u}U7|sY3C^?pWb73Y`2gv25m2M>;cpwACxdMA}-8_(ix5}h53&;$4nVdlP2K1FFG}e<*UnAuniiMKI$n!63e&C0tuSyVpl_s~MmhJG zwwW`IBX4&08kdKsjfMm7jd~mYXn?Q+}v!>?HPwk1Q+dw61)SrZ%cAHxZGpV`) y$Y&-#wxx3kcm?c7NUH-6@nrzMC4B`h^Bla-Jo{9AETi0O7!{+&K=D|mz z7G35J2GSaAiyIRjg#PGrG`~AX%CCa+c{!_pU5kJ3wFGYsKZk1`uRofmuz!Evex8Bp zLdN&mi!S-!{_1*h#nMafe!R(0`ug{=Z=}d|rpQ+t-iOS1mcMzfK$mO&%|{F_E05{C zXA3b4I{s&&WRUou8!V$Qgi^Xbo=1&2&Z&-hzKt~{#ExMDlo zR)@lkEbafBUpSkt-{OAQkB8y$>36}UTQ{wB%olyOa+gT2Qc+Y~fA!)-b*4Sc)dyTY UPPS2P1%?=dr>mdKI;Vst02P>%-~a#s diff --git a/res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial_disable.png b/res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial_disable.png deleted file mode 100644 index cdd377903dd668dfa2e2e1f347396cbfc43921d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9EWz97u_dHRv%3=E81 zo-U3d5v^~}8R|PHiZnd@KF3?vq&E3$ zT<6>+duQLd_gk{VWAe3vw)KZAx0P47%I`I~&vAiuT8GDk=ZPBr-){B3+1(gtX2I*2 z*RXR&as3`)i#wUu=RdJKYc87CnD%D1U}R9}?^OjSzno><;^nC)64702G_9qNY4b*V zwTLS>*#CcY)Ry1!2_IpiC2}G;-si(ImYvRA!uWF zML_gUA$Cf{i-p_Bn%2Ul_iMJL86w%fqVxfh?1#Wen*W$8!duPyM*!7Be zs+srumU(vADRu-yl6;G85i=* zfvHoSjrG-cX_^emwcsKl^}%PjNn$f+v)uymvH`*Qu-Ry(f+x=hhkhw}-fuvx9DU{G zgN1g^a;W&e+uhhPCs4B)!Ovq8(b<;o#|i*qS5*h!z`0a}900U@KMtD)pedszD1Tm9DFC40OA=_M z_7&-rg}~8tEQo>R$DMeSc4?ljob&e-Emyh_Obd~2j%fWl6(seGv;eq~>)kC<@pcTxZ_ivj5HkYIOf*G_mLG9}<$*Ik?Is=GM;vc@gC;4Q-8 zxZN*SiDOqsJD<`@Ra+UWD~^Y1G_-mQ3}bKJxidGvJ?*+DM^j=L|7W|ibUT9X%|2SxB;zKUB>&9pA<$AI) zUO4k5CvWTdGrt(bcO^)kIGTNFpKtzZh09O-#NG$8TA1yviMnJkflEx6(N^~Cy$LGz ze)qc1Z`-|L+Uw~rSj!j^_L$CJ_vYf2a(0E4>CDUboQyrOB)GZoF~hC?7wKwmV_A1c zN_GjKTPb{C@2ofqP#%eCY@Ky&3|&aiiO24#vV_Z**FU*6rKD zrZbIw5?^Sj*q2YIPsJ(3O8$sqFG%NGK8>eZJnFWs;qFUOsq(K`cQrnKdcUog(ZVxr zS^I&if_Dx4-{<>0UHwhPeR;8L+dBs9=8NI-^*l{?*Ixhpxpeis5F_X5GOQ@y4< zymoBor1;CK75iMb2t5juU|*;naqi5WA7@@!p4`u7#adrBGwF@A!JFA^R?&M-&Uim{ d%imwTuN<~q3SM^cEHG&?c)I$ztaD0e0su)rDTn|7 diff --git a/res/theme/feiyangqingyun/qss/lightblue/menu_checked.png b/res/theme/feiyangqingyun/qss/lightblue/menu_checked.png deleted file mode 100644 index bb52701c9794a4831b572ecc2b39e31f9f3a4600..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 554 zcmV+_0@eMAP)Mdad8m*5B$;Y zU0NUN{5*DYEvOGJ9lO*`(9IvMAc#|GwG`T+;OZdsx$98V#y&}N$#eGzlFt%yTyifi z$>n=crc9YKUL$uk@aTN4f|88@x{Q(P(dP1YQolO^jxD|#F{0lBy*EwQ2#ZJS)i?k4 zRh^|J;s8QmruWg*mv1M#XYu|W1*YJlBqBTZh%<2UTxu;-9~#T20D*s6E*wC&Ql>vT z3H`BtCnpXApN!E5(Yena9_nEB{l?25QA_GOII>iq3@~-zvxNHj;p>&}zHv&c znf=C#kMY(-;Nzv*9LN^%AiB7zW%{^Lz33z^5!j0RdlMU^OI!;u9cPDhjk^ULezH2G zjM)PoX5BOi=>rF`uQ=4El8pn90Tq~?;f4C2waOO`Vlz1LbhYoNHoL%}?I2Ap)1d@C zzHqmI0OJd_F&)S|pvP`7E^m&O{qx~kfKUi&nfy3NSqI%Ci{aoue-d}d;yB1Cx7tUx zhXadxusE`P99R~t`H^mLkfWluN4mp7&XPYH=@thC_9W>@_c$oHX=+9K!9g3ly3zSs sMU*@f!JnC_Oqu(VUwqRt6^xAucmMzZ07*qoM6N<$f|LUQX8-^I diff --git a/res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked.png b/res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked.png deleted file mode 100644 index cd6561d2a1540cc34d244ffe6304e0c0f3a23bb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1516 zcmV@VAG;_hFG6WS$qfig0YieJ30(@Pi6&yy1c^M_z&tbw` zFp5Xq{z2fMEyDLM%X2Q?vHq8T4X~;qGK&R;z<8DbcRphnY=FeERFd91d!*^Ndjc4E zCGrST6d}f}LIUK9fwM{B-tukx`rG-~j|mvmb$6n#_zhxyX(C{mV!#wcZSm;F55}@} z1PrdyeTGLn42FNQ;IhR%Uca$6rt7B4LJ3$A-ZRz1=10WvjTr`flZY;W`3?Xuk&hU2 z06drnpx^lRmbMVV8bmBFNy2rG(j=VZVEKup(7Z+T)pz?XCTh~XRRD}Fh% zEe%)`-dhIH!rVbj==Yk5*2#d&gmhVWlL6bJu$b$Ex}Hu_`XR+bwX*DyW$lk0(fgeO zE_!{R_piZ$`Ibxg06-vhqo_Q2-l@KwKGbqG5ZZYZjI4|iP@3rT1_5*q`x+6$3g$Xt z@D1;bp)Z`^sza^I|Ge{ejD^@ojIG4j%Iu0JdcCo#hKOxOGNpME?XoNq(+i`|0sw+* z^inX*v%BC z5LrkTFIDwtM0>Teaku2`B-b5Ci7vLoHX+fTs>`y2S*^qOkAHql)Re=_@X&1Gqbe^i+mBep z=SH71S63&Hy)OcoTiMte=sviyOBP_{n7+EYVf$k&Tq~J*E&ved6qrBh>S(^Af&&2K zdeLGcluF(#ZUbatouHJ+1%!aL4uBMJRPm0@=K*YRvdPu(-=bJcoXZAND>9DVjX$F9 z1Dh_$R~yZ)Zj6Kr37(IPUTvHFpNCJx+VpgV$f|=Di>S;QgF>?7_;@w)!Zv>lm>!|E zXSQV(|@4mV;Q8)&L6x~PS(G6c`HrTcxEtz;H;n4!Q z=_kMq5}N~|ofQvgQ{Gnn$Es8j-ZRx>nOhlnN^ZfIHNRn-LFsR2oqVhRr(|JYsthi*~wjbvk(YHYQeN^tQ#x_E)n4z13%?B zo&&~n0LsT0Z?a3Q=swUSE&7gkGlB4~`LL}0`ChXXDkk)^5NlL$fWv790J_?ndKk0} zgx*P~Bfh956U&_7fIRO9gSze>>h;!Jpt^}>lfi~yio`pbkLP1w&<*R|58hhhaL1yE z#DfIUk!H(;i!L))KVb}eM$1z=3K0TT~0({%wp#ez?sY2P%V`uV@ctA79ib2vt4 SDaC;R0000=tURloyR5l-*_8 z0*OF15|W}uF%qN+0a9a=hKTwFgQAIsNPIy_T8QZvR8lBdcXkV1qmU3wH=(jK+tSje z-EBLanQdox=DEiYGo1`G_nDn;m&6bLzs)`Oob$Wq-sipy{Etf(>Nq~GttVS%K5L9! zugK>?*8tt+uqQ$HtH?i9G!c)5-xU%2p#!R_BqnFp7`2b0Yy{exbj_l=Lqqnb@mTMn z4+U^ImAl5X`i!F20p?@<%+EyS0l-@zSZ&&Kpg$h! z4?j8I)HeitJ$ZV0Q9~~RYZeErF8MC&`QM1gdeaU1xHX*0tTdKgK>s{>e9&VcZy{s= zL?x=|wLp~lm%~`Gz{U-$BBz=LeC?O^N5UkJ5746Jp8u%J72Hh*<|MfdJz;Q&O$4)sU=;QOJ^3G8!^g1e{N zGk4c>S7|tvyT(|%2UuDMr}~@dT;!3U>1{mKcz!Y90OG4J%h`~BUqB^ShOGT&bwdR(F*&maVWYz->G}4Divd@GjP<6) z=oa9N!+ihVLo=Va@FP?qwf0tAQwvWFT-B3mlyK~bcI5M!K`Cu37?IS{+3=f<1~m|i zyfK=}{t;zMjj2xyb^zE3Y^xbtIrETNmSL0Ok}ps&E}?!$ZiqYE+X3% z_D!{APmE4vzNM50eB=xNI&7`c9#(^R&xW@QSVv)FN7}0E%#m`Z9O>A^+2^po3S2r+_~<$!AsM zxp+*R%cZ=7)D|MAs>iDU%>?zwPL~z^Vo+A~{Ko@tCe;$2kLJVA1;!D*g}S9CX|E-8 zxB}SH>c0=XU+ZjCzVha=W3Ek@c&s;FDDY+A<$&C!>sda(Iq1kMpSd^Jk4fM5Cp)Jq zfHz*zeOgq8>qZARWY2f43jkols>rFK{_yP>20?cMhkzvTJ8R7KL;aCW@zs~*g8XgK zzXRy2YZu)Uk4v!xTh{Qq*&!pmW+z{(oaabNAg z`YU^qP&N0FCD}K?jyYnj^;_Dv1f8b~(fGKwhSWakfCJ^_bt}3nx2Fo=7nUtMry{=u zW@}uGs&3nx%6x4x;8azn+jHAcT=Ax@XMR=vocQ3^^Imwj!k-+DZK~$CqZ8ThUP>iP zkEh&Ys``Vv+@&5*$74(OSGUw9o9D@C6}A_p`0y?LA31w5aDPvo`IKk9r-9pCa6wN` zXElZ=GHVUI2=oTzORMk%`TSFjzEXh1!OT)Z`Xf>E?SOkDK+?1JR@covaJoj4XYWDH z?;8XK@KFwk@Heaa?+*kvay`nOl(vyZXZ{%rcdlCzac=qz*VAb7?3Yw!2f>xRR2?-& z?;7mu{#Q_khX0!k^>@EyRBi&s7n@9ApYMlmXara8H^0V?Xh&f#w^fy`^Gta&N{jHl z!M^ab&HU?nQCmjhU}mX!@)+p7)L)V2)07IoXy(IOFyIBLsu@XTZ#1f3S7ZPr=BVsh rkfaEI6_FPPVoMGz^ndFA>r(Vzu8V{&QEuFw00000NkvXXu0mjf4Xif@ diff --git a/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked.png b/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked.png deleted file mode 100644 index 8f0b4fc9e3d974e8c133507dc8cc51a8c98cc789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1298 zcmV+t1?~EYP)4txAOlS+MiYUE!3eDyK+^?; zd7U@!bHNm*@7*^uZD))NztzjR=brnU^X8s+?*abfl8H_@1L^Lz-~Khgg=%A-R}v5a zP9N&H48c*~dO&?v8Dn_O|x?em(1n6`M$yVHO?prZ4TZf2m9g?a3ad+EVPX&#zW_N z24hVvvjw{Dfta%%f9|ir@4vn{SvW!x0mDbqU2l9-Xz(3Sjcn$9GW?#9IQ@UCzdu zFkqn;C3ZYUhL%vc0K{}3u<#ZsR~2}QiYL!>Zk<&9{9hB*KPsZqJCh#AfdBvi07*qo IM6N<$f{No|vH$=8 diff --git a/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked_disable.png b/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked_disable.png deleted file mode 100644 index 57c0eb3d1afd574191d8ebe5020d6ba1d6357cb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1385 zcmV-v1(y1WP)M6cZz`1a#{_U_>^c>pB=~ z@BRCsTP=N_c4KQ3KluOFd(J)Q*K_Z4?{gmDKQ0;T#I`n;o-66UT`9AWkb3~u0q_ll zJqPeGMSi9*913{;6cJN&K#~&f?5|TaFM(JAplp;=`#^4|%e)Z^_)ioCusIT+Z5e({ z@Gb!AT9RbQ0NA6IUb8UhKUomK)>ACMG8lVPD9-_~#t4?3_7cF?8mc^7Mq2v3fCoA* zOi$9a6F^;Iz--BP8B0GN3izXWe4N!BiPb2BT>z>^$fE&v0rDFJF#r%sCBZuZRAS`q z(3(tO#iE(s^P>j*@sDUwE44QdoPqE#jC#Ftu;T05S{ZbVb294JN$X*NFM;z^qWUZ}({706;`cLzVXvtzCBraM1Z*U{#Mhw#v?(q2@?@ zwlby(z+@X7`9bBN_k~=`Ia-C=&Q+*V_cjVvI+%|qm1Fb063F9Sy3MRwX17FvEYX8+y&X=0v^$w6S~uYb8qoo5K42l zBY`=6mn#~J1!$6b8GxW|T-X!}$-jFL05tGbs@u9sWkPH?aI8Cf6OUU>k@yV&*28|1 zoHd3!hkvAv-idvO0MqIaYo0D~uN%I+3!%0)mbz%R0I0DUiSHV#ed)KSG~m5cr(UAS zI{>a^O;IE_wnSo&76Oi>^tj_2K{&AqbI-Y4`#O3b z0o6x&f&zdBaa07~8}j$70xP*2#B!vLk$iXlMFT8vtoAy#qj~q!{*K-UNZF3ul^m~5 zE5()bgT7yKIpqCr(op65R#EN;&{k+N0S;>Inv)N1$Zuq|o?>}oFusArh7p#0G>VE~ z{rsS3+i1SJUel2gJ|3GamTUsJ8unMDk}OABcvXXfJ0?_ r0qGFIHzKm5J}~j/dev/null 2>&1 || echo "[Warning] pkill not found" - -BASEDIR=$(dirname "$0") -cd $BASEDIR - -pre_start_linux() { - # for Tun2Socket - iptables -I INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT - ip6tables -I INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT -} - -start() { - pre_start_linux - "./nekobox_core" run -c "$CONFIG_PATH" -} - -stop() { - iptables -D INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT - ip6tables -D INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT -} - -if [ "$1" != "stop" ]; then - start || true -fi - -stop || true diff --git a/rpc/gRPC.cpp b/rpc/gRPC.cpp deleted file mode 100644 index e588315..0000000 --- a/rpc/gRPC.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "gRPC.h" - -#include -#include - -#ifndef NKR_NO_GRPC - -#include "main/NekoGui.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace QtGrpc { - const char *GrpcAcceptEncodingHeader = "grpc-accept-encoding"; - const char *AcceptEncodingHeader = "accept-encoding"; - const char *TEHeader = "te"; - const char *GrpcStatusHeader = "grpc-status"; - const char *GrpcStatusMessage = "grpc-message"; - const int GrpcMessageSizeHeaderSize = 5; - - class NoCache : public QAbstractNetworkCache { - public: - QNetworkCacheMetaData metaData(const QUrl &url) override { - return {}; - } - void updateMetaData(const QNetworkCacheMetaData &metaData) override { - } - QIODevice *data(const QUrl &url) override { - return nullptr; - } - bool remove(const QUrl &url) override { - return false; - } - [[nodiscard]] qint64 cacheSize() const override { - return 0; - } - QIODevice *prepare(const QNetworkCacheMetaData &metaData) override { - return nullptr; - } - void insert(QIODevice *device) override { - } - void clear() override { - } - }; - - class Http2GrpcChannelPrivate { - private: - QThread *thread; - QNetworkAccessManager *nm; - - QString url_base; - QString serviceName; - QByteArray nekoray_auth; - - // async - QNetworkReply *post(const QString &method, const QString &service, const QByteArray &args) { - QUrl callUrl = url_base + "/" + service + "/" + method; - // qDebug() << "Service call url: " << callUrl; - - QNetworkRequest request(callUrl); - // request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false); - // request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - request.setAttribute(QNetworkRequest::Http2DirectAttribute, true); -#endif - request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String{"application/grpc"}); - request.setRawHeader("Cache-Control", "no-store"); - request.setRawHeader(GrpcAcceptEncodingHeader, QByteArray{"identity,deflate,gzip"}); - request.setRawHeader(AcceptEncodingHeader, QByteArray{"identity,gzip"}); - request.setRawHeader(TEHeader, QByteArray{"trailers"}); - request.setRawHeader("nekoray_auth", nekoray_auth); - - QByteArray msg(GrpcMessageSizeHeaderSize, '\0'); - *reinterpret_cast(msg.data() + 1) = qToBigEndian((int) args.size()); - msg += args; - // qDebug() << "SEND: " << msg.size(); - - QNetworkReply *networkReply = nm->post(request, msg); - return networkReply; - } - - static QByteArray processReply(QNetworkReply *networkReply, QNetworkReply::NetworkError &statusCode) { - // Check if no network error occured - if (networkReply->error() != QNetworkReply::NoError) { - statusCode = networkReply->error(); - return {}; - } - - // Check if server answer with error - auto errCode = networkReply->rawHeader(GrpcStatusHeader).toInt(); - if (errCode != 0) { - QStringList errstr; - errstr << "grpc-status error code:" << Int2String(errCode) << ", error msg:" - << QLatin1String(networkReply->rawHeader(GrpcStatusMessage)); - MW_show_log(errstr.join(" ")); - statusCode = QNetworkReply::NetworkError::ProtocolUnknownError; - return {}; - } - statusCode = QNetworkReply::NetworkError::NoError; - return networkReply->readAll().mid(GrpcMessageSizeHeaderSize); - } - - QNetworkReply::NetworkError call(const QString &method, const QString &service, const QByteArray &args, QByteArray &qByteArray, int timeout_ms) { - QNetworkReply *networkReply = post(method, service, args); - - QTimer *abortTimer = nullptr; - if (timeout_ms > 0) { - abortTimer = new QTimer; - abortTimer->setSingleShot(true); - abortTimer->setInterval(timeout_ms); - QObject::connect(abortTimer, &QTimer::timeout, networkReply, &QNetworkReply::abort); - abortTimer->start(); - } - - { - QEventLoop loop; - QObject::connect(networkReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - loop.exec(); - } - - if (abortTimer != nullptr) { - abortTimer->stop(); - abortTimer->deleteLater(); - } - - auto grpcStatus = QNetworkReply::NetworkError::ProtocolUnknownError; - qByteArray = processReply(networkReply, grpcStatus); - // qDebug() << __func__ << "RECV: " << qByteArray.toHex() << "grpcStatus" << grpcStatus; - // qDebug() << networkReply->rawHeaderPairs(); - - networkReply->deleteLater(); - return grpcStatus; - } - - public: - Http2GrpcChannelPrivate(const QString &url_, const QString &nekoray_auth_, const QString &serviceName_) { - url_base = "http://" + url_; - nekoray_auth = nekoray_auth_.toLatin1(); - serviceName = serviceName_; - // - thread = new QThread; - nm = new QNetworkAccessManager(); - nm->setCache(new NoCache); - nm->moveToThread(thread); - thread->start(); - } - - ~Http2GrpcChannelPrivate() { - nm->deleteLater(); - thread->quit(); - thread->wait(); - thread->deleteLater(); - } - - QNetworkReply::NetworkError Call(const QString &methodName, - const google::protobuf::Message &req, google::protobuf::Message *rsp, - int timeout_ms = 0) { - if (!NekoGui::dataStore->core_running) return QNetworkReply::NetworkError(-1919); - - std::string reqStr; - req.SerializeToString(&reqStr); - auto requestArray = QByteArray::fromStdString(reqStr); - - QByteArray responseArray; - QNetworkReply::NetworkError err; - QMutex lock; - lock.lock(); - - runOnUiThread( - [&] { - err = call(methodName, serviceName, requestArray, responseArray, timeout_ms); - lock.unlock(); - }, - nm); - - lock.lock(); - lock.unlock(); - // qDebug() << "rsp err" << err; - // qDebug() << "rsp array" << responseArray; - - if (err != QNetworkReply::NetworkError::NoError) { - return err; - } - if (!rsp->ParseFromArray(responseArray.data(), responseArray.size())) { - return QNetworkReply::NetworkError(-114514); - } - return QNetworkReply::NetworkError::NoError; - } - }; -} // namespace QtGrpc - -namespace NekoGui_rpc { - - Client::Client(std::function onError, const QString &target, const QString &token) { - this->make_grpc_channel = [=]() { return std::make_unique(target, token, "libcore.LibcoreService"); }; - this->default_grpc_channel = make_grpc_channel(); - this->onError = std::move(onError); - } - -#define NOT_OK \ - *rpcOK = false; \ - onError(QStringLiteral("QNetworkReply::NetworkError code: %1\n").arg(status)); - - void Client::Exit() { - libcore::EmptyReq request; - libcore::EmptyResp reply; - default_grpc_channel->Call("Exit", request, &reply, 500); - } - - QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) { - libcore::ErrorResp reply; - auto status = default_grpc_channel->Call("Start", request, &reply); - - if (status == QNetworkReply::NoError) { - *rpcOK = true; - return {reply.error().c_str()}; - } else { - NOT_OK - return ""; - } - } - - QString Client::Stop(bool *rpcOK) { - libcore::EmptyReq request; - libcore::ErrorResp reply; - auto status = default_grpc_channel->Call("Stop", request, &reply); - - if (status == QNetworkReply::NoError) { - *rpcOK = true; - return {reply.error().c_str()}; - } else { - NOT_OK - return ""; - } - } - - long long Client::QueryStats(const std::string &tag, const std::string &direct) { - libcore::QueryStatsReq request; - request.set_tag(tag); - request.set_direct(direct); - - libcore::QueryStatsResp reply; - auto status = default_grpc_channel->Call("QueryStats", request, &reply, 500); - - if (status == QNetworkReply::NoError) { - return reply.traffic(); - } else { - return 0; - } - } - - std::string Client::ListConnections() { - libcore::EmptyReq request; - libcore::ListConnectionsResp reply; - auto status = default_grpc_channel->Call("ListConnections", request, &reply, 500); - - if (status == QNetworkReply::NoError) { - return reply.nekoray_connections_json(); - } else { - return ""; - } - } - - // - - libcore::TestResp Client::Test(bool *rpcOK, const libcore::TestReq &request) { - libcore::TestResp reply; - auto status = make_grpc_channel()->Call("Test", request, &reply); - - if (status == QNetworkReply::NoError) { - *rpcOK = true; - return reply; - } else { - NOT_OK - return reply; - } - } - - libcore::UpdateResp Client::Update(bool *rpcOK, const libcore::UpdateReq &request) { - libcore::UpdateResp reply; - auto status = default_grpc_channel->Call("Update", request, &reply); - - if (status == QNetworkReply::NoError) { - *rpcOK = true; - return reply; - } else { - NOT_OK - return reply; - } - } -} // namespace NekoGui_rpc - -#endif diff --git a/rpc/gRPC.h b/rpc/gRPC.h deleted file mode 100644 index 25c03a0..0000000 --- a/rpc/gRPC.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#ifndef NKR_NO_GRPC - -#include "go/grpc_server/gen/libcore.pb.h" -#include - -namespace QtGrpc { - class Http2GrpcChannelPrivate; -} - -namespace NekoGui_rpc { - class Client { - public: - explicit Client(std::function onError, const QString &target, const QString &token); - - void Exit(); - - bool KeepAlive(); - - // QString returns is error string - - QString Start(bool *rpcOK, const libcore::LoadConfigReq &request); - - QString Stop(bool *rpcOK); - - long long QueryStats(const std::string &tag, const std::string &direct); - - std::string ListConnections(); - - libcore::TestResp Test(bool *rpcOK, const libcore::TestReq &request); - - libcore::UpdateResp Update(bool *rpcOK, const libcore::UpdateReq &request); - - private: - std::function()> make_grpc_channel; - std::unique_ptr default_grpc_channel; - std::function onError; - }; - - inline Client *defaultClient; -} // namespace NekoGui_rpc -#endif diff --git a/sub/GroupUpdater.cpp b/sub/GroupUpdater.cpp deleted file mode 100644 index 6ad7eb1..0000000 --- a/sub/GroupUpdater.cpp +++ /dev/null @@ -1,665 +0,0 @@ -#include "db/ProfileFilter.hpp" -#include "fmt/includes.h" -#include "fmt/Preset.hpp" -#include "main/HTTPRequestHelper.hpp" - -#include "GroupUpdater.hpp" - -#include -#include - -#ifndef NKR_NO_YAML - -#include - -#endif - -namespace NekoGui_sub { - - GroupUpdater *groupUpdater = new GroupUpdater; - - void RawUpdater_FixEnt(const std::shared_ptr &ent) { - if (ent == nullptr) return; - auto stream = NekoGui_fmt::GetStreamSettings(ent->bean.get()); - if (stream == nullptr) return; - // 1. "security" - if (stream->security == "none" || stream->security == "0" || stream->security == "false") { - stream->security = ""; - } else if (stream->security == "1" || stream->security == "true") { - stream->security = "tls"; - } - // 2. TLS SNI: v2rayN config builder generate sni like this, so set sni here for their format. - if (stream->security == "tls" && IsIpAddress(ent->bean->serverAddress) && (!stream->host.isEmpty()) && stream->sni.isEmpty()) { - stream->sni = stream->host; - } - } - - void RawUpdater::update(const QString &str) { - // Base64 encoded subscription - if (auto str2 = DecodeB64IfValid(str); !str2.isEmpty()) { - update(str2); - return; - } - - // Clash - if (str.contains("proxies:")) { - updateClash(str); - return; - } - - // Multi line - if (str.count("\n") > 0) { - auto list = str.split("\n"); - for (const auto &str2: list) { - update(str2.trimmed()); - } - return; - } - - std::shared_ptr ent; - bool needFix = true; - - // Nekoray format - if (str.startsWith("nekoray://")) { - needFix = false; - auto link = QUrl(str); - if (!link.isValid()) return; - ent = NekoGui::ProfileManager::NewProxyEntity(link.host()); - if (ent->bean->version == -114514) return; - auto j = DecodeB64IfValid(link.fragment().toUtf8(), QByteArray::Base64UrlEncoding); - if (j.isEmpty()) return; - ent->bean->FromJsonBytes(j); - } - - // SOCKS - if (str.startsWith("socks5://") || str.startsWith("socks4://") || - str.startsWith("socks4a://") || str.startsWith("socks://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("socks"); - auto ok = ent->SocksHTTPBean()->TryParseLink(str); - if (!ok) return; - } - - // HTTP - if (str.startsWith("http://") || str.startsWith("https://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("http"); - auto ok = ent->SocksHTTPBean()->TryParseLink(str); - if (!ok) return; - } - - // ShadowSocks - if (str.startsWith("ss://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("shadowsocks"); - auto ok = ent->ShadowSocksBean()->TryParseLink(str); - if (!ok) return; - } - - // VMess - if (str.startsWith("vmess://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("vmess"); - auto ok = ent->VMessBean()->TryParseLink(str); - if (!ok) return; - } - - // VLESS - if (str.startsWith("vless://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("vless"); - auto ok = ent->TrojanVLESSBean()->TryParseLink(str); - if (!ok) return; - } - - // Trojan - if (str.startsWith("trojan://")) { - ent = NekoGui::ProfileManager::NewProxyEntity("trojan"); - auto ok = ent->TrojanVLESSBean()->TryParseLink(str); - if (!ok) return; - } - - // Naive - if (str.startsWith("naive+")) { - needFix = false; - ent = NekoGui::ProfileManager::NewProxyEntity("naive"); - auto ok = ent->NaiveBean()->TryParseLink(str); - if (!ok) return; - } - - // Hysteria2 - if (str.startsWith("hysteria2://") || str.startsWith("hy2://")) { - needFix = false; - ent = NekoGui::ProfileManager::NewProxyEntity("hysteria2"); - auto ok = ent->QUICBean()->TryParseLink(str); - if (!ok) return; - } - - // TUIC - if (str.startsWith("tuic://")) { - needFix = false; - ent = NekoGui::ProfileManager::NewProxyEntity("tuic"); - auto ok = ent->QUICBean()->TryParseLink(str); - if (!ok) return; - } - - if (ent == nullptr) return; - - // Fix - if (needFix) RawUpdater_FixEnt(ent); - - // End - NekoGui::profileManager->AddProfile(ent, gid_add_to); - updated_order += ent; - } - -#ifndef NKR_NO_YAML - - QString Node2QString(const YAML::Node &n, const QString &def = "") { - try { - return n.as().c_str(); - } catch (const YAML::Exception &ex) { - qDebug() << ex.what(); - return def; - } - } - - QStringList Node2QStringList(const YAML::Node &n) { - try { - if (n.IsSequence()) { - QStringList list; - for (auto item: n) { - list << item.as().c_str(); - } - return list; - } else { - return {}; - } - } catch (const YAML::Exception &ex) { - qDebug() << ex.what(); - return {}; - } - } - - int Node2Int(const YAML::Node &n, const int &def = 0) { - try { - return n.as(); - } catch (const YAML::Exception &ex) { - qDebug() << ex.what(); - return def; - } - } - - bool Node2Bool(const YAML::Node &n, const bool &def = false) { - try { - return n.as(); - } catch (const YAML::Exception &ex) { - try { - return n.as(); - } catch (const YAML::Exception &ex2) { - qDebug() << ex2.what(); - } - qDebug() << ex.what(); - return def; - } - } - - // NodeChild returns the first defined children or Null Node - YAML::Node NodeChild(const YAML::Node &n, const std::list &keys) { - for (const auto &key: keys) { - auto child = n[key]; - if (child.IsDefined()) return child; - } - return {}; - } - -#endif - - // https://github.com/Dreamacro/clash/wiki/configuration - void RawUpdater::updateClash(const QString &str) { -#ifndef NKR_NO_YAML - try { - auto proxies = YAML::Load(str.toStdString())["proxies"]; - for (auto proxy: proxies) { - auto type = Node2QString(proxy["type"]).toLower(); - auto type_clash = type; - - if (type == "ss" || type == "ssr") type = "shadowsocks"; - if (type == "socks5") type = "socks"; - - auto ent = NekoGui::ProfileManager::NewProxyEntity(type); - if (ent->bean->version == -114514) continue; - bool needFix = false; - - // common - ent->bean->name = Node2QString(proxy["name"]); - ent->bean->serverAddress = Node2QString(proxy["server"]); - ent->bean->serverPort = Node2Int(proxy["port"]); - - if (type_clash == "ss") { - auto bean = ent->ShadowSocksBean(); - bean->method = Node2QString(proxy["cipher"]).replace("dummy", "none"); - bean->password = Node2QString(proxy["password"]); - auto plugin_n = proxy["plugin"]; - auto pluginOpts_n = proxy["plugin-opts"]; - - // UDP over TCP - if (Node2Bool(proxy["udp-over-tcp"])) { - bean->uot = Node2Int(proxy["udp-over-tcp-version"]); - if (bean->uot == 0) bean->uot = 2; - } - - if (plugin_n.IsDefined() && pluginOpts_n.IsDefined()) { - QStringList ssPlugin; - auto plugin = Node2QString(plugin_n); - if (plugin == "obfs") { - ssPlugin << "obfs-local"; - ssPlugin << "obfs=" + Node2QString(pluginOpts_n["mode"]); - ssPlugin << "obfs-host=" + Node2QString(pluginOpts_n["host"]); - } else if (plugin == "v2ray-plugin") { - auto mode = Node2QString(pluginOpts_n["mode"]); - auto host = Node2QString(pluginOpts_n["host"]); - auto path = Node2QString(pluginOpts_n["path"]); - ssPlugin << "v2ray-plugin"; - if (!mode.isEmpty() && mode != "websocket") ssPlugin << "mode=" + mode; - if (Node2Bool(pluginOpts_n["tls"])) ssPlugin << "tls"; - if (!host.isEmpty()) ssPlugin << "host=" + host; - if (!path.isEmpty()) ssPlugin << "path=" + path; - // clash only: skip-cert-verify - // clash only: headers - // clash: mux=? - } - bean->plugin = ssPlugin.join(";"); - } - - // sing-mux - auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; - } else if (type == "socks" || type == "http") { - auto bean = ent->SocksHTTPBean(); - bean->username = Node2QString(proxy["username"]); - bean->password = Node2QString(proxy["password"]); - if (Node2Bool(proxy["tls"])) bean->stream->security = "tls"; - if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true; - } else if (type == "trojan" || type == "vless") { - needFix = true; - auto bean = ent->TrojanVLESSBean(); - if (type == "vless") { - bean->flow = Node2QString(proxy["flow"]); - bean->password = Node2QString(proxy["uuid"]); - // meta packet encoding - if (Node2Bool(proxy["packet-addr"])) { - bean->stream->packet_encoding = "packetaddr"; - } else { - // For VLESS, default to use xudp - bean->stream->packet_encoding = "xudp"; - } - } else { - bean->password = Node2QString(proxy["password"]); - } - bean->stream->security = "tls"; - bean->stream->network = Node2QString(proxy["network"], "tcp"); - bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"])); - bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(","); - bean->stream->allow_insecure = Node2Bool(proxy["skip-cert-verify"]); - bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]); - if (bean->stream->utlsFingerprint.isEmpty()) { - bean->stream->utlsFingerprint = NekoGui::dataStore->utlsFingerprint; - } - - // sing-mux - auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; - - // opts - auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"}); - if (ws.IsMap()) { - auto headers = ws["headers"]; - for (auto header: headers) { - if (Node2QString(header.first).toLower() == "host") { - bean->stream->host = Node2QString(header.second); - } - } - bean->stream->path = Node2QString(ws["path"]); - bean->stream->ws_early_data_length = Node2Int(ws["max-early-data"]); - bean->stream->ws_early_data_name = Node2QString(ws["early-data-header-name"]); - } - - auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"}); - if (grpc.IsMap()) { - bean->stream->path = Node2QString(grpc["grpc-service-name"]); - } - - auto reality = NodeChild(proxy, {"reality-opts"}); - if (reality.IsMap()) { - bean->stream->reality_pbk = Node2QString(reality["public-key"]); - bean->stream->reality_sid = Node2QString(reality["short-id"]); - } - } else if (type == "vmess") { - needFix = true; - auto bean = ent->VMessBean(); - bean->uuid = Node2QString(proxy["uuid"]); - bean->aid = Node2Int(proxy["alterId"]); - bean->security = Node2QString(proxy["cipher"], bean->security); - bean->stream->network = Node2QString(proxy["network"], "tcp").replace("h2", "http"); - bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"])); - bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(","); - if (Node2Bool(proxy["tls"])) bean->stream->security = "tls"; - if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true; - bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]); - bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]); - if (bean->stream->utlsFingerprint.isEmpty()) { - bean->stream->utlsFingerprint = NekoGui::dataStore->utlsFingerprint; - } - - // sing-mux - auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; - - // meta packet encoding - if (Node2Bool(proxy["xudp"])) bean->stream->packet_encoding = "xudp"; - if (Node2Bool(proxy["packet-addr"])) bean->stream->packet_encoding = "packetaddr"; - - // opts - auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"}); - if (ws.IsMap()) { - auto headers = ws["headers"]; - for (auto header: headers) { - if (Node2QString(header.first).toLower() == "host") { - bean->stream->host = Node2QString(header.second); - } - } - bean->stream->path = Node2QString(ws["path"]); - bean->stream->ws_early_data_length = Node2Int(ws["max-early-data"]); - bean->stream->ws_early_data_name = Node2QString(ws["early-data-header-name"]); - // for Xray - if (Node2QString(ws["early-data-header-name"]) == "Sec-WebSocket-Protocol") { - bean->stream->path += "?ed=" + Node2QString(ws["max-early-data"]); - } - } - - auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"}); - if (grpc.IsMap()) { - bean->stream->path = Node2QString(grpc["grpc-service-name"]); - } - - auto h2 = NodeChild(proxy, {"h2-opts", "h2-opt"}); - if (h2.IsMap()) { - auto hosts = h2["host"]; - for (auto host: hosts) { - bean->stream->host = Node2QString(host); - break; - } - bean->stream->path = Node2QString(h2["path"]); - } - - auto tcp_http = NodeChild(proxy, {"http-opts", "http-opt"}); - if (tcp_http.IsMap()) { - bean->stream->network = "tcp"; - bean->stream->header_type = "http"; - auto headers = tcp_http["headers"]; - for (auto header: headers) { - if (Node2QString(header.first).toLower() == "host") { - bean->stream->host = Node2QString(header.second[0]); - } - break; - } - auto paths = tcp_http["path"]; - for (auto path: paths) { - bean->stream->path = Node2QString(path); - break; - } - } - } else if (type == "hysteria2") { - auto bean = ent->QUICBean(); - - bean->hopPort = Node2QString(proxy["ports"]); - - bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]); - bean->caText = Node2QString(proxy["ca-str"]); - bean->sni = Node2QString(proxy["sni"]); - - bean->obfsPassword = Node2QString(proxy["obfs-password"]); - bean->password = Node2QString(proxy["password"]); - - bean->uploadMbps = Node2QString(proxy["up"]).split(" ")[0].toInt(); - bean->downloadMbps = Node2QString(proxy["down"]).split(" ")[0].toInt(); - } else if (type == "tuic") { - auto bean = ent->QUICBean(); - - bean->uuid = Node2QString(proxy["uuid"]); - bean->password = Node2QString(proxy["password"]); - - if (Node2Int(proxy["heartbeat-interval"]) != 0) { - bean->heartbeat = Int2String(Node2Int(proxy["heartbeat-interval"])) + "ms"; - } - - bean->udpRelayMode = Node2QString(proxy["udp-relay-mode"], bean->udpRelayMode); - bean->congestionControl = Node2QString(proxy["congestion-controller"], bean->congestionControl); - - bean->disableSni = Node2Bool(proxy["disable-sni"]); - bean->zeroRttHandshake = Node2Bool(proxy["reduce-rtt"]); - bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]); - bean->alpn = Node2QStringList(proxy["alpn"]).join(","); - bean->caText = Node2QString(proxy["ca-str"]); - bean->sni = Node2QString(proxy["sni"]); - - if (Node2Bool(proxy["udp-over-stream"])) bean->uos = true; - - if (!Node2QString(proxy["ip"]).isEmpty()) { - if (bean->sni.isEmpty()) bean->sni = bean->serverAddress; - bean->serverAddress = Node2QString(proxy["ip"]); - } - } else { - continue; - } - - if (needFix) RawUpdater_FixEnt(ent); - NekoGui::profileManager->AddProfile(ent, gid_add_to); - updated_order += ent; - } - } catch (const YAML::Exception &ex) { - runOnUiThread([=] { - MessageBoxWarning("YAML Exception", ex.what()); - }); - } -#endif - } - - // 在新的 thread 运行 - void GroupUpdater::AsyncUpdate(const QString &str, int _sub_gid, const std::function &finish) { - auto content = str.trimmed(); - bool asURL = false; - bool createNewGroup = false; - - if (_sub_gid < 0 && (content.startsWith("http://") || content.startsWith("https://"))) { - auto items = QStringList{ - QObject::tr("As Subscription (add to this group)"), - QObject::tr("As Subscription (create new group)"), - QObject::tr("As link"), - }; - bool ok; - auto a = QInputDialog::getItem(nullptr, - QObject::tr("url detected"), - QObject::tr("%1\nHow to update?").arg(content), - items, 0, false, &ok); - if (!ok) return; - if (items.indexOf(a) <= 1) asURL = true; - if (items.indexOf(a) == 1) createNewGroup = true; - } - - runOnNewThread([=] { - auto gid = _sub_gid; - if (createNewGroup) { - auto group = NekoGui::ProfileManager::NewGroup(); - group->name = QUrl(str).host(); - group->url = str; - NekoGui::profileManager->AddGroup(group); - gid = group->id; - MW_dialog_message("SubUpdater", "NewGroup"); - } - Update(str, gid, asURL); - emit asyncUpdateCallback(gid); - if (finish != nullptr) finish(); - }); - } - - void GroupUpdater::Update(const QString &_str, int _sub_gid, bool _not_sub_as_url) { - // 创建 rawUpdater - NekoGui::dataStore->imported_count = 0; - auto rawUpdater = std::make_unique(); - rawUpdater->gid_add_to = _sub_gid; - - // 准备 - QString sub_user_info; - bool asURL = _sub_gid >= 0 || _not_sub_as_url; // 把 _str 当作 url 处理(下载内容) - auto content = _str.trimmed(); - auto group = NekoGui::profileManager->GetGroup(_sub_gid); - if (group != nullptr && group->archive) return; - - // 网络请求 - if (asURL) { - auto groupName = group == nullptr ? content : group->name; - MW_show_log(">>>>>>>> " + QObject::tr("Requesting subscription: %1").arg(groupName)); - - auto resp = NetworkRequestHelper::HttpGet(content); - if (!resp.error.isEmpty()) { - MW_show_log("<<<<<<<< " + QObject::tr("Requesting subscription %1 error: %2").arg(groupName, resp.error + "\n" + resp.data)); - return; - } - - content = resp.data; - sub_user_info = NetworkRequestHelper::GetHeader(resp.header, "Subscription-UserInfo"); - - MW_show_log("<<<<<<<< " + QObject::tr("Subscription request fininshed: %1").arg(groupName)); - } - - QList> in; // 更新前 - QList> out_all; // 更新前 + 更新后 - QList> out; // 更新后 - QList> only_in; // 只在更新前有的 - QList> only_out; // 只在更新后有的 - QList> update_del; // 更新前后都有的,需要删除的新配置 - QList> update_keep; // 更新前后都有的,被保留的旧配置 - - // 订阅解析前 - if (group != nullptr) { - in = group->Profiles(); - group->sub_last_update = QDateTime::currentMSecsSinceEpoch() / 1000; - group->info = sub_user_info; - group->order.clear(); - group->Save(); - // - if (NekoGui::dataStore->sub_clear) { - MW_show_log(QObject::tr("Clearing servers...")); - for (const auto &profile: in) { - NekoGui::profileManager->DeleteProfile(profile->id); - } - } - } - - // 解析并添加 profile - rawUpdater->update(content); - - if (group != nullptr) { - out_all = group->Profiles(); - - QString change_text; - - if (NekoGui::dataStore->sub_clear) { - // all is new profile - for (const auto &ent: out_all) { - change_text += "[+] " + ent->bean->DisplayTypeAndName() + "\n"; - } - } else { - // find and delete not updated profile by ProfileFilter - NekoGui::ProfileFilter::OnlyInSrc_ByPointer(out_all, in, out); - NekoGui::ProfileFilter::OnlyInSrc(in, out, only_in); - NekoGui::ProfileFilter::OnlyInSrc(out, in, only_out); - NekoGui::ProfileFilter::Common(in, out, update_keep, update_del, false); - - QString notice_added; - QString notice_deleted; - for (const auto &ent: only_out) { - notice_added += "[+] " + ent->bean->DisplayTypeAndName() + "\n"; - } - for (const auto &ent: only_in) { - notice_deleted += "[-] " + ent->bean->DisplayTypeAndName() + "\n"; - } - - // sort according to order in remote - group->order = {}; - for (const auto &ent: rawUpdater->updated_order) { - auto deleted_index = update_del.indexOf(ent); - if (deleted_index > 0) { - if (deleted_index >= update_keep.count()) continue; // should not happen - auto ent2 = update_keep[deleted_index]; - group->order.append(ent2->id); - } else { - group->order.append(ent->id); - } - } - group->Save(); - - // cleanup - for (const auto &ent: out_all) { - if (!group->order.contains(ent->id)) { - NekoGui::profileManager->DeleteProfile(ent->id); - } - } - - change_text = "\n" + QObject::tr("Added %1 profiles:\n%2\nDeleted %3 Profiles:\n%4") - .arg(only_out.length()) - .arg(notice_added) - .arg(only_in.length()) - .arg(notice_deleted); - if (only_out.length() + only_in.length() == 0) change_text = QObject::tr("Nothing"); - } - - MW_show_log("<<<<<<<< " + QObject::tr("Change of %1:").arg(group->name) + "\n" + change_text); - MW_dialog_message("SubUpdater", "finish-dingyue"); - } else { - NekoGui::dataStore->imported_count = rawUpdater->updated_order.count(); - MW_dialog_message("SubUpdater", "finish"); - } - } -} // namespace NekoGui_sub - -bool UI_update_all_groups_Updating = false; - -#define should_skip_group(g) (g == nullptr || g->url.isEmpty() || g->archive || (onlyAllowed && g->skip_auto_update)) - -void serialUpdateSubscription(const QList &groupsTabOrder, int _order, bool onlyAllowed) { - if (_order >= groupsTabOrder.size()) { - UI_update_all_groups_Updating = false; - return; - } - - // calculate this group - auto group = NekoGui::profileManager->GetGroup(groupsTabOrder[_order]); - if (group == nullptr || should_skip_group(group)) { - serialUpdateSubscription(groupsTabOrder, _order + 1, onlyAllowed); - return; - } - - int nextOrder = _order + 1; - while (nextOrder < groupsTabOrder.size()) { - auto nextGid = groupsTabOrder[nextOrder]; - auto nextGroup = NekoGui::profileManager->GetGroup(nextGid); - if (!should_skip_group(nextGroup)) { - break; - } - nextOrder += 1; - } - - // Async update current group - UI_update_all_groups_Updating = true; - NekoGui_sub::groupUpdater->AsyncUpdate(group->url, group->id, [=] { - serialUpdateSubscription(groupsTabOrder, nextOrder, onlyAllowed); - }); -} - -void UI_update_all_groups(bool onlyAllowed) { - if (UI_update_all_groups_Updating) { - MW_show_log("The last subscription update has not exited."); - return; - } - - auto groupsTabOrder = NekoGui::profileManager->groupsTabOrder; - serialUpdateSubscription(groupsTabOrder, 0, onlyAllowed); -} diff --git a/sub/GroupUpdater.hpp b/sub/GroupUpdater.hpp deleted file mode 100644 index 87d3612..0000000 --- a/sub/GroupUpdater.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "db/Database.hpp" - -namespace NekoGui_sub { - class RawUpdater { - public: - void updateClash(const QString &str); - - void update(const QString &str); - - int gid_add_to = -1; // 导入到指定组 -1 为当前选中组 - - QList> updated_order; // 新增的配置,按照导入时处理的先后排序 - }; - - class GroupUpdater : public QObject { - Q_OBJECT - - public: - void AsyncUpdate(const QString &str, int _sub_gid = -1, const std::function &finish = nullptr); - - void Update(const QString &_str, int _sub_gid = -1, bool _not_sub_as_url = false); - - signals: - - void asyncUpdateCallback(int gid); - }; - - extern GroupUpdater *groupUpdater; -} // namespace NekoGui_sub - -// 更新所有订阅 关闭分组窗口时 更新动作继续执行 -void UI_update_all_groups(bool onlyAllowed = false); diff --git a/sys/AutoRun.cpp b/sys/AutoRun.cpp deleted file mode 100644 index dbceffd..0000000 --- a/sys/AutoRun.cpp +++ /dev/null @@ -1,231 +0,0 @@ -#include "AutoRun.hpp" - -#include -#include - -#include "3rdparty/fix_old_qt.h" -#include "main/NekoGui.hpp" - -// macOS headers (possibly OBJ-c) -#if defined(Q_OS_MACOS) -#include -#include -#endif - -#ifdef Q_OS_WIN - -#include - -QString Windows_GenAutoRunString() { - auto appPath = QApplication::applicationFilePath(); - appPath = "\"" + QDir::toNativeSeparators(appPath) + "\""; - appPath += " -tray"; - return appPath; -} - -void AutoRun_SetEnabled(bool enable) { - // 以程序名称作为注册表中的键 - // 根据键获取对应的值(程序路径) - auto appPath = QApplication::applicationFilePath(); - QFileInfo fInfo(appPath); - QString name = fInfo.baseName(); - - QSettings settings("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); - - if (enable) { - settings.setValue(name, Windows_GenAutoRunString()); - } else { - settings.remove(name); - } -} - -bool AutoRun_IsEnabled() { - QSettings settings("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); - - // 以程序名称作为注册表中的键 - // 根据键获取对应的值(程序路径) - auto appPath = QApplication::applicationFilePath(); - QFileInfo fInfo(appPath); - QString name = fInfo.baseName(); - - return settings.value(name).toString() == Windows_GenAutoRunString(); -} - -#endif - -#ifdef Q_OS_MACOS - -void AutoRun_SetEnabled(bool enable) { - // From - // https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp - QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); - CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); - CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); - LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0); - - if (loginItems && enable) { - // Insert an item to the list. - LSSharedFileListItemRef item = - LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, 0, 0, urlRef, 0, 0); - - if (item) CFRelease(item); - - CFRelease(loginItems); - } else if (loginItems && !enable) { - // We need to iterate over the items and check which one is "ours". - UInt32 seedValue; - CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); - CFStringRef appUrlRefString = CFURLGetString(urlRef); - - for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { - LSSharedFileListItemRef item = (LSSharedFileListItemRef) CFArrayGetValueAtIndex(itemsArray, i); - CFURLRef itemUrlRef = NULL; - - if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) { - CFStringRef itemUrlString = CFURLGetString(itemUrlRef); - - if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { - LSSharedFileListItemRemove(loginItems, item); // remove it! - } - - CFRelease(itemUrlRef); - } - } - - CFRelease(itemsArray); - CFRelease(loginItems); - } - - CFRelease(folderCFStr); - CFRelease(urlRef); -} - -bool AutoRun_IsEnabled() { - // From - // https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp - // this is quite some duplicate code with setLaunchOnStartup, at some - // point we should fix this FIXME. - bool returnValue = false; - QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); - CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); - CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); - LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0); - - if (loginItems) { - // We need to iterate over the items and check which one is "ours". - UInt32 seedValue; - CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); - CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release - - for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { - LSSharedFileListItemRef item = (LSSharedFileListItemRef) CFArrayGetValueAtIndex(itemsArray, i); - CFURLRef itemUrlRef = NULL; - - if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) { - CFStringRef itemUrlString = CFURLGetString(itemUrlRef); - - if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { - returnValue = true; - } - - CFRelease(itemUrlRef); - } - } - - CFRelease(itemsArray); - } - - CFRelease(loginItems); - CFRelease(folderCFStr); - CFRelease(urlRef); - return returnValue; -} - -#endif - -#ifdef Q_OS_LINUX - -#include -#include -#include - -#define NEWLINE "\n" - -// launchatlogin.cpp -// ShadowClash -// -// Created by TheWanderingCoel on 2018/6/12. -// Copyright © 2019 Coel Wu. All rights reserved. -// -QString getUserAutostartDir_private() { - QString config = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - config += QLatin1String("/autostart/"); - return config; -} - -void AutoRun_SetEnabled(bool enable) { - // From https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp - QString appName = QCoreApplication::applicationName(); - QString userAutoStartPath = getUserAutostartDir_private(); - QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop"); - QStringList appCmdList; - - // nekoray: launcher - if (qEnvironmentVariable("NKR_FROM_LAUNCHER") == "1") { - appCmdList << QApplication::applicationDirPath() + "/launcher" - << "--"; - } else { - if (QProcessEnvironment::systemEnvironment().contains("APPIMAGE")) { - appCmdList << QProcessEnvironment::systemEnvironment().value("APPIMAGE"); - } else { - appCmdList << QApplication::applicationFilePath(); - } - } - - appCmdList << "-tray"; - - if (NekoGui::dataStore->flag_use_appdata) { - appCmdList << "-appdata"; - } - - if (enable) { - if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) { - // qCWarning(lcUtility) << "Could not create autostart folder" - // << userAutoStartPath; - return; - } - - QFile iniFile(desktopFileLocation); - - if (!iniFile.open(QIODevice::WriteOnly)) { - // qCWarning(lcUtility) << "Could not write auto start entry" << - // desktopFileLocation; - return; - } - - QTextStream ts(&iniFile); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - ts.setCodec("UTF-8"); -#endif - ts << QLatin1String("[Desktop Entry]") << NEWLINE - << QLatin1String("Name=") << appName << NEWLINE - << QLatin1String("Exec=") << appCmdList.join(" ") << NEWLINE - << QLatin1String("Terminal=") << "false" << NEWLINE - << QLatin1String("Categories=") << "Network" << NEWLINE - << QLatin1String("Type=") << "Application" << NEWLINE - << QLatin1String("StartupNotify=") << "false" << NEWLINE - << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << NEWLINE; - ts.flush(); - iniFile.close(); - } else { - QFile::remove(desktopFileLocation); - } -} - -bool AutoRun_IsEnabled() { - QString appName = QCoreApplication::applicationName(); - QString desktopFileLocation = getUserAutostartDir_private() + appName + QLatin1String(".desktop"); - return QFile::exists(desktopFileLocation); -} - -#endif diff --git a/sys/AutoRun.hpp b/sys/AutoRun.hpp deleted file mode 100644 index 7fd25a9..0000000 --- a/sys/AutoRun.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -void AutoRun_SetEnabled(bool enable); - -bool AutoRun_IsEnabled(); diff --git a/sys/ExternalProcess.cpp b/sys/ExternalProcess.cpp deleted file mode 100644 index 02770ec..0000000 --- a/sys/ExternalProcess.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "ExternalProcess.hpp" -#include "main/NekoGui.hpp" - -#include -#include -#include -#include - -namespace NekoGui_sys { - - ExternalProcess::ExternalProcess() : QProcess() { - // qDebug() << "[Debug] ExternalProcess()" << this << running_ext; - this->env = QProcessEnvironment::systemEnvironment().toStringList(); - } - - ExternalProcess::~ExternalProcess() { - // qDebug() << "[Debug] ~ExternalProcess()" << this << running_ext; - } - - void ExternalProcess::Start() { - if (started) return; - started = true; - - if (managed) { - connect(this, &QProcess::readyReadStandardOutput, this, [&]() { - auto log = readAllStandardOutput(); - if (logCounter.fetchAndAddRelaxed(log.count("\n")) > NekoGui::dataStore->max_log_line) return; - MW_show_log_ext_vt100(log); - }); - connect(this, &QProcess::readyReadStandardError, this, [&]() { - MW_show_log_ext_vt100(readAllStandardError().trimmed()); - }); - connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) { - if (!killed) { - crashed = true; - MW_show_log_ext(tag, "errorOccurred:" + errorString()); - MW_dialog_message("ExternalProcess", "Crashed"); - } - }); - connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { - if (state == QProcess::NotRunning) { - if (killed) { // 用户命令退出 - MW_show_log_ext(tag, "External core stopped"); - } else if (!crashed) { // 异常退出 - crashed = true; - MW_show_log_ext(tag, "[Error] Program exited accidentally: " + errorString()); - Kill(); - MW_dialog_message("ExternalProcess", "Crashed"); - } - } - }); - MW_show_log_ext(tag, "External core starting: " + env.join(" ") + " " + program + " " + arguments.join(" ")); - } - - QProcess::setEnvironment(env); - QProcess::start(program, arguments); - } - - void ExternalProcess::Kill() { - if (killed) return; - killed = true; - - if (!crashed) { - QProcess::kill(); - QProcess::waitForFinished(500); - } - } - - // - - QElapsedTimer coreRestartTimer; - - CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) : ExternalProcess() { - ExternalProcess::managed = false; - ExternalProcess::program = core_path; - ExternalProcess::arguments = args; - - connect(this, &QProcess::readyReadStandardOutput, this, [&]() { - auto log = readAllStandardOutput(); - if (!NekoGui::dataStore->core_running) { - if (log.contains("grpc server listening")) { - // The core really started - NekoGui::dataStore->core_running = true; - if (start_profile_when_core_is_up >= 0) { - MW_dialog_message("ExternalProcess", "CoreStarted," + Int2String(start_profile_when_core_is_up)); - start_profile_when_core_is_up = -1; - } - } else if (log.contains("failed to serve")) { - // The core failed to start - QProcess::kill(); - } - } - if (logCounter.fetchAndAddRelaxed(log.count("\n")) > NekoGui::dataStore->max_log_line) return; - MW_show_log(log); - }); - connect(this, &QProcess::readyReadStandardError, this, [&]() { - auto log = readAllStandardError().trimmed(); - if (show_stderr) { - MW_show_log(log); - return; - } - if (log.contains("token is set")) { - show_stderr = true; - } - }); - connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) { - if (error == QProcess::ProcessError::FailedToStart) { - failed_to_start = true; - MW_show_log("start core error occurred: " + errorString() + "\n"); - } - }); - connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { - if (state == QProcess::NotRunning) { - NekoGui::dataStore->core_running = false; - } - - if (!NekoGui::dataStore->prepare_exit && state == QProcess::NotRunning) { - if (failed_to_start) return; // no retry - if (restarting) return; - - MW_dialog_message("ExternalProcess", "CoreCrashed"); - - // Retry rate limit - if (coreRestartTimer.isValid()) { - if (coreRestartTimer.restart() < 10 * 1000) { - coreRestartTimer = QElapsedTimer(); - MW_show_log("[Error] " + QObject::tr("Core exits too frequently, stop automatic restart this profile.")); - return; - } - } else { - coreRestartTimer.start(); - } - - // Restart - start_profile_when_core_is_up = NekoGui::dataStore->started_id; - MW_show_log("[Error] " + QObject::tr("Core exited, restarting.")); - setTimeout([=] { Restart(); }, this, 1000); - } - }); - } - - void CoreProcess::Start() { - show_stderr = false; - // cwd: same as GUI, at ./config - ExternalProcess::Start(); - write((NekoGui::dataStore->core_token + "\n").toUtf8()); - } - - void CoreProcess::Restart() { - restarting = true; - QProcess::kill(); - QProcess::waitForFinished(500); - ExternalProcess::started = false; - Start(); - restarting = false; - } - -} // namespace NekoGui_sys diff --git a/sys/ExternalProcess.hpp b/sys/ExternalProcess.hpp deleted file mode 100644 index b70b64f..0000000 --- a/sys/ExternalProcess.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -namespace NekoGui_sys { - class ExternalProcess : public QProcess { - public: - QString tag; - QString program; - QStringList arguments; - QStringList env; - - bool managed = true; // MW_dialog_message - - ExternalProcess(); - ~ExternalProcess(); - - // start & kill is one time - - virtual void Start(); - - void Kill(); - - protected: - bool started = false; - bool killed = false; - bool crashed = false; - }; - - class CoreProcess : public ExternalProcess { - public: - CoreProcess(const QString &core_path, const QStringList &args); - - void Start() override; - - void Restart(); - - int start_profile_when_core_is_up = -1; - - private: - bool show_stderr = false; - bool failed_to_start = false; - bool restarting = false; - }; - - // 手动管理 - inline std::list> running_ext; - - inline QAtomicInt logCounter; -} // namespace NekoGui_sys diff --git a/sys/linux/LinuxCap.cpp b/sys/linux/LinuxCap.cpp deleted file mode 100644 index 5e65311..0000000 --- a/sys/linux/LinuxCap.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "LinuxCap.h" - -#include -#include -#include - -#define EXIT_CODE(p) (p.exitStatus() == QProcess::NormalExit ? p.exitCode() : -1) - -QString Linux_GetCapString(const QString &path) { - QProcess p; - p.setProgram(Linux_FindCapProgsExec("getcap")); - p.setArguments({path}); - p.start(); - p.waitForFinished(500); - return p.readAllStandardOutput(); -} - -int Linux_Pkexec_SetCapString(const QString &path, const QString &cap) { - QProcess p; - p.setProgram("pkexec"); - p.setArguments({Linux_FindCapProgsExec("setcap"), cap, path}); - p.start(); - p.waitForFinished(-1); - return EXIT_CODE(p); -} - -bool Linux_HavePkexec() { - QProcess p; - p.setProgram("pkexec"); - p.setArguments({"--help"}); - p.setProcessChannelMode(QProcess::SeparateChannels); - p.start(); - p.waitForFinished(500); - return EXIT_CODE(p) == 0; -} - -QString Linux_FindCapProgsExec(const QString &name) { - QString exec = QStandardPaths::findExecutable(name); - if (exec.isEmpty()) - exec = QStandardPaths::findExecutable(name, {"/usr/sbin", "/sbin"}); - - if (exec.isEmpty()) - qDebug() << "Executable" << name << "could not be resolved"; - else - qDebug() << "Found exec" << name << "at" << exec; - - return exec.isEmpty() ? name : exec; -} diff --git a/sys/linux/LinuxCap.h b/sys/linux/LinuxCap.h deleted file mode 100644 index 64750e4..0000000 --- a/sys/linux/LinuxCap.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -QString Linux_GetCapString(const QString &path); - -int Linux_Pkexec_SetCapString(const QString &path, const QString &cap); - -bool Linux_HavePkexec(); - -QString Linux_FindCapProgsExec(const QString &name); diff --git a/sys/windows/MiniDump.cpp b/sys/windows/MiniDump.cpp deleted file mode 100644 index 798df6b..0000000 --- a/sys/windows/MiniDump.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "MiniDump.h" - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include - -#include -#include -#include -#include - -typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)( - HANDLE hProcess, - DWORD dwPid, - HANDLE hFile, - MINIDUMP_TYPE DumpType, - CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); - -LONG __stdcall CreateCrashHandler(EXCEPTION_POINTERS *pException) { - QDir::setCurrent(QApplication::applicationDirPath()); - - HMODULE DllHandle = NULL; - DllHandle = LoadLibrary(_T("DBGHELP.DLL")); - - if (DllHandle) { - MINIDUMPWRITEDUMP Dump = (MINIDUMPWRITEDUMP) GetProcAddress(DllHandle, "MiniDumpWriteDump"); - if (Dump) { - // 创建 Dump 文件 - QDateTime CurDTime = QDateTime::currentDateTime(); - QString current_date = CurDTime.toString("yyyy_MM_dd_hh_mm_ss"); - // dmp文件的命名 - QString dumpText = "Dump_" + current_date + ".dmp"; - EXCEPTION_RECORD *record = pException->ExceptionRecord; - QString errCode(QString::number(record->ExceptionCode, 16)); - QString errAddr(QString::number((uintptr_t) record->ExceptionAddress, 16)); - QString errFlag(QString::number(record->ExceptionFlags, 16)); - QString errPara(QString::number(record->NumberParameters, 16)); - HANDLE DumpHandle = CreateFile((LPCWSTR) dumpText.utf16(), - GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (DumpHandle != INVALID_HANDLE_VALUE) { - MINIDUMP_EXCEPTION_INFORMATION dumpInfo; - dumpInfo.ExceptionPointers = pException; - dumpInfo.ThreadId = GetCurrentThreadId(); - dumpInfo.ClientPointers = TRUE; - // 将dump信息写入dmp文件 - Dump(GetCurrentProcess(), GetCurrentProcessId(), DumpHandle, MiniDumpNormal, &dumpInfo, - NULL, NULL); - CloseHandle(DumpHandle); - } else { - dumpText = ""; - } - // 创建消息提示 - QMessageBox::warning(NULL, "Application crashed", - QStringLiteral("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6") - .arg(errCode) - .arg(errAddr) - .arg(errFlag) - .arg(errPara) - .arg(NKR_VERSION) - .arg(dumpText), - QMessageBox::Ok); - } - } - return EXCEPTION_EXECUTE_HANDLER; -} - -void Windows_SetCrashHandler() { - SetErrorMode(SEM_FAILCRITICALERRORS); - SetUnhandledExceptionFilter(CreateCrashHandler); -} diff --git a/sys/windows/MiniDump.h b/sys/windows/MiniDump.h deleted file mode 100644 index a8fb3ff..0000000 --- a/sys/windows/MiniDump.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void Windows_SetCrashHandler(); diff --git a/sys/windows/guihelper.cpp b/sys/windows/guihelper.cpp deleted file mode 100644 index f29ebc1..0000000 --- a/sys/windows/guihelper.cpp +++ /dev/null @@ -1,24 +0,0 @@ - -#include "guihelper.h" - -#include - -#include -#include - -void Windows_QWidget_SetForegroundWindow(QWidget *w) { - HWND hForgroundWnd = GetForegroundWindow(); - DWORD dwForeID = ::GetWindowThreadProcessId(hForgroundWnd, NULL); - DWORD dwCurID = ::GetCurrentThreadId(); - ::AttachThreadInput(dwCurID, dwForeID, TRUE); - ::SetForegroundWindow((HWND) w->winId()); - ::AttachThreadInput(dwCurID, dwForeID, FALSE); -} - -int isThisAdmin = -1; // cached - -bool Windows_IsInAdmin() { - if (isThisAdmin >= 0) return isThisAdmin; - isThisAdmin = IsUserAnAdmin(); - return isThisAdmin; -} diff --git a/sys/windows/guihelper.h b/sys/windows/guihelper.h deleted file mode 100644 index 3969b4c..0000000 --- a/sys/windows/guihelper.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -class QWidget; - -void Windows_QWidget_SetForegroundWindow(QWidget* w); - -bool Windows_IsInAdmin(); diff --git a/test/test-qt512-sdk-build.sh b/test/test-qt512-sdk-build.sh deleted file mode 100644 index 9414691..0000000 --- a/test/test-qt512-sdk-build.sh +++ /dev/null @@ -1,7 +0,0 @@ -QT=$PWD/qtsdk/5.12.12/gcc_64 -BUILD=build-sdk-qt512 -rm -rf $BUILD -mkdir -p $BUILD -cd $BUILD -cmake -GNinja -DCMAKE_PREFIX_PATH=$QT .. -ninja diff --git a/test/test-qt6-build.sh b/test/test-qt6-build.sh deleted file mode 100644 index 7c1247b..0000000 --- a/test/test-qt6-build.sh +++ /dev/null @@ -1,5 +0,0 @@ -rm -rf build-qt6 -mkdir -p build-qt6 -cd build-qt6 -cmake -GNinja -DQT_VERSION_MAJOR=6 .. -ninja diff --git a/translations/fa_IR.ts b/translations/fa_IR.ts deleted file mode 100644 index 9fc28a3..0000000 --- a/translations/fa_IR.ts +++ /dev/null @@ -1,1651 +0,0 @@ - - - - - DialogBasicSettings - - Basic Settings - تنظیمات پایه - - - Enable - فعال کردن - - - Listen Address - آدرس درحال شنود - - - concurrency - همزمانی - - - User Agent - عامل کاربر - - - Common - متداول - - - Style - استایل - - - Theme - پوسته - - - System - سیستم - - - Subscription - اشتراک - - - Core - هسته - - - Extra Core - هسته اضافی - - - Select - برگزیدن - - - Edit - ویرایش - - - Custom Inbound - ورودی سفارشی - - - Concurrent - هم زمان - - - Use proxy when updating subscription - استفاده از پروکسی زمانی که اشتراک را بروزرسانی می کنید - - - Security - امنیت security - امنیت - - - Statistics refresh rate - نرخ تازه سازی آمار ترافیک - - - Off - خاموش - - - Add - اضافه کردن - - - Delete - حذف - حذف کردن - - - Please input the core name. - لطفا نام هسته را وارد کنید - - - Please select the core name. - لطفا نام هسته را انتخاب کنید - - - Connection statistics - آمار اتصال - - - Include Pre-release when checking update - هنگام بررسی به‌روزرسانی، نسخه پیش‌انتشار را نیز لحاظ شود - - - System proxy format - فرمت پروکسی سیستم - - - Set custom icon - تنظیم آیکون سفارشی - - - Hide dashboard at startup - مخفی کردن داشبورد هنگام راه‌اندازی - - - Clear servers before updating subscription - قبل از به‌روزرسانی اشتراک، سرورها را پاک شود - - - Ignore TLS errors when updating subscription - هنگام به‌روزرسانی اشتراک، خطاهای TLS را نادیده گرفته شود - - - Advanced system proxy settings. Please select a format. - تنظیمات پیشرفته پروکسی سیستم. لطفا یک قالب را انتخاب کنید. - - - Please select a PNG file. - لطفاً یک فایل PNG انتخاب کنید. - - - Reset - بازنشانی - - - Cancel - لغو کردن - - - Please select a valid square image. - لطفاً یک تصویر مربع معتبر انتخاب کنید. - - - Max log lines - حداکثر خطوط فایل لاگ - - - Inbound Auth - اعتبار ورودی - - - Username - نام کاربری - - - Password - رمز عبور - - - Skip TLS certificate authentication by default (allowInsecure) - رد شدن از احراز هویت گواهی TLS به طور پیش فرض (allowInsecure) - - - Default uTLS Fingerprint - اثرانگشت پیشفرض uTLS - - - Core Options - تنظیمات هسته - - - Override underlying DNS - لغو دی ان اس زیربنایی - - - Default On - به صورت پیشفرض فعال - - - Multiplex (mux) - Multiplex (mux) - - - Latency Test URL - آدرس تست تاخیر - - - Download Test URL - آدرس تست دانلود - - - Timeout (s) - تایم اوت (به ثانیه) - - - Automatic update - آپدیت اتوماتیک - - - Interval (minute, invalid if less than 30) - فاصله (به دقیقا ، اگر کمتر از ۳۰ باشد نادرست است) - - - Share VMess Link with v2rayN Format - - - - Old Share Link Format - - - - Mixed (SOCKS+HTTP) Listen Port - - - - - DialogEditGroup - - Edit Group - ویرایش گروه - - - Type - نوع - - - Name - نام - - - Basic - پایه - - - Subscription - اشتراک - - - Archive - بایگانی - - - URL - URL - - - Copy profile share links - لینک های اشتراک گذاری نمایه را کپی کنید - - - Copy profile share links (Neko Links) - لینک های اشتراک گذاری نمایه را کپی کنید (لینک های Neko) - - - Warning - هشدار - - - Please input URL - لطفا URL را وارد کنید - - - Copied - کپی شده است - - - Manually column width - عرض ستون به صورت دستی - - - Front Proxy - پروکسی front - - - None - هیچ یک - - - Clear - پاک کردن - - - Skip automatic update - لغو آپدیت اتوماتیک - - - Common - متداول - - - Share - اشتراک گذاری - - - - DialogEditProfile - - Edit - ویرایش کردن - - - Common - متداول - - - Type - نوع - - - Port - پورت - - - Address - آدرس - - - Name - اسم - - - Settings - تنظیمات - - - The underlying transport method. It must be consistent with the server, otherwise, the connection cannot be established. - روش انتقال باید با سرور سازگار باشد، در غیر این صورت، اتصال نمی تواند برقرار شود. - - - Network - شبکه - - - Transport Layer Security. It must be consistent with the server, otherwise, the connection cannot be established. - امنیت لایه انتقال باید با سرور سازگار باشد، در غیر این صورت، اتصال نمی تواند برقرار شود. - - - Security - امنیت - - - UDP FullCone Packet encoding for implementing features such as UDP FullCone. Server support is required, if the wrong selection is made, the connection cannot be made. Please leave it blank. - - - - Packet Encoding - رمزنگاری بسته ها - - - Network Settings (%1) - تنظیمات شبکه (1%) - - - When enabled, V2Ray will not check the validity of the TLS certificate provided by the remote host (the security is equivalent to plaintext) - - - - Allow insecure - اجازه ناامن بودن - - - Certificate - گواهی - - - Server name indication, clear text. - شناسه نام سرور ، متن صریح - - - Application layer protocol negotiation, clear text. Please separate them with commas. - - - - Custom (Extra Core) - سفارشی ( هسته اضافه) - - - Not set - تنظیم نشده - - - Already set - تنظیم شده - - - TLS Security Settings - تنظیمات امنیت TLS - - - TLS Camouflage Settings - تنظیمات استتار TLS - - - Reality public key. If not empty, turn TLS into REALITY. - - - - Custom (%1 outbound) - - - - Custom (%1 config) - - - - Custom Outbound Settings - - - - Custom Config Settings - - - - Apply settings to this group - تنظیمات به این گروه اعمال شود - - - Multiplex - - - - Keep Default - نگه داری مقدار پیشفرض - - - On - فعال - - - Off - خاموش - - - Confirm - تایید - - - Server support is required - نیازمند پشتیبانی در سمت سرور - - - Reality short id. Accept only one value. - - - - - DialogHotkey - - Hotkey - کلید میانبر - - - Show routes - مسیرها را نمایش بده - - - Show groups - نمایش گروه ها - - - Trigger main window - نمایش پنجره اصلی - - - System Proxy - پروکسی سیستم - - - - DialogManageGroups - - Groups - گروه هاگروه ها - - - New group - گروه جدید - - - Update all subscriptions - به روز رسانی تمام اشتراک ها - - - Confirmation - تاییدیه - - - Update all subscriptions? - آیا همه اشتراک ها بروزرسانی شوند؟ - - - - DialogManageRoutes - - Routes - مسیرها - - - Sniffing Mode - - - - Disable - غیرفعال کردن - - - Remote DNS - دی ان اس سمت-سرور - - - Direct DNS - دی ان اس مستقیم - - - Enable DNS Routing - فعال کردن مسیریابی DNS - - - Block - مسدود کردن - - - Direct - مستقیم - - - Domain - دامنه - - - Proxy - پروکسی - - - IP - آی پی - - - Preset - پیشفرض - - - Mange route set - مدیریت مسیرها - - - Bypass LAN and China - عبور ندادن ترافیک LAN و کشور چین - - - Global - همگانی - - - Load - بارگیری - - - Save - ذخیره کردن - - - Remove - حذف کردن - - - Cancel - لغو کردن - - - Load routing: %1 - بارگیری مسیر : 1% - - - Save routing: %1 - ذخیره مسیر : 1% - - - Remove routing: %1 - حذف مسیر : 1% - - - Default Outbound - - - - Domain Strategy - استراتژی دامنه - - - Server Address Strategy - استراتژی آدرس سرور - - - Sniff result for routing - - - - Sniff result for destination - - - - Common - متداول - - - DNS - دی ان اس - - - Simple DNS Settings - تنظیمات دی ان اس ساده - - - Use DNS Object - - - - DNS Object Settings - - - - Simple Route - مسیر ساده - - - Custom Route - مسیر سفارشی - - - Custom Route (global) - - - - Note: Other settings are independent for each route set. - - - - Route sets - - - - Query Strategy - - - - Document - اسناد - - - Format - فرمت - - - This is especially important and it is recommended to use the default value of "localhost". -If the default value does not work, try changing it to "223.5.5.5". -For more information, see the document "Configuration/DNS". - - - - Final DNS Out - - - - - DialogVPNSettings - - Tun Settings - تنظیمات vpn - - - Hide Console - مخفی کردن کنسول - - - Tun Enable IPv6 - - - - Bypass CIDR - - - - Bypass Process Name - - - - Whether blacklisted or whitelisted, your traffic will be handled by nekobox_core (sing-tun). This is NOT equal to "process mode" of some software. - - - - Whitelist mode - حالت لیست سفید - - - Proxy CIDR - - - - Proxy Process Name - نام پروسه پراکسی - - - Troubleshooting - عیب یابی - - - If you have trouble starting VPN, you can force reset nekobox_core process here. - -If still not working, see documentation for more information. -https://matsuridayo.github.io/n-configuration/#vpn-tun - - - - Reset - بازنشانی - - - Cancel - لغو کردن - - - Internal Tun - حالت تونل داخلی - - - Add a tun inbound to the profile startup, instead of using two processes. -This needs to be run NekoBox with administrator privileges. - - - - - EditChain - - Traffic order is from top to bottom - عبور ترافیک از بالا به پایین است - - - Select Profile - انتخاب کردن پروفایل - - - Name cannot be empty. - نام نمیتواند خالی باشد - - - - EditCustom - - Core - هسته - - - Json Editor - ویرایشگر Json - - - Command - فرمان - - - Config Suffix - پسوند کانفیگ - - - Outbound JSON, please read the documentation. - JSON خروجی، لطفاً مستندات را بخوانید. - - - Please pick a core. - لطفا یک هسته انتخاب کنید. - - - Random if it's empty or zero. - - - - Preview - پیش نمایش - - - Preview replace - - - - Preview config - - - - Please fill the complete config. - - - - Name cannot be empty. - نام نمیتواند خالی باشد - - - - EditNaive - - Protocol - پروتکل - - - Password - رمزعبور - - - Extra headers - - - - SNI - - - - Username - نام کاربری - - - Certificate - گواهی - - - Insecure concurrency - همزمانی ناامن - - - Disable logs - - - - Turn on this option if your connection is lost after a while - - - - - EditQUIC - - Certificate - گواهی - - - Download (Mbps) - دانلود (مگابیت بر ثانیه) - - - Disable MTU Discovery - - - - Hop Interval (s) - - - - Allow Insecure - اجازه ارتباطات ناامن داده شود - - - Hop Port - - - - Upload (Mbps) - آپلود (مگابیت بر ثانیه) - - - Obfs Password - - - - SNI - - - - Disable SNI - - - - Password - - - - Generate UUID - - - - Heartbeat - - - - Zero Rtt Handshake - - - - Congestion Control - - - - UDP Relay Mode - - - - Force use external core - - - - - EditShadowSocks - - Plugin Args - - - - Password - کلمه عبور - - - Encryption - رمزگذاری - - - Plugin - پلاگین - - - Version of UDP over TCP protocol, server support is required. - - - - Off - خاموش - - - - EditSocksHttp - - Version - نسخه - - - Username - نام کاربری - - - Password - کلمه عبور - - - - EditTrojanVLESS - - Password - کلمه عبور - - - - EditVMess - - Alter Id - - - - Security - امنیت - - - UUID - - - - Generate UUID - - - - - GroupItem - - Update Subscription - به روز رسانی اشتراک - - - Edit - ویرایش - - - Remove - حذف کردن - - - Basic - پایه - - - Subscription - اشتراک - - - Archive - آرشیو - - - Last update: %1 - آخرین آپدیت : %1 - - - Confirmation - تایید - - - Remove %1? - حذف %1? - - - - JsonEditor - - JSON Editor - ویرایشگر JSON - - - Format JSON - فرمت JSON - - - Remove All Comments - حذف همه کامنت ها - - - Json Editor - ویرایشگر Json - - - Structure Preview - پیش نمایش ساختار - - - OK - تایید - - - Json Contains Syntax Errors - - - - Original Json may contain syntax errors. Json tree is disabled. - - - - You must correct these errors before continuing. - شما باید این ایرادات را قبل از ادامه دادن اصلاح کنید - - - Syntax Errors - خطاهای نحوی - - - Please fix the JSON errors or remove the comments before continue - لطفا قبل از ادامه دادن ایرادات JSON را اصلاح کنید یا کامنت ها را حذف کنید - - - - MainWindow - - Program - برنامه - - - Preferences - تنظیمات - - - Server - سرور - - - Ads - تبلیغات - - - Document - اسناد - - - Update - بروزرسانی - - - Tun Mode - حالتvpn - - - System Proxy - پروکسی سیستمی - - - Type - نوع - - - Address - آدرس - - - Name - اسم - - - Test Result - نتیجه تست - - - Traffic - ترافیک - - - Log - ثبت رویدادها - - - Connection - اتصال - - - Status - وضعیت - - - Outbound - خروجی - - - Destination - مقصد - - - Active Server - سرور فعال - - - Active Routing - مسیریابی فعال - - - Share - اشتراک گذاری - - - Current Group - گروه فعلی - - - Exit - خروج - - - Basic Settings - تنظیمات پایه - - - New profile - پروفایل جدید - - - Groups - گروه ها - - - Start - آغازکردن - - - Stop - متوقف کردن - - - Routing Settings - تنظیمات مسیریابی - - - Add profile from clipboard - افزودن پروفایل از کلیپ بورد - - - Delete - از بین بردن - - - Debug Info - اطلاعات اشکال زدایی - - - QR Code and link - کد QR و پیوند - - - Copy Link - لینک را کپی کنید - - - Clear Test Result - نتایج تست پاک شود - - - Export %1 config - - - - Reset Traffic - بازنشانی ترافیک - - - Scan QR Code - کد QR را اسکن کنید - - - Enable System Proxy - پروکسی سیستم را فعال کنید - - - Disable - غیرفعال کردن - - - Remove Duplicates - موارد تکراری را حذف کردن - - - fake - جعلی - - - Move - جابجایی - - - Start with system - با سیستم شروع شود - - - Remember last profile - آخرین پروفایل را به خاطر بسپار - - - Allow other devices to connect - به دستگاه های دیگر اجازه اتصال دهید - - - Remove Unavailable - غیرقابل دستیابی پاک شود - - - Full Test - تست کامل - - - Hotkey Settings - تنظیمات کلید میانبر - - - Select All - انتخاب همه - - - Copy links of selected (Neko Links) - کپی لینک های انتخاب شده (پیوندهای Neko) - - - Copy links of selected - لینک های انتخاب شده را کپی کنید - - - Enable Tun - فعال کردن tun - - - Clone - همزادسازی - - - Update subscription - اشتراک را به روز کنید - - - Resolve domain - دامنه را حل کنید - - - Tun Settings - تنظیمات vpn - - - Restart Program - اجرا دوباره برنامه - - - Open Config Folder - پوشه Config باز شود - - - Load routing and apply: %1 - - - - Error - خطا - - - Tun Settings changed - تنظیمات Tun تغییر کرد - - - Restart Tun to take effect. - Tun را مجدداً راه اندازی کنید تا اعمال شود. - - - Confirmation - تائیدیه - - - Settings changed, restart proxy? - تنظیمات تغییر کرد، پراکسی راه اندازی مجدد شود؟ - - - Imported %1 profile(s) - - - - Current server is incompatible with Tun. Please stop the server first, enable Tun Mode, and then restart. - - - - Not Running - در حال اجرا نیست - - - Select - انتخاب - - - Clone %1 item(s) - - - - Move %1 item(s) - - - - Remove %1 item(s) ? - - - - Copied %1 item(s) - - - - Config copied - کانفیگ کپی شد - - - QR Code not found - کد QR یافت نشد - - - Resolving domain to IP, if support. - - - - Set ignore keyword - کلیدواژه نادیده گرفتن را تنظیم کنید - - - Set the following keywords to ignore? -Split by line. - - - - Save as route - ذخیره به عنوان مسیر - - - Edit - ویرایش کردن - - - Save "%1" as a routing rule? - - - - Clear - پاک کردن - - - Start: %1 -End: %2 - - - - Failed to stop Tun process - فرآیند Tun متوقف نشد - - - [%1] test error: %2 - - - - Testing - آزمایش کردن - - - Unavailable - غیرقایل دسترسی - - - Starting profile %1 - اغاز پروفایل %1 - - - Stopping profile %1 - متوقف کردن پروفایل %1 - - - Current Select - انتخاب فعلی - - - Show Window - نمایش پنجره برنامه - - - Settings changed - تنظیمات تغییر کرد - - - Please run NekoBox as admin - لطفا Nekobox را با مجوز ادمین اجرا کنید - - - Restart Proxy - راه اندازی مجدد پروکسی - - - If there is no response for a long time, it is recommended to restart the software. - اگر برای مدت زمان طولانی هیچ پاسخی دریافت نشد ، پیشنهاد میشود که نرم افزار را دوباره اجرا کنید - - - Failed to start profile %1 - - - - Failed to stop, please restart the program. - توقف ناموفق بود ، لطفا برنامه را دوباره اجرا کنید - - - Select mode, double-click or press Enter to select a profile, press ESC to exit. - - - - Latency - تاخیر - - - UDP latency - - - - Download speed - سرعت دانلود - - - In and Out IP - - - - Test Options - گزینه های تست - - - Restart the program to take effect. - برای مشاهده نتیجه برنامه را دوباره راه اندازی کنید - - - Stop Testing - - - - URL Test - - - - - ProxyItem - - Confirmation - تائیدیه - - - Remove %1? - حذف %1? - - - - QGuiApplication - - QT_LAYOUT_DIRECTION - RTL - - - - QObject - - Used: %1 Remain: %2 Expire: %3 - - - - Select - انتخاب کردن - - - Update - بروزرسانی - - - No update - بدون بروزرسانی جدید - - - Update found: %1 -Release note: -%2 - - - - Open in browser - در مرور گر باز شود - - - Close - بستن - - - Update is ready, restart to install? - به روز رسانی آماده است، برای نصب مجدد راه اندازی شود؟ - - - As link - به عنوان لینک - - - url detected - آدرس شناسایی شد - - - %1 -How to update? - چگونه بروزرسانی کنم ؟ - - - Requesting subscription: %1 - - - - Requesting subscription %1 error: %2 - - - - Clearing servers... - پاک کردن سرورها - - - Added %1 profiles: -%2 -Deleted %3 Profiles: -%4 - - - - Nothing - خالی - - - Change of %1: - - - - Chain Proxy - پروکسی زنجیره ای - - - Core not found: %1 - هسته برنامه یافت نشد : %1 - - - Proxy: %1 -Direct: %2 - - - - Unavailable - غیرقابل دسترس - - - Request with proxy but no profile started. - - - - Subscription request fininshed: %1 - - - - Core exited, restarting. - هسته برنامه متوقف شد ، در حال راه اندازی مجدد - - - Core exits too frequently, stop automatic restart this profile. - - - - As Subscription (create new group) - - - - As Subscription (add to this group) - - - - Default - پیش فرض - - - The last speed test did not exit completely, please wait. If it persists, please restart the program. - - - - - Qv2ray::ui::widgets::AutoCompleteTextEdit - - You can not input space characters here. - شما نمی توانید کاراکتر فضای خالی در اینجا استفاده کنید. - - - diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts deleted file mode 100644 index 7abdd86..0000000 --- a/translations/ru_RU.ts +++ /dev/null @@ -1,1664 +0,0 @@ - - - - - DialogBasicSettings - - Basic Settings - Основные настройки - - - Common - Общие - - - Listen Address - Адрес входящих подключений - - - Custom Inbound - Кастомный inbound - - - Edit - Изменить - - - Enable - Вкл - - - Latency Test URL - URL теста задержки - - - Concurrent - Параллельно - - - Download Test URL - URL теста загрузки - - - Include Pre-release when checking update - Проверять пре-релизы при обновлениях - - - System proxy format - Формат строки системного прокси - - - Style - Стиль - - - Theme - Тема - - - System - Системная - - - Set custom icon - Задать иконку - - - Statistics refresh rate - Период обновления статистики - - - Off - Выкл - - - Connection statistics - Статистика подключений - - - Hide dashboard at startup - Спрятать окно при старте - - - Max log lines - Макс. строк в логе - - - Subscription - Подписка - - - User Agent - User Agent - - - Use proxy when updating subscription - Использовать прокси при обновлении подписок - - - Ignore TLS errors when updating subscription - Игнорировать ошибки TLS при обновлении подписок - - - Clear servers before updating subscription - Очищать список серверов при обновлении подписок - - - Core - Ядро - - - Select - Выбрать - - - Multiplex (mux) - Мультиплексирование (mux) - - - concurrency - многопоточность - - - Default On - Вкл. по умолчанию - - - Core Options - Параметры ядра - - - Extra Core - Дополнительные ядра - - - Add - Добавить - - - Delete - Удалить - - - Security - Безопасность - - - Skip TLS certificate authentication by default (allowInsecure) - Не проверять подлинность TLS сертификатов по умолчанию - - - Default uTLS Fingerprint - uTLS fingerprint по умолчанию - - - Advanced system proxy settings. Please select a format. - Дополнительные настройки системного прокси. Пожалуйста, выберите формат. - - - Please input the core name. - Введите имя ядра. - - - Please select the core name. - Выберите имя ядра. - - - Please select a PNG file. - Выберите PNG файл - - - Reset - Сброс - - - Cancel - Отмена - - - Please select a valid square image. - Пожалуйста, выберите корректное квадратное изображение. - - - Inbound Auth - Аутентификация inbound - - - Username - Имя пользователя - - - Password - Пароль - - - Override underlying DNS - Переопределить нижестоящий DNS - - - Timeout (s) - Таймаут (с) - - - Automatic update - Автоматическое обновление - - - Interval (minute, invalid if less than 30) - Интервал (в минутах, значение считается неправильным, если меньше 30) - - - Share VMess Link with v2rayN Format - Поделиться ссылкой VMess в формате v2rayN - - - Old Share Link Format - Поделиться ссылкой в старом формате - - - Mixed (SOCKS+HTTP) Listen Port - - - - - DialogEditGroup - - Edit Group - Изменить группу - - - Name - Имя - - - Manually column width - Уст. ширину колонок - - - Archive - Архив - - - Type - Тип - - - Basic - Простая - - - Subscription - Подписка (subscription) - - - Front Proxy - Фронт-прокси - - - Clear - Сбросить - - - URL - URL - - - Copy profile share links - Скопировать ссылки на профиль - - - Copy profile share links (Neko Links) - Скопировать ссылки на профиль (Neko links) - - - Copied - Скопировано - - - Warning - Предупреждение - - - Please input URL - Пожалуйста, введите URL - - - None - Нет - - - Skip automatic update - Пропустить автоматическое обновление - - - Common - Общие - - - Share - Поделиться - - - - DialogEditProfile - - Edit - Редактировать - - - Common - Общие - - - Type - Тип - - - Port - Порт - - - Address - Адрес - - - Name - Имя - - - Custom Outbound Settings - Доп. настройки outbound - - - Custom Config Settings - Доп. настройки конфига - - - Apply settings to this group - Применить настройки к этой группе - - - Settings - Настройки - - - The underlying transport method. It must be consistent with the server, otherwise, the connection cannot be established. - Нижележащий транспорт. Должен соответствовать конфигурации сервера, иначе подключение будет невозможно. - - - Network - Транспорт - - - Transport Layer Security. It must be consistent with the server, otherwise, the connection cannot be established. - TLS. Должно совпадать с параметрами сервера, иначе подключение будет невозможно. - - - Security - Безопасность - - - UDP FullCone Packet encoding for implementing features such as UDP FullCone. Server support is required, if the wrong selection is made, the connection cannot be made. Please leave it blank. - UDP FullCone кодирование пакетов для реализации функционала типа UDP FullCone. Необходима поддержка со стороны сервера, при неправильном выборе подключение не будет работать. Оставьте пустым, если вы не знаете что и зачем это. - - - Packet Encoding - Кодирование пакетов - - - Server support is required - Необходима поддержка со стороны сервера - - - Multiplex - Мультиплексирование - - - Keep Default - По умолчанию - - - On - Вкл - - - Off - Выкл - - - Network Settings (%1) - Настройки транспорта (%1) - - - TLS Security Settings - Настройки TLS - - - When enabled, V2Ray will not check the validity of the TLS certificate provided by the remote host (the security is equivalent to plaintext) - Если вкл., то клиент не будет проверять валидность TLS-сертификата, предоставленного сервером - - - Allow insecure - Разрешить небезопасн. - - - Certificate - Сертификат - - - Server name indication, clear text. - SNI (идентификатор сервера, передается в открытом виде). - - - Application layer protocol negotiation, clear text. Please separate them with commas. - ALPN, идентификатор протокола приложения, передается открытым текстом. Используйте запятую в качестве разделителя. - - - TLS Camouflage Settings - Настройки маскировки TLS - - - Reality public key. If not empty, turn TLS into REALITY. - Публичный ключ Reality. Если задано значение, то будет использован протокол Reality для TLS. - - - Reality short id. Accept only one value. - Короткий ID для Reality. Можно задать только одно значение. - - - Custom (%1 outbound) - Кастомный (%1 outbound) - - - Custom (%1 config) - Кастомный (%1 конфиг) - - - Custom (Extra Core) - Кастомный (доп. ядро) - - - Not set - Не задано - - - Already set - Уже задано - - - Confirm - Подтвердить - - - - DialogHotkey - - Hotkey - Горячие клавиши - - - Show routes - Показать маршруты - - - Show groups - Показать группы - - - Trigger main window - Показать главное окно - - - System Proxy - Режим системного прокси - - - - DialogManageGroups - - Groups - Группы - - - New group - Новая группа - - - Update all subscriptions - Обновить все подписки - - - Confirmation - Подтвердить - - - Update all subscriptions? - Обновить все подписки? - - - - DialogManageRoutes - - Routes - Маршруты - - - Common - Общие - - - Route sets - Набор маршрутов - - - Mange route set - Изменить набор маршрутов - - - Custom Route (global) - Кастомные маршруты (global) - - - Note: Other settings are independent for each route set. - Остальные настройки будут индивидуальны -для каждого набора маршрутов. - - - Domain Strategy - Стратегия доменов - - - Disable - Выкл - - - Sniff result for routing - Подслушивать для маршрутизации - - - Sniff result for destination - Подслушивать для точки назначения - - - Sniffing Mode - Режим подслушивания - - - Server Address Strategy - Стратегия выбора адреса сервера - - - DNS - DNS - - - Simple DNS Settings - Базовые настройки DNS - - - Direct DNS - DNS для "прямых" запросов - - - Query Strategy - Стратегия запросов - - - Remote DNS - Удаленный (remote) DNS - - - Enable DNS Routing - Вкл. DNS-маршрутизацию - - - DNS Object Settings - Специальные настройки DNS - - - Use DNS Object - Использовать DNS-объект - - - Format - Форматировать - - - Document - Помощь - - - Simple Route - Базовые маршруты - - - Block - Блок - - - Direct - Напрямую - - - Domain - Домен - - - Proxy - Прокси - - - IP - IP - - - Preset - Пресет - - - Custom Route - Кастомные маршруты - - - Default Outbound - Outbound по-умолчанию - - - Bypass LAN and China - Пропускать LAN и китайские ресурсы - - - Global - Глобально - - - Load - Загрузить - - - Save - Сохранить - - - Remove - Удалить - - - Cancel - Отменить - - - Load routing: %1 - Загрузить машруты: %1 - - - Save routing: %1 - Сохранить маршруты: %1 - - - Remove routing: %1 - Удалить маршруты: %1 - - - This is especially important and it is recommended to use the default value of "localhost". -If the default value does not work, try changing it to "223.5.5.5". -For more information, see the document "Configuration/DNS". - - - - Final DNS Out - - - - - DialogVPNSettings - - Tun Settings - Настройки Tun - - - Tun Enable IPv6 - Вкл. IPv6 в Tun - - - Add a tun inbound to the profile startup, instead of using two processes. -This needs to be run NekoBox with administrator privileges. - Добавить inbound c Tun в конфигурацию профиля вместо того, чтобы использовать два отдельных процесса. -Для этого необходимо запускать NekoBox c правами администратора. - - - Internal Tun - Встроен. Tun - - - Hide Console - Скрывать окно - - - Bypass CIDR - Пропускать CIDR - - - Bypass Process Name - Пропускать процессы - - - Whether blacklisted or whitelisted, your traffic will be handled by nekobox_core (sing-tun). This is NOT equal to "process mode" of some software. - При использовании белого или черного списка, ваш трафик будет обработан ядром nekobox (sign-tun). Это НЕ эквивалент "process mode" как в некотором ПО. - - - Whitelist mode - Режим белого списка - - - Troubleshooting - Исправление проблем - - - Proxy CIDR - Проксировать CIDR - - - Proxy Process Name - Проксировать процессы - - - If you have trouble starting VPN, you can force reset nekobox_core process here. - -If still not working, see documentation for more information. -https://matsuridayo.github.io/n-configuration/#vpn-tun - Если у вас проблемы с запуском VPN, можно принудительно перезапустить процесс nekobox-core. - -Если ничего по-прежнему не работает, ознакомьтесь с документацией: -https://matsuridayo.github.io/n-configuration/#vpn-tun - - - Reset - Перезапустить - - - Cancel - Отмена - - - - EditChain - - Traffic order is from top to bottom - Порядок трафика сверху вниз - - - Select Profile - Выбор профиля - - - Name cannot be empty. - Имя не может быть пустым. - - - - EditCustom - - Core - Ядро - - - Json Editor - Редактор JSON - - - Command - Команда - - - Config Suffix - Суффикс конфига - - - Random if it's empty or zero. - Если пусто или 0, то будет выбран случайным образом. - - - Preview - Предпросмотр - - - Outbound JSON, please read the documentation. - Outbound JSON, читайте документацию. - - - Please fill the complete config. - Пожалуйста, введите полную конфигурацию. - - - Preview replace - Предпросмотр замен - - - Preview config - Предпросмотр конфигурации - - - Name cannot be empty. - Имя не может быть пустым. - - - Please pick a core. - Пожалуйста, выберите ядро. - - - - EditNaive - - Protocol - Протокол - - - Password - Пароль - - - Extra headers - Дополнительные заголовки - - - SNI - SNI - - - Username - Имя пользователя - - - Certificate - Сертификат - - - Insecure concurrency - - - - Disable logs - - - - Turn on this option if your connection is lost after a while - - - - - EditQUIC - - Download (Mbps) - Скорость приема (Mbps) - - - Disable MTU Discovery - Выкл. MTU discovery - - - Hop Interval (s) - - - - Certificate - Сертификат - - - Allow Insecure - Разрешить небезопасные - - - Hop Port - - - - Upload (Mbps) - Скорость отдачи (Mbps) - - - Obfs Password - Пароль для обфускации - - - SNI - SNI - - - Disable SNI - Отключить SNI - - - Password - Пароль - - - Generate UUID - Сгенерировать UUID - - - Heartbeat - Сердцебиение (Hearbeat) - - - Zero Rtt Handshake - Без рукопожатия Rtt - - - Congestion Control - Управление перегрузкой - - - UDP Relay Mode - Режим UDP Relay - - - Force use external core - - - - - EditShadowSocks - - Encryption - Шифрование - - - Plugin - Плагин - - - Password - Пароль - - - Plugin Args - Аргументы - - - Version of UDP over TCP protocol, server support is required. - Версия протокола "UDP over TCP", требуется поддержка со стороны сервера. - - - Off - Выкл - - - - EditSocksHttp - - Version - Версия - - - Username - Имя пользователя - - - Password - Пароль - - - - EditTrojanVLESS - - Password - Пароль - - - - EditVMess - - Security - Шифрование - - - Alter Id - Альт. ID - - - UUID - UUID - - - Generate UUID - Сгенерировать UUID - - - - GroupItem - - Update Subscription - Обновить - - - Edit - Изменить - - - Remove - Удалить - - - Basic - Простая - - - Subscription - Подписка - - - Archive - Архив - - - Last update: %1 - Последнее обновление: %1 - - - Confirmation - Подтверждение - - - Remove %1? - Удалить %1? - - - - JsonEditor - - JSON Editor - Редактор JSON - - - Format JSON - Форматировать JSON - - - Remove All Comments - Удалить все комментарии - - - Json Editor - Редактор JSON - - - Structure Preview - Предпросмотр структуры - - - OK - OK - - - Json Contains Syntax Errors - JSON содержит синтаксические ошибки - - - Original Json may contain syntax errors. Json tree is disabled. - Кажется, оригинальный JSON содержит синтатсические ошибки. Дерево JSON отключено. - - - You must correct these errors before continuing. - Вы должны исправить эти ошибки чтобы продолжить. - - - Syntax Errors - Синтаксические ошибки - - - Please fix the JSON errors or remove the comments before continue - Пожалуйста, чтобы продолжить, исправьте ошибки в JSON или удалите комментарии - - - - MainWindow - - Program - Программа - - - Preferences - Настройки - - - Server - Сервер - - - Ads - Реклама - - - Document - Документация - - - Update - Обновление - - - Tun Mode - Режим TUN - - - System Proxy - Режим системного прокси - - - Type - Тип - - - Address - Адрес - - - Name - Имя - - - Test Result - Результат теста - - - Traffic - Трафик - - - Log - Журнал - - - Connection - Подключение - - - Status - Статус - - - Outbound - Outbound - - - Destination - Пункт назначения - - - Active Server - Активный сервер - - - Active Routing - Активное правило роутинга - - - Share - Поделиться - - - Current Group - Текущая группа - - - Current Select - Текущий выбор - - - Exit - Выход - - - Show Window - Показать окно - - - Basic Settings - Основные настройки - - - New profile - Новый профиль - - - Groups - Группы - - - Start - Запустить - - - Stop - Остановить - - - Routing Settings - Настройки маршрутов - - - Add profile from clipboard - Добавить профиль из буфера обмена - - - Delete - Удалить - - - Debug Info - Отладочная информация - - - QR Code and link - QR-код и ссылка - - - Copy Link - Скопировать ссылку - - - Clear Test Result - Очистить результат теста - - - Export %1 config - Экспортировать конфиг %1 - - - Reset Traffic - Сбросить трафик - - - Scan QR Code - Сканировать QR-код - - - Enable System Proxy - Активировать системный прокси - - - Disable - Отключить - - - Remove Duplicates - Удалить дубликаты - - - fake - фейк - - - Move - Переместить - - - Start with system - Запускаться вместе с системой - - - Remember last profile - Запомнить последний профиль - - - Allow other devices to connect - Разрешить подключаться другим устройствам - - - Remove Unavailable - Удалить недоступные - - - Full Test - Полный тест - - - Hotkey Settings - Настройки комбинаций клавиш - - - Select All - Выбрать всё - - - Copy links of selected (Neko Links) - Скопировать ссылки для выбранных (Neko links) - - - Copy links of selected - Скопировать ссылки для выбранных - - - Enable Tun - Включить TUN-режим - - - Clone - Клонировать - - - Update subscription - Обновить подписки - - - Resolve domain - Ну слово "разрешить" можно использоваться и в значении "разобраться" - Разрешить доменное имя - - - Tun Settings - Настройки TUN-режима - - - Restart Program - Перезапустить программу - - - Open Config Folder - Открыть папку с конфигами - - - Restart Proxy - Перезапустить прокси - - - Load routing and apply: %1 - Загрузить маршруты и активировать: %1 - - - Error - Ошибка - - - Tun Settings changed - Настройки TUN изменились - - - Restart Tun to take effect. - Перезапустите TUN чтобы применить изменения. - - - Confirmation - Подтверждение - - - Settings changed, restart proxy? - Настройки изменены, перезапустить прокси? - - - Settings changed - Настройки изменены - - - Restart the program to take effect. - Перезапустите программу чтобы новые настройки вступили в силу. - - - Imported %1 profile(s) - Импортирован(ы) %1 профиль(ей) - - - Please run NekoBox as admin - Пожалуйста, запустите NekoBox с правами администратора - - - Current server is incompatible with Tun. Please stop the server first, enable Tun Mode, and then restart. - Текущий сервер не совместим с TUN-режимом. Пожалуйста, сначала остановите подключение к серверу, активируйте TUN-режим, и потом перезапустите. - - - Not Running - Не запущен - - - Select - Выбор - - - Select mode, double-click or press Enter to select a profile, press ESC to exit. - Режим выбора, дважды кликните или нажмите Enter для выбора профиля, либо ESC чтобы выйти. - - - Clone %1 item(s) - Клонировать %1 записей - - - Move %1 item(s) - Переместить %1 записей - - - Remove %1 item(s) ? - Удалить %1 записей ? - - - Copied %1 item(s) - Скопировано %1 записей - - - Config copied - Конфиг скопирован - - - QR Code not found - QR-код не найден - - - Resolving domain to IP, if support. - Отрезолвить домен в IP-адрес, если поддерживается. - - - Set ignore keyword - Ключевые слова для игнорирования - - - Set the following keywords to ignore? -Split by line. - Задайте ключнвые слова для игнорирования, -каждое на отдельной строке. - - - Save as route - Сохранить как маршрут - - - Edit - Редактировать - - - Save "%1" as a routing rule? - Сохранить "%1" как профиль маршрутизации? - - - Clear - Очистить - - - Start: %1 -End: %2 - Начало %1 -Конец %2 - - - Failed to stop Tun process - Не удалось остановить TUN-процесс - - - Test Options - Параметры тестирования - - - Latency - Задержка - - - UDP latency - Задержка UDP - - - Download speed - Скорость загрузки - - - In and Out IP - Входящий и исходящий IP - - - [%1] test error: %2 - [%1] ошибка теста: %2 - - - Testing - Тестируем - - - Unavailable - Недоступен - - - If there is no response for a long time, it is recommended to restart the software. - Если нет ответа в течении долгого времени, рекомендуем перезапустить приложение. - - - Starting profile %1 - Запускаем профиль %1 - - - Failed to start profile %1 - Не удалось запустить профиль %1 - - - Stopping profile %1 - Останавливаем профиль %1 - - - Failed to stop, please restart the program. - Не удалось остановить, пожалуйста, перезапустите приложение. - - - Stop Testing - - - - URL Test - - - - - ProxyItem - - Confirmation - Подтверждение - - - Remove %1? - Удалить %1? - - - - QGuiApplication - - QT_LAYOUT_DIRECTION - - - - - QObject - - Core not found: %1 - Ядро не найдено: %1 - - - Unavailable - Недоступно - - - Proxy: %1 -Direct: %2 - Через прокси: %1 -Напрямую: %2 - - - Chain Proxy - Цепочка прокси - - - Request with proxy but no profile started. - Запрос через прокси, но профиль не запущен. - - - As Subscription (add to this group) - Как подписку (добавить в эту группу) - - - As Subscription (create new group) - Как подписку (создать новую группу) - - - As link - Как ссылку - - - url detected - Обнаружен URL - - - %1 -How to update? - %1 -Как обновить? - - - Requesting subscription: %1 - Запрашиваем подписку: %1 - - - Requesting subscription %1 error: %2 - Запрашиваем подписку %1 ошибка: %2 - - - Subscription request fininshed: %1 - Запрос подписки завершен: %1 - - - Clearing servers... - Очишаем серверы... - - - Added %1 profiles: -%2 -Deleted %3 Profiles: -%4 - Добавлено %1 профилей: -%2 -Удалено %3 профилей: -%4 - - - Nothing - Ничего - - - Change of %1: - Изменение %1: - - - Core exits too frequently, stop automatic restart this profile. - Ядро слишком часто прекращает свою работу, отмена автоматического перезапуска этого профиля. - - - Core exited, restarting. - Ядро прекратило свою работу, перезапуск. - - - Select - Выбор - - - Update - Обновление - - - No update - Нет обновлений - - - Update found: %1 -Release note: -%2 - Найдено обновление: %1 -Примечания к выпуску: -%2 - - - Open in browser - Открыть в браузере - - - Close - Закрыть - - - Update is ready, restart to install? - Обновление готово, перезапуститься для установки? - - - Used: %1 Remain: %2 Expire: %3 - Использовано: %1, осталось: %2, истекло: %3 - - - Default - По умолчанию - - - The last speed test did not exit completely, please wait. If it persists, please restart the program. - - - - - Qv2ray::ui::widgets::AutoCompleteTextEdit - - You can not input space characters here. - Сюда нельзя вводить пробелы. - - - diff --git a/translations/translations.qrc b/translations/translations.qrc deleted file mode 100644 index 44f03db..0000000 --- a/translations/translations.qrc +++ /dev/null @@ -1,7 +0,0 @@ - - - zh_CN.qm - fa_IR.qm - ru_RU.qm - - diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts deleted file mode 100644 index 8a9f13a..0000000 --- a/translations/zh_CN.ts +++ /dev/null @@ -1,1664 +0,0 @@ - - - - - DialogBasicSettings - - Basic Settings - 基本设置 - - - Enable - 启用 - - - Listen Address - 监听地址 - - - concurrency - 并发 - - - User Agent - - - - Common - 通用 - - - Style - 样式 - - - Theme - 主题 - - - System - 系统 - - - Subscription - 订阅 - - - Core - 核心 - - - Extra Core - 其他核心 - - - Select - 选择 - - - Edit - 编辑 - - - Custom Inbound - 自定义入站 - - - Concurrent - 并发 - - - Use proxy when updating subscription - 更新订阅时使用代理 - - - Security - 安全 - - - Statistics refresh rate - 流量统计刷新率 - - - Off - 关闭 - - - Add - 添加 - - - Delete - 删除 - - - Please input the core name. - 请输入核心名. - - - Please select the core name. - 请选择核心名. - - - Connection statistics - 连接统计 - - - Include Pre-release when checking update - 检查更新时包括 Pre-release 版本 - - - Set custom icon - 自定义图标 - - - Please select a PNG file. - 请选择一个 PNG 文件。 - - - Reset - 重置 - - - Please select a valid square image. - 请选择有效的正方形图像。 - - - Cancel - 取消 - - - System proxy format - 系统代理格式 - - - Advanced system proxy settings. Please select a format. - 高级系统代理设置。请选择一种格式。 - - - Old Share Link Format - 旧分享链接格式 - - - Share VMess Link with v2rayN Format - 用 v2rayN 的格式分享 VMess 链接 - - - Clear servers before updating subscription - 更新订阅前清除服务器 - - - Ignore TLS errors when updating subscription - 更新订阅时忽略 TLS 错误 - - - Hide dashboard at startup - 启动时不显示仪表盘 - - - Max log lines - 日志最大行数限制 - - - Inbound Auth - 入站认证设置 - - - Username - 用户名 - - - Password - 密码 - - - Skip TLS certificate authentication by default (allowInsecure) - 默认跳过 TLS 证书验证 (allowInsecure) - - - Default uTLS Fingerprint - 默认 uTLS 指纹 - - - Core Options - 核心选项 - - - Override underlying DNS - 覆盖底层 DNS - - - Default On - 默认开启 - - - Multiplex (mux) - 多路复用 Mux - - - Latency Test URL - 延迟测试 URL - - - Download Test URL - 下载测试 URL - - - Timeout (s) - 超时(秒) - - - Automatic update - 自动更新订阅 - - - Interval (minute, invalid if less than 30) - 时间间隔(分钟,少于 30 分钟无效) - - - Mixed (SOCKS+HTTP) Listen Port - Mixed (SOCKS+HTTP) 监听端口 - - - - DialogEditGroup - - Edit Group - 编辑分组 - - - Type - 类型 - - - Name - 名称 - - - Basic - 基本 - - - Subscription - 订阅 - - - URL - - - - Archive - 归档 - - - Warning - 警告 - - - Please input URL - 请输入 URL - - - Copy profile share links - 复制分组内配置的分享链接 - - - Copied - 复制成功 - - - Copy profile share links (Neko Links) - 复制分组内配置的分享链接 (Neko Links) - - - Manually column width - 手动调节列宽 - - - Front Proxy - 前置代理 - - - None - - - - Clear - 清除 - - - Skip automatic update - 跳过自动更新 - - - Common - 通用 - - - Share - 分享 - - - - DialogEditProfile - - Edit - 编辑 - - - Common - 通用 - - - Type - 类型 - - - Port - 端口 - - - Address - 地址 - - - Name - 名称 - - - Network - 传输 - - - Security - 传输层安全 - - - Network Settings (%1) - 传输设置 (%1) - - - The underlying transport method. It must be consistent with the server, otherwise, the connection cannot be established. - 底层传输方式。必须与服务器一致,否则无法建立连接。 - - - Transport Layer Security. It must be consistent with the server, otherwise, the connection cannot be established. - 传输层安全。必须与服务器一致,否则无法建立连接。 - - - UDP FullCone Packet encoding for implementing features such as UDP FullCone. Server support is required, if the wrong selection is made, the connection cannot be made. Please leave it blank. - 包编码,用于实现 UDP FullCone 等特性。需要服务器支持,选错无法连接。不懂请留空。 - - - When enabled, V2Ray will not check the validity of the TLS certificate provided by the remote host (the security is equivalent to plaintext) - 开启后 V2Ray 不会检查远端主机所提供的 TLS 证书的有效性(安全性相当于明文) - - - Server name indication, clear text. - 服务器名称指示,明文。 - - - Application layer protocol negotiation, clear text. Please separate them with commas. - 应用层协议协商,明文。多个请以英文逗号分隔。 - - - Allow insecure - 不检查服务器证书 - - - Certificate - 证书 - - - Not set - 未设置 - - - Already set - 已设置 - - - Packet Encoding - 包编码 - - - Settings - 设置 - - - Custom (Extra Core) - 自定义 (其他核心) - - - TLS Security Settings - TLS 安全设置 - - - TLS Camouflage Settings - TLS 伪装设置 - - - Reality public key. If not empty, turn TLS into REALITY. - Reality public key. 如果不为空,则将 TLS 变为 REALITY。 - - - Custom (%1 outbound) - 自定义 (%1 出站) - - - Custom (%1 config) - 自定义 (%1 完整配置) - - - Custom Outbound Settings - 自定义出站 JSON 设置 - - - Custom Config Settings - 自定义配置 JSON 设置 - - - Apply settings to this group - 将设置应用于该组 - - - Multiplex - 多路复用 - - - Keep Default - 保持默认 - - - On - 开启 - - - Off - 关闭 - - - Confirm - 确认 - - - Server support is required - 需要服务器支持 - - - Reality short id. Accept only one value. - Reality short id. 只接受一个值。 - - - - DialogHotkey - - Hotkey - 热键 - - - Show groups - 显示分组 - - - Show routes - 显示路由 - - - Trigger main window - 显示/隐藏主窗口 - - - System Proxy - 系统代理 - - - - DialogManageGroups - - Groups - 分组 - - - New group - 新建分组 - - - Update all subscriptions - 更新所有订阅 - - - Confirmation - 确认 - - - Update all subscriptions? - 更新所有订阅? - - - - DialogManageRoutes - - Routes - 路由 - - - Disable - 禁用 - - - Sniffing Mode - 流量探测 - - - Sniff result for routing - 探测结果用于路由判断 - - - Sniff result for destination - 探测结果用于目标地址 - - - Direct DNS - 直连 DNS - - - Remote DNS - 远程 DNS - - - Enable DNS Routing - 启用 DNS 路由 - - - Block - 阻止 - - - Direct - 直连 - - - Domain - 域名 - - - Proxy - 代理 - - - Preset - 预设 - - - Bypass LAN and China - 绕过局域网和大陆 - - - Global - 全局 - - - IP - - - - Save - 保存 - - - Load - 加载 - - - Cancel - 取消 - - - Remove - 删除 - - - Save routing: %1 - 保存路由: %1 - - - Load routing: %1 - 加载路由: %1 - - - Remove routing: %1 - 删除路由: %1 - - - Mange route set - 管理路由规则 - - - Default Outbound - 默认出站 - - - Domain Strategy - 域名策略 - - - Server Address Strategy - 服务器地址策略 - - - Common - 通用 - - - DNS - - - - Simple DNS Settings - 简易 DNS 设置 - - - Use DNS Object - 使用 DNS Object - - - DNS Object Settings - DNS Object 设置 - - - Simple Route - 简易路由 - - - Custom Route - 自定义路由 - - - Custom Route (global) - 自定义路由(全局) - - - Note: Other settings are independent for each route set. - 注意:其他设置对于每个路由集都是独立的。 - - - Route sets - 路由集 - - - Query Strategy - 查询策略 - - - Document - 文档 - - - Format - 格式化 - - - This is especially important and it is recommended to use the default value of "localhost". -If the default value does not work, try changing it to "223.5.5.5". -For more information, see the document "Configuration/DNS". - 此项尤为重要,建议使用默认值 "localhost"。 -如果默认值不工作,可以尝试更改为 "223.5.5.5"。 -更多信息,请参阅文档 "配置/DNS"。 - - - Final DNS Out - 默认 DNS 出站 - - - - DialogVPNSettings - - Tun Settings - Tun 设置 - - - Hide Console - 隐藏控制台 - - - Tun Enable IPv6 - 启用 Tun IPv6 - - - Bypass CIDR - 绕过 CIDR - - - Bypass Process Name - 绕过进程名 - - - Whitelist mode - 白名单模式 - - - Proxy CIDR - 代理 CIDR - - - Proxy Process Name - 代理进程名 - - - Whether blacklisted or whitelisted, your traffic will be handled by nekobox_core (sing-tun). This is NOT equal to "process mode" of some software. - 无论是黑名单还是白名单,您的流量都将由 nekobox_core (sing-tun) 处理。这不等于某些软件的「进程模式」。 - - - Troubleshooting - 故障排除 - - - If you have trouble starting VPN, you can force reset nekobox_core process here. - -If still not working, see documentation for more information. -https://matsuridayo.github.io/n-configuration/#vpn-tun - 如果您在启动 Tun 时遇到问题,您可以在此处强制重置 nekobox_core 进程。 - -如果仍然无法正常工作,请参阅文档以获取更多信息。 -https://matsuridayo.github.io/n-configuration/#vpn-tun - - - Reset - 重置 - - - Cancel - 取消 - - - Internal Tun - 内部 Tun - - - Add a tun inbound to the profile startup, instead of using two processes. -This needs to be run NekoBox with administrator privileges. - 在配置文件启动时添加一个tun inbound,而不是使用两个进程。 -这需要以管理员权限运行NekoBox。 - - - - EditChain - - Select Profile - 选择配置 - - - Traffic order is from top to bottom - 流量顺序是从上到下(最后一个配置为流量的出口) - - - Name cannot be empty. - 名称 不能为空 - - - - EditCustom - - Core - 核心 - - - Command - 命令 - - - Json Editor - JSON 编辑器 - - - Please pick a core. - 请选择一个核心。 - - - Outbound JSON, please read the documentation. - 填写出站 JSON 对象,详细请看文档。 - - - Config Suffix - 配置文件后缀 - - - Random if it's empty or zero. - 如果为空或为零,则表示使用随机端口。 - - - Preview - 预览 - - - Preview replace - 预览替换串 - - - Preview config - 预览配置 - - - Please fill the complete config. - 请填写完整配置。 - - - Name cannot be empty. - 名称 不能为空 - - - - EditNaive - - Protocol - 协议 - - - Password - 密码 - - - Extra headers - 附加标头 - - - SNI - - - - Username - 用户名 - - - Certificate - 证书 - - - Insecure concurrency - 不安全并发 - - - Disable logs - 关闭日志 - - - Turn on this option if your connection is lost after a while - 如果连接一段时间后出现中断,请打开此选项 - - - - EditQUIC - - Certificate - 证书 - - - Download (Mbps) - 下载速度 (Mbps) - - - Disable MTU Discovery - 禁用 MTU 探测 - - - Hop Interval (s) - 端口跳跃间隔 (秒) - - - Allow Insecure - 不检查服务器证书 - - - Hop Port - 跳跃端口 - - - Upload (Mbps) - 上传速度 (Mbps) - - - Obfs Password - 混淆密码 - - - SNI - SNI - - - Generate UUID - 生成 UUID - - - Password - 密码 - - - Zero Rtt Handshake - 0-Rtt 握手 - - - UDP Relay Mode - UDP 中继模式 - - - Congestion Control - 拥塞控制 - - - Heartbeat - 心跳包发送间隔 - - - Disable SNI - 不发送服务器名称指示 - - - Force use external core - 强制使用外部核心 - - - - EditShadowSocks - - Plugin Args - 插件参数 - - - Password - 密码 - - - Encryption - 加密 - - - Plugin - 插件 - - - Version of UDP over TCP protocol, server support is required. - UDP over TCP 协议版本,需要服务器支持。 - - - Off - 关闭 - - - - EditSocksHttp - - Version - 版本 - - - Username - 用户名 - - - Password - 密码 - - - - EditTrojanVLESS - - Password - 密码 - - - - EditVMess - - Security - 加密 - - - Alter Id - - - - UUID - - - - Generate UUID - 生成 UUID - - - - GroupItem - - Update Subscription - 更新订阅 - - - Edit - 编辑 - - - Basic - 基本 - - - Subscription - 订阅 - - - Confirmation - 确认 - - - Remove - 删除 - - - Remove %1? - 删除 %1 ? - - - Archive - 归档 - - - Last update: %1 - 最后更新 %1 - - - - JsonEditor - - JSON Editor - - - - Format JSON - - - - Remove All Comments - - - - Json Editor - - - - Structure Preview - - - - OK - - - - Json Contains Syntax Errors - - - - Original Json may contain syntax errors. Json tree is disabled. - - - - You must correct these errors before continuing. - - - - Syntax Errors - - - - Please fix the JSON errors or remove the comments before continue - - - - - MainWindow - - Program - 程序 - - - Preferences - 首选项 - - - Server - 服务器 - - - Ads - 推广 - - - Type - 类型 - - - Address - 地址 - - - Name - 名称 - - - Test Result - 测试结果 - - - Traffic - 流量 - - - System Proxy - 系统代理 - - - Share - 分享 - - - Exit - 退出 - - - Basic Settings - 基本设置 - - - Groups - 分组 - - - Stop - 停止 - - - Add profile from clipboard - 从剪贴板添加 - - - Debug Info - 调试信息 - - - Copy Link - 复制链接 - - - Clear Test Result - 清理测试结果 - - - Scan QR Code - 扫描 QR Code - - - Disable - 禁用 - - - Error - 错误 - - - Confirmation - 确认 - - - Settings changed, restart proxy? - 设置已改变,是否重启代理? - - - Imported %1 profile(s) - 导入了 %1 个配置 - - - Unavailable - 不可用 - - - Remove %1 item(s) ? - 删除 %1 个项目? - - - Config copied - 配置已复制 - - - [%1] test error: %2 - [%1] 测试错误: %2 - - - Clear - 清除 - - - fake - - - - Testing - 正在测试 - - - Update - 更新 - - - Document - 文档 - - - Select - 选择 - - - QR Code not found - 未扫描到 QR Code - - - Move - 移动 - - - Log - 日志 - - - Connection - 连接 - - - Status - 状态 - - - Outbound - 出站 - - - Destination - 目标地址 - - - Start: %1 -End: %2 - 开始: %1 -结束: %2 - - - Starting profile %1 - 正在启动配置 %1 - - - Stopping profile %1 - 正在停止配置 %1 - - - Start with system - 跟随系统启动 - - - Remember last profile - 记住最后的配置 - - - Move %1 item(s) - 移动 %1 个项目 - - - Remove Unavailable - 删除不可用的配置 - - - New profile - 手动输入配置 - - - Hotkey Settings - 热键设置 - - - QR Code and link - 显示 QR Code 和分享链接 - - - Active Routing - 当前路由规则 - - - Active Server - 当前服务器 - - - Load routing and apply: %1 - 加载路由规则并应用: %1 - - - Copied %1 item(s) - 复制了 %1 个项目 - - - Full Test - 完整测试 - - - Current Group - 当前分组 - - - Reset Traffic - 重置流量 - - - Remove Duplicates - 删除重复的配置 - - - Select All - 全选 - - - Tun Mode - Tun 模式 - - - Failed to stop Tun process - 停止 Tun 失败 - - - Enable System Proxy - 启用系统代理 - - - Enable Tun - 启用 Tun - - - Tun Settings changed - Tun 设置改变 - - - Restart Tun to take effect. - 重启 Tun 生效。 - - - Start - 启动 - - - Delete - 删除 - - - Copy links of selected - 批量复制选中项目的分享链接 - - - Clone - 克隆 - - - Update subscription - 更新订阅 - - - Clone %1 item(s) - 克隆 %1 个项目 - - - Copy links of selected (Neko Links) - 批量复制选中项目的分享链接 (Neko Links) - - - Allow other devices to connect - 允许其他设备连接 - - - Resolve domain - 将服务器域名解析为 IP - - - Resolving domain to IP, if support. - 将服务器域名解析为 IP(如果支持)。 - - - Export %1 config - 导出 %1 配置 - - - Routing Settings - 路由设置 - - - Tun Settings - Tun 设置 - - - Restart Program - 重启程序 - - - Not Running - 未启动 - - - Current server is incompatible with Tun. Please stop the server first, enable Tun Mode, and then restart. - 当前服务器与 Tun 不兼容。请先停止服务器,打开 Tun 模式后再启动。 - - - Open Config Folder - 打开配置目录 - - - Set ignore keyword - 设置忽略关键字 - - - Set the following keywords to ignore? -Split by line. - 将以下关键字设置为忽略? -一行一个。 - - - Save as route - 保存为路由规则 - - - Save "%1" as a routing rule? - 将"%1"保存为一条路由规则? - - - Edit - 编辑 - - - Current Select - 当前选中 - - - Show Window - 显示主窗口 - - - Settings changed - 设置改变 - - - Restart the program to take effect. - 重启程序生效。 - - - Please run NekoBox as admin - 请以管理员权限运行 NekoBox - - - Restart Proxy - 重启代理 - - - Failed to start profile %1 - 启动配置失败: %1 - - - Failed to stop, please restart the program. - 停止失败,请重启程序。 - - - If there is no response for a long time, it is recommended to restart the software. - 如果长时间没有反应,建议重启软件。 - - - Select mode, double-click or press Enter to select a profile, press ESC to exit. - 选择模式,双击或按回车键选择一个配置文件,按ESC键退出。 - - - Latency - 延迟 - - - UDP latency - UDP 延迟 - - - Download speed - 下载速度 - - - In and Out IP - 入口出口 IP - - - Test Options - 测试选项 - - - Stop Testing - 停止测试 - - - URL Test - URL 测试 - - - - ProxyItem - - Confirmation - 确认 - - - Remove %1? - 删除 %1 ? - - - - QGuiApplication - - QT_LAYOUT_DIRECTION - LTR - - - - QObject - - As link - 作为链接 - - - url detected - 检测到 URL - - - %1 -How to update? - %1 -如何处理? - - - Added %1 profiles: -%2 -Deleted %3 Profiles: -%4 - 增加了 %1 个配置: -%2 -删除了 %3 个配置: -%4 - - - Proxy: %1 -Direct: %2 - 代理: %1 -直连: %2 - - - Used: %1 Remain: %2 Expire: %3 - 已用 %1 剩余 %2 过期 %3 - - - Core not found: %1 - 找不到 "%1" 核心。请前往设置 - - - Update - 更新 - - - No update - 无更新 - - - Open in browser - 浏览器打开 - - - Close - 关闭 - - - Update is ready, restart to install? - 更新已下载好,重启应用? - - - Update found: %1 -Release note: -%2 - 检测到更新: %1 -更新日志: -%2 - - - Unavailable - 不可用 - - - Request with proxy but no profile started. - 即将使用代理请求,但是代理未启动。 - - - Chain Proxy - 链式代理 - - - Requesting subscription: %1 - 正在请求订阅: %1 - - - Requesting subscription %1 error: %2 - 请求订阅 %1 错误: %2 - - - Nothing - - - - Change of %1: - %1 变化: - - - Select - 选择 - - - Clearing servers... - 正在清理服务器... - - - Subscription request fininshed: %1 - 订阅请求完成: %1 - - - Core exited, restarting. - Core 退出,正在重新启动。 - - - Core exits too frequently, stop automatic restart this profile. - Core 退出太频繁,停止自动重启。 - - - As Subscription (create new group) - 作为订阅(创建新组) - - - As Subscription (add to this group) - 作为订阅(添加到该组) - - - Default - 默认 - - - The last speed test did not exit completely, please wait. If it persists, please restart the program. - 上次速度测试未完全退出,请等待。如果问题仍然存在,请重新启动程序。 - - - - Qv2ray::ui::widgets::AutoCompleteTextEdit - - You can not input space characters here. - - - - diff --git a/ui/GroupSort.hpp b/ui/GroupSort.hpp deleted file mode 100644 index ca49eba..0000000 --- a/ui/GroupSort.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -// implement in mainwindow -namespace GroupSortMethod { - enum GroupSortMethod { - Raw, - ByType, - ByAddress, - ByName, - ByLatency, - ById, - }; -} - -struct GroupSortAction { - GroupSortMethod::GroupSortMethod method = GroupSortMethod::Raw; - bool save_sort = false; // 保存到文件 - bool descending = false; // 默认升序,开这个就是降序 - bool scroll_to_started = false; -}; diff --git a/ui/Icon.cpp b/ui/Icon.cpp deleted file mode 100644 index b248f3c..0000000 --- a/ui/Icon.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "Icon.hpp" - -#include "main/NekoGui.hpp" - -#include - -QPixmap Icon::GetTrayIcon(Icon::TrayIconStatus status) { - QPixmap pixmap; - - // software embedded icon - auto pixmap_read = QPixmap(":/neko/" + software_name.toLower() + ".png"); - if (!pixmap_read.isNull()) pixmap = pixmap_read; - - // software pack icon - pixmap_read = QPixmap("../" + software_name.toLower() + ".png"); - if (!pixmap_read.isNull()) pixmap = pixmap_read; - - // user icon - pixmap_read = QPixmap("./" + software_name.toLower() + ".png"); - if (!pixmap_read.isNull()) pixmap = pixmap_read; - - if (status == TrayIconStatus::NONE) return pixmap; - - auto p = QPainter(&pixmap); - - auto side = pixmap.width(); - auto radius = side * 0.4; - auto d = side * 0.3; - auto margin = side * 0.05; - - if (status == TrayIconStatus::RUNNING) { - p.setBrush(QBrush(Qt::darkGreen)); - } else if (status == TrayIconStatus::SYSTEM_PROXY) { - p.setBrush(QBrush(Qt::blue)); - } else if (status == TrayIconStatus::VPN) { - p.setBrush(QBrush(Qt::red)); - } - p.drawRoundedRect( - QRect(side - d - margin, - side - d - margin, - d, - d), - radius, - radius); - p.end(); - - return pixmap; -} - -QPixmap Icon::GetMaterialIcon(const QString &name) { - QPixmap pixmap(":/icon/material/" + name + ".svg"); - return pixmap; -} diff --git a/ui/Icon.hpp b/ui/Icon.hpp deleted file mode 100644 index 4395344..0000000 --- a/ui/Icon.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace Icon { - - enum TrayIconStatus { - NONE, - RUNNING, - SYSTEM_PROXY, - VPN, - }; - - QPixmap GetTrayIcon(TrayIconStatus status); - - QPixmap GetMaterialIcon(const QString &name); - -} // namespace Icon diff --git a/ui/ThemeManager.cpp b/ui/ThemeManager.cpp deleted file mode 100644 index 1c112aa..0000000 --- a/ui/ThemeManager.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include - -#include "ThemeManager.hpp" - -ThemeManager *themeManager = new ThemeManager; - -extern QString ReadFileText(const QString &path); - -void ThemeManager::ApplyTheme(const QString &theme) { - auto internal = [=] { - if (this->system_style_name.isEmpty()) { - this->system_style_name = qApp->style()->objectName(); - } - if (this->current_theme == theme) { - return; - } - - bool ok; - auto themeId = theme.toInt(&ok); - - if (ok) { - // System & Built-in - QString qss; - - if (themeId != 0) { - QString path; - std::map replace; - switch (themeId) { - case 1: - path = ":/themes/feiyangqingyun/qss/flatgray.css"; - replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; - break; - case 2: - path = ":/themes/feiyangqingyun/qss/lightblue.css"; - replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; - break; - case 3: - path = ":/themes/feiyangqingyun/qss/blacksoft.css"; - replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; - break; - default: - return; - } - qss = ReadFileText(path); - for (auto const &[a, b]: replace) { - qss = qss.replace(a, b); - } - } - - auto system_style = QStyleFactory::create(this->system_style_name); - - if (themeId == 0) { - // system theme - qApp->setPalette(system_style->standardPalette()); - qApp->setStyle(system_style); - qApp->setStyleSheet(""); - } else { - if (themeId == 1 || themeId == 2 || themeId == 3) { - // feiyangqingyun theme - QString paletteColor = qss.mid(20, 7); - qApp->setPalette(QPalette(paletteColor)); - } else { - // other theme - qApp->setPalette(system_style->standardPalette()); - } - qApp->setStyleSheet(qss); - } - } else { - // QStyleFactory - const auto &_style = QStyleFactory::create(theme); - if (_style != nullptr) { - qApp->setPalette(_style->standardPalette()); - qApp->setStyle(_style); - qApp->setStyleSheet(""); - } - } - - current_theme = theme; - }; - internal(); - - auto nekoray_css = ReadFileText(":/neko/neko.css"); - qApp->setStyleSheet(qApp->styleSheet().append("\n").append(nekoray_css)); -} diff --git a/ui/ThemeManager.hpp b/ui/ThemeManager.hpp deleted file mode 100644 index 1514f23..0000000 --- a/ui/ThemeManager.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -class ThemeManager { -public: - QString system_style_name = ""; - QString current_theme = "0"; // int: 0:system 1+:builtin string: QStyleFactory - - void ApplyTheme(const QString &theme); -}; - -extern ThemeManager *themeManager; diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp deleted file mode 100644 index 9d37a57..0000000 --- a/ui/dialog_basic_settings.cpp +++ /dev/null @@ -1,416 +0,0 @@ -#include "dialog_basic_settings.h" -#include "ui_dialog_basic_settings.h" - -#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp" -#include "fmt/Preset.hpp" -#include "ui/ThemeManager.hpp" -#include "ui/Icon.hpp" -#include "main/GuiUtils.hpp" -#include "main/NekoGui.hpp" - -#include -#include -#include -#include -#include - -class ExtraCoreWidget : public QWidget { -public: - QString coreName; - - QLabel *label_name; - MyLineEdit *lineEdit_path; - QPushButton *pushButton_pick; - - explicit ExtraCoreWidget(QJsonObject *extraCore, const QString &coreName_, - QWidget *parent = nullptr) - : QWidget(parent) { - coreName = coreName_; - label_name = new QLabel; - label_name->setText(coreName); - lineEdit_path = new MyLineEdit; - lineEdit_path->setText(extraCore->value(coreName).toString()); - pushButton_pick = new QPushButton; - pushButton_pick->setText(QObject::tr("Select")); - auto layout = new QHBoxLayout; - layout->addWidget(label_name); - layout->addWidget(lineEdit_path); - layout->addWidget(pushButton_pick); - setLayout(layout); - setContentsMargins(0, 0, 0, 0); - // - connect(pushButton_pick, &QPushButton::clicked, this, [=] { - auto fn = QFileDialog::getOpenFileName(this, QObject::tr("Select"), QDir::currentPath(), - "", nullptr, QFileDialog::Option::ReadOnly); - if (!fn.isEmpty()) { - lineEdit_path->setText(fn); - } - }); - connect(lineEdit_path, &QLineEdit::textChanged, this, [=](const QString &newTxt) { - extraCore->insert(coreName, newTxt); - }); - } -}; - -DialogBasicSettings::DialogBasicSettings(QWidget *parent) - : QDialog(parent), ui(new Ui::DialogBasicSettings) { - ui->setupUi(this); - ADD_ASTERISK(this); - - // Common - - ui->log_level->addItems(QStringLiteral("trace debug info warn error fatal panic").split(" ")); - ui->mux_protocol->addItems({"h2mux", "smux", "yamux"}); - - refresh_auth(); - - D_LOAD_STRING(inbound_address) - D_LOAD_COMBO_STRING(log_level) - CACHE.custom_inbound = NekoGui::dataStore->custom_inbound; - D_LOAD_INT(inbound_socks_port) - D_LOAD_INT(test_concurrent) - D_LOAD_INT(test_download_timeout) - D_LOAD_STRING(test_latency_url) - D_LOAD_STRING(test_download_url) - D_LOAD_BOOL(old_share_link_format) - - connect(ui->custom_inbound_edit, &QPushButton::clicked, this, [=] { - C_EDIT_JSON_ALLOW_EMPTY(custom_inbound) - }); - -#ifdef Q_OS_WIN - connect(ui->sys_proxy_format, &QPushButton::clicked, this, [=] { - bool ok; - auto str = QInputDialog::getItem(this, ui->sys_proxy_format->text() + " (Windows)", - tr("Advanced system proxy settings. Please select a format."), - Preset::Windows::system_proxy_format, - Preset::Windows::system_proxy_format.indexOf(NekoGui::dataStore->system_proxy_format), - false, &ok); - if (ok) NekoGui::dataStore->system_proxy_format = str; - }); -#else - ui->sys_proxy_format->hide(); -#endif - - // Style - ui->connection_statistics_box->setDisabled(true); - // - D_LOAD_BOOL(check_include_pre) - D_LOAD_BOOL(connection_statistics) - D_LOAD_BOOL(start_minimal) - D_LOAD_INT(max_log_line) - // - if (NekoGui::dataStore->traffic_loop_interval == 500) { - ui->rfsh_r->setCurrentIndex(0); - } else if (NekoGui::dataStore->traffic_loop_interval == 1000) { - ui->rfsh_r->setCurrentIndex(1); - } else if (NekoGui::dataStore->traffic_loop_interval == 2000) { - ui->rfsh_r->setCurrentIndex(2); - } else if (NekoGui::dataStore->traffic_loop_interval == 3000) { - ui->rfsh_r->setCurrentIndex(3); - } else if (NekoGui::dataStore->traffic_loop_interval == 5000) { - ui->rfsh_r->setCurrentIndex(4); - } else { - ui->rfsh_r->setCurrentIndex(5); - } - // - ui->language->setCurrentIndex(NekoGui::dataStore->language); - connect(ui->language, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { - CACHE.needRestart = true; - }); - // - int built_in_len = ui->theme->count(); - ui->theme->addItems(QStyleFactory::keys()); - // - bool ok; - auto themeId = NekoGui::dataStore->theme.toInt(&ok); - if (ok) { - ui->theme->setCurrentIndex(themeId); - } else { - ui->theme->setCurrentText(NekoGui::dataStore->theme); - } - // - connect(ui->theme, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { - if (index + 1 <= built_in_len) { - themeManager->ApplyTheme(Int2String(index)); - NekoGui::dataStore->theme = Int2String(index); - } else { - themeManager->ApplyTheme(ui->theme->currentText()); - NekoGui::dataStore->theme = ui->theme->currentText(); - } - repaint(); - mainwindow->repaint(); - NekoGui::dataStore->Save(); - }); - - // Subscription - - ui->user_agent->setText(NekoGui::dataStore->user_agent); - ui->user_agent->setPlaceholderText(NekoGui::dataStore->GetUserAgent(true)); - D_LOAD_BOOL(sub_use_proxy) - D_LOAD_BOOL(sub_clear) - D_LOAD_BOOL(sub_insecure) - D_LOAD_INT_ENABLE(sub_auto_update, sub_auto_update_enable) - - // Core - - ui->groupBox_core->setTitle(software_core_name); - // - CACHE.extraCore = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map); - if (!CACHE.extraCore.contains("naive")) CACHE.extraCore.insert("naive", ""); - if (!CACHE.extraCore.contains("hysteria2")) CACHE.extraCore.insert("hysteria2", ""); - if (!CACHE.extraCore.contains("tuic")) CACHE.extraCore.insert("tuic", ""); - // - auto extra_core_layout = ui->extra_core_box_scrollAreaWidgetContents->layout(); - for (const auto &s: CACHE.extraCore.keys()) { - extra_core_layout->addWidget(new ExtraCoreWidget(&CACHE.extraCore, s)); - } - // - connect(ui->extra_core_add, &QPushButton::clicked, this, [=] { - bool ok; - auto s = QInputDialog::getText(nullptr, tr("Add"), - tr("Please input the core name."), - QLineEdit::Normal, "", &ok) - .trimmed(); - if (s.isEmpty() || !ok) return; - if (CACHE.extraCore.contains(s)) return; - extra_core_layout->addWidget(new ExtraCoreWidget(&CACHE.extraCore, s)); - CACHE.extraCore.insert(s, ""); - }); - connect(ui->extra_core_del, &QPushButton::clicked, this, [=] { - bool ok; - auto s = QInputDialog::getItem(nullptr, tr("Delete"), - tr("Please select the core name."), - CACHE.extraCore.keys(), 0, false, &ok); - if (s.isEmpty() || !ok) return; - for (int i = 0; i < extra_core_layout->count(); i++) { - auto item = extra_core_layout->itemAt(i); - auto ecw = dynamic_cast(item->widget()); - if (ecw != nullptr && ecw->coreName == s) { - ecw->deleteLater(); - CACHE.extraCore.remove(s); - return; - } - } - }); - - // Mux - D_LOAD_INT(mux_concurrency) - D_LOAD_COMBO_STRING(mux_protocol) - D_LOAD_BOOL(mux_padding) - D_LOAD_BOOL(mux_default_on) - - // Security - - ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint); - - D_LOAD_BOOL(skip_cert) - ui->utlsFingerprint->setCurrentText(NekoGui::dataStore->utlsFingerprint); -} - -DialogBasicSettings::~DialogBasicSettings() { - delete ui; -} - -void DialogBasicSettings::accept() { - // Common - - D_SAVE_STRING(inbound_address) - D_SAVE_COMBO_STRING(log_level) - NekoGui::dataStore->custom_inbound = CACHE.custom_inbound; - D_SAVE_INT(inbound_socks_port) - D_SAVE_INT(test_concurrent) - D_SAVE_INT(test_download_timeout) - D_SAVE_STRING(test_latency_url) - D_SAVE_STRING(test_download_url) - D_SAVE_BOOL(old_share_link_format) - - // Style - - NekoGui::dataStore->language = ui->language->currentIndex(); - D_SAVE_BOOL(connection_statistics) - D_SAVE_BOOL(check_include_pre) - D_SAVE_BOOL(start_minimal) - D_SAVE_INT(max_log_line) - - if (NekoGui::dataStore->max_log_line <= 0) { - NekoGui::dataStore->max_log_line = 200; - } - - if (ui->rfsh_r->currentIndex() == 0) { - NekoGui::dataStore->traffic_loop_interval = 500; - } else if (ui->rfsh_r->currentIndex() == 1) { - NekoGui::dataStore->traffic_loop_interval = 1000; - } else if (ui->rfsh_r->currentIndex() == 2) { - NekoGui::dataStore->traffic_loop_interval = 2000; - } else if (ui->rfsh_r->currentIndex() == 3) { - NekoGui::dataStore->traffic_loop_interval = 3000; - } else if (ui->rfsh_r->currentIndex() == 4) { - NekoGui::dataStore->traffic_loop_interval = 5000; - } else { - NekoGui::dataStore->traffic_loop_interval = 0; - } - - // Subscription - - if (ui->sub_auto_update_enable->isChecked()) { - TM_auto_update_subsctiption_Reset_Minute(ui->sub_auto_update->text().toInt()); - } else { - TM_auto_update_subsctiption_Reset_Minute(0); - } - - NekoGui::dataStore->user_agent = ui->user_agent->text(); - D_SAVE_BOOL(sub_use_proxy) - D_SAVE_BOOL(sub_clear) - D_SAVE_BOOL(sub_insecure) - D_SAVE_INT_ENABLE(sub_auto_update, sub_auto_update_enable) - - // Core - - NekoGui::dataStore->extraCore->core_map = QJsonObject2QString(CACHE.extraCore, true); - - // Mux - D_SAVE_INT(mux_concurrency) - D_SAVE_COMBO_STRING(mux_protocol) - D_SAVE_BOOL(mux_padding) - D_SAVE_BOOL(mux_default_on) - - // Security - - D_SAVE_BOOL(skip_cert) - NekoGui::dataStore->utlsFingerprint = ui->utlsFingerprint->currentText(); - - // 关闭连接统计,停止刷新前清空记录。 - if (NekoGui::dataStore->traffic_loop_interval == 0 || !NekoGui::dataStore->connection_statistics) { - MW_dialog_message("", "ClearConnectionList"); - } - - QStringList str{"UpdateDataStore"}; - if (CACHE.needRestart) str << "NeedRestart"; - MW_dialog_message(Dialog_DialogBasicSettings, str.join(",")); - QDialog::accept(); -} - -// slots - -void DialogBasicSettings::refresh_auth() { - ui->inbound_auth->setText({}); - if (NekoGui::dataStore->inbound_auth->NeedAuth()) { - ui->inbound_auth->setIcon(Icon::GetMaterialIcon("lock-outline")); - } else { - ui->inbound_auth->setIcon(Icon::GetMaterialIcon("lock-open-outline")); - } -} - -void DialogBasicSettings::on_set_custom_icon_clicked() { - auto title = ui->set_custom_icon->text(); - QString user_icon_path = "./" + software_name.toLower() + ".png"; - auto c = QMessageBox::question(this, title, tr("Please select a PNG file."), - tr("Select"), tr("Reset"), tr("Cancel"), 2, 2); - if (c == 0) { - auto fn = QFileDialog::getOpenFileName(this, QObject::tr("Select"), QDir::currentPath(), - "*.png", nullptr, QFileDialog::Option::ReadOnly); - QImage img(fn); - if (img.isNull() || img.height() != img.width()) { - MessageBoxWarning(title, tr("Please select a valid square image.")); - return; - } - QFile::remove(user_icon_path); - QFile::copy(fn, user_icon_path); - } else if (c == 1) { - QFile::remove(user_icon_path); - } else { - return; - } - MW_dialog_message(Dialog_DialogBasicSettings, "UpdateIcon"); -} - -void DialogBasicSettings::on_inbound_auth_clicked() { - auto w = new QDialog(this); - w->setWindowTitle(tr("Inbound Auth")); - auto layout = new QGridLayout; - w->setLayout(layout); - // - auto user_l = new QLabel(tr("Username")); - auto pass_l = new QLabel(tr("Password")); - auto user = new MyLineEdit; - auto pass = new MyLineEdit; - user->setText(NekoGui::dataStore->inbound_auth->username); - pass->setText(NekoGui::dataStore->inbound_auth->password); - // - layout->addWidget(user_l, 0, 0); - layout->addWidget(user, 0, 1); - layout->addWidget(pass_l, 1, 0); - layout->addWidget(pass, 1, 1); - auto box = new QDialogButtonBox; - box->setOrientation(Qt::Horizontal); - box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - connect(box, &QDialogButtonBox::accepted, w, [=] { - NekoGui::dataStore->inbound_auth->username = user->text(); - NekoGui::dataStore->inbound_auth->password = pass->text(); - MW_dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore"); - w->accept(); - }); - connect(box, &QDialogButtonBox::rejected, w, &QDialog::reject); - layout->addWidget(box, 2, 1); - // - w->exec(); - w->deleteLater(); - refresh_auth(); -} - -void DialogBasicSettings::on_core_settings_clicked() { - auto w = new QDialog(this); - w->setWindowTitle(software_core_name + " Core Options"); - auto layout = new QGridLayout; - w->setLayout(layout); - // - auto line = -1; - QCheckBox *core_box_enable_clash_api; - MyLineEdit *core_box_clash_api; - MyLineEdit *core_box_clash_api_secret; - MyLineEdit *core_box_underlying_dns; - // - auto core_box_underlying_dns_l = new QLabel(tr("Override underlying DNS")); - core_box_underlying_dns = new MyLineEdit; - core_box_underlying_dns->setText(NekoGui::dataStore->core_box_underlying_dns); - core_box_underlying_dns->setMinimumWidth(300); - layout->addWidget(core_box_underlying_dns_l, ++line, 0); - layout->addWidget(core_box_underlying_dns, line, 1); - // - auto core_box_enable_clash_api_l = new QLabel("Enable Clash API"); - core_box_enable_clash_api = new QCheckBox; - core_box_enable_clash_api->setChecked(NekoGui::dataStore->core_box_clash_api > 0); - layout->addWidget(core_box_enable_clash_api_l, ++line, 0); - layout->addWidget(core_box_enable_clash_api, line, 1); - // - auto core_box_clash_api_l = new QLabel("Clash API Listen Port"); - core_box_clash_api = new MyLineEdit; - core_box_clash_api->setText(Int2String(std::abs(NekoGui::dataStore->core_box_clash_api))); - layout->addWidget(core_box_clash_api_l, ++line, 0); - layout->addWidget(core_box_clash_api, line, 1); - // - auto core_box_clash_api_secret_l = new QLabel("Clash API Secret"); - core_box_clash_api_secret = new MyLineEdit; - core_box_clash_api_secret->setText(NekoGui::dataStore->core_box_clash_api_secret); - layout->addWidget(core_box_clash_api_secret_l, ++line, 0); - layout->addWidget(core_box_clash_api_secret, line, 1); - // - auto box = new QDialogButtonBox; - box->setOrientation(Qt::Horizontal); - box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - connect(box, &QDialogButtonBox::accepted, w, [=] { - NekoGui::dataStore->core_box_underlying_dns = core_box_underlying_dns->text(); - NekoGui::dataStore->core_box_clash_api = core_box_clash_api->text().toInt() * (core_box_enable_clash_api->isChecked() ? 1 : -1); - NekoGui::dataStore->core_box_clash_api_secret = core_box_clash_api_secret->text(); - MW_dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore"); - w->accept(); - }); - connect(box, &QDialogButtonBox::rejected, w, &QDialog::reject); - layout->addWidget(box, ++line, 1); - // - ADD_ASTERISK(w) - w->exec(); - w->deleteLater(); - refresh_auth(); -} diff --git a/ui/dialog_basic_settings.h b/ui/dialog_basic_settings.h deleted file mode 100644 index 3a7ff19..0000000 --- a/ui/dialog_basic_settings.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef DIALOG_BASIC_SETTINGS_H -#define DIALOG_BASIC_SETTINGS_H - -#include -#include - -namespace Ui { - class DialogBasicSettings; -} - -class DialogBasicSettings : public QDialog { - Q_OBJECT - -public: - explicit DialogBasicSettings(QWidget *parent = nullptr); - - ~DialogBasicSettings(); - -public slots: - - void accept(); - -private: - Ui::DialogBasicSettings *ui; - - struct { - QJsonObject extraCore; - QString custom_inbound; - bool needRestart = false; - } CACHE; - -private slots: - - void refresh_auth(); - - void on_set_custom_icon_clicked(); - - void on_inbound_auth_clicked(); - - void on_core_settings_clicked(); -}; - -#endif // DIALOG_BASIC_SETTINGS_H diff --git a/ui/dialog_basic_settings.ui b/ui/dialog_basic_settings.ui deleted file mode 100644 index c17b62e..0000000 --- a/ui/dialog_basic_settings.ui +++ /dev/null @@ -1,780 +0,0 @@ - - - DialogBasicSettings - - - - 0 - 0 - 600 - 400 - - - - - 0 - 0 - - - - Basic Settings - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - 0 - - - - Common - - - - - - - - - - - Listen Address - - - - - - - - - - - - - - - - - - - - - - - Custom Inbound - - - - - - - Edit - - - - - - - - - - - - - - - - - Mixed (SOCKS+HTTP) Listen Port - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - Latency Test URL - - - - - - - - - - Concurrent - - - - - - - - - - - - - - - - Download Test URL - - - - - - - - - - Timeout (s) - - - - - - - - - - - - - - - - Include Pre-release when checking update - - - - - - - Qt::Vertical - - - - - - - Share VMess Link with v2rayN Format - - - Old Share Link Format - - - - - - - System proxy format - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Style - - - - - - - - - - - - 0 - 0 - - - - Theme - - - - - - - - System - - - - - flatgray - - - - - lightblue - - - - - blacksoft - - - - - - - - - 0 - 0 - - - - Set custom icon - - - - - - - - - - - - - - 0 - 0 - - - - Language - - - - - - - - System - - - - - English - - - - - 简体中文 - - - - - فارسی - - - - - Русский - - - - - - - - - - - - - - - - - - Statistics refresh rate - - - - - - - - 500ms - - - - - 1s - - - - - 2s - - - - - 3s - - - - - 5s - - - - - Off - - - - - - - - - - - - - - Connection statistics - - - - - - - Enable - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Hide dashboard at startup - - - - - - - - - - - - - Max log lines - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Subscription - - - - - - - - - 0 - 0 - - - - Enable - - - - - - - Interval (minute, invalid if less than 30) - - - - - - - - 0 - 0 - - - - - - - - - - - - - Use proxy when updating subscription - - - - - - - Ignore TLS errors when updating subscription - - - - - - - Clear servers before updating subscription - - - - - - - - 0 - 0 - - - - Automatic update - - - - - - - User Agent - - - - - - - - Core - - - - - - - 0 - 0 - - - - core_name - - - - - - - - - - 0 - 0 - - - - - - - - Multiplex (mux) - - - - - - - Loglevel - - - - - - - - - - - - concurrency - - - - - - - - - - Padding - - - - - - - Default On - - - - - - - - - - - - Core Options - - - - - - - - - - - Extra Core - - - - - - QFrame::NoFrame - - - true - - - - - 0 - 0 - 632 - 299 - - - - - - - - 0 - 0 - - - - - - - Add - - - - - - - Delete - - - - - - - - - - - - - - - Security - - - - - - Skip TLS certificate authentication by default (allowInsecure) - - - - - - - - 0 - 0 - - - - - - - Default uTLS Fingerprint - - - - - - - true - - - - - - - - - - - - - - - MyLineEdit - QLineEdit -

ui/widget/MyLineEdit.h
- - - - - - buttonBox - accepted() - DialogBasicSettings - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - DialogBasicSettings - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/ui/dialog_hotkey.cpp b/ui/dialog_hotkey.cpp deleted file mode 100644 index d53278d..0000000 --- a/ui/dialog_hotkey.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "dialog_hotkey.h" -#include "ui_dialog_hotkey.h" - -#include "ui/mainwindow_interface.h" - -DialogHotkey::DialogHotkey(QWidget *parent) : QDialog(parent), ui(new Ui::DialogHotkey) { - ui->setupUi(this); - ui->show_mainwindow->setKeySequence(NekoGui::dataStore->hotkey_mainwindow); - ui->show_groups->setKeySequence(NekoGui::dataStore->hotkey_group); - ui->show_routes->setKeySequence(NekoGui::dataStore->hotkey_route); - ui->system_proxy->setKeySequence(NekoGui::dataStore->hotkey_system_proxy_menu); - GetMainWindow()->RegisterHotkey(true); -} - -DialogHotkey::~DialogHotkey() { - if (result() == QDialog::Accepted) { - NekoGui::dataStore->hotkey_mainwindow = ui->show_mainwindow->keySequence().toString(); - NekoGui::dataStore->hotkey_group = ui->show_groups->keySequence().toString(); - NekoGui::dataStore->hotkey_route = ui->show_routes->keySequence().toString(); - NekoGui::dataStore->hotkey_system_proxy_menu = ui->system_proxy->keySequence().toString(); - NekoGui::dataStore->Save(); - } - GetMainWindow()->RegisterHotkey(false); - delete ui; -} diff --git a/ui/dialog_hotkey.h b/ui/dialog_hotkey.h deleted file mode 100644 index 6e42508..0000000 --- a/ui/dialog_hotkey.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include "main/NekoGui.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class DialogHotkey; -} -QT_END_NAMESPACE - -class DialogHotkey : public QDialog { - Q_OBJECT - -public: - explicit DialogHotkey(QWidget *parent = nullptr); - - ~DialogHotkey() override; - -private: - Ui::DialogHotkey *ui; -}; diff --git a/ui/dialog_hotkey.ui b/ui/dialog_hotkey.ui deleted file mode 100644 index 6cdda0f..0000000 --- a/ui/dialog_hotkey.ui +++ /dev/null @@ -1,118 +0,0 @@ - - - DialogHotkey - - - - 0 - 0 - 400 - 300 - - - - Hotkey - - - - - - - - - Show routes - - - - - - - - - - Qt::StrongFocus - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - Show groups - - - - - - - Trigger main window - - - - - - - System Proxy - - - - - - - - - - - QtExtKeySequenceEdit - QKeySequenceEdit -
3rdparty/QtExtKeySequenceEdit.h
-
-
- - show_mainwindow - show_groups - show_routes - system_proxy - buttonBox - - - - - buttonBox - accepted() - DialogHotkey - accept() - - - 258 - 255 - - - 199 - 149 - - - - - buttonBox - rejected() - DialogHotkey - reject() - - - 258 - 255 - - - 199 - 149 - - - - -
diff --git a/ui/dialog_manage_groups.cpp b/ui/dialog_manage_groups.cpp deleted file mode 100644 index 0cf3c8b..0000000 --- a/ui/dialog_manage_groups.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "dialog_manage_groups.h" -#include "ui_dialog_manage_groups.h" - -#include "db/Database.hpp" -#include "sub/GroupUpdater.hpp" -#include "main/GuiUtils.hpp" -#include "ui/widget/GroupItem.h" -#include "ui/edit/dialog_edit_group.h" - -#include -#include -#include - -#define AddGroupToListIfExist(_id) \ - auto __ent = NekoGui::profileManager->GetGroup(_id); \ - if (__ent != nullptr) { \ - auto wI = new QListWidgetItem(); \ - auto w = new GroupItem(this, __ent, wI); \ - wI->setData(114514, _id); \ - ui->listWidget->addItem(wI); \ - ui->listWidget->setItemWidget(wI, w); \ - } - -DialogManageGroups::DialogManageGroups(QWidget *parent) : QDialog(parent), ui(new Ui::DialogManageGroups) { - ui->setupUi(this); - - for (auto id: NekoGui::profileManager->groupsTabOrder) { - AddGroupToListIfExist(id) - } - - connect(ui->listWidget, &QListWidget::itemDoubleClicked, this, [=](QListWidgetItem *wI) { - auto w = dynamic_cast(ui->listWidget->itemWidget(wI)); - emit w->edit_clicked(); - }); -} - -DialogManageGroups::~DialogManageGroups() { - delete ui; -} - -void DialogManageGroups::on_add_clicked() { - auto ent = NekoGui::ProfileManager::NewGroup(); - auto dialog = new DialogEditGroup(ent, this); - int ret = dialog->exec(); - dialog->deleteLater(); - - if (ret == QDialog::Accepted) { - NekoGui::profileManager->AddGroup(ent); - AddGroupToListIfExist(ent->id); - MW_dialog_message(Dialog_DialogManageGroups, "refresh-1"); - } -} - -void DialogManageGroups::on_update_all_clicked() { - if (QMessageBox::question(this, tr("Confirmation"), tr("Update all subscriptions?")) == QMessageBox::StandardButton::Yes) { - UI_update_all_groups(); - } -} diff --git a/ui/dialog_manage_groups.h b/ui/dialog_manage_groups.h deleted file mode 100644 index 04e4412..0000000 --- a/ui/dialog_manage_groups.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "db/Group.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class DialogManageGroups; -} -QT_END_NAMESPACE - -class DialogManageGroups : public QDialog { - Q_OBJECT - -public: - explicit DialogManageGroups(QWidget *parent = nullptr); - - ~DialogManageGroups() override; - -private: - Ui::DialogManageGroups *ui; - -private slots: - - void on_add_clicked(); - - void on_update_all_clicked(); -}; diff --git a/ui/dialog_manage_groups.ui b/ui/dialog_manage_groups.ui deleted file mode 100644 index dcba1ff..0000000 --- a/ui/dialog_manage_groups.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - DialogManageGroups - - - - 0 - 0 - 640 - 480 - - - - Qt::TabFocus - - - Groups - - - - - - Qt::NoFocus - - - - - - - - - Qt::NoFocus - - - New group - - - - - - - Qt::NoFocus - - - Update all subscriptions - - - - - - - - - - diff --git a/ui/dialog_manage_routes.cpp b/ui/dialog_manage_routes.cpp deleted file mode 100644 index 98fde5b..0000000 --- a/ui/dialog_manage_routes.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "dialog_manage_routes.h" -#include "ui_dialog_manage_routes.h" - -#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp" -#include "3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp" -#include "main/GuiUtils.hpp" -#include "fmt/Preset.hpp" - -#include -#include -#include -#include - -#define REFRESH_ACTIVE_ROUTING(name, obj) \ - this->active_routing = name; \ - setWindowTitle(title_base + " [" + name + "]"); \ - UpdateDisplayRouting(obj, false); - -DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(new Ui::DialogManageRoutes) { - ui->setupUi(this); - title_base = windowTitle(); - - QStringList qsValue = {""}; - QString dnsHelpDocumentUrl; - // - ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy); - ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy); - qsValue += QStringLiteral("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" "); - ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10sCiAgInJ1bGVzIjogW10sCiAgImZpbmFsIjogIiIsCiAgInN0cmF0ZWd5IjogIiIsCiAgImRpc2FibGVfY2FjaGUiOiBmYWxzZSwKICAiZGlzYWJsZV9leHBpcmUiOiBmYWxzZSwKICAiaW5kZXBlbmRlbnRfY2FjaGUiOiBmYWxzZSwKICAicmV2ZXJzZV9tYXBwaW5nIjogZmFsc2UsCiAgImZha2VpcCI6IHt9Cn0=")); - dnsHelpDocumentUrl = "https://sing-box.sagernet.org/configuration/dns/"; - // - ui->direct_dns_strategy->addItems(qsValue); - ui->remote_dns_strategy->addItems(qsValue); - // - D_C_LOAD_STRING(custom_route_global) - // - connect(ui->use_dns_object, &QCheckBox::stateChanged, this, [=](int state) { - auto useDNSObject = state == Qt::Checked; - ui->simple_dns_box->setDisabled(useDNSObject); - ui->dns_object->setDisabled(!useDNSObject); - }); - ui->use_dns_object->stateChanged(Qt::Unchecked); // uncheck to uncheck - connect(ui->dns_document, &QPushButton::clicked, this, [=] { - MessageBoxInfo("DNS", dnsHelpDocumentUrl); - }); - connect(ui->format_dns_object, &QPushButton::clicked, this, [=] { - auto obj = QString2QJsonObject(ui->dns_object->toPlainText()); - if (obj.isEmpty()) { - MessageBoxInfo("DNS", "invaild json"); - } else { - ui->dns_object->setPlainText(QJsonObject2QString(obj, false)); - } - }); - // - connect(ui->custom_route_edit, &QPushButton::clicked, this, [=] { - C_EDIT_JSON_ALLOW_EMPTY(custom_route) - }); - connect(ui->custom_route_global_edit, &QPushButton::clicked, this, [=] { - C_EDIT_JSON_ALLOW_EMPTY(custom_route_global) - }); - // - builtInSchemesMenu = new QMenu(this); - builtInSchemesMenu->addActions(this->getBuiltInSchemes()); - ui->preset->setMenu(builtInSchemesMenu); - - QString geoipFn = NekoGui::FindCoreAsset("geoip.dat"); - QString geositeFn = NekoGui::FindCoreAsset("geosite.dat"); - // - const auto sourceStringsDomain = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geositeFn); - directDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this); - proxyDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this); - blockDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this); - // - const auto sourceStringsIP = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geoipFn); - directIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this); - proxyIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this); - blockIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this); - // - ui->directTxtLayout->addWidget(directDomainTxt, 0, 0); - ui->proxyTxtLayout->addWidget(proxyDomainTxt, 0, 0); - ui->blockTxtLayout->addWidget(blockDomainTxt, 0, 0); - // - ui->directIPLayout->addWidget(directIPTxt, 0, 0); - ui->proxyIPLayout->addWidget(proxyIPTxt, 0, 0); - ui->blockIPLayout->addWidget(blockIPTxt, 0, 0); - // - REFRESH_ACTIVE_ROUTING(NekoGui::dataStore->active_routing, NekoGui::dataStore->routing.get()) - - ADD_ASTERISK(this) -} - -DialogManageRoutes::~DialogManageRoutes() { - delete ui; -} - -void DialogManageRoutes::accept() { - D_C_SAVE_STRING(custom_route_global) - bool routeChanged = false; - if (NekoGui::dataStore->active_routing != active_routing) routeChanged = true; - SaveDisplayRouting(NekoGui::dataStore->routing.get()); - NekoGui::dataStore->active_routing = active_routing; - NekoGui::dataStore->routing->fn = ROUTES_PREFIX + NekoGui::dataStore->active_routing; - if (NekoGui::dataStore->routing->Save()) routeChanged = true; - // - QString info = "UpdateDataStore"; - if (routeChanged) info += "RouteChanged"; - MW_dialog_message(Dialog_DialogManageRoutes, info); - QDialog::accept(); -} - -// built in settings - -QList DialogManageRoutes::getBuiltInSchemes() { - QList list; - list.append(this->schemeToAction(tr("Bypass LAN and China"), routing_cn_lan)); - list.append(this->schemeToAction(tr("Global"), routing_global)); - return list; -} - -QAction *DialogManageRoutes::schemeToAction(const QString &name, const NekoGui::Routing &scheme) { - auto *action = new QAction(name, this); - connect(action, &QAction::triggered, [this, &scheme] { this->UpdateDisplayRouting((NekoGui::Routing *) &scheme, true); }); - return action; -} - -void DialogManageRoutes::UpdateDisplayRouting(NekoGui::Routing *conf, bool qv) { - // - directDomainTxt->setPlainText(conf->direct_domain); - proxyDomainTxt->setPlainText(conf->proxy_domain); - blockDomainTxt->setPlainText(conf->block_domain); - // - blockIPTxt->setPlainText(conf->block_ip); - directIPTxt->setPlainText(conf->direct_ip); - proxyIPTxt->setPlainText(conf->proxy_ip); - // - CACHE.custom_route = conf->custom; - ui->def_outbound->setCurrentText(conf->def_outbound); - // - if (qv) return; - // - ui->sniffing_mode->setCurrentIndex(conf->sniffing_mode); - ui->outbound_domain_strategy->setCurrentText(conf->outbound_domain_strategy); - ui->domainStrategyCombo->setCurrentText(conf->domain_strategy); - ui->use_dns_object->setChecked(conf->use_dns_object); - ui->dns_object->setPlainText(conf->dns_object); - ui->dns_routing->setChecked(conf->dns_routing); - ui->remote_dns->setCurrentText(conf->remote_dns); - ui->remote_dns_strategy->setCurrentText(conf->remote_dns_strategy); - ui->direct_dns->setCurrentText(conf->direct_dns); - ui->direct_dns_strategy->setCurrentText(conf->direct_dns_strategy); - ui->dns_final_out->setCurrentText(conf->dns_final_out); -} - -void DialogManageRoutes::SaveDisplayRouting(NekoGui::Routing *conf) { - conf->direct_ip = directIPTxt->toPlainText(); - conf->direct_domain = directDomainTxt->toPlainText(); - conf->proxy_ip = proxyIPTxt->toPlainText(); - conf->proxy_domain = proxyDomainTxt->toPlainText(); - conf->block_ip = blockIPTxt->toPlainText(); - conf->block_domain = blockDomainTxt->toPlainText(); - conf->def_outbound = ui->def_outbound->currentText(); - conf->custom = CACHE.custom_route; - // - conf->sniffing_mode = ui->sniffing_mode->currentIndex(); - conf->domain_strategy = ui->domainStrategyCombo->currentText(); - conf->outbound_domain_strategy = ui->outbound_domain_strategy->currentText(); - conf->use_dns_object = ui->use_dns_object->isChecked(); - conf->dns_object = ui->dns_object->toPlainText(); - conf->dns_routing = ui->dns_routing->isChecked(); - conf->remote_dns = ui->remote_dns->currentText(); - conf->remote_dns_strategy = ui->remote_dns_strategy->currentText(); - conf->direct_dns = ui->direct_dns->currentText(); - conf->direct_dns_strategy = ui->direct_dns_strategy->currentText(); - conf->dns_final_out = ui->dns_final_out->currentText(); -} - -void DialogManageRoutes::on_load_save_clicked() { - auto w = new QDialog; - auto layout = new QVBoxLayout; - w->setLayout(layout); - auto lineEdit = new QLineEdit; - layout->addWidget(lineEdit); - auto list = new QListWidget; - layout->addWidget(list); - for (const auto &name: NekoGui::Routing::List()) { - list->addItem(name); - } - connect(list, &QListWidget::currentTextChanged, lineEdit, &QLineEdit::setText); - auto bottom = new QHBoxLayout; - layout->addLayout(bottom); - auto load = new QPushButton; - load->setText(tr("Load")); - bottom->addWidget(load); - auto save = new QPushButton; - save->setText(tr("Save")); - bottom->addWidget(save); - auto remove = new QPushButton; - remove->setText(tr("Remove")); - bottom->addWidget(remove); - auto cancel = new QPushButton; - cancel->setText(tr("Cancel")); - bottom->addWidget(cancel); - connect(load, &QPushButton::clicked, w, [=] { - auto fn = lineEdit->text(); - if (!fn.isEmpty()) { - auto r = std::make_unique(); - r->load_control_must = true; - r->fn = ROUTES_PREFIX + fn; - if (r->Load()) { - if (QMessageBox::question(nullptr, software_name, tr("Load routing: %1").arg(fn) + "\n" + r->DisplayRouting()) == QMessageBox::Yes) { - REFRESH_ACTIVE_ROUTING(fn, r.get()) // temp save to the window - w->accept(); - } - } - } - }); - connect(save, &QPushButton::clicked, w, [=] { - auto fn = lineEdit->text(); - if (!fn.isEmpty()) { - auto r = std::make_unique(); - SaveDisplayRouting(r.get()); - r->fn = ROUTES_PREFIX + fn; - if (QMessageBox::question(nullptr, software_name, tr("Save routing: %1").arg(fn) + "\n" + r->DisplayRouting()) == QMessageBox::Yes) { - r->Save(); - REFRESH_ACTIVE_ROUTING(fn, r.get()) - w->accept(); - } - } - }); - connect(remove, &QPushButton::clicked, w, [=] { - auto fn = lineEdit->text(); - if (!fn.isEmpty() && NekoGui::Routing::List().length() > 1) { - if (QMessageBox::question(nullptr, software_name, tr("Remove routing: %1").arg(fn)) == QMessageBox::Yes) { - QFile f(ROUTES_PREFIX + fn); - f.remove(); - if (NekoGui::dataStore->active_routing == fn) { - NekoGui::Routing::SetToActive(NekoGui::Routing::List().first()); - REFRESH_ACTIVE_ROUTING(NekoGui::dataStore->active_routing, NekoGui::dataStore->routing.get()) - } - w->accept(); - } - } - }); - connect(cancel, &QPushButton::clicked, w, &QDialog::accept); - connect(list, &QListWidget::itemDoubleClicked, this, [=](QListWidgetItem *item) { - lineEdit->setText(item->text()); - emit load->clicked(); - }); - w->exec(); - w->deleteLater(); -} diff --git a/ui/dialog_manage_routes.h b/ui/dialog_manage_routes.h deleted file mode 100644 index 0030622..0000000 --- a/ui/dialog_manage_routes.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include - -#include "3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp" -#include "main/NekoGui.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class DialogManageRoutes; -} -QT_END_NAMESPACE - -class DialogManageRoutes : public QDialog { - Q_OBJECT - -public: - explicit DialogManageRoutes(QWidget *parent = nullptr); - - ~DialogManageRoutes() override; - -private: - Ui::DialogManageRoutes *ui; - - struct { - QString custom_route; - QString custom_route_global; - } CACHE; - - QMenu *builtInSchemesMenu; - Qv2ray::ui::widgets::AutoCompleteTextEdit *directDomainTxt; - Qv2ray::ui::widgets::AutoCompleteTextEdit *proxyDomainTxt; - Qv2ray::ui::widgets::AutoCompleteTextEdit *blockDomainTxt; - // - Qv2ray::ui::widgets::AutoCompleteTextEdit *directIPTxt; - Qv2ray::ui::widgets::AutoCompleteTextEdit *blockIPTxt; - Qv2ray::ui::widgets::AutoCompleteTextEdit *proxyIPTxt; - // - NekoGui::Routing routing_cn_lan = NekoGui::Routing(1); - NekoGui::Routing routing_global = NekoGui::Routing(0); - // - QString title_base; - QString active_routing; - -public slots: - - void accept() override; - - QList getBuiltInSchemes(); - - QAction *schemeToAction(const QString &name, const NekoGui::Routing &scheme); - - void UpdateDisplayRouting(NekoGui::Routing *conf, bool qv); - - void SaveDisplayRouting(NekoGui::Routing *conf); - - void on_load_save_clicked(); -}; diff --git a/ui/dialog_manage_routes.ui b/ui/dialog_manage_routes.ui deleted file mode 100644 index cfb846e..0000000 --- a/ui/dialog_manage_routes.ui +++ /dev/null @@ -1,687 +0,0 @@ - - - DialogManageRoutes - - - - 0 - 0 - 800 - 600 - - - - Routes - - - - - - 0 - - - - Common - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Route sets - - - - - - Mange route set - - - - - - - Custom Route (global) - - - - - - - Note: Other settings are independent for each route set. - - - - - - - - - - For V2Ray, it sets routing.domainStrategy -For sing-box, it sets inbound.domain_strategy - - - Domain Strategy - - - - - - - false - - - - - - - - Disable - - - - - Sniff result for routing - - - - - Sniff result for destination - - - - - - - - Sniffing Mode - - - - - - - Server Address Strategy - - - - - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - DNS - - - - - - Simple DNS Settings - - - - - - This is especially important and it is recommended to use the default value of "localhost". -If the default value does not work, try changing it to "223.5.5.5". -For more information, see the document "Configuration/DNS". - - - Direct DNS - - - - - - - - - - 0 - 0 - - - - true - - - - https://8.8.8.8/dns-query - - - - - https://1.0.0.1/dns-query - - - - - https://1.1.1.1/dns-query - - - - - https://dns.google/dns-query - - - - - https://one.one.one.one/dns-query - - - - - https://[2001:4860:4860::8888]/dns-query - - - - - https://[2606:4700:4700::1111]/dns-query - - - - - - - - Query Strategy - - - - - - - - - - - - Remote DNS - - - widget - - - - - - - - - Enable DNS Routing - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Final DNS Out - - - - - - - - proxy - - - - - bypass - - - - - - - - - - - - - 0 - 0 - - - - true - - - - local - - - - - tls://120.53.53.53 - - - - - https://223.5.5.5/dns-query - - - - - https://1.12.12.12/dns-query - - - - - https://dns.alidns.com/dns-query - - - - - https://doh.pub/dns-query - - - - - 223.5.5.5 - - - - - 119.29.29.29 - - - - - 2400:3200::1 - - - - - 2402:4e00:: - - - - - - - - Query Strategy - - - - - - - - - - - - - - - DNS Object Settings - - - - - - - - Use DNS Object - - - - - - - - 0 - 0 - - - - Format - - - - - - - - 0 - 0 - - - - Document - - - - - - - - - - - - - - - - Simple Route - - - - - - - - - - - Block - - - Qt::AlignCenter - - - - - - - - - - Direct - - - Qt::AlignCenter - - - - - - - Domain - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - Proxy - - - Qt::AlignCenter - - - - - - - - - - - - - IP - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Preset - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextBesideIcon - - - - - - - Custom Route - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Default Outbound - - - - - - - - proxy - - - - - bypass - - - - - block - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - tabWidget - sniffing_mode - domainStrategyCombo - outbound_domain_strategy - load_save - custom_route_global_edit - remote_dns - remote_dns_strategy - direct_dns - direct_dns_strategy - dns_routing - dns_final_out - use_dns_object - format_dns_object - dns_document - dns_object - preset - custom_route_edit - def_outbound - - - - - buttonBox - rejected() - DialogManageRoutes - reject() - - - 399 - 574 - - - 399 - 299 - - - - - buttonBox - accepted() - DialogManageRoutes - accept() - - - 399 - 574 - - - 399 - 299 - - - - - diff --git a/ui/dialog_vpn_settings.cpp b/ui/dialog_vpn_settings.cpp deleted file mode 100644 index 12096ee..0000000 --- a/ui/dialog_vpn_settings.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "dialog_vpn_settings.h" -#include "ui_dialog_vpn_settings.h" - -#include "main/GuiUtils.hpp" -#include "main/NekoGui.hpp" -#include "ui/mainwindow_interface.h" - -#include - -DialogVPNSettings::DialogVPNSettings(QWidget *parent) : QDialog(parent), ui(new Ui::DialogVPNSettings) { - ui->setupUi(this); - ADD_ASTERISK(this); - - ui->fake_dns->setChecked(NekoGui::dataStore->fake_dns); - ui->vpn_implementation->setCurrentIndex(NekoGui::dataStore->vpn_implementation); - ui->vpn_mtu->setCurrentText(Int2String(NekoGui::dataStore->vpn_mtu)); - ui->vpn_ipv6->setChecked(NekoGui::dataStore->vpn_ipv6); - ui->hide_console->setChecked(NekoGui::dataStore->vpn_hide_console); -#ifndef Q_OS_WIN - ui->hide_console->setVisible(false); -#endif - ui->strict_route->setChecked(NekoGui::dataStore->vpn_strict_route); - ui->single_core->setChecked(NekoGui::dataStore->vpn_internal_tun); - // - D_LOAD_STRING_PLAIN(vpn_rule_cidr) - D_LOAD_STRING_PLAIN(vpn_rule_process) - // - connect(ui->whitelist_mode, &QCheckBox::stateChanged, this, [=](int state) { - if (state == Qt::Checked) { - ui->gb_cidr->setTitle(tr("Proxy CIDR")); - ui->gb_process_name->setTitle(tr("Proxy Process Name")); - } else { - ui->gb_cidr->setTitle(tr("Bypass CIDR")); - ui->gb_process_name->setTitle(tr("Bypass Process Name")); - } - }); - ui->whitelist_mode->setChecked(NekoGui::dataStore->vpn_rule_white); -} - -DialogVPNSettings::~DialogVPNSettings() { - delete ui; -} - -void DialogVPNSettings::accept() { - // - auto mtu = ui->vpn_mtu->currentText().toInt(); - if (mtu > 10000 || mtu < 1000) mtu = 9000; - NekoGui::dataStore->vpn_implementation = ui->vpn_implementation->currentIndex(); - NekoGui::dataStore->fake_dns = ui->fake_dns->isChecked(); - NekoGui::dataStore->vpn_mtu = mtu; - NekoGui::dataStore->vpn_ipv6 = ui->vpn_ipv6->isChecked(); - NekoGui::dataStore->vpn_hide_console = ui->hide_console->isChecked(); - NekoGui::dataStore->vpn_strict_route = ui->strict_route->isChecked(); - NekoGui::dataStore->vpn_rule_white = ui->whitelist_mode->isChecked(); - bool isInternalChanged = NekoGui::dataStore->vpn_internal_tun != ui->single_core->isChecked(); - NekoGui::dataStore->vpn_internal_tun = ui->single_core->isChecked(); - // - D_SAVE_STRING_PLAIN(vpn_rule_cidr) - D_SAVE_STRING_PLAIN(vpn_rule_process) - // - QStringList msg{"UpdateDataStore"}; - if (isInternalChanged) { - msg << "NeedRestart"; - } else { - msg << "VPNChanged"; - } - MW_dialog_message("", msg.join(",")); - QDialog::accept(); -} - -void DialogVPNSettings::on_troubleshooting_clicked() { - auto r = QMessageBox::information(this, tr("Troubleshooting"), - tr("If you have trouble starting VPN, you can force reset nekobox_core process here.\n\n" - "If still not working, see documentation for more information.\n" - "https://matsuridayo.github.io/n-configuration/#vpn-tun"), - tr("Reset"), tr("Cancel"), "", - 1, 1); - if (r == 0) { - GetMainWindow()->StopVPNProcess(true); - } -} diff --git a/ui/dialog_vpn_settings.h b/ui/dialog_vpn_settings.h deleted file mode 100644 index 0b5b87d..0000000 --- a/ui/dialog_vpn_settings.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef NEKORAY_DIALOG_VPN_SETTINGS_H -#define NEKORAY_DIALOG_VPN_SETTINGS_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { - class DialogVPNSettings; -} -QT_END_NAMESPACE - -class DialogVPNSettings : public QDialog { - Q_OBJECT - -public: - explicit DialogVPNSettings(QWidget *parent = nullptr); - - ~DialogVPNSettings() override; - -private: - Ui::DialogVPNSettings *ui; - -public slots: - - void accept() override; - - void on_troubleshooting_clicked(); -}; - -#endif // NEKORAY_DIALOG_VPN_SETTINGS_H diff --git a/ui/dialog_vpn_settings.ui b/ui/dialog_vpn_settings.ui deleted file mode 100644 index bbb5f69..0000000 --- a/ui/dialog_vpn_settings.ui +++ /dev/null @@ -1,262 +0,0 @@ - - - DialogVPNSettings - - - - 0 - 0 - 600 - 600 - - - - Tun Settings - - - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - Stack - - - - - - - - Mixed - - - - - gVisor - - - - - System - - - - - - - - - 0 - 0 - - - - MTU - - - - - - - true - - - - 9000 - - - - - 1500 - - - - - - - - - - - - - - Tun Enable IPv6 - - - - - - - Strict Route - - - - - - - FakeDNS - - - - - - - Qt::Vertical - - - - - - - Add a tun inbound to the profile startup, instead of using two processes. -This needs to be run NekoBox with administrator privileges. - - - Internal Tun - - - - - - - Hide Console - - - - - - - - - - - - Bypass CIDR - - - - - - - - - - - - Bypass Process Name - - - - - - - - - - - - - - - - Whether blacklisted or whitelisted, your traffic will be handled by nekobox_core (sing-tun). This is NOT equal to "process mode" of some software. - - - Whitelist mode - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Troubleshooting - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - vpn_implementation - vpn_mtu - vpn_ipv6 - strict_route - fake_dns - single_core - hide_console - vpn_rule_cidr - vpn_rule_process - whitelist_mode - troubleshooting - - - - - buttonBox - accepted() - DialogVPNSettings - accept() - - - 399 - 575 - - - 399 - 299 - - - - - buttonBox - rejected() - DialogVPNSettings - reject() - - - 399 - 575 - - - 399 - 299 - - - - - diff --git a/ui/edit/dialog_edit_group.cpp b/ui/edit/dialog_edit_group.cpp deleted file mode 100644 index 061f654..0000000 --- a/ui/edit/dialog_edit_group.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "dialog_edit_group.h" -#include "ui_dialog_edit_group.h" - -#include "db/Database.hpp" -#include "ui/mainwindow_interface.h" - -#include - -#define ADJUST_SIZE runOnUiThread([=] { adjustSize(); adjustPosition(mainwindow); }, this); - -DialogEditGroup::DialogEditGroup(const std::shared_ptr &ent, QWidget *parent) : QDialog(parent), ui(new Ui::DialogEditGroup) { - ui->setupUi(this); - this->ent = ent; - - connect(ui->type, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { - ui->cat_sub->setHidden(index == 0); - ADJUST_SIZE - }); - - ui->name->setText(ent->name); - ui->archive->setChecked(ent->archive); - ui->skip_auto_update->setChecked(ent->skip_auto_update); - ui->url->setText(ent->url); - ui->type->setCurrentIndex(ent->url.isEmpty() ? 0 : 1); - ui->type->currentIndexChanged(ui->type->currentIndex()); - ui->manually_column_width->setChecked(ent->manually_column_width); - ui->cat_share->setVisible(false); - - if (ent->id >= 0) { // already a group - ui->type->setDisabled(true); - if (!ent->Profiles().isEmpty()) { - ui->cat_share->setVisible(true); - } - } else { // new group - ui->front_proxy->hide(); - ui->front_proxy_l->hide(); - ui->front_proxy_clear->hide(); - } - - CACHE.front_proxy = ent->front_proxy_id; - refresh_front_proxy(); - connect(ui->front_proxy_clear, &QPushButton::clicked, this, [=] { - CACHE.front_proxy = -1; - refresh_front_proxy(); - }); - - connect(ui->copy_links, &QPushButton::clicked, this, [=] { - QStringList links; - for (const auto &[_, profile]: NekoGui::profileManager->profiles) { - if (profile->gid != ent->id) continue; - links += profile->bean->ToShareLink(); - } - QApplication::clipboard()->setText(links.join("\n")); - MessageBoxInfo(software_name, tr("Copied")); - }); - connect(ui->copy_links_nkr, &QPushButton::clicked, this, [=] { - QStringList links; - for (const auto &[_, profile]: NekoGui::profileManager->profiles) { - if (profile->gid != ent->id) continue; - links += profile->bean->ToNekorayShareLink(profile->type); - } - QApplication::clipboard()->setText(links.join("\n")); - MessageBoxInfo(software_name, tr("Copied")); - }); - - ADJUST_SIZE -} - -DialogEditGroup::~DialogEditGroup() { - delete ui; -} - -void DialogEditGroup::accept() { - if (ent->id >= 0) { // already a group - if (!ent->url.isEmpty() && ui->url->text().isEmpty()) { - MessageBoxWarning(tr("Warning"), tr("Please input URL")); - return; - } - } - ent->name = ui->name->text(); - ent->url = ui->url->text(); - ent->archive = ui->archive->isChecked(); - ent->skip_auto_update = ui->skip_auto_update->isChecked(); - ent->manually_column_width = ui->manually_column_width->isChecked(); - ent->front_proxy_id = CACHE.front_proxy; - QDialog::accept(); -} - -void DialogEditGroup::refresh_front_proxy() { - auto fEnt = NekoGui::profileManager->GetProfile(CACHE.front_proxy); - ui->front_proxy->setText(fEnt == nullptr ? tr("None") : fEnt->bean->DisplayTypeAndName()); -} - -void DialogEditGroup::on_front_proxy_clicked() { - auto parent = dynamic_cast(this->parent()); - parent->hide(); - this->hide(); - GetMainWindow()->start_select_mode(this, [=](int id) { - CACHE.front_proxy = id; - refresh_front_proxy(); - parent->show(); - show(); - }); -} diff --git a/ui/edit/dialog_edit_group.h b/ui/edit/dialog_edit_group.h deleted file mode 100644 index 5aa3d9f..0000000 --- a/ui/edit/dialog_edit_group.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include "db/Group.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class DialogEditGroup; -} -QT_END_NAMESPACE - -class DialogEditGroup : public QDialog { - Q_OBJECT - -public: - explicit DialogEditGroup(const std::shared_ptr &ent, QWidget *parent = nullptr); - - ~DialogEditGroup() override; - -private: - Ui::DialogEditGroup *ui; - - std::shared_ptr ent; - - struct { - int front_proxy; - } CACHE; - - void refresh_front_proxy(); - -private slots: - - void accept() override; - - void on_front_proxy_clicked(); -}; diff --git a/ui/edit/dialog_edit_group.ui b/ui/edit/dialog_edit_group.ui deleted file mode 100644 index e6556c7..0000000 --- a/ui/edit/dialog_edit_group.ui +++ /dev/null @@ -1,230 +0,0 @@ - - - DialogEditGroup - - - - 0 - 0 - 400 - 468 - - - - - 400 - 300 - - - - Edit Group - - - - - - - 0 - 0 - - - - Common - - - - - - - - - Type - - - - - - - - Basic - - - - - Subscription - - - - - - - - Front Proxy - - - - - - - - - Manually column width - - - - - - - Archive - - - - - - - - - Name - - - - - - - - - - - - - - - - - 0 - 0 - - - - Clear - - - - - - - - - - - - Subscription - - - - - - URL - - - - - - - - - - Skip automatic update - - - - - - - - - - Share - - - - - - Copy profile share links - - - - - - - Copy profile share links (Neko Links) - - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - name - type - front_proxy - front_proxy_clear - manually_column_width - archive - url - skip_auto_update - copy_links - copy_links_nkr - - - - - buttonBox - rejected() - DialogEditGroup - reject() - - - 199 - 275 - - - 199 - 149 - - - - - buttonBox - accepted() - DialogEditGroup - accept() - - - 199 - 291 - - - 199 - 157 - - - - -
diff --git a/ui/edit/dialog_edit_profile.cpp b/ui/edit/dialog_edit_profile.cpp deleted file mode 100644 index 47d3faf..0000000 --- a/ui/edit/dialog_edit_profile.cpp +++ /dev/null @@ -1,523 +0,0 @@ -#include "dialog_edit_profile.h" -#include "ui_dialog_edit_profile.h" - -#include "ui/edit/edit_socks_http.h" -#include "ui/edit/edit_shadowsocks.h" -#include "ui/edit/edit_chain.h" -#include "ui/edit/edit_vmess.h" -#include "ui/edit/edit_trojan_vless.h" -#include "ui/edit/edit_naive.h" -#include "ui/edit/edit_quic.h" -#include "ui/edit/edit_custom.h" - -#include "fmt/includes.h" -#include "fmt/Preset.hpp" - -#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp" -#include "main/GuiUtils.hpp" - -#include - -#define ADJUST_SIZE runOnUiThread([=] { adjustSize(); adjustPosition(mainwindow); }, this); -#define LOAD_TYPE(a) ui->type->addItem(NekoGui::ProfileManager::NewProxyEntity(a)->bean->DisplayType(), a); - -DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId, QWidget *parent) - : QDialog(parent), ui(new Ui::DialogEditProfile) { - // setup UI - ui->setupUi(this); - ui->dialog_layout->setAlignment(ui->left, Qt::AlignTop); - - // network changed - network_title_base = ui->network_box->title(); - connect(ui->network, &QComboBox::currentTextChanged, this, [=](const QString &txt) { - ui->network_box->setTitle(network_title_base.arg(txt)); - // 传输设置 - if (txt == "tcp") { - ui->header_type->setVisible(true); - ui->header_type_l->setVisible(true); - ui->path->setVisible(true); - ui->path_l->setVisible(true); - ui->host->setVisible(true); - ui->host_l->setVisible(true); - } else if (txt == "grpc") { - ui->header_type->setVisible(false); - ui->header_type_l->setVisible(false); - ui->path->setVisible(true); - ui->path_l->setVisible(true); - ui->host->setVisible(false); - ui->host_l->setVisible(false); - } else if (txt == "ws" || txt == "http" || txt == "httpupgrade") { - ui->header_type->setVisible(false); - ui->header_type_l->setVisible(false); - ui->path->setVisible(true); - ui->path_l->setVisible(true); - ui->host->setVisible(true); - ui->host_l->setVisible(true); - } else { - ui->header_type->setVisible(false); - ui->header_type_l->setVisible(false); - ui->path->setVisible(false); - ui->path_l->setVisible(false); - ui->host->setVisible(false); - ui->host_l->setVisible(false); - } - // 传输设置 ED - if (txt == "ws") { - ui->ws_early_data_length->setVisible(true); - ui->ws_early_data_length_l->setVisible(true); - ui->ws_early_data_name->setVisible(true); - ui->ws_early_data_name_l->setVisible(true); - } else { - ui->ws_early_data_length->setVisible(false); - ui->ws_early_data_length_l->setVisible(false); - ui->ws_early_data_name->setVisible(false); - ui->ws_early_data_name_l->setVisible(false); - } - // 传输设置 for NekoBox - if (!ui->utlsFingerprint->count()) ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint); - // 传输设置 是否可见 - int networkBoxVisible = 0; - for (auto label: ui->network_box->findChildren()) { - if (!label->isHidden()) networkBoxVisible++; - } - ui->network_box->setVisible(networkBoxVisible); - ADJUST_SIZE - }); - ui->network->removeItem(0); - - // security changed - connect(ui->security, &QComboBox::currentTextChanged, this, [=](const QString &txt) { - if (txt == "tls") { - ui->security_box->setVisible(true); - ui->tls_camouflage_box->setVisible(true); - ui->reality_spx->hide(); - ui->reality_spx_l->hide(); - } else { - ui->security_box->setVisible(false); - ui->tls_camouflage_box->setVisible(false); - } - ADJUST_SIZE - }); - emit ui->security->currentTextChanged(ui->security->currentText()); - - // 确定模式和 ent - newEnt = _type != ""; - if (newEnt) { - this->groupId = profileOrGroupId; - this->type = _type; - - // load type to combo box - LOAD_TYPE("socks") - LOAD_TYPE("http") - LOAD_TYPE("shadowsocks") - LOAD_TYPE("trojan") - LOAD_TYPE("vmess") - LOAD_TYPE("vless") - LOAD_TYPE("naive") - LOAD_TYPE("hysteria2") - LOAD_TYPE("tuic") - ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal"); - ui->type->addItem(tr("Custom (%1 config)").arg(software_core_name), "internal-full"); - ui->type->addItem(tr("Custom (Extra Core)"), "custom"); - LOAD_TYPE("chain") - - // type changed - connect(ui->type, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { - typeSelected(ui->type->itemData(index).toString()); - }); - - ui->apply_to_group->hide(); - } else { - this->ent = NekoGui::profileManager->GetProfile(profileOrGroupId); - if (this->ent == nullptr) return; - this->type = ent->type; - ui->type->setVisible(false); - ui->type_l->setVisible(false); - } - - typeSelected(this->type); -} - -DialogEditProfile::~DialogEditProfile() { - delete ui; -} - -void DialogEditProfile::typeSelected(const QString &newType) { - QString customType; - type = newType; - bool validType = true; - - if (type == "socks" || type == "http") { - auto _innerWidget = new EditSocksHttp(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "shadowsocks") { - auto _innerWidget = new EditShadowSocks(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "chain") { - auto _innerWidget = new EditChain(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "vmess") { - auto _innerWidget = new EditVMess(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "trojan" || type == "vless") { - auto _innerWidget = new EditTrojanVLESS(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "naive") { - auto _innerWidget = new EditNaive(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "hysteria2" || type == "tuic") { - auto _innerWidget = new EditQUIC(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - } else if (type == "custom" || type == "internal" || type == "internal-full") { - auto _innerWidget = new EditCustom(this); - innerWidget = _innerWidget; - innerEditor = _innerWidget; - customType = newEnt ? type : ent->CustomBean()->core; - if (customType != "custom") _innerWidget->preset_core = customType; - type = "custom"; - } else { - validType = false; - } - - if (!validType) { - MessageBoxWarning(newType, "Wrong type"); - return; - } - - if (newEnt) { - this->ent = NekoGui::ProfileManager::NewProxyEntity(type); - this->ent->gid = groupId; - } - - // hide some widget - auto showAddressPort = type != "chain" && customType != "internal" && customType != "internal-full"; - ui->address->setVisible(showAddressPort); - ui->address_l->setVisible(showAddressPort); - ui->port->setVisible(showAddressPort); - ui->port_l->setVisible(showAddressPort); - - // 右边 stream - auto stream = GetStreamSettings(ent->bean.get()); - if (stream != nullptr) { - ui->right_all_w->setVisible(true); - ui->network->setCurrentText(stream->network); - ui->security->setCurrentText(stream->security); - ui->packet_encoding->setCurrentText(stream->packet_encoding); - ui->path->setText(stream->path); - ui->host->setText(stream->host); - ui->sni->setText(stream->sni); - ui->alpn->setText(stream->alpn); - if (newEnt) { - ui->utlsFingerprint->setCurrentText(NekoGui::dataStore->utlsFingerprint); - } else { - ui->utlsFingerprint->setCurrentText(stream->utlsFingerprint); - } - ui->insecure->setChecked(stream->allow_insecure); - ui->header_type->setCurrentText(stream->header_type); - ui->ws_early_data_name->setText(stream->ws_early_data_name); - ui->ws_early_data_length->setText(Int2String(stream->ws_early_data_length)); - ui->reality_pbk->setText(stream->reality_pbk); - ui->reality_sid->setText(stream->reality_sid); - ui->multiplex->setCurrentIndex(stream->multiplex_status); - CACHE.certificate = stream->certificate; - } else { - ui->right_all_w->setVisible(false); - } - - // left: custom - CACHE.custom_config = ent->bean->custom_config; - CACHE.custom_outbound = ent->bean->custom_outbound; - bool show_custom_config = true; - bool show_custom_outbound = true; - if (type == "chain") { - show_custom_outbound = false; - } else if (type == "custom") { - if (customType == "internal") { - show_custom_outbound = false; - } else if (customType == "internal-full") { - show_custom_outbound = false; - show_custom_config = false; - } - } - ui->custom_box->setVisible(show_custom_outbound); - ui->custom_global_box->setVisible(show_custom_config); - - // 左边 bean - auto old = ui->bean->layout()->itemAt(0)->widget(); - ui->bean->layout()->removeWidget(old); - innerWidget->layout()->setContentsMargins(0, 0, 0, 0); - ui->bean->layout()->addWidget(innerWidget); - ui->bean->setTitle(ent->bean->DisplayType()); - delete old; - - // 左边 bean inner editor - innerEditor->get_edit_dialog = [&]() { return (QWidget *) this; }; - innerEditor->get_edit_text_name = [&]() { return ui->name->text(); }; - innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); }; - innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); }; - innerEditor->editor_cache_updated = [=] { editor_cache_updated_impl(); }; - innerEditor->onStart(ent); - - // 左边 common - ui->name->setText(ent->bean->name); - ui->address->setText(ent->bean->serverAddress); - ui->port->setText(Int2String(ent->bean->serverPort)); - ui->port->setValidator(QRegExpValidator_Number); - - // 星号 - ADD_ASTERISK(this) - - // 设置 for NekoBox - if (type == "vmess" || type == "vless") { - ui->packet_encoding->setVisible(true); - ui->packet_encoding_l->setVisible(true); - } else { - ui->packet_encoding->setVisible(false); - ui->packet_encoding_l->setVisible(false); - } - if (type == "vmess" || type == "vless" || type == "trojan") { - ui->network_l->setVisible(true); - ui->network->setVisible(true); - ui->network_box->setVisible(true); - } else { - ui->network_l->setVisible(false); - ui->network->setVisible(false); - ui->network_box->setVisible(false); - } - if (type == "vmess" || type == "vless" || type == "trojan" || type == "http") { - ui->security->setVisible(true); - ui->security_l->setVisible(true); - } else { - ui->security->setVisible(false); - ui->security_l->setVisible(false); - } - if (type == "vmess" || type == "vless" || type == "trojan" || type == "shadowsocks") { - ui->multiplex->setVisible(true); - ui->multiplex_l->setVisible(true); - } else { - ui->multiplex->setVisible(false); - ui->multiplex_l->setVisible(false); - } - - // 设置 是否可见 - int streamBoxVisible = 0; - for (auto label: ui->stream_box->findChildren()) { - if (!label->isHidden()) streamBoxVisible++; - } - ui->stream_box->setVisible(streamBoxVisible); - - // 载入 type 之后,有些类型没有右边的设置 - auto rightNoBox = (ui->stream_box->isHidden() && ui->network_box->isHidden() && ui->security_box->isHidden()); - if (rightNoBox && !ui->right_all_w->isHidden()) { - ui->right_all_w->setVisible(false); - } - - editor_cache_updated_impl(); - ADJUST_SIZE - - // 第一次显示 - if (isHidden()) { - runOnUiThread([=] { show(); }, this); - } -} - -bool DialogEditProfile::onEnd() { - // bean - if (!innerEditor->onEnd()) { - return false; - } - - // 左边 - ent->bean->name = ui->name->text(); - ent->bean->serverAddress = ui->address->text().remove(' '); - ent->bean->serverPort = ui->port->text().toInt(); - - // 右边 stream - auto stream = GetStreamSettings(ent->bean.get()); - if (stream != nullptr) { - stream->network = ui->network->currentText(); - stream->security = ui->security->currentText(); - stream->packet_encoding = ui->packet_encoding->currentText(); - stream->path = ui->path->text(); - stream->host = ui->host->text(); - stream->sni = ui->sni->text(); - stream->alpn = ui->alpn->text(); - stream->utlsFingerprint = ui->utlsFingerprint->currentText(); - stream->allow_insecure = ui->insecure->isChecked(); - stream->header_type = ui->header_type->currentText(); - stream->ws_early_data_name = ui->ws_early_data_name->text(); - stream->ws_early_data_length = ui->ws_early_data_length->text().toInt(); - stream->reality_pbk = ui->reality_pbk->text(); - stream->reality_sid = ui->reality_sid->text(); - stream->multiplex_status = ui->multiplex->currentIndex(); - stream->certificate = CACHE.certificate; - } - - // cached custom - ent->bean->custom_outbound = CACHE.custom_outbound; - ent->bean->custom_config = CACHE.custom_config; - - return true; -} - -void DialogEditProfile::accept() { - // save to ent - if (!onEnd()) { - return; - } - - // finish - QStringList msg = {"accept"}; - - if (newEnt) { - auto ok = NekoGui::profileManager->AddProfile(ent); - if (!ok) { - MessageBoxWarning("???", "id exists"); - } - } else { - auto changed = ent->Save(); - if (changed && NekoGui::dataStore->started_id == ent->id) msg << "restart"; - } - - MW_dialog_message(Dialog_DialogEditProfile, msg.join(",")); - QDialog::accept(); -} - -// cached editor (dialog) - -void DialogEditProfile::editor_cache_updated_impl() { - if (CACHE.certificate.isEmpty()) { - ui->certificate_edit->setText(tr("Not set")); - } else { - ui->certificate_edit->setText(tr("Already set")); - } - if (CACHE.custom_outbound.isEmpty()) { - ui->custom_outbound_edit->setText(tr("Not set")); - } else { - ui->custom_outbound_edit->setText(tr("Already set")); - } - if (CACHE.custom_config.isEmpty()) { - ui->custom_config_edit->setText(tr("Not set")); - } else { - ui->custom_config_edit->setText(tr("Already set")); - } - - // CACHE macro - for (auto a: innerEditor->get_editor_cached()) { - if (a.second.isEmpty()) { - a.first->setText(tr("Not set")); - } else { - a.first->setText(tr("Already set")); - } - } -} - -void DialogEditProfile::on_custom_outbound_edit_clicked() { - C_EDIT_JSON_ALLOW_EMPTY(custom_outbound) - editor_cache_updated_impl(); -} - -void DialogEditProfile::on_custom_config_edit_clicked() { - C_EDIT_JSON_ALLOW_EMPTY(custom_config) - editor_cache_updated_impl(); -} - -void DialogEditProfile::on_certificate_edit_clicked() { - bool ok; - auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.certificate, &ok); - if (ok) { - CACHE.certificate = txt; - editor_cache_updated_impl(); - } -} - -void DialogEditProfile::on_apply_to_group_clicked() { - if (apply_to_group_ui.empty()) { - apply_to_group_ui[ui->multiplex] = new FloatCheckBox(ui->multiplex, this); - apply_to_group_ui[ui->sni] = new FloatCheckBox(ui->sni, this); - apply_to_group_ui[ui->alpn] = new FloatCheckBox(ui->alpn, this); - apply_to_group_ui[ui->host] = new FloatCheckBox(ui->host, this); - apply_to_group_ui[ui->path] = new FloatCheckBox(ui->path, this); - apply_to_group_ui[ui->utlsFingerprint] = new FloatCheckBox(ui->utlsFingerprint, this); - apply_to_group_ui[ui->insecure] = new FloatCheckBox(ui->insecure, this); - apply_to_group_ui[ui->certificate_edit] = new FloatCheckBox(ui->certificate_edit, this); - apply_to_group_ui[ui->custom_config_edit] = new FloatCheckBox(ui->custom_config_edit, this); - apply_to_group_ui[ui->custom_outbound_edit] = new FloatCheckBox(ui->custom_outbound_edit, this); - ui->apply_to_group->setText(tr("Confirm")); - } else { - auto group = NekoGui::profileManager->GetGroup(ent->gid); - if (group == nullptr) { - MessageBoxWarning("failed", "unknown group"); - return; - } - // save this - if (onEnd()) { - ent->Save(); - } else { - MessageBoxWarning("failed", "failed to save"); - return; - } - // copy keys - for (const auto &pair: apply_to_group_ui) { - if (pair.second->isChecked()) { - do_apply_to_group(group, pair.first); - } - delete pair.second; - } - apply_to_group_ui.clear(); - ui->apply_to_group->setText(tr("Apply settings to this group")); - } -} - -void DialogEditProfile::do_apply_to_group(const std::shared_ptr &group, QWidget *key) { - auto stream = GetStreamSettings(ent->bean.get()); - - auto copyStream = [=](void *p) { - for (const auto &profile: group->Profiles()) { - auto newStream = GetStreamSettings(profile->bean.get()); - if (newStream == nullptr) continue; - if (stream == newStream) continue; - newStream->_setValue(stream->_name(p), p); - // qDebug() << newStream->ToJsonBytes(); - profile->Save(); - } - }; - - auto copyBean = [=](void *p) { - for (const auto &profile: group->Profiles()) { - if (profile == ent) continue; - profile->bean->_setValue(ent->bean->_name(p), p); - // qDebug() << profile->bean->ToJsonBytes(); - profile->Save(); - } - }; - - if (key == ui->multiplex) { - copyStream(&stream->multiplex_status); - } else if (key == ui->sni) { - copyStream(&stream->sni); - } else if (key == ui->alpn) { - copyStream(&stream->alpn); - } else if (key == ui->host) { - copyStream(&stream->host); - } else if (key == ui->path) { - copyStream(&stream->path); - } else if (key == ui->utlsFingerprint) { - copyStream(&stream->utlsFingerprint); - } else if (key == ui->insecure) { - copyStream(&stream->allow_insecure); - } else if (key == ui->certificate_edit) { - copyStream(&stream->certificate); - } else if (key == ui->custom_config_edit) { - copyBean(&ent->bean->custom_config); - } else if (key == ui->custom_outbound_edit) { - copyBean(&ent->bean->custom_outbound); - } -} diff --git a/ui/edit/dialog_edit_profile.h b/ui/edit/dialog_edit_profile.h deleted file mode 100644 index 3e8ae50..0000000 --- a/ui/edit/dialog_edit_profile.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef DIALOG_EDIT_PROFILE_H -#define DIALOG_EDIT_PROFILE_H - -#include -#include "db/Database.hpp" -#include "profile_editor.h" - -#include "ui/widget/FloatCheckBox.h" - -namespace Ui { - class DialogEditProfile; -} - -class DialogEditProfile : public QDialog { - Q_OBJECT - -public: - explicit DialogEditProfile(const QString &_type, int profileOrGroupId, QWidget *parent = nullptr); - - ~DialogEditProfile() override; - -public slots: - - void accept() override; - -private slots: - - void on_custom_outbound_edit_clicked(); - - void on_custom_config_edit_clicked(); - - void on_certificate_edit_clicked(); - - void on_apply_to_group_clicked(); - -private: - Ui::DialogEditProfile *ui; - - std::map apply_to_group_ui; - - QWidget *innerWidget{}; - ProfileEditor *innerEditor{}; - - QString type; - int groupId; - bool newEnt = false; - std::shared_ptr ent; - - QString network_title_base; - - struct { - QString custom_outbound; - QString custom_config; - QString certificate; - } CACHE; - - void typeSelected(const QString &newType); - - bool onEnd(); - - void editor_cache_updated_impl(); - - void do_apply_to_group(const std::shared_ptr &group, QWidget *key); -}; - -#endif // DIALOG_EDIT_PROFILE_H diff --git a/ui/edit/dialog_edit_profile.ui b/ui/edit/dialog_edit_profile.ui deleted file mode 100644 index a39379a..0000000 --- a/ui/edit/dialog_edit_profile.ui +++ /dev/null @@ -1,705 +0,0 @@ - - - DialogEditProfile - - - - 0 - 0 - 1000 - 802 - - - - - 0 - 0 - - - - Edit - - - - - - - 0 - 0 - - - - - 400 - 0 - - - - - QLayout::SetDefaultConstraint - - - - - - 0 - 0 - - - - Common - - - - - - - - - - - - - - - - - Type - - - - - - - Port - - - - - - - Address - - - - - - - Name - - - - - - - - - - - - - - - - 0 - 0 - - - - Bean - - - - - - - - - - - - - - - 0 - 0 - - - - Custom Outbound Settings - - - - - - Edit - - - - - - - - - - Custom Config Settings - - - - - - Edit - - - - - - - - - - - - - - Apply settings to this group - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - - - 0 - 0 - - - - - 400 - 0 - - - - - - - - QLayout::SetDefaultConstraint - - - - - - 0 - 0 - - - - Settings - - - - - - - - - 0 - 0 - - - - - - - - - - tcp - - - - - ws - - - - - httpupgrade - - - - - http - - - - - grpc - - - - - quic - - - - - - - - - - - - - - packetaddr - - - - - xudp - - - - - - - - The underlying transport method. It must be consistent with the server, otherwise, the connection cannot be established. - - - Network - - - - - - - - - - - - - tls - - - - - - - - Transport Layer Security. It must be consistent with the server, otherwise, the connection cannot be established. - - - Security - - - - - - - UDP FullCone Packet encoding for implementing features such as UDP FullCone. Server support is required, if the wrong selection is made, the connection cannot be made. Please leave it blank. - - - Packet Encoding - - - - - - - Server support is required - - - Multiplex - - - - - - - - Keep Default - - - - - On - - - - - Off - - - - - - - - - - - - - - 0 - 0 - - - - Network Settings (%1) - - - - - - http path (ws/http/伪装http) -serviceName (gRPC) -key (QUIC) - - - Path - - - - - - - - - - http host (ws/http/伪装http) -security (QUIC) - - - Host - - - - - - - - 0 - 0 - - - - 伪装头部类型 (tcp/quic) - - - header - - - - - - - - - - - 0 - 0 - - - - true - - - - - - - - - http - - - - - - - - - - - EarlyData Length - - - - - - - - - - EarlyData Name - - - - - - - - - - - 0 - 0 - - - - TLS Security Settings - - - - - - - - - 0 - 0 - - - - When enabled, V2Ray will not check the validity of the TLS certificate provided by the remote host (the security is equivalent to plaintext) - - - Allow insecure - - - - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - - - - Certificate - - - - - - - Edit - - - - - - - - - - - - - - Server name indication, clear text. - - - SNI - - - - - - - Application layer protocol negotiation, clear text. Please separate them with commas. - - - ALPN - - - - - - - - - - - - - - - TLS Camouflage Settings - - - - - - true - - - - - - - - 0 - 0 - - - - Fingerprint - - - - - - - Reality public key. If not empty, turn TLS into REALITY. - - - Reality Pbk - - - - - - - Reality short id. Accept only one value. - - - Reality Sid - - - - - - - - - - - - - - - - SpiderX - - - - - - - - - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - type - name - address - port - custom_outbound_edit - custom_config_edit - apply_to_group - network - security - packet_encoding - multiplex - header_type - path - host - ws_early_data_length - ws_early_data_name - insecure - certificate_edit - sni - alpn - utlsFingerprint - reality_pbk - reality_sid - - - - - buttonBox - accepted() - DialogEditProfile - accept() - - - 151 - 500 - - - 299 - 299 - - - - - buttonBox - rejected() - DialogEditProfile - reject() - - - 151 - 500 - - - 299 - 299 - - - - -
diff --git a/ui/edit/edit_chain.cpp b/ui/edit/edit_chain.cpp deleted file mode 100644 index a84d365..0000000 --- a/ui/edit/edit_chain.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "edit_chain.h" -#include "ui_edit_chain.h" - -#include "ui/mainwindow_interface.h" -#include "ui/widget/ProxyItem.h" - -#include "db/Database.hpp" -#include "fmt/ChainBean.hpp" - -EditChain::EditChain(QWidget *parent) : QWidget(parent), ui(new Ui::EditChain) { - ui->setupUi(this); -} - -EditChain::~EditChain() { - delete ui; -} - -void EditChain::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->ChainBean(); - - for (auto id: bean->list) { - AddProfileToListIfExist(id); - } -} - -bool EditChain::onEnd() { - if (get_edit_text_name().isEmpty()) { - MessageBoxWarning(software_name, tr("Name cannot be empty.")); - return false; - } - - auto bean = this->ent->ChainBean(); - - QList idList; - for (int i = 0; i < ui->listWidget->count(); i++) { - idList << ui->listWidget->item(i)->data(114514).toInt(); - } - bean->list = idList; - - return true; -} - -void EditChain::on_select_profile_clicked() { - get_edit_dialog()->hide(); - GetMainWindow()->start_select_mode(this, [=](int id) { - get_edit_dialog()->show(); - AddProfileToListIfExist(id); - }); -} - -void EditChain::AddProfileToListIfExist(int profileId) { - auto _ent = NekoGui::profileManager->GetProfile(profileId); - if (_ent != nullptr && _ent->type != "chain") { - auto wI = new QListWidgetItem(); - wI->setData(114514, profileId); - auto w = new ProxyItem(this, _ent, wI); - ui->listWidget->addItem(wI); - ui->listWidget->setItemWidget(wI, w); - // change button - connect(w->get_change_button(), &QPushButton::clicked, w, [=] { - get_edit_dialog()->hide(); - GetMainWindow()->start_select_mode(w, [=](int newId) { - get_edit_dialog()->show(); - ReplaceProfile(w, newId); - }); - }); - } -} - -void EditChain::ReplaceProfile(ProxyItem *w, int profileId) { - auto _ent = NekoGui::profileManager->GetProfile(profileId); - if (_ent != nullptr && _ent->type != "chain") { - w->item->setData(114514, profileId); - w->ent = _ent; - w->refresh_data(); - } -} diff --git a/ui/edit/edit_chain.h b/ui/edit/edit_chain.h deleted file mode 100644 index 61348d4..0000000 --- a/ui/edit/edit_chain.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditChain; -} -QT_END_NAMESPACE - -class ProxyItem; - -class EditChain : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditChain(QWidget *parent = nullptr); - - ~EditChain() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditChain *ui; - std::shared_ptr ent; - - void AddProfileToListIfExist(int profileId); - - static void ReplaceProfile(ProxyItem *w, int profileId); - -private slots: - - void on_select_profile_clicked(); -}; diff --git a/ui/edit/edit_chain.ui b/ui/edit/edit_chain.ui deleted file mode 100644 index 13996bb..0000000 --- a/ui/edit/edit_chain.ui +++ /dev/null @@ -1,57 +0,0 @@ - - - EditChain - - - - 0 - 0 - 400 - 400 - - - - EditChain - - - - - - Traffic order is from top to bottom - - - - - - - - 0 - 300 - - - - Qt::ActionsContextMenu - - - QAbstractItemView::InternalMove - - - Qt::MoveAction - - - QListView::Free - - - - - - - Select Profile - - - - - - - - diff --git a/ui/edit/edit_custom.cpp b/ui/edit/edit_custom.cpp deleted file mode 100644 index e49a0c9..0000000 --- a/ui/edit/edit_custom.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "edit_custom.h" -#include "ui_edit_custom.h" - -#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp" -#include "fmt/CustomBean.hpp" -#include "fmt/Preset.hpp" -#include "db/ConfigBuilder.hpp" -#include "db/Database.hpp" - -#include -#include - -EditCustom::EditCustom(QWidget *parent) : QWidget(parent), ui(new Ui::EditCustom) { - ui->setupUi(this); - ui->config_simple->setPlaceholderText( - "example:\n" - " server-address: \"127.0.0.1:%mapping_port%\"\n" - " listen-address: \"127.0.0.1\"\n" - " listen-port: %socks_port%\n" - " host: your-domain.com\n" - " sni: your-domain.com\n"); -} - -EditCustom::~EditCustom() { - delete ui; -} - -#define SAVE_CUSTOM_BEAN \ - P_SAVE_COMBO_STRING(core) \ - bean->command = ui->command->text().split(" "); \ - P_SAVE_STRING_PLAIN(config_simple) \ - P_SAVE_COMBO_STRING(config_suffix) \ - P_SAVE_INT(mapping_port) \ - P_SAVE_INT(socks_port) - -void EditCustom::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->CustomBean(); - - // load known core - auto core_map = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map); - for (const auto &key: core_map.keys()) { - ui->core->addItem(key); - } - if (preset_core == "internal") { - preset_command = preset_config = ""; - ui->config_simple->setPlaceholderText( - "{\n" - " \"type\": \"socks\",\n" - " // ...\n" - "}"); - } else if (preset_core == "internal-full") { - preset_command = preset_config = ""; - ui->config_simple->setPlaceholderText( - "{\n" - " \"inbounds\": [],\n" - " \"outbounds\": []\n" - "}"); - } - - // load core ui - P_LOAD_COMBO_STRING(core) - ui->command->setText(bean->command.join(" ")); - ui->config_simple->setPlainText(bean->config_simple); - P_LOAD_COMBO_STRING(config_suffix) - P_LOAD_INT(mapping_port) - P_LOAD_INT(socks_port) - - // custom external - if (!bean->core.isEmpty()) { - ui->core->setDisabled(true); - } else if (!preset_core.isEmpty()) { - bean->core = preset_core; - ui->core->setDisabled(true); - ui->core->setCurrentText(preset_core); - ui->command->setText(preset_command); - ui->config_simple->setPlainText(preset_config); - } - - // custom internal - if (preset_core == "internal" || preset_core == "internal-full") { - ui->core->hide(); - if (preset_core == "internal") { - ui->core_l->setText(tr("Outbound JSON, please read the documentation.")); - } else { - ui->core_l->setText(tr("Please fill the complete config.")); - } - ui->w_ext1->hide(); - ui->w_ext2->hide(); - } - - // Preview - connect(ui->preview, &QPushButton::clicked, this, [=] { - // CustomBean::BuildExternal - QStringList th; - auto mapping_port = ui->mapping_port->text().toInt(); - auto socks_port = ui->socks_port->text().toInt(); - th << "%mapping_port% => " + (mapping_port <= 0 ? "Random" : Int2String(mapping_port)); - th << "%socks_port% => " + (socks_port <= 0 ? "Random" : Int2String(socks_port)); - th << "%server_addr% => " + get_edit_text_serverAddress(); - th << "%server_port% => " + get_edit_text_serverPort(); - MessageBoxInfo(tr("Preview replace"), th.join("\n")); - // EditCustom::onEnd - auto tmpEnt = NekoGui::ProfileManager::NewProxyEntity("custom"); - auto bean = tmpEnt->CustomBean(); - SAVE_CUSTOM_BEAN - // 补充 - bean->serverAddress = get_edit_text_serverAddress(); - bean->serverPort = get_edit_text_serverPort().toInt(); - if (bean->core.isEmpty()) return; - // - auto result = NekoGui::BuildConfig(tmpEnt, false, false); - if (!result->error.isEmpty()) { - MessageBoxInfo(software_name, result->error); - return; - } - for (const auto &extR: result->extRs) { - auto command = QStringList{extR->program}; - command += extR->arguments; - auto btn = QMessageBox::information(this, tr("Preview config"), - QStringLiteral("Command: %1\n\n%2").arg(QStringList2Command(command), extR->config_export), - "OK", "Copy", "", 0, 0); - if (btn == 1) { - QApplication::clipboard()->setText(extR->config_export); - } - } - }); -} - -bool EditCustom::onEnd() { - if (get_edit_text_name().isEmpty()) { - MessageBoxWarning(software_name, tr("Name cannot be empty.")); - return false; - } - if (ui->core->currentText().isEmpty()) { - MessageBoxWarning(software_name, tr("Please pick a core.")); - return false; - } - - auto bean = this->ent->CustomBean(); - - SAVE_CUSTOM_BEAN - - return true; -} - -void EditCustom::on_as_json_clicked() { - auto editor = new JsonEditor(QString2QJsonObject(ui->config_simple->toPlainText()), this); - auto result = editor->OpenEditor(); - if (!result.isEmpty()) { - ui->config_simple->setPlainText(QJsonObject2QString(result, false)); - } -} diff --git a/ui/edit/edit_custom.h b/ui/edit/edit_custom.h deleted file mode 100644 index 9de5373..0000000 --- a/ui/edit/edit_custom.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditCustom; -} -QT_END_NAMESPACE - -class EditCustom : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - QString preset_core; - QString preset_command; - QString preset_config; - - explicit EditCustom(QWidget *parent = nullptr); - - ~EditCustom() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditCustom *ui; - std::shared_ptr ent; - -private slots: - - void on_as_json_clicked(); -}; diff --git a/ui/edit/edit_custom.ui b/ui/edit/edit_custom.ui deleted file mode 100644 index cb1fa41..0000000 --- a/ui/edit/edit_custom.ui +++ /dev/null @@ -1,209 +0,0 @@ - - - EditCustom - - - - 0 - 0 - 400 - 450 - - - - EditCustom - - - - - - - - - 0 - 0 - - - - Core - - - - - - - - 0 - 0 - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Json Editor - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Command - - - - - - - %config% - - - - - - - Config Suffix - - - - - - - true - - - - - - - - - json - - - - - yml - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Random if it's empty or zero. - - - Mapping Port - - - - - - - - - - Random if it's empty or zero. - - - Socks Port - - - - - - - - - - Preview - - - - - - - - - - - 0 - 300 - - - - - - - - core - as_json - command - config_suffix - mapping_port - socks_port - preview - config_simple - - - - diff --git a/ui/edit/edit_naive.cpp b/ui/edit/edit_naive.cpp deleted file mode 100644 index 7539251..0000000 --- a/ui/edit/edit_naive.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "edit_naive.h" -#include "ui_edit_naive.h" - -#include "fmt/NaiveBean.hpp" - -#include - -EditNaive::EditNaive(QWidget *parent) : QWidget(parent), ui(new Ui::EditNaive) { - ui->setupUi(this); -} - -EditNaive::~EditNaive() { - delete ui; -} - -void EditNaive::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->NaiveBean(); - - P_LOAD_STRING(username); - P_LOAD_STRING(password); - P_LOAD_COMBO_STRING(protocol); - P_C_LOAD_STRING(extra_headers); - P_LOAD_STRING(sni); - P_C_LOAD_STRING(certificate); - P_LOAD_INT(insecure_concurrency); - P_LOAD_BOOL(disable_log); -} - -bool EditNaive::onEnd() { - auto bean = this->ent->NaiveBean(); - - P_SAVE_STRING(username); - P_SAVE_STRING(password); - P_SAVE_COMBO_STRING(protocol); - P_C_SAVE_STRING(extra_headers); - P_SAVE_STRING(sni); - P_C_SAVE_STRING(certificate); - P_SAVE_INT(insecure_concurrency); - P_SAVE_BOOL(disable_log); - - return true; -} - -QList> EditNaive::get_editor_cached() { - return { - {ui->certificate, CACHE.certificate}, - {ui->extra_headers, CACHE.extra_headers}, - }; -} - -void EditNaive::on_certificate_clicked() { - bool ok; - auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.certificate, &ok); - if (ok) { - CACHE.certificate = txt; - editor_cache_updated(); - } -} - -void EditNaive::on_extra_headers_clicked() { - bool ok; - auto txt = QInputDialog::getMultiLineText(this, tr("Extra headers"), "", CACHE.extra_headers, &ok); - if (ok) { - CACHE.extra_headers = txt; - editor_cache_updated(); - } -} diff --git a/ui/edit/edit_naive.h b/ui/edit/edit_naive.h deleted file mode 100644 index f1334cf..0000000 --- a/ui/edit/edit_naive.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditNaive; -} -QT_END_NAMESPACE - -class EditNaive : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditNaive(QWidget *parent = nullptr); - - ~EditNaive() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - - QList> get_editor_cached() override; - -private: - Ui::EditNaive *ui; - std::shared_ptr ent; - - struct { - QString certificate; - QString extra_headers; - } CACHE; - -private slots: - - void on_certificate_clicked(); - - void on_extra_headers_clicked(); -}; diff --git a/ui/edit/edit_naive.ui b/ui/edit/edit_naive.ui deleted file mode 100644 index 9063699..0000000 --- a/ui/edit/edit_naive.ui +++ /dev/null @@ -1,131 +0,0 @@ - - - EditNaive - - - - 0 - 0 - 525 - 304 - - - - EditNaive - - - - - - Username - - - - - - - - - - Password - - - - - - - - - - Protocol - - - - - - - - https - - - - - quic - - - - - - - - Extra headers - - - - - - - PushButton - - - - - - - SNI - - - - - - - - - - Certificate - - - - - - - PushButton - - - - - - - Insecure concurrency - - - - - - - - - - Disable logs - - - - - - - Turn on this option if your connection is lost after a while - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - -
diff --git a/ui/edit/edit_quic.cpp b/ui/edit/edit_quic.cpp deleted file mode 100644 index aa869ea..0000000 --- a/ui/edit/edit_quic.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "edit_quic.h" -#include "ui_edit_quic.h" - -#include "fmt/QUICBean.hpp" - -#include -#include - -EditQUIC::EditQUIC(QWidget *parent) : QWidget(parent), ui(new Ui::EditQUIC) { - ui->setupUi(this); - connect(ui->uuidgen, &QPushButton::clicked, this, [=] { ui->uuid->setText(QUuid::createUuid().toString().remove("{").remove("}")); }); -} - -EditQUIC::~EditQUIC() { - delete ui; -} - -void EditQUIC::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->QUICBean(); - - P_LOAD_STRING(hopPort); - P_LOAD_INT(hopInterval); - P_LOAD_INT(uploadMbps); - P_LOAD_INT(downloadMbps); - P_LOAD_BOOL(disableMtuDiscovery) - P_LOAD_STRING(obfsPassword); - P_LOAD_INT(streamReceiveWindow); - P_LOAD_INT(connectionReceiveWindow); - - P_LOAD_BOOL(forceExternal); - P_LOAD_STRING(uuid); - P_LOAD_STRING(password); - - P_LOAD_COMBO_STRING(congestionControl); - P_LOAD_COMBO_STRING(udpRelayMode); - P_LOAD_BOOL(zeroRttHandshake); - P_LOAD_STRING(heartbeat); - P_LOAD_BOOL(uos); - - // TLS - P_LOAD_STRING(sni); - P_LOAD_STRING(alpn); - P_C_LOAD_STRING(caText); - P_LOAD_BOOL(allowInsecure); - P_LOAD_BOOL(disableSni); - - if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria2) { - ui->uuid->hide(); - ui->uuid_l->hide(); - ui->uuidgen->hide(); - ui->congestionControl->hide(); - ui->congestionControl_l->hide(); - ui->udpRelayMode->hide(); - ui->udpRelayMode_l->hide(); - ui->zeroRttHandshake->hide(); - ui->heartbeat->hide(); - ui->heartbeat_l->hide(); - ui->uos->hide(); - - ui->alpn->hide(); - ui->alpn_l->hide(); - ui->TLS->removeItem(ui->alpn_sp); - ui->disableMtuDiscovery->hide(); - ui->connectionReceiveWindow->hide(); - ui->connectionReceiveWindow_l->hide(); - ui->streamReceiveWindow->hide(); - ui->streamReceiveWindow_l->hide(); - } else if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_TUIC) { - ui->hopPort->hide(); - ui->hopPort_l->hide(); - ui->hopInterval->hide(); - ui->hopInterval_l->hide(); - ui->uploadMbps->hide(); - ui->uploadMbps_l->hide(); - ui->downloadMbps->hide(); - ui->downloadMbps_l->hide(); - ui->disableMtuDiscovery->hide(); - ui->obfsPassword->hide(); - ui->obfsPassword_l->hide(); - ui->streamReceiveWindow->hide(); - ui->streamReceiveWindow_l->hide(); - ui->connectionReceiveWindow->hide(); - ui->connectionReceiveWindow_l->hide(); - ui->uos->hide(); - } -} - -bool EditQUIC::onEnd() { - auto bean = this->ent->QUICBean(); - - P_SAVE_BOOL(forceExternal); - - // Hysteria 2 - P_SAVE_STRING(hopPort); - P_SAVE_INT(hopInterval); - P_SAVE_INT(uploadMbps); - P_SAVE_INT(downloadMbps); - P_SAVE_BOOL(disableMtuDiscovery) - P_SAVE_STRING(obfsPassword); - P_SAVE_INT(streamReceiveWindow); - P_SAVE_INT(connectionReceiveWindow); - - // TUIC - P_SAVE_STRING(uuid); - P_SAVE_STRING(password); - P_SAVE_COMBO_STRING(congestionControl); - P_SAVE_COMBO_STRING(udpRelayMode); - P_SAVE_BOOL(zeroRttHandshake); - P_SAVE_STRING(heartbeat); - P_SAVE_BOOL(uos); - - // TLS - P_SAVE_STRING(sni); - P_SAVE_STRING(alpn); - P_SAVE_BOOL(allowInsecure); - P_C_SAVE_STRING(caText); - P_SAVE_BOOL(disableSni); - return true; -} - -QList> EditQUIC::get_editor_cached() { - return { - {ui->certificate, CACHE.caText}, - }; -} - -void EditQUIC::on_certificate_clicked() { - bool ok; - auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.caText, &ok); - if (ok) { - CACHE.caText = txt; - editor_cache_updated(); - } -} diff --git a/ui/edit/edit_quic.h b/ui/edit/edit_quic.h deleted file mode 100644 index 43996f9..0000000 --- a/ui/edit/edit_quic.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditQUIC; -} -QT_END_NAMESPACE - -class EditQUIC : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditQUIC(QWidget *parent = nullptr); - - ~EditQUIC() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - - QList> get_editor_cached() override; - -private: - Ui::EditQUIC *ui; - std::shared_ptr ent; - - struct { - QString caText; - } CACHE; - -private slots: - - void on_certificate_clicked(); -}; diff --git a/ui/edit/edit_quic.ui b/ui/edit/edit_quic.ui deleted file mode 100644 index 3416d89..0000000 --- a/ui/edit/edit_quic.ui +++ /dev/null @@ -1,372 +0,0 @@ - - - EditQUIC - - - - 0 - 0 - 500 - 628 - - - - EditHysteria - - - - - - - - - - Download (Mbps) - - - - - - - - - - - - - - Hop Port - - - - - - - - - - - - - - Hop Interval (s) - - - - - - - - - - - - - - Heartbeat - - - - - - - - 0 - 0 - - - - - - - - - - - - Upload (Mbps) - - - - - - - - - - - - Zero Rtt Handshake - - - - - - - - - Congestion Control - - - - - - - - bbr - - - - - cubic - - - - - new_reno - - - - - - - - - - - - UDP Relay Mode - - - - - - - - native - - - - - quic - - - - - - - - - - - - - - Force use external core - - - - - - - Requires sing-box server - - - UDP over Stream - - - - - - - - 0 - 0 - - - - Disable MTU Discovery - - - - - - - - - - - - - - Obfs Password - - - - - - - - - - - Generate UUID - - - - - - - UUID - - - - - - - - - - - 0 - 0 - - - - Password - - - - - - - - - - - - - - Certificate - - - - - - - PushButton - - - - - - - - - - Disable SNI - - - - - - - - - - ALPN - - - - - - - SNI - - - - - - - Allow Insecure - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 40 - 20 - - - - - - - - - - - - recv_window - - - - - - - - - - recv_window_conn - - - - - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - hopPort - hopInterval - uploadMbps - downloadMbps - congestionControl - udpRelayMode - heartbeat - zeroRttHandshake - forceExternal - uos - disableMtuDiscovery - obfsPassword - uuid - uuidgen - password - sni - disableSni - alpn - certificate - allowInsecure - streamReceiveWindow - connectionReceiveWindow - - - -
diff --git a/ui/edit/edit_shadowsocks.cpp b/ui/edit/edit_shadowsocks.cpp deleted file mode 100644 index e016e34..0000000 --- a/ui/edit/edit_shadowsocks.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "edit_shadowsocks.h" -#include "ui_edit_shadowsocks.h" - -#include "fmt/ShadowSocksBean.hpp" -#include "fmt/Preset.hpp" - -EditShadowSocks::EditShadowSocks(QWidget *parent) : QWidget(parent), - ui(new Ui::EditShadowSocks) { - ui->setupUi(this); - ui->method->addItems(Preset::SingBox::ShadowsocksMethods); -} - -EditShadowSocks::~EditShadowSocks() { - delete ui; -} - -void EditShadowSocks::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->ShadowSocksBean(); - - ui->method->setCurrentText(bean->method); - ui->uot->setCurrentIndex(bean->uot); - ui->password->setText(bean->password); - auto ssPlugin = bean->plugin.split(";"); - if (!ssPlugin.empty()) { - ui->plugin->setCurrentText(ssPlugin[0]); - ui->plugin_opts->setText(SubStrAfter(bean->plugin, ";")); - } -} - -bool EditShadowSocks::onEnd() { - auto bean = this->ent->ShadowSocksBean(); - - bean->method = ui->method->currentText(); - bean->password = ui->password->text(); - bean->uot = ui->uot->currentIndex(); - bean->plugin = ui->plugin->currentText(); - if (!bean->plugin.isEmpty()) { - bean->plugin += ";" + ui->plugin_opts->text(); - } - - return true; -} diff --git a/ui/edit/edit_shadowsocks.h b/ui/edit/edit_shadowsocks.h deleted file mode 100644 index d1131b9..0000000 --- a/ui/edit/edit_shadowsocks.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef EDIT_SHADOWSOCKS_H -#define EDIT_SHADOWSOCKS_H - -#include -#include "profile_editor.h" - -namespace Ui { - class EditShadowSocks; -} - -class EditShadowSocks : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditShadowSocks(QWidget *parent = nullptr); - - ~EditShadowSocks() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditShadowSocks *ui; - std::shared_ptr ent; -}; - -#endif // EDIT_SHADOWSOCKS_H diff --git a/ui/edit/edit_shadowsocks.ui b/ui/edit/edit_shadowsocks.ui deleted file mode 100644 index 2916b6e..0000000 --- a/ui/edit/edit_shadowsocks.ui +++ /dev/null @@ -1,126 +0,0 @@ - - - EditShadowSocks - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - true - - - - - - - - - - Plugin - - - - - - - Encryption - - - - - - - Plugin Args - - - - - - - - - - Password - - - - - - - - - - - - - obfs-local - - - - - v2ray-plugin - - - - - - - - Version of UDP over TCP protocol, server support is required. - - - - - - UoT - - - - - - - - Off - - - - - 1 - - - - - 2 - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - method - password - plugin - plugin_opts - - - -
diff --git a/ui/edit/edit_socks_http.cpp b/ui/edit/edit_socks_http.cpp deleted file mode 100644 index 1598c6e..0000000 --- a/ui/edit/edit_socks_http.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "edit_socks_http.h" -#include "ui_edit_socks_http.h" - -#include "fmt/SocksHttpBean.hpp" - -EditSocksHttp::EditSocksHttp(QWidget *parent) : QWidget(parent), - ui(new Ui::EditSocksHttp) { - ui->setupUi(this); -} - -EditSocksHttp::~EditSocksHttp() { - delete ui; -} - -void EditSocksHttp::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->SocksHTTPBean(); - - if (bean->socks_http_type == NekoGui_fmt::SocksHttpBean::type_Socks4) { - ui->version->setCurrentIndex(1); - } else { - ui->version->setCurrentIndex(0); - } - if (bean->socks_http_type == NekoGui_fmt::SocksHttpBean::type_HTTP) { - ui->version->setVisible(false); - ui->version_l->setVisible(false); - } - - ui->username->setText(bean->username); - ui->password->setText(bean->password); -} - -bool EditSocksHttp::onEnd() { - auto bean = this->ent->SocksHTTPBean(); - - if (ui->version->isVisible()) { - if (ui->version->currentIndex() == 1) { - bean->socks_http_type = NekoGui_fmt::SocksHttpBean::type_Socks4; - } else { - bean->socks_http_type = NekoGui_fmt::SocksHttpBean::type_Socks5; - } - } - - bean->username = ui->username->text(); - bean->password = ui->password->text(); - - return true; -} diff --git a/ui/edit/edit_socks_http.h b/ui/edit/edit_socks_http.h deleted file mode 100644 index ccc3801..0000000 --- a/ui/edit/edit_socks_http.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -namespace Ui { - class EditSocksHttp; -} - -class EditSocksHttp : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditSocksHttp(QWidget *parent = nullptr); - - ~EditSocksHttp() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditSocksHttp *ui; - std::shared_ptr ent; -}; diff --git a/ui/edit/edit_socks_http.ui b/ui/edit/edit_socks_http.ui deleted file mode 100644 index c179709..0000000 --- a/ui/edit/edit_socks_http.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - EditSocksHttp - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - Version - - - - - - - - - - - - - Username - - - - - - - - 5 - - - - - 4 - - - - - - - - Password - - - - - - - version - username - password - - - - diff --git a/ui/edit/edit_trojan_vless.cpp b/ui/edit/edit_trojan_vless.cpp deleted file mode 100644 index 4d29883..0000000 --- a/ui/edit/edit_trojan_vless.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "edit_trojan_vless.h" -#include "ui_edit_trojan_vless.h" - -#include "fmt/TrojanVLESSBean.hpp" -#include "fmt/Preset.hpp" - -EditTrojanVLESS::EditTrojanVLESS(QWidget *parent) : QWidget(parent), ui(new Ui::EditTrojanVLESS) { - ui->setupUi(this); -} - -EditTrojanVLESS::~EditTrojanVLESS() { - delete ui; -} - -void EditTrojanVLESS::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->TrojanVLESSBean(); - if (bean->proxy_type == NekoGui_fmt::TrojanVLESSBean::proxy_VLESS) { - ui->label->setText("UUID"); - } - if (bean->proxy_type != NekoGui_fmt::TrojanVLESSBean::proxy_VLESS) { - ui->flow->hide(); - ui->flow_l->hide(); - } - ui->password->setText(bean->password); - ui->flow->addItems(Preset::SingBox::Flows); - ui->flow->setCurrentText(bean->flow); -} - -bool EditTrojanVLESS::onEnd() { - auto bean = this->ent->TrojanVLESSBean(); - bean->password = ui->password->text(); - bean->flow = ui->flow->currentText(); - return true; -} diff --git a/ui/edit/edit_trojan_vless.h b/ui/edit/edit_trojan_vless.h deleted file mode 100644 index d19b283..0000000 --- a/ui/edit/edit_trojan_vless.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditTrojanVLESS; -} -QT_END_NAMESPACE - -class EditTrojanVLESS : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditTrojanVLESS(QWidget *parent = nullptr); - - ~EditTrojanVLESS() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditTrojanVLESS *ui; - std::shared_ptr ent; -}; diff --git a/ui/edit/edit_trojan_vless.ui b/ui/edit/edit_trojan_vless.ui deleted file mode 100644 index ae622b5..0000000 --- a/ui/edit/edit_trojan_vless.ui +++ /dev/null @@ -1,54 +0,0 @@ - - - EditTrojanVLESS - - - - 0 - 0 - 400 - 300 - - - - - - - - - - - - - Password - - - - - - - Flow - - - - - - - - - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - -
diff --git a/ui/edit/edit_vmess.cpp b/ui/edit/edit_vmess.cpp deleted file mode 100644 index 0fb05a7..0000000 --- a/ui/edit/edit_vmess.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "edit_vmess.h" -#include "ui_edit_vmess.h" - -#include "fmt/VMessBean.hpp" - -#include - -EditVMess::EditVMess(QWidget *parent) : QWidget(parent), ui(new Ui::EditVMess) { - ui->setupUi(this); - connect(ui->uuidgen, &QPushButton::clicked, this, [=] { ui->uuid->setText(QUuid::createUuid().toString().remove("{").remove("}")); }); -} - -EditVMess::~EditVMess() { - delete ui; -} - -void EditVMess::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->VMessBean(); - - ui->uuid->setText(bean->uuid); - ui->aid->setText(Int2String(bean->aid)); - ui->security->setCurrentText(bean->security); -} - -bool EditVMess::onEnd() { - auto bean = this->ent->VMessBean(); - - bean->uuid = ui->uuid->text(); - bean->aid = ui->aid->text().toInt(); - bean->security = ui->security->currentText(); - - return true; -} diff --git a/ui/edit/edit_vmess.h b/ui/edit/edit_vmess.h deleted file mode 100644 index 38efa6c..0000000 --- a/ui/edit/edit_vmess.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "profile_editor.h" - -QT_BEGIN_NAMESPACE -namespace Ui { - class EditVMess; -} -QT_END_NAMESPACE - -class EditVMess : public QWidget, public ProfileEditor { - Q_OBJECT - -public: - explicit EditVMess(QWidget *parent = nullptr); - - ~EditVMess() override; - - void onStart(std::shared_ptr _ent) override; - - bool onEnd() override; - -private: - Ui::EditVMess *ui; - std::shared_ptr ent; -}; diff --git a/ui/edit/edit_vmess.ui b/ui/edit/edit_vmess.ui deleted file mode 100644 index b29dbee..0000000 --- a/ui/edit/edit_vmess.ui +++ /dev/null @@ -1,124 +0,0 @@ - - - EditVMess - - - - 0 - 0 - 400 - 300 - - - - EditVMess - - - - - - true - - - - auto - - - - - zero - - - - - none - - - - - chacha20-poly1305 - - - - - aes-128-gcm - - - - - - - - Security - - - - - - - Alter Id - - - - - - - UUID - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Generate UUID - - - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - uuid - aid - uuidgen - security - - - -
diff --git a/ui/edit/profile_editor.h b/ui/edit/profile_editor.h deleted file mode 100644 index cf43847..0000000 --- a/ui/edit/profile_editor.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include "db/ProxyEntity.hpp" -#include "main/GuiUtils.hpp" - -class ProfileEditor { -public: - virtual void onStart(std::shared_ptr ent) = 0; - - virtual bool onEnd() = 0; - - std::function get_edit_dialog; - std::function get_edit_text_name; - std::function get_edit_text_serverAddress; - std::function get_edit_text_serverPort; - - // cached editor - - std::function editor_cache_updated; - - virtual QList> get_editor_cached() { return {}; }; -}; diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp deleted file mode 100644 index 9155f61..0000000 --- a/ui/mainwindow.cpp +++ /dev/null @@ -1,1844 +0,0 @@ -#include "./ui_mainwindow.h" -#include "mainwindow.h" - -#include "fmt/Preset.hpp" -#include "db/ProfileFilter.hpp" -#include "db/ConfigBuilder.hpp" -#include "sub/GroupUpdater.hpp" -#include "sys/ExternalProcess.hpp" -#include "sys/AutoRun.hpp" - -#include "ui/ThemeManager.hpp" -#include "ui/Icon.hpp" -#include "ui/edit/dialog_edit_profile.h" -#include "ui/dialog_basic_settings.h" -#include "ui/dialog_manage_groups.h" -#include "ui/dialog_manage_routes.h" -#include "ui/dialog_vpn_settings.h" -#include "ui/dialog_hotkey.h" - -#include "3rdparty/fix_old_qt.h" -#include "3rdparty/qrcodegen.hpp" -#include "3rdparty/VT100Parser.hpp" -#include "3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp" - -#ifndef NKR_NO_ZXING -#include "3rdparty/ZxingQtReader.hpp" -#endif - -#ifdef Q_OS_WIN -#include "3rdparty/WinCommander.hpp" -#else -#ifdef Q_OS_LINUX -#include "sys/linux/LinuxCap.h" -#endif -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void UI_InitMainWindow() { - mainwindow = new MainWindow; -} - -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - mainwindow = this; - MW_dialog_message = [=](const QString &a, const QString &b) { - runOnUiThread([=] { dialog_message_impl(a, b); }); - }; - - // Load Manager - NekoGui::profileManager->LoadManager(); - - // Setup misc UI - themeManager->ApplyTheme(NekoGui::dataStore->theme); - ui->setupUi(this); - // - connect(ui->menu_start, &QAction::triggered, this, [=]() { neko_start(); }); - connect(ui->menu_stop, &QAction::triggered, this, [=]() { neko_stop(); }); - connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, this, [=](int from, int to) { - // use tabData to track tab & gid - NekoGui::profileManager->groupsTabOrder.clear(); - for (int i = 0; i < ui->tabWidget->tabBar()->count(); i++) { - NekoGui::profileManager->groupsTabOrder += ui->tabWidget->tabBar()->tabData(i).toInt(); - } - NekoGui::profileManager->SaveManager(); - }); - ui->label_running->installEventFilter(this); - ui->label_inbound->installEventFilter(this); - ui->splitter->installEventFilter(this); - // - RegisterHotkey(false); - // - auto last_size = NekoGui::dataStore->mw_size.split("x"); - if (last_size.length() == 2) { - auto w = last_size[0].toInt(); - auto h = last_size[1].toInt(); - if (w > 0 && h > 0) { - resize(w, h); - } - } - - if (QDir("dashboard").count() == 0) { - QDir().mkdir("dashboard"); - QFile::copy(":/neko/dashboard-notice.html", "dashboard/index.html"); - } - - // top bar - ui->toolButton_program->setMenu(ui->menu_program); - ui->toolButton_preferences->setMenu(ui->menu_preferences); - ui->toolButton_server->setMenu(ui->menu_server); - ui->menubar->setVisible(false); - connect(ui->toolButton_document, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuridayo.github.io/")); }); - connect(ui->toolButton_ads, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://neko-box.pages.dev/喵")); }); - connect(ui->toolButton_update, &QToolButton::clicked, this, [=] { runOnNewThread([=] { CheckUpdate(); }); }); - connect(ui->toolButton_url_test, &QToolButton::clicked, this, [=] { speedtest_current_group(1, true); }); - - // Setup log UI - ui->splitter->restoreState(DecodeB64IfValid(NekoGui::dataStore->splitter_state)); - qvLogDocument->setUndoRedoEnabled(false); - ui->masterLogBrowser->setUndoRedoEnabled(false); - ui->masterLogBrowser->setDocument(qvLogDocument); - ui->masterLogBrowser->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - { - auto font = ui->masterLogBrowser->font(); - font.setPointSize(9); - ui->masterLogBrowser->setFont(font); - qvLogDocument->setDefaultFont(font); - } - connect(ui->masterLogBrowser->verticalScrollBar(), &QSlider::valueChanged, this, [=](int value) { - if (ui->masterLogBrowser->verticalScrollBar()->maximum() == value) - qvLogAutoScoll = true; - else - qvLogAutoScoll = false; - }); - connect(ui->masterLogBrowser, &QTextBrowser::textChanged, this, [=]() { - if (!qvLogAutoScoll) - return; - auto bar = ui->masterLogBrowser->verticalScrollBar(); - bar->setValue(bar->maximum()); - }); - MW_show_log = [=](const QString &log) { - runOnUiThread([=] { show_log_impl(log); }); - }; - MW_show_log_ext = [=](const QString &tag, const QString &log) { - runOnUiThread([=] { show_log_impl("[" + tag + "] " + log); }); - }; - MW_show_log_ext_vt100 = [=](const QString &log) { - runOnUiThread([=] { show_log_impl(cleanVT100String(log)); }); - }; - - // table UI - ui->proxyListTable->callback_save_order = [=] { - auto group = NekoGui::profileManager->CurrentGroup(); - group->order = ui->proxyListTable->order; - group->Save(); - }; - ui->proxyListTable->refresh_data = [=](int id) { refresh_proxy_list_impl_refresh_data(id); }; - if (auto button = ui->proxyListTable->findChild(QString(), Qt::FindDirectChildrenOnly)) { - // Corner Button - connect(button, &QAbstractButton::clicked, this, [=] { refresh_proxy_list_impl(-1, {GroupSortMethod::ById}); }); - } - connect(ui->proxyListTable->horizontalHeader(), &QHeaderView::sectionClicked, this, [=](int logicalIndex) { - GroupSortAction action; - // 不正确的descending实现 - if (proxy_last_order == logicalIndex) { - action.descending = true; - proxy_last_order = -1; - } else { - proxy_last_order = logicalIndex; - } - action.save_sort = true; - // 表头 - if (logicalIndex == 0) { - action.method = GroupSortMethod::ByType; - } else if (logicalIndex == 1) { - action.method = GroupSortMethod::ByAddress; - } else if (logicalIndex == 2) { - action.method = GroupSortMethod::ByName; - } else if (logicalIndex == 3) { - action.method = GroupSortMethod::ByLatency; - } else { - return; - } - refresh_proxy_list_impl(-1, action); - }); - connect(ui->proxyListTable->horizontalHeader(), &QHeaderView::sectionResized, this, [=](int logicalIndex, int oldSize, int newSize) { - auto group = NekoGui::profileManager->CurrentGroup(); - if (NekoGui::dataStore->refreshing_group || group == nullptr || !group->manually_column_width) return; - // save manually column width - group->column_width.clear(); - for (int i = 0; i < ui->proxyListTable->horizontalHeader()->count(); i++) { - group->column_width.push_back(ui->proxyListTable->horizontalHeader()->sectionSize(i)); - } - group->column_width[logicalIndex] = newSize; - group->Save(); - }); - ui->tableWidget_conn->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - ui->tableWidget_conn->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - ui->tableWidget_conn->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); - ui->proxyListTable->verticalHeader()->setDefaultSectionSize(24); - - // search box - ui->search->setVisible(false); - connect(shortcut_ctrl_f, &QShortcut::activated, this, [=] { - ui->search->setVisible(true); - ui->search->setFocus(); - }); - connect(shortcut_esc, &QShortcut::activated, this, [=] { - if (ui->search->isVisible()) { - ui->search->setText(""); - ui->search->textChanged(""); - ui->search->setVisible(false); - } - if (select_mode) { - emit profile_selected(-1); - select_mode = false; - refresh_status(); - } - }); - connect(ui->search, &QLineEdit::textChanged, this, [=](const QString &text) { - if (text.isEmpty()) { - for (int i = 0; i < ui->proxyListTable->rowCount(); i++) { - ui->proxyListTable->setRowHidden(i, false); - } - } else { - QList findItem = ui->proxyListTable->findItems(text, Qt::MatchContains); - for (int i = 0; i < ui->proxyListTable->rowCount(); i++) { - ui->proxyListTable->setRowHidden(i, true); - } - for (auto item: findItem) { - if (item != nullptr) ui->proxyListTable->setRowHidden(item->row(), false); - } - } - }); - - // refresh - this->refresh_groups(); - - // Setup Tray - tray = new QSystemTrayIcon(this); // 初始化托盘对象tray - tray->setIcon(Icon::GetTrayIcon(Icon::NONE)); - tray->setContextMenu(ui->menu_program); // 创建托盘菜单 - tray->show(); // 让托盘图标显示在系统托盘上 - connect(tray, &QSystemTrayIcon::activated, this, [=](QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::Trigger) { - if (this->isVisible()) { - hide(); - } else { - ActivateWindow(this); - } - } - }); - - // Misc menu - connect(ui->menu_open_config_folder, &QAction::triggered, this, [=] { QDesktopServices::openUrl(QUrl::fromLocalFile(QDir::currentPath())); }); - ui->menu_program_preference->addActions(ui->menu_preferences->actions()); - connect(ui->menu_add_from_clipboard2, &QAction::triggered, ui->menu_add_from_clipboard, &QAction::trigger); - connect(ui->actionRestart_Proxy, &QAction::triggered, this, [=] { if (NekoGui::dataStore->started_id>=0) neko_start(NekoGui::dataStore->started_id); }); - connect(ui->actionRestart_Program, &QAction::triggered, this, [=] { MW_dialog_message("", "RestartProgram"); }); - connect(ui->actionShow_window, &QAction::triggered, this, [=] { tray->activated(QSystemTrayIcon::ActivationReason::Trigger); }); - // - connect(ui->menu_program, &QMenu::aboutToShow, this, [=]() { - ui->actionRemember_last_proxy->setChecked(NekoGui::dataStore->remember_enable); - ui->actionStart_with_system->setChecked(AutoRun_IsEnabled()); - ui->actionAllow_LAN->setChecked(QStringList{"::", "0.0.0.0"}.contains(NekoGui::dataStore->inbound_address)); - // active server - for (const auto &old: ui->menuActive_Server->actions()) { - ui->menuActive_Server->removeAction(old); - old->deleteLater(); - } - int active_server_item_count = 0; - for (const auto &pf: NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder()) { - auto a = new QAction(pf->bean->DisplayTypeAndName(), this); - a->setProperty("id", pf->id); - a->setCheckable(true); - if (NekoGui::dataStore->started_id == pf->id) a->setChecked(true); - ui->menuActive_Server->addAction(a); - if (++active_server_item_count == 100) break; - } - // active routing - for (const auto &old: ui->menuActive_Routing->actions()) { - ui->menuActive_Routing->removeAction(old); - old->deleteLater(); - } - for (const auto &name: NekoGui::Routing::List()) { - auto a = new QAction(name, this); - a->setCheckable(true); - a->setChecked(name == NekoGui::dataStore->active_routing); - ui->menuActive_Routing->addAction(a); - } - }); - connect(ui->menuActive_Server, &QMenu::triggered, this, [=](QAction *a) { - bool ok; - auto id = a->property("id").toInt(&ok); - if (!ok) return; - if (NekoGui::dataStore->started_id == id) { - neko_stop(); - } else { - neko_start(id); - } - }); - connect(ui->menuActive_Routing, &QMenu::triggered, this, [=](QAction *a) { - auto fn = a->text(); - if (!fn.isEmpty()) { - NekoGui::Routing r; - r.load_control_must = true; - r.fn = ROUTES_PREFIX + fn; - if (r.Load()) { - if (QMessageBox::question(GetMessageBoxParent(), software_name, tr("Load routing and apply: %1").arg(fn) + "\n" + r.DisplayRouting()) == QMessageBox::Yes) { - NekoGui::Routing::SetToActive(fn); - if (NekoGui::dataStore->started_id >= 0) { - neko_start(NekoGui::dataStore->started_id); - } else { - refresh_status(); - } - } - } - } - }); - connect(ui->actionRemember_last_proxy, &QAction::triggered, this, [=](bool checked) { - NekoGui::dataStore->remember_enable = checked; - NekoGui::dataStore->Save(); - }); - connect(ui->actionStart_with_system, &QAction::triggered, this, [=](bool checked) { - AutoRun_SetEnabled(checked); - }); - connect(ui->actionAllow_LAN, &QAction::triggered, this, [=](bool checked) { - NekoGui::dataStore->inbound_address = checked ? "::" : "127.0.0.1"; - MW_dialog_message("", "UpdateDataStore"); - }); - // - connect(ui->checkBox_VPN, &QCheckBox::clicked, this, [=](bool checked) { neko_set_spmode_vpn(checked); }); - connect(ui->checkBox_SystemProxy, &QCheckBox::clicked, this, [=](bool checked) { neko_set_spmode_system_proxy(checked); }); - connect(ui->menu_spmode, &QMenu::aboutToShow, this, [=]() { - ui->menu_spmode_disabled->setChecked(!(NekoGui::dataStore->spmode_system_proxy || NekoGui::dataStore->spmode_vpn)); - ui->menu_spmode_system_proxy->setChecked(NekoGui::dataStore->spmode_system_proxy); - ui->menu_spmode_vpn->setChecked(NekoGui::dataStore->spmode_vpn); - }); - connect(ui->menu_spmode_system_proxy, &QAction::triggered, this, [=](bool checked) { neko_set_spmode_system_proxy(checked); }); - connect(ui->menu_spmode_vpn, &QAction::triggered, this, [=](bool checked) { neko_set_spmode_vpn(checked); }); - connect(ui->menu_spmode_disabled, &QAction::triggered, this, [=]() { - neko_set_spmode_system_proxy(false); - neko_set_spmode_vpn(false); - }); - connect(ui->menu_qr, &QAction::triggered, this, [=]() { display_qr_link(false); }); - connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0, false); }); - connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1, false); }); - connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2, false); }); - connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { speedtest_current_group(114514, false); }); - // - auto set_selected_or_group = [=](int mode) { - // 0=group 1=select 2=unknown(menu is hide) - ui->menu_server->setProperty("selected_or_group", mode); - }; - auto move_tests_to_menu = [=](bool menuCurrent_Select) { - return [=] { - if (menuCurrent_Select) { - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_tcp_ping); - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_url_test); - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_full_test); - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_stop_testing); - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_clear_test_result); - ui->menuCurrent_Select->insertAction(ui->actionfake_4, ui->menu_resolve_domain); - } else { - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_tcp_ping); - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_url_test); - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_full_test); - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_stop_testing); - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_clear_test_result); - ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_resolve_domain); - } - set_selected_or_group(menuCurrent_Select ? 1 : 0); - }; - }; - connect(ui->menuCurrent_Select, &QMenu::aboutToShow, this, move_tests_to_menu(true)); - connect(ui->menuCurrent_Group, &QMenu::aboutToShow, this, move_tests_to_menu(false)); - connect(ui->menu_server, &QMenu::aboutToHide, this, [=] { - setTimeout([=] { set_selected_or_group(2); }, this, 200); - }); - set_selected_or_group(2); - // - connect(ui->menu_share_item, &QMenu::aboutToShow, this, [=] { - QString name; - auto selected = get_now_selected_list(); - if (!selected.isEmpty()) { - auto ent = selected.first(); - name = ent->bean->DisplayCoreType(); - } - ui->menu_export_config->setVisible(name == software_core_name); - ui->menu_export_config->setText(tr("Export %1 config").arg(name)); - }); - refresh_status(); - - // Prepare core - NekoGui::dataStore->core_token = GetRandomString(32); - NekoGui::dataStore->core_port = MkPort(); - if (NekoGui::dataStore->core_port <= 0) NekoGui::dataStore->core_port = 19810; - - auto core_path = QApplication::applicationDirPath() + "/"; - core_path += "nekobox_core"; - - QStringList args; - args.push_back("nekobox"); - args.push_back("-port"); - args.push_back(Int2String(NekoGui::dataStore->core_port)); - if (NekoGui::dataStore->flag_debug) args.push_back("-debug"); - - // Start core - runOnUiThread( - [=] { - core_process = new NekoGui_sys::CoreProcess(core_path, args); - // Remember last started - if (NekoGui::dataStore->remember_enable && NekoGui::dataStore->remember_id >= 0) { - core_process->start_profile_when_core_is_up = NekoGui::dataStore->remember_id; - } - // Setup - core_process->Start(); - setup_grpc(); - }, - DS_cores); - - // Remember system proxy - if (NekoGui::dataStore->remember_enable || NekoGui::dataStore->flag_restart_tun_on) { - if (NekoGui::dataStore->remember_spmode.contains("system_proxy")) { - neko_set_spmode_system_proxy(true, false); - } - if (NekoGui::dataStore->remember_spmode.contains("vpn") || NekoGui::dataStore->flag_restart_tun_on) { - neko_set_spmode_vpn(true, false); - } - } - - connect(qApp, &QGuiApplication::commitDataRequest, this, &MainWindow::on_commitDataRequest); - - auto t = new QTimer; - connect(t, &QTimer::timeout, this, [=]() { refresh_status(); }); - t->start(2000); - - t = new QTimer; - connect(t, &QTimer::timeout, this, [&] { NekoGui_sys::logCounter.fetchAndStoreRelaxed(0); }); - t->start(1000); - - TM_auto_update_subsctiption = new QTimer; - TM_auto_update_subsctiption_Reset_Minute = [&](int m) { - TM_auto_update_subsctiption->stop(); - if (m >= 30) TM_auto_update_subsctiption->start(m * 60 * 1000); - }; - connect(TM_auto_update_subsctiption, &QTimer::timeout, this, [&] { UI_update_all_groups(true); }); - TM_auto_update_subsctiption_Reset_Minute(NekoGui::dataStore->sub_auto_update); - - if (!NekoGui::dataStore->flag_tray) show(); -} - -void MainWindow::closeEvent(QCloseEvent *event) { - if (tray->isVisible()) { - hide(); // 隐藏窗口 - event->ignore(); // 忽略事件 - } -} - -MainWindow::~MainWindow() { - delete ui; -} - -// Group tab manage - -inline int tabIndex2GroupId(int index) { - if (NekoGui::profileManager->groupsTabOrder.length() <= index) return -1; - return NekoGui::profileManager->groupsTabOrder[index]; -} - -inline int groupId2TabIndex(int gid) { - for (int key = 0; key < NekoGui::profileManager->groupsTabOrder.count(); key++) { - if (NekoGui::profileManager->groupsTabOrder[key] == gid) return key; - } - return 0; -} - -void MainWindow::on_tabWidget_currentChanged(int index) { - if (NekoGui::dataStore->refreshing_group_list) return; - if (tabIndex2GroupId(index) == NekoGui::dataStore->current_group) return; - show_group(tabIndex2GroupId(index)); -} - -void MainWindow::show_group(int gid) { - if (NekoGui::dataStore->refreshing_group) return; - NekoGui::dataStore->refreshing_group = true; - - auto group = NekoGui::profileManager->GetGroup(gid); - if (group == nullptr) { - MessageBoxWarning(tr("Error"), QStringLiteral("No such group: %1").arg(gid)); - NekoGui::dataStore->refreshing_group = false; - return; - } - - if (NekoGui::dataStore->current_group != gid) { - NekoGui::dataStore->current_group = gid; - NekoGui::dataStore->Save(); - } - ui->tabWidget->widget(groupId2TabIndex(gid))->layout()->addWidget(ui->proxyListTable); - - // 列宽是否可调 - if (group->manually_column_width) { - for (int i = 0; i <= 4; i++) { - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Interactive); - auto size = group->column_width.value(i); - if (size <= 0) size = ui->proxyListTable->horizontalHeader()->defaultSectionSize(); - ui->proxyListTable->horizontalHeader()->resizeSection(i, size); - } - } else { - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); - ui->proxyListTable->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); - } - - // show proxies - GroupSortAction gsa; - gsa.scroll_to_started = true; - refresh_proxy_list_impl(-1, gsa); - - NekoGui::dataStore->refreshing_group = false; -} - -// callback - -void MainWindow::dialog_message_impl(const QString &sender, const QString &info) { - // info - if (info.contains("UpdateIcon")) { - icon_status = -1; - refresh_status(); - } - if (info.contains("UpdateDataStore")) { - auto suggestRestartProxy = NekoGui::dataStore->Save(); - if (info.contains("RouteChanged")) { - suggestRestartProxy = true; - } - if (info.contains("NeedRestart")) { - suggestRestartProxy = false; - } - refresh_proxy_list(); - if (info.contains("VPNChanged") && NekoGui::dataStore->spmode_vpn) { - MessageBoxWarning(tr("Tun Settings changed"), tr("Restart Tun to take effect.")); - } - if (suggestRestartProxy && NekoGui::dataStore->started_id >= 0 && - QMessageBox::question(GetMessageBoxParent(), tr("Confirmation"), tr("Settings changed, restart proxy?")) == QMessageBox::StandardButton::Yes) { - neko_start(NekoGui::dataStore->started_id); - } - refresh_status(); - } - if (info.contains("NeedRestart")) { - auto n = QMessageBox::warning(GetMessageBoxParent(), tr("Settings changed"), tr("Restart the program to take effect."), QMessageBox::Yes | QMessageBox::No); - if (n == QMessageBox::Yes) { - this->exit_reason = 2; - on_menu_exit_triggered(); - } - } - // - if (info == "RestartProgram") { - this->exit_reason = 2; - on_menu_exit_triggered(); - } else if (info == "Raise") { - ActivateWindow(this); - } else if (info == "ClearConnectionList") { - refresh_connection_list({}); - } - // sender - if (sender == Dialog_DialogEditProfile) { - auto msg = info.split(","); - if (msg.contains("accept")) { - refresh_proxy_list(); - if (msg.contains("restart")) { - if (QMessageBox::question(GetMessageBoxParent(), tr("Confirmation"), tr("Settings changed, restart proxy?")) == QMessageBox::StandardButton::Yes) { - neko_start(NekoGui::dataStore->started_id); - } - } - } - } else if (sender == Dialog_DialogManageGroups) { - if (info.startsWith("refresh")) { - this->refresh_groups(); - } - } else if (sender == "SubUpdater") { - if (info.startsWith("finish")) { - refresh_proxy_list(); - if (!info.contains("dingyue")) { - show_log_impl(tr("Imported %1 profile(s)").arg(NekoGui::dataStore->imported_count)); - } - } else if (info == "NewGroup") { - refresh_groups(); - } - } else if (sender == "ExternalProcess") { - if (info == "Crashed") { - neko_stop(); - } else if (info == "CoreCrashed") { - neko_stop(true); - } else if (info.startsWith("CoreStarted")) { - neko_start(info.split(",")[1].toInt()); - } - } -} - -// top bar & tray menu - -inline bool dialog_is_using = false; - -#define USE_DIALOG(a) \ - if (dialog_is_using) return; \ - dialog_is_using = true; \ - auto dialog = new a(this); \ - connect(dialog, &QDialog::finished, this, [=] { \ - dialog->deleteLater(); \ - dialog_is_using = false; \ - }); \ - dialog->show(); - -void MainWindow::on_menu_basic_settings_triggered() { - USE_DIALOG(DialogBasicSettings) -} - -void MainWindow::on_menu_manage_groups_triggered() { - USE_DIALOG(DialogManageGroups) -} - -void MainWindow::on_menu_routing_settings_triggered() { - USE_DIALOG(DialogManageRoutes) -} - -void MainWindow::on_menu_vpn_settings_triggered() { - USE_DIALOG(DialogVPNSettings) -} - -void MainWindow::on_menu_hotkey_settings_triggered() { - USE_DIALOG(DialogHotkey) -} - -void MainWindow::on_commitDataRequest() { - qDebug() << "Start of data save"; - // - if (!isMaximized()) { - auto olds = NekoGui::dataStore->mw_size; - auto news = QStringLiteral("%1x%2").arg(size().width()).arg(size().height()); - if (olds != news) { - NekoGui::dataStore->mw_size = news; - } - } - // - NekoGui::dataStore->splitter_state = ui->splitter->saveState().toBase64(); - // - auto last_id = NekoGui::dataStore->started_id; - if (NekoGui::dataStore->remember_enable && last_id >= 0) { - NekoGui::dataStore->remember_id = last_id; - } - // - NekoGui::dataStore->Save(); - NekoGui::profileManager->SaveManager(); - qDebug() << "End of data save"; -} - -void MainWindow::on_menu_exit_triggered() { - if (mu_exit.tryLock()) { - NekoGui::dataStore->prepare_exit = true; - // - neko_set_spmode_system_proxy(false, false); - neko_set_spmode_vpn(false, false); - if (NekoGui::dataStore->spmode_vpn) { - mu_exit.unlock(); // retry - return; - } - RegisterHotkey(true); - // - on_commitDataRequest(); - // - NekoGui::dataStore->save_control_no_save = true; // don't change datastore after this line - neko_stop(false, true); - // - hide(); - runOnNewThread([=] { - sem_stopped.acquire(); - stop_core_daemon(); - runOnUiThread([=] { - on_menu_exit_triggered(); // continue exit progress - }); - }); - return; - } - // - MF_release_runguard(); - if (exit_reason == 1) { - QDir::setCurrent(QApplication::applicationDirPath()); - QProcess::startDetached("./updater", QStringList{}); - } else if (exit_reason == 2 || exit_reason == 3) { - QDir::setCurrent(QApplication::applicationDirPath()); - - auto arguments = NekoGui::dataStore->argv; - if (arguments.length() > 0) { - arguments.removeFirst(); - arguments.removeAll("-tray"); - arguments.removeAll("-flag_restart_tun_on"); - arguments.removeAll("-flag_reorder"); - } - auto isLauncher = qEnvironmentVariable("NKR_FROM_LAUNCHER") == "1"; - if (isLauncher) arguments.prepend("--"); - auto program = isLauncher ? "./launcher" : QApplication::applicationFilePath(); - - if (exit_reason == 3) { - // Tun restart as admin - arguments << "-flag_restart_tun_on"; -#ifdef Q_OS_WIN - WinCommander::runProcessElevated(program, arguments, "", WinCommander::SW_NORMAL, false); -#else - QProcess::startDetached(program, arguments); -#endif - } else { - QProcess::startDetached(program, arguments); - } - } - tray->hide(); - QCoreApplication::quit(); -} - -#define neko_set_spmode_FAILED \ - refresh_status(); \ - return; - -void MainWindow::neko_set_spmode_system_proxy(bool enable, bool save) { - if (enable != NekoGui::dataStore->spmode_system_proxy) { - if (enable) { - auto socks_port = NekoGui::dataStore->inbound_socks_port; - auto http_port = NekoGui::dataStore->inbound_socks_port; - SetSystemProxy(http_port, socks_port); - } else { - ClearSystemProxy(); - } - } - - if (save) { - NekoGui::dataStore->remember_spmode.removeAll("system_proxy"); - if (enable && NekoGui::dataStore->remember_enable) { - NekoGui::dataStore->remember_spmode.append("system_proxy"); - } - NekoGui::dataStore->Save(); - } - - NekoGui::dataStore->spmode_system_proxy = enable; - refresh_status(); -} - -void MainWindow::neko_set_spmode_vpn(bool enable, bool save) { - if (enable != NekoGui::dataStore->spmode_vpn) { - if (enable) { - if (NekoGui::dataStore->vpn_internal_tun) { - bool requestPermission = !NekoGui::IsAdmin(); - if (requestPermission) { -#ifdef Q_OS_LINUX - if (!Linux_HavePkexec()) { - MessageBoxWarning(software_name, "Please install \"pkexec\" first."); - neko_set_spmode_FAILED - } - auto ret = Linux_Pkexec_SetCapString(NekoGui::FindNekoBoxCoreRealPath(), "cap_net_admin=ep"); - if (ret == 0) { - this->exit_reason = 3; - on_menu_exit_triggered(); - } else { - MessageBoxWarning(software_name, "Setcap for Tun mode failed.\n\n1. You may canceled the dialog.\n2. You may be using an incompatible environment like AppImage."); - if (QProcessEnvironment::systemEnvironment().contains("APPIMAGE")) { - MW_show_log("If you are using AppImage, it's impossible to start a Tun. Please use other package instead."); - } - } -#endif -#ifdef Q_OS_WIN - auto n = QMessageBox::warning(GetMessageBoxParent(), software_name, tr("Please run NekoBox as admin"), QMessageBox::Yes | QMessageBox::No); - if (n == QMessageBox::Yes) { - this->exit_reason = 3; - on_menu_exit_triggered(); - } -#endif - neko_set_spmode_FAILED - } - } else { - if (NekoGui::dataStore->need_keep_vpn_off) { - MessageBoxWarning(software_name, tr("Current server is incompatible with Tun. Please stop the server first, enable Tun Mode, and then restart.")); - neko_set_spmode_FAILED - } - if (!StartVPNProcess()) { - neko_set_spmode_FAILED - } - } - } else { - if (NekoGui::dataStore->vpn_internal_tun) { - // current core is sing-box - } else { - if (!StopVPNProcess()) { - neko_set_spmode_FAILED - } - } - } - } - - if (save) { - NekoGui::dataStore->remember_spmode.removeAll("vpn"); - if (enable && NekoGui::dataStore->remember_enable) { - NekoGui::dataStore->remember_spmode.append("vpn"); - } - NekoGui::dataStore->Save(); - } - - NekoGui::dataStore->spmode_vpn = enable; - refresh_status(); - - if (NekoGui::dataStore->vpn_internal_tun && NekoGui::dataStore->started_id >= 0) neko_start(NekoGui::dataStore->started_id); -} - -void MainWindow::refresh_status(const QString &traffic_update) { - auto refresh_speed_label = [=] { - if (traffic_update_cache == "") { - ui->label_speed->setText(QObject::tr("Proxy: %1\nDirect: %2").arg("", "")); - } else { - ui->label_speed->setText(traffic_update_cache); - } - }; - - // From TrafficLooper - if (!traffic_update.isEmpty()) { - traffic_update_cache = traffic_update; - if (traffic_update == "STOP") { - traffic_update_cache = ""; - } else { - refresh_speed_label(); - return; - } - } - - refresh_speed_label(); - - // From UI - QString group_name; - if (running != nullptr) { - auto group = NekoGui::profileManager->GetGroup(running->gid); - if (group != nullptr) group_name = group->name; - } - - if (last_test_time.addSecs(2) < QTime::currentTime()) { - auto txt = running == nullptr ? tr("Not Running") - : QStringLiteral("[%1] %2").arg(group_name, running->bean->DisplayName()).left(30); - ui->label_running->setText(txt); - } - // - auto display_socks = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_socks_port); - auto inbound_txt = QStringLiteral("Mixed: %1").arg(display_socks); - ui->label_inbound->setText(inbound_txt); - // - ui->checkBox_VPN->setChecked(NekoGui::dataStore->spmode_vpn); - ui->checkBox_SystemProxy->setChecked(NekoGui::dataStore->spmode_system_proxy); - if (select_mode) { - ui->label_running->setText(tr("Select") + " *"); - ui->label_running->setToolTip(tr("Select mode, double-click or press Enter to select a profile, press ESC to exit.")); - } else { - ui->label_running->setToolTip({}); - } - - auto make_title = [=](bool isTray) { - QStringList tt; - if (!isTray && NekoGui::IsAdmin()) tt << "[Admin]"; - if (select_mode) tt << "[" + tr("Select") + "]"; - if (!title_error.isEmpty()) tt << "[" + title_error + "]"; - if (NekoGui::dataStore->spmode_vpn && !NekoGui::dataStore->spmode_system_proxy) tt << "[Tun]"; - if (!NekoGui::dataStore->spmode_vpn && NekoGui::dataStore->spmode_system_proxy) tt << "[" + tr("System Proxy") + "]"; - if (NekoGui::dataStore->spmode_vpn && NekoGui::dataStore->spmode_system_proxy) tt << "[Tun+" + tr("System Proxy") + "]"; - tt << software_name; - if (!isTray) tt << "(" + QString(NKR_VERSION) + ")"; - if (!NekoGui::dataStore->active_routing.isEmpty() && NekoGui::dataStore->active_routing != "Default") { - tt << "[" + NekoGui::dataStore->active_routing + "]"; - } - if (running != nullptr) tt << running->bean->DisplayTypeAndName() + "@" + group_name; - return tt.join(isTray ? "\n" : " "); - }; - - auto icon_status_new = Icon::NONE; - - if (running != nullptr) { - if (NekoGui::dataStore->spmode_vpn) { - icon_status_new = Icon::VPN; - } else if (NekoGui::dataStore->spmode_system_proxy) { - icon_status_new = Icon::SYSTEM_PROXY; - } else { - icon_status_new = Icon::RUNNING; - } - } - - // refresh title & window icon - setWindowTitle(make_title(false)); - if (icon_status_new != icon_status) QApplication::setWindowIcon(Icon::GetTrayIcon(Icon::NONE)); - - // refresh tray - if (tray != nullptr) { - tray->setToolTip(make_title(true)); - if (icon_status_new != icon_status) tray->setIcon(Icon::GetTrayIcon(icon_status_new)); - } - - icon_status = icon_status_new; -} - -// table显示 - -// refresh_groups -> show_group -> refresh_proxy_list -void MainWindow::refresh_groups() { - NekoGui::dataStore->refreshing_group_list = true; - - // refresh group? - for (int i = ui->tabWidget->count() - 1; i > 0; i--) { - ui->tabWidget->removeTab(i); - } - - int index = 0; - for (const auto &gid: NekoGui::profileManager->groupsTabOrder) { - auto group = NekoGui::profileManager->GetGroup(gid); - if (index == 0) { - ui->tabWidget->setTabText(0, group->name); - } else { - auto widget2 = new QWidget(); - auto layout2 = new QVBoxLayout(); - layout2->setContentsMargins(QMargins()); - layout2->setSpacing(0); - widget2->setLayout(layout2); - ui->tabWidget->addTab(widget2, group->name); - } - ui->tabWidget->tabBar()->setTabData(index, gid); - index++; - } - - // show after group changed - if (NekoGui::profileManager->CurrentGroup() == nullptr) { - NekoGui::dataStore->current_group = -1; - ui->tabWidget->setCurrentIndex(groupId2TabIndex(0)); - show_group(NekoGui::profileManager->groupsTabOrder.count() > 0 ? NekoGui::profileManager->groupsTabOrder.first() : 0); - } else { - ui->tabWidget->setCurrentIndex(groupId2TabIndex(NekoGui::dataStore->current_group)); - show_group(NekoGui::dataStore->current_group); - } - - NekoGui::dataStore->refreshing_group_list = false; -} - -void MainWindow::refresh_proxy_list(const int &id) { - refresh_proxy_list_impl(id, {}); -} - -void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSortAction) { - // id < 0 重绘 - if (id < 0) { - // 清空数据 - ui->proxyListTable->row2Id.clear(); - ui->proxyListTable->setRowCount(0); - // 添加行 - int row = -1; - for (const auto &[id, profile]: NekoGui::profileManager->profiles) { - if (NekoGui::dataStore->current_group != profile->gid) continue; - row++; - ui->proxyListTable->insertRow(row); - ui->proxyListTable->row2Id += id; - } - } - - // 显示排序 - if (id < 0) { - switch (groupSortAction.method) { - case GroupSortMethod::Raw: { - auto group = NekoGui::profileManager->CurrentGroup(); - if (group == nullptr) return; - ui->proxyListTable->order = group->order; - break; - } - case GroupSortMethod::ById: { - // Clear Order - ui->proxyListTable->order.clear(); - ui->proxyListTable->callback_save_order(); - break; - } - case GroupSortMethod::ByAddress: - case GroupSortMethod::ByName: - case GroupSortMethod::ByLatency: - case GroupSortMethod::ByType: { - std::sort(ui->proxyListTable->order.begin(), ui->proxyListTable->order.end(), - [=](int a, int b) { - QString ms_a; - QString ms_b; - if (groupSortAction.method == GroupSortMethod::ByType) { - ms_a = NekoGui::profileManager->GetProfile(a)->bean->DisplayType(); - ms_b = NekoGui::profileManager->GetProfile(b)->bean->DisplayType(); - } else if (groupSortAction.method == GroupSortMethod::ByName) { - ms_a = NekoGui::profileManager->GetProfile(a)->bean->name; - ms_b = NekoGui::profileManager->GetProfile(b)->bean->name; - } else if (groupSortAction.method == GroupSortMethod::ByAddress) { - ms_a = NekoGui::profileManager->GetProfile(a)->bean->DisplayAddress(); - ms_b = NekoGui::profileManager->GetProfile(b)->bean->DisplayAddress(); - } else if (groupSortAction.method == GroupSortMethod::ByLatency) { - ms_a = NekoGui::profileManager->GetProfile(a)->full_test_report; - ms_b = NekoGui::profileManager->GetProfile(b)->full_test_report; - } - auto get_latency_for_sort = [](int id) { - auto i = NekoGui::profileManager->GetProfile(id)->latency; - if (i == 0) i = 100000; - if (i < 0) i = 99999; - return i; - }; - if (groupSortAction.descending) { - if (groupSortAction.method == GroupSortMethod::ByLatency) { - if (ms_a.isEmpty() && ms_b.isEmpty()) { - // compare latency if full_test_report is empty - return get_latency_for_sort(a) > get_latency_for_sort(b); - } - } - return ms_a > ms_b; - } else { - if (groupSortAction.method == GroupSortMethod::ByLatency) { - auto int_a = NekoGui::profileManager->GetProfile(a)->latency; - auto int_b = NekoGui::profileManager->GetProfile(b)->latency; - if (ms_a.isEmpty() && ms_b.isEmpty()) { - // compare latency if full_test_report is empty - return get_latency_for_sort(a) < get_latency_for_sort(b); - } - } - return ms_a < ms_b; - } - }); - break; - } - } - ui->proxyListTable->update_order(groupSortAction.save_sort); - } - - // refresh data - refresh_proxy_list_impl_refresh_data(id); -} - -void MainWindow::refresh_proxy_list_impl_refresh_data(const int &id) { - // 绘制或更新item(s) - for (int row = 0; row < ui->proxyListTable->rowCount(); row++) { - auto profileId = ui->proxyListTable->row2Id[row]; - if (id >= 0 && profileId != id) continue; // refresh ONE item - auto profile = NekoGui::profileManager->GetProfile(profileId); - if (profile == nullptr) continue; - - auto isRunning = profileId == NekoGui::dataStore->started_id; - auto f0 = std::make_unique(); - f0->setData(114514, profileId); - - // Check state - auto check = f0->clone(); - check->setText(isRunning ? "✓" : Int2String(row + 1)); - ui->proxyListTable->setVerticalHeaderItem(row, check); - - // C0: Type - auto f = f0->clone(); - f->setText(profile->bean->DisplayType()); - if (isRunning) f->setForeground(palette().link()); - ui->proxyListTable->setItem(row, 0, f); - - // C1: Address+Port - f = f0->clone(); - f->setText(profile->bean->DisplayAddress()); - if (isRunning) f->setForeground(palette().link()); - ui->proxyListTable->setItem(row, 1, f); - - // C2: Name - f = f0->clone(); - f->setText(profile->bean->name); - if (isRunning) f->setForeground(palette().link()); - ui->proxyListTable->setItem(row, 2, f); - - // C3: Test Result - f = f0->clone(); - if (profile->full_test_report.isEmpty()) { - auto color = profile->DisplayLatencyColor(); - if (color.isValid()) f->setForeground(color); - f->setText(profile->DisplayLatency()); - } else { - f->setText(profile->full_test_report); - } - ui->proxyListTable->setItem(row, 3, f); - - // C4: Traffic - f = f0->clone(); - f->setText(profile->traffic_data->DisplayTraffic()); - ui->proxyListTable->setItem(row, 4, f); - } -} - -// table菜单相关 - -void MainWindow::on_proxyListTable_itemDoubleClicked(QTableWidgetItem *item) { - auto id = item->data(114514).toInt(); - if (select_mode) { - emit profile_selected(id); - select_mode = false; - refresh_status(); - return; - } - auto dialog = new DialogEditProfile("", id, this); - connect(dialog, &QDialog::finished, dialog, &QDialog::deleteLater); -} - -void MainWindow::on_menu_add_from_input_triggered() { - auto dialog = new DialogEditProfile("socks", NekoGui::dataStore->current_group, this); - connect(dialog, &QDialog::finished, dialog, &QDialog::deleteLater); -} - -void MainWindow::on_menu_add_from_clipboard_triggered() { - auto clipboard = QApplication::clipboard()->text(); - NekoGui_sub::groupUpdater->AsyncUpdate(clipboard); -} - -void MainWindow::on_menu_clone_triggered() { - auto ents = get_now_selected_list(); - if (ents.isEmpty()) return; - - auto btn = QMessageBox::question(this, tr("Clone"), tr("Clone %1 item(s)").arg(ents.count())); - if (btn != QMessageBox::Yes) return; - - QStringList sls; - for (const auto &ent: ents) { - sls << ent->bean->ToNekorayShareLink(ent->type); - } - - NekoGui_sub::groupUpdater->AsyncUpdate(sls.join("\n")); -} - -void MainWindow::on_menu_move_triggered() { - auto ents = get_now_selected_list(); - if (ents.isEmpty()) return; - - auto items = QStringList{}; - for (auto gid: NekoGui::profileManager->groupsTabOrder) { - auto group = NekoGui::profileManager->GetGroup(gid); - if (group == nullptr) continue; - items += Int2String(gid) + " " + group->name; - } - - bool ok; - auto a = QInputDialog::getItem(nullptr, - tr("Move"), - tr("Move %1 item(s)").arg(ents.count()), - items, 0, false, &ok); - if (!ok) return; - auto gid = SubStrBefore(a, " ").toInt(); - for (const auto &ent: ents) { - NekoGui::profileManager->MoveProfile(ent, gid); - } - refresh_proxy_list(); -} - -void MainWindow::on_menu_delete_triggered() { - auto ents = get_now_selected_list(); - if (ents.count() == 0) return; - if (QMessageBox::question(this, tr("Confirmation"), QString(tr("Remove %1 item(s) ?")).arg(ents.count())) == - QMessageBox::StandardButton::Yes) { - for (const auto &ent: ents) { - NekoGui::profileManager->DeleteProfile(ent->id); - } - refresh_proxy_list(); - } -} - -void MainWindow::on_menu_reset_traffic_triggered() { - auto ents = get_now_selected_list(); - if (ents.count() == 0) return; - for (const auto &ent: ents) { - ent->traffic_data->Reset(); - ent->Save(); - refresh_proxy_list(ent->id); - } -} - -void MainWindow::on_menu_profile_debug_info_triggered() { - auto ents = get_now_selected_list(); - if (ents.count() != 1) return; - auto btn = QMessageBox::information(this, software_name, ents.first()->ToJsonBytes(), "OK", "Edit", "Reload", 0, 0); - if (btn == 1) { - QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(QStringLiteral("profiles/%1.json").arg(ents.first()->id)).absoluteFilePath())); - } else if (btn == 2) { - NekoGui::dataStore->Load(); - NekoGui::profileManager->LoadManager(); - refresh_proxy_list(); - } -} - -void MainWindow::on_menu_copy_links_triggered() { - if (ui->masterLogBrowser->hasFocus()) { - ui->masterLogBrowser->copy(); - return; - } - auto ents = get_now_selected_list(); - QStringList links; - for (const auto &ent: ents) { - links += ent->bean->ToShareLink(); - } - if (links.length() == 0) return; - QApplication::clipboard()->setText(links.join("\n")); - show_log_impl(tr("Copied %1 item(s)").arg(links.length())); -} - -void MainWindow::on_menu_copy_links_nkr_triggered() { - auto ents = get_now_selected_list(); - QStringList links; - for (const auto &ent: ents) { - links += ent->bean->ToNekorayShareLink(ent->type); - } - if (links.length() == 0) return; - QApplication::clipboard()->setText(links.join("\n")); - show_log_impl(tr("Copied %1 item(s)").arg(links.length())); -} - -void MainWindow::on_menu_export_config_triggered() { - auto ents = get_now_selected_list(); - if (ents.count() != 1) return; - auto ent = ents.first(); - if (ent->bean->DisplayCoreType() != software_core_name) return; - - auto result = BuildConfig(ent, false, true); - QString config_core = QJsonObject2QString(result->coreConfig, false); - QApplication::clipboard()->setText(config_core); - - QMessageBox msg(QMessageBox::Information, tr("Config copied"), tr("Config copied")); - msg.addButton("Copy core config", QMessageBox::YesRole); - msg.addButton("Copy test config", QMessageBox::NoRole); - msg.addButton(QMessageBox::Ok); - msg.setEscapeButton(QMessageBox::Ok); - msg.setDefaultButton(QMessageBox::Ok); - auto ret = msg.exec(); - if (ret == 2) { - result = BuildConfig(ent, false, false); - config_core = QJsonObject2QString(result->coreConfig, false); - QApplication::clipboard()->setText(config_core); - } else if (ret == 3) { - result = BuildConfig(ent, true, false); - config_core = QJsonObject2QString(result->coreConfig, false); - QApplication::clipboard()->setText(config_core); - } -} - -void MainWindow::display_qr_link(bool nkrFormat) { - auto ents = get_now_selected_list(); - if (ents.count() != 1) return; - - class W : public QDialog { - public: - QLabel *l = nullptr; - QCheckBox *cb = nullptr; - // - QPlainTextEdit *l2 = nullptr; - QImage im; - // - QString link; - QString link_nk; - - void show_qr(const QSize &size) const { - auto side = size.height() - 20 - l2->size().height() - cb->size().height(); - l->setPixmap(QPixmap::fromImage(im.scaled(side, side, Qt::KeepAspectRatio, Qt::FastTransformation), - Qt::MonoOnly)); - l->resize(side, side); - } - - void refresh(bool is_nk) { - auto link_display = is_nk ? link_nk : link; - l2->setPlainText(link_display); - constexpr qint32 qr_padding = 2; - // - try { - qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(link_display.toUtf8().data(), qrcodegen::QrCode::Ecc::MEDIUM); - qint32 sz = qr.getSize(); - im = QImage(sz + qr_padding * 2, sz + qr_padding * 2, QImage::Format_RGB32); - QRgb black = qRgb(0, 0, 0); - QRgb white = qRgb(255, 255, 255); - im.fill(white); - for (int y = 0; y < sz; y++) - for (int x = 0; x < sz; x++) - if (qr.getModule(x, y)) - im.setPixel(x + qr_padding, y + qr_padding, black); - show_qr(size()); - } catch (const std::exception &ex) { - QMessageBox::warning(nullptr, "error", ex.what()); - } - } - - W(const QString &link_, const QString &link_nk_) { - link = link_; - link_nk = link_nk_; - // - setLayout(new QVBoxLayout); - setMinimumSize(256, 256); - QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - sizePolicy.setHeightForWidth(true); - setSizePolicy(sizePolicy); - // - l = new QLabel(); - l->setMinimumSize(256, 256); - l->setMargin(6); - l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - l->setScaledContents(true); - layout()->addWidget(l); - cb = new QCheckBox; - cb->setText("Neko Links"); - layout()->addWidget(cb); - l2 = new QPlainTextEdit(); - l2->setReadOnly(true); - layout()->addWidget(l2); - // - connect(cb, &QCheckBox::toggled, this, &W::refresh); - refresh(false); - } - - void resizeEvent(QResizeEvent *resizeEvent) override { - show_qr(resizeEvent->size()); - } - }; - - auto link = ents.first()->bean->ToShareLink(); - auto link_nk = ents.first()->bean->ToNekorayShareLink(ents.first()->type); - auto w = new W(link, link_nk); - w->setWindowTitle(ents.first()->bean->DisplayTypeAndName()); - w->exec(); - w->deleteLater(); -} - -void MainWindow::on_menu_scan_qr_triggered() { -#ifndef NKR_NO_ZXING - using namespace ZXingQt; - - hide(); - QThread::sleep(1); - - auto screen = QGuiApplication::primaryScreen(); - auto geom = screen->geometry(); - auto qpx = screen->grabWindow(0, geom.x(), geom.y(), geom.width(), geom.height()); - - show(); - - auto hints = DecodeHints() - .setFormats(BarcodeFormat::QRCode) - .setTryRotate(false) - .setBinarizer(Binarizer::FixedThreshold); - - auto result = ReadBarcode(qpx.toImage(), hints); - const auto &text = result.text(); - if (text.isEmpty()) { - MessageBoxInfo(software_name, tr("QR Code not found")); - } else { - show_log_impl("QR Code Result:\n" + text); - NekoGui_sub::groupUpdater->AsyncUpdate(text); - } -#endif -} - -void MainWindow::on_menu_clear_test_result_triggered() { - for (const auto &profile: get_selected_or_group()) { - profile->latency = 0; - profile->full_test_report = ""; - profile->Save(); - } - refresh_proxy_list(); -} - -void MainWindow::on_menu_select_all_triggered() { - if (ui->masterLogBrowser->hasFocus()) { - ui->masterLogBrowser->selectAll(); - return; - } - ui->proxyListTable->selectAll(); -} - -void MainWindow::on_menu_delete_repeat_triggered() { - QList> out; - QList> out_del; - - NekoGui::ProfileFilter::Uniq(NekoGui::profileManager->CurrentGroup()->Profiles(), out, true, false); - NekoGui::ProfileFilter::OnlyInSrc_ByPointer(NekoGui::profileManager->CurrentGroup()->Profiles(), out, out_del); - - int remove_display_count = 0; - QString remove_display; - for (const auto &ent: out_del) { - remove_display += ent->bean->DisplayTypeAndName() + "\n"; - if (++remove_display_count == 20) { - remove_display += "..."; - break; - } - } - - if (out_del.length() > 0 && - QMessageBox::question(this, tr("Confirmation"), tr("Remove %1 item(s) ?").arg(out_del.length()) + "\n" + remove_display) == QMessageBox::StandardButton::Yes) { - for (const auto &ent: out_del) { - NekoGui::profileManager->DeleteProfile(ent->id); - } - refresh_proxy_list(); - } -} - -bool mw_sub_updating = false; - -void MainWindow::on_menu_update_subscription_triggered() { - auto group = NekoGui::profileManager->CurrentGroup(); - if (group->url.isEmpty()) return; - if (mw_sub_updating) return; - mw_sub_updating = true; - NekoGui_sub::groupUpdater->AsyncUpdate(group->url, group->id, [&] { mw_sub_updating = false; }); -} - -void MainWindow::on_menu_remove_unavailable_triggered() { - QList> out_del; - - for (const auto &[_, profile]: NekoGui::profileManager->profiles) { - if (NekoGui::dataStore->current_group != profile->gid) continue; - if (profile->latency < 0) out_del += profile; - } - - int remove_display_count = 0; - QString remove_display; - for (const auto &ent: out_del) { - remove_display += ent->bean->DisplayTypeAndName() + "\n"; - if (++remove_display_count == 20) { - remove_display += "..."; - break; - } - } - - if (out_del.length() > 0 && - QMessageBox::question(this, tr("Confirmation"), tr("Remove %1 item(s) ?").arg(out_del.length()) + "\n" + remove_display) == QMessageBox::StandardButton::Yes) { - for (const auto &ent: out_del) { - NekoGui::profileManager->DeleteProfile(ent->id); - } - refresh_proxy_list(); - } -} - -void MainWindow::on_menu_resolve_domain_triggered() { - auto profiles = get_selected_or_group(); - if (profiles.isEmpty()) return; - - if (QMessageBox::question(this, - tr("Confirmation"), - tr("Resolving domain to IP, if support.")) != QMessageBox::StandardButton::Yes) { - return; - } - if (mw_sub_updating) return; - mw_sub_updating = true; - NekoGui::dataStore->resolve_count = profiles.count(); - - for (const auto &profile: profiles) { - profile->bean->ResolveDomainToIP([=] { - profile->Save(); - if (--NekoGui::dataStore->resolve_count != 0) return; - refresh_proxy_list(); - mw_sub_updating = false; - }); - } -} - -void MainWindow::on_proxyListTable_customContextMenuRequested(const QPoint &pos) { - ui->menu_server->popup(ui->proxyListTable->viewport()->mapToGlobal(pos)); // 弹出菜单 -} - -QList> MainWindow::get_now_selected_list() { - auto items = ui->proxyListTable->selectedItems(); - QList> list; - for (auto item: items) { - auto id = item->data(114514).toInt(); - auto ent = NekoGui::profileManager->GetProfile(id); - if (ent != nullptr && !list.contains(ent)) list += ent; - } - return list; -} - -QList> MainWindow::get_selected_or_group() { - auto selected_or_group = ui->menu_server->property("selected_or_group").toInt(); - QList> profiles; - if (selected_or_group > 0) { - profiles = get_now_selected_list(); - if (profiles.isEmpty() && selected_or_group == 2) profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder(); - } else { - profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder(); - } - return profiles; -} - -void MainWindow::keyPressEvent(QKeyEvent *event) { - switch (event->key()) { - case Qt::Key_Escape: - // take over by shortcut_esc - break; - case Qt::Key_Enter: - neko_start(); - break; - default: - QMainWindow::keyPressEvent(event); - } -} - -// Log - -inline void FastAppendTextDocument(const QString &message, QTextDocument *doc) { - QTextCursor cursor(doc); - cursor.movePosition(QTextCursor::End); - cursor.beginEditBlock(); - cursor.insertBlock(); - cursor.insertText(message); - cursor.endEditBlock(); -} - -void MainWindow::show_log_impl(const QString &log) { - auto lines = SplitLines(log.trimmed()); - if (lines.isEmpty()) return; - - QStringList newLines; - auto log_ignore = NekoGui::dataStore->log_ignore; - for (const auto &line: lines) { - bool showThisLine = true; - for (const auto &str: log_ignore) { - if (line.contains(str)) { - showThisLine = false; - break; - } - } - if (showThisLine) newLines << line; - } - if (newLines.isEmpty()) return; - - FastAppendTextDocument(newLines.join("\n"), qvLogDocument); - // qvLogDocument->setPlainText(qvLogDocument->toPlainText() + log); - // From https://gist.github.com/jemyzhang/7130092 - auto block = qvLogDocument->begin(); - - while (block.isValid()) { - if (qvLogDocument->blockCount() > NekoGui::dataStore->max_log_line) { - QTextCursor cursor(block); - block = block.next(); - cursor.select(QTextCursor::BlockUnderCursor); - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - continue; - } - break; - } -} - -#define ADD_TO_CURRENT_ROUTE(a, b) \ - NekoGui::dataStore->routing->a = (SplitLines(NekoGui::dataStore->routing->a) << (b)).join("\n"); \ - NekoGui::dataStore->routing->Save(); - -void MainWindow::on_masterLogBrowser_customContextMenuRequested(const QPoint &pos) { - QMenu *menu = ui->masterLogBrowser->createStandardContextMenu(); - - auto sep = new QAction(this); - sep->setSeparator(true); - menu->addAction(sep); - - auto action_add_ignore = new QAction(this); - action_add_ignore->setText(tr("Set ignore keyword")); - connect(action_add_ignore, &QAction::triggered, this, [=] { - auto list = NekoGui::dataStore->log_ignore; - auto newStr = ui->masterLogBrowser->textCursor().selectedText().trimmed(); - if (!newStr.isEmpty()) list << newStr; - bool ok; - newStr = QInputDialog::getMultiLineText(GetMessageBoxParent(), tr("Set ignore keyword"), tr("Set the following keywords to ignore?\nSplit by line."), list.join("\n"), &ok); - if (ok) { - NekoGui::dataStore->log_ignore = SplitLines(newStr); - NekoGui::dataStore->Save(); - } - }); - menu->addAction(action_add_ignore); - - auto action_add_route = new QAction(this); - action_add_route->setText(tr("Save as route")); - connect(action_add_route, &QAction::triggered, this, [=] { - auto newStr = ui->masterLogBrowser->textCursor().selectedText().trimmed(); - if (newStr.isEmpty()) return; - // - bool ok; - newStr = QInputDialog::getText(GetMessageBoxParent(), tr("Save as route"), tr("Edit"), {}, newStr, &ok).trimmed(); - if (!ok) return; - if (newStr.isEmpty()) return; - // - auto select = IsIpAddress(newStr) ? 0 : 3; - QStringList items = {"proxyIP", "bypassIP", "blockIP", "proxyDomain", "bypassDomain", "blockDomain"}; - auto item = QInputDialog::getItem(GetMessageBoxParent(), tr("Save as route"), - tr("Save \"%1\" as a routing rule?").arg(newStr), - items, select, false, &ok); - if (ok) { - auto index = items.indexOf(item); - switch (index) { - case 0: - ADD_TO_CURRENT_ROUTE(proxy_ip, newStr); - break; - case 1: - ADD_TO_CURRENT_ROUTE(direct_ip, newStr); - break; - case 2: - ADD_TO_CURRENT_ROUTE(block_ip, newStr); - break; - case 3: - ADD_TO_CURRENT_ROUTE(proxy_domain, newStr); - break; - case 4: - ADD_TO_CURRENT_ROUTE(direct_domain, newStr); - break; - case 5: - ADD_TO_CURRENT_ROUTE(block_domain, newStr); - break; - default: - break; - } - MW_dialog_message("", "UpdateDataStore,RouteChanged"); - } - }); - menu->addAction(action_add_route); - - auto action_clear = new QAction(this); - action_clear->setText(tr("Clear")); - connect(action_clear, &QAction::triggered, this, [=] { - qvLogDocument->clear(); - ui->masterLogBrowser->clear(); - }); - menu->addAction(action_clear); - - menu->exec(ui->masterLogBrowser->viewport()->mapToGlobal(pos)); // 弹出菜单 -} - -// eventFilter - -bool MainWindow::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::MouseButtonPress) { - auto mouseEvent = dynamic_cast(event); - if (obj == ui->label_running && mouseEvent->button() == Qt::LeftButton && running != nullptr) { - speedtest_current(); - return true; - } else if (obj == ui->label_inbound && mouseEvent->button() == Qt::LeftButton) { - on_menu_basic_settings_triggered(); - return true; - } - } else if (event->type() == QEvent::MouseButtonDblClick) { - if (obj == ui->splitter) { - auto size = ui->splitter->size(); - ui->splitter->setSizes({size.height() / 2, size.height() / 2}); - } - } - return QMainWindow::eventFilter(obj, event); -} - -// profile selector - -void MainWindow::start_select_mode(QObject *context, const std::function &callback) { - select_mode = true; - connectOnce(this, &MainWindow::profile_selected, context, callback); - refresh_status(); -} - -// 连接列表 - -inline QJsonArray last_arr; // format is nekoray_connections_json - -void MainWindow::refresh_connection_list(const QJsonArray &arr) { - if (last_arr == arr) { - return; - } - last_arr = arr; - - if (NekoGui::dataStore->flag_debug) qDebug() << arr; - - ui->tableWidget_conn->setRowCount(0); - - int row = -1; - for (const auto &_item: arr) { - auto item = _item.toObject(); - if (NekoGui::dataStore->ignoreConnTag.contains(item["Tag"].toString())) continue; - - row++; - ui->tableWidget_conn->insertRow(row); - - auto f0 = std::make_unique(); - f0->setData(114514, item["ID"].toInt()); - - // C0: Status - auto c0 = new QLabel; - auto start_t = item["Start"].toInt(); - auto end_t = item["End"].toInt(); - // icon - auto outboundTag = item["Tag"].toString(); - if (outboundTag == "block") { - c0->setPixmap(Icon::GetMaterialIcon("cancel")); - } else { - if (end_t > 0) { - c0->setPixmap(Icon::GetMaterialIcon("history")); - } else { - c0->setPixmap(Icon::GetMaterialIcon("swap-vertical")); - } - } - c0->setAlignment(Qt::AlignCenter); - c0->setToolTip(tr("Start: %1\nEnd: %2").arg(DisplayTime(start_t), end_t > 0 ? DisplayTime(end_t) : "")); - ui->tableWidget_conn->setCellWidget(row, 0, c0); - - // C1: Outbound - auto f = f0->clone(); - f->setToolTip(""); - f->setText(outboundTag); - ui->tableWidget_conn->setItem(row, 1, f); - - // C2: Destination - f = f0->clone(); - QString target1 = item["Dest"].toString(); - QString target2 = item["RDest"].toString(); - if (target2.isEmpty() || target1 == target2) { - target2 = ""; - } - f->setText("[" + target1 + "] " + target2); - ui->tableWidget_conn->setItem(row, 2, f); - } -} - -// Hotkey - -#ifndef NKR_NO_QHOTKEY - -#include - -inline QList> RegisteredHotkey; - -void MainWindow::RegisterHotkey(bool unregister) { - while (!RegisteredHotkey.isEmpty()) { - auto hk = RegisteredHotkey.takeFirst(); - hk->deleteLater(); - } - if (unregister) return; - - QStringList regstr{ - NekoGui::dataStore->hotkey_mainwindow, - NekoGui::dataStore->hotkey_group, - NekoGui::dataStore->hotkey_route, - NekoGui::dataStore->hotkey_system_proxy_menu, - }; - - for (const auto &key: regstr) { - if (key.isEmpty()) continue; - if (regstr.count(key) > 1) return; // Conflict hotkey - } - for (const auto &key: regstr) { - QKeySequence k(key); - if (k.isEmpty()) continue; - auto hk = std::make_shared(k, true); - if (hk->isRegistered()) { - RegisteredHotkey += hk; - connect(hk.get(), &QHotkey::activated, this, [=] { HotkeyEvent(key); }); - } else { - hk->deleteLater(); - } - } -} - -void MainWindow::HotkeyEvent(const QString &key) { - if (key.isEmpty()) return; - runOnUiThread([=] { - if (key == NekoGui::dataStore->hotkey_mainwindow) { - tray->activated(QSystemTrayIcon::ActivationReason::Trigger); - } else if (key == NekoGui::dataStore->hotkey_group) { - on_menu_manage_groups_triggered(); - } else if (key == NekoGui::dataStore->hotkey_route) { - on_menu_routing_settings_triggered(); - } else if (key == NekoGui::dataStore->hotkey_system_proxy_menu) { - ui->menu_spmode->popup(QCursor::pos()); - } - }); -} - -#else - -void MainWindow::RegisterHotkey(bool unregister) {} - -void MainWindow::HotkeyEvent(const QString &key) {} - -#endif - -// VPN Launcher - -bool MainWindow::StartVPNProcess() { - // - if (vpn_pid != 0) { - return true; - } - // - auto configPath = NekoGui::WriteVPNSingBoxConfig(); - auto scriptPath = NekoGui::WriteVPNLinuxScript(configPath); - // -#ifdef Q_OS_WIN - runOnNewThread([=] { - vpn_pid = 1; // TODO get pid? - WinCommander::runProcessElevated(QApplication::applicationDirPath() + "/nekobox_core.exe", - {"--disable-color", "run", "-c", configPath}, "", - NekoGui::dataStore->vpn_hide_console ? WinCommander::SW_HIDE : WinCommander::SW_SHOWMINIMIZED); // blocking - vpn_pid = 0; - runOnUiThread([=] { neko_set_spmode_vpn(false); }); - }); -#else - // - auto vpn_process = new QProcess; - QProcess::connect(vpn_process, &QProcess::stateChanged, this, [=](QProcess::ProcessState state) { - if (state == QProcess::NotRunning) { - vpn_pid = 0; - vpn_process->deleteLater(); - GetMainWindow()->neko_set_spmode_vpn(false); - } - }); - // - vpn_process->setProcessChannelMode(QProcess::ForwardedChannels); -#ifdef Q_OS_MACOS - vpn_process->start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges") - .arg("bash " + scriptPath)}); -#else - vpn_process->start("pkexec", {"bash", scriptPath}); -#endif - vpn_process->waitForStarted(); - vpn_pid = vpn_process->processId(); // actually it's pkexec or bash PID -#endif - return true; -} - -bool MainWindow::StopVPNProcess(bool unconditional) { - if (unconditional || vpn_pid != 0) { - bool ok; - core_process->processId(); -#ifdef Q_OS_WIN - auto ret = WinCommander::runProcessElevated("taskkill", {"/IM", "nekobox_core.exe", - "/FI", - "PID ne " + Int2String(core_process->processId())}); - ok = ret == 0; -#else - QProcess p; -#ifdef Q_OS_MACOS - p.start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges") - .arg("pkill -2 -U 0 nekobox_core")}); -#else - if (unconditional) { - p.start("pkexec", {"killall", "-2", "nekobox_core"}); - } else { - p.start("pkexec", {"pkill", "-2", "-P", Int2String(vpn_pid)}); - } -#endif - p.waitForFinished(); - ok = p.exitCode() == 0; -#endif - if (!unconditional) { - ok ? vpn_pid = 0 : MessageBoxWarning(tr("Error"), tr("Failed to stop Tun process")); - } - return ok; - } - return true; -} diff --git a/ui/mainwindow.h b/ui/mainwindow.h deleted file mode 100644 index 5ca492d..0000000 --- a/ui/mainwindow.h +++ /dev/null @@ -1,206 +0,0 @@ -#pragma once - -#include - -#include "main/NekoGui.hpp" - -#ifndef MW_INTERFACE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "GroupSort.hpp" - -#include "db/ProxyEntity.hpp" -#include "main/GuiUtils.hpp" - -#endif - -namespace NekoGui_sys { - class CoreProcess; -} - -QT_BEGIN_NAMESPACE -namespace Ui { - class MainWindow; -} -QT_END_NAMESPACE - -class MainWindow : public QMainWindow { - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = nullptr); - - ~MainWindow() override; - - void refresh_proxy_list(const int &id = -1); - - void show_group(int gid); - - void refresh_groups(); - - void refresh_status(const QString &traffic_update = ""); - - void neko_start(int _id = -1); - - void neko_stop(bool crash = false, bool sem = false); - - void neko_set_spmode_system_proxy(bool enable, bool save = true); - - void neko_set_spmode_vpn(bool enable, bool save = true); - - void show_log_impl(const QString &log); - - void start_select_mode(QObject *context, const std::function &callback); - - void refresh_connection_list(const QJsonArray &arr); - - void RegisterHotkey(bool unregister); - - bool StopVPNProcess(bool unconditional = false); - -signals: - - void profile_selected(int id); - -public slots: - - void on_commitDataRequest(); - - void on_menu_exit_triggered(); - -#ifndef MW_INTERFACE - -private slots: - - void on_masterLogBrowser_customContextMenuRequested(const QPoint &pos); - - void on_menu_basic_settings_triggered(); - - void on_menu_routing_settings_triggered(); - - void on_menu_vpn_settings_triggered(); - - void on_menu_hotkey_settings_triggered(); - - void on_menu_add_from_input_triggered(); - - void on_menu_add_from_clipboard_triggered(); - - void on_menu_clone_triggered(); - - void on_menu_move_triggered(); - - void on_menu_delete_triggered(); - - void on_menu_reset_traffic_triggered(); - - void on_menu_profile_debug_info_triggered(); - - void on_menu_copy_links_triggered(); - - void on_menu_copy_links_nkr_triggered(); - - void on_menu_export_config_triggered(); - - void display_qr_link(bool nkrFormat = false); - - void on_menu_scan_qr_triggered(); - - void on_menu_clear_test_result_triggered(); - - void on_menu_manage_groups_triggered(); - - void on_menu_select_all_triggered(); - - void on_menu_delete_repeat_triggered(); - - void on_menu_remove_unavailable_triggered(); - - void on_menu_update_subscription_triggered(); - - void on_menu_resolve_domain_triggered(); - - void on_proxyListTable_itemDoubleClicked(QTableWidgetItem *item); - - void on_proxyListTable_customContextMenuRequested(const QPoint &pos); - - void on_tabWidget_currentChanged(int index); - -private: - Ui::MainWindow *ui; - QSystemTrayIcon *tray; - QShortcut *shortcut_ctrl_f = new QShortcut(QKeySequence("Ctrl+F"), this); - QShortcut *shortcut_esc = new QShortcut(QKeySequence("Esc"), this); - // - NekoGui_sys::CoreProcess *core_process; - qint64 vpn_pid = 0; - // - bool qvLogAutoScoll = true; - QTextDocument *qvLogDocument = new QTextDocument(this); - // - QString title_error; - int icon_status = -1; - std::shared_ptr running; - QString traffic_update_cache; - QTime last_test_time; - // - int proxy_last_order = -1; - bool select_mode = false; - QMutex mu_starting; - QMutex mu_stopping; - QMutex mu_exit; - QSemaphore sem_stopped; - int exit_reason = 0; - - QList> get_now_selected_list(); - - QList> get_selected_or_group(); - - void dialog_message_impl(const QString &sender, const QString &info); - - void refresh_proxy_list_impl(const int &id = -1, GroupSortAction groupSortAction = {}); - - void refresh_proxy_list_impl_refresh_data(const int &id = -1); - - void keyPressEvent(QKeyEvent *event) override; - - void closeEvent(QCloseEvent *event) override; - - // - - void HotkeyEvent(const QString &key); - - bool StartVPNProcess(); - - // grpc and ... - - static void setup_grpc(); - - void speedtest_current_group(int mode, bool test_group); - - void speedtest_current(); - - static void stop_core_daemon(); - - void CheckUpdate(); - -protected: - bool eventFilter(QObject *obj, QEvent *event) override; - -#endif // MW_INTERFACE -}; - -inline MainWindow *GetMainWindow() { - return (MainWindow *) mainwindow; -} - -void UI_InitMainWindow(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui deleted file mode 100644 index 585577a..0000000 --- a/ui/mainwindow.ui +++ /dev/null @@ -1,932 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - - 800 - 600 - - - - nya - - - - - - - - - Program - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Preferences - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Server - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Ads - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Document - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Update - - - - .. - - - - 24 - 24 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - - - - 0 - 0 - - - - Tun Mode - - - - - - - System Proxy - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - - - - URL Test - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - - - Qt::Vertical - - - - 0 - - - true - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::CustomContextMenu - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - false - - - 16 - - - false - - - false - - - 30 - - - - Type - - - - - Address - - - - - Name - - - - - Test Result - - - - - Traffic - - - - - - - - - - - - - 0 - - - - Log - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::CustomContextMenu - - - false - - - - - - - - Connection - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::CustomContextMenu - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - - Status - - - - - Outbound - - - - - Destination - - - - - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 0 - 20 - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 800 - 32 - - - - - Program - - - - System Proxy - - - - - - - - Preferences - - - - - - Active Server - - - - - - Active Routing - - - - - - - - - - - - - - - - - - - - - - Preferences - - - - - - - - - - - Server - - - - Share - - - - - - - - - - Current Group - - - - - - - - - - - - - - - - - Current Select - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Exit - - - - - Show Window - - - - - Basic Settings - - - - - New profile - - - - - Groups - - - - - Start - - - Return - - - - - Stop - - - Ctrl+S - - - - - Routing Settings - - - - - Add profile from clipboard - - - Ctrl+V - - - - - Delete - - - Del - - - - - Add profile from clipboard - - - - - Debug Info - - - - - QR Code and link - - - Ctrl+Q - - - - - Copy Link - - - - - Tcp Ping - - - Ctrl+Alt+T - - - - - Url Test - - - Ctrl+Alt+U - - - - - Clear Test Result - - - Ctrl+Alt+C - - - - - Export %1 config - - - Ctrl+E - - - - - Reset Traffic - - - Ctrl+R - - - - - Scan QR Code - - - - - true - - - Enable System Proxy - - - - - true - - - Disable - - - - - Remove Duplicates - - - Ctrl+Alt+D - - - - - fake - - - false - - - - - Move - - - Ctrl+M - - - - - true - - - Start with system - - - - - true - - - Remember last profile - - - - - true - - - Allow other devices to connect - - - - - Remove Unavailable - - - Ctrl+Alt+R - - - - - Full Test - - - Ctrl+Alt+F - - - - - Hotkey Settings - - - - - Select All - - - Ctrl+A - - - - - Copy links of selected (Neko Links) - - - Ctrl+N - - - - - fake - - - false - - - - - fake - - - false - - - - - Copy links of selected - - - Ctrl+C - - - - - true - - - Enable Tun - - - - - Clone - - - Ctrl+D - - - - - Update subscription - - - Ctrl+U - - - - - Resolve domain - - - Ctrl+Alt+I - - - - - Tun Settings - - - - - Restart Program - - - - - Open Config Folder - - - - - fake - - - false - - - - - fake - - - false - - - - - Restart Proxy - - - - - Stop Testing - - - Ctrl+Alt+S - - - - - - MyTableWidget - QTableWidget -
ui/widget/MyTableWidget.h
-
-
- - -
diff --git a/ui/mainwindow_grpc.cpp b/ui/mainwindow_grpc.cpp deleted file mode 100644 index f9123ee..0000000 --- a/ui/mainwindow_grpc.cpp +++ /dev/null @@ -1,554 +0,0 @@ -#include "./ui_mainwindow.h" -#include "mainwindow.h" - -#include "db/Database.hpp" -#include "db/ConfigBuilder.hpp" -#include "db/traffic/TrafficLooper.hpp" -#include "rpc/gRPC.h" -#include "ui/widget/MessageBoxTimer.h" - -#include -#include -#include -#include -#include -#include -#include - -// ext core - -std::list> CreateExtCFromExtR(const std::list> &extRs, bool start) { - // plz run and start in same thread - std::list> l; - for (const auto &extR: extRs) { - std::shared_ptr extC(new NekoGui_sys::ExternalProcess()); - extC->tag = extR->tag; - extC->program = extR->program; - extC->arguments = extR->arguments; - extC->env = extR->env; - l.emplace_back(extC); - // - if (start) extC->Start(); - } - return l; -} - -// grpc - -#ifndef NKR_NO_GRPC -using namespace NekoGui_rpc; -#endif - -void MainWindow::setup_grpc() { -#ifndef NKR_NO_GRPC - // Setup Connection - defaultClient = new Client( - [=](const QString &errStr) { - MW_show_log("[Error] gRPC: " + errStr); - }, - "127.0.0.1:" + Int2String(NekoGui::dataStore->core_port), NekoGui::dataStore->core_token); - - // Looper - runOnNewThread([=] { NekoGui_traffic::trafficLooper->Loop(); }); -#endif -} - -// 测速 - -inline bool speedtesting = false; -inline QList speedtesting_threads = {}; - -void MainWindow::speedtest_current_group(int mode, bool test_group) { - if (speedtesting) { - MessageBoxWarning(software_name, QObject::tr("The last speed test did not exit completely, please wait. If it persists, please restart the program.")); - return; - } - - auto profiles = get_selected_or_group(); - if (test_group) profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder(); - if (profiles.isEmpty()) return; - auto group = NekoGui::profileManager->CurrentGroup(); - if (group->archive) return; - - // menu_stop_testing - if (mode == 114514) { - while (!speedtesting_threads.isEmpty()) { - auto t = speedtesting_threads.takeFirst(); - if (t != nullptr) t->exit(); - } - speedtesting = false; - return; - } - -#ifndef NKR_NO_GRPC - QStringList full_test_flags; - if (mode == libcore::FullTest) { - auto w = new QDialog(this); - auto layout = new QVBoxLayout(w); - w->setWindowTitle(tr("Test Options")); - // - auto l1 = new QCheckBox(tr("Latency")); - auto l2 = new QCheckBox(tr("UDP latency")); - auto l3 = new QCheckBox(tr("Download speed")); - auto l4 = new QCheckBox(tr("In and Out IP")); - // - auto box = new QDialogButtonBox; - box->setOrientation(Qt::Horizontal); - box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - connect(box, &QDialogButtonBox::accepted, w, &QDialog::accept); - connect(box, &QDialogButtonBox::rejected, w, &QDialog::reject); - // - layout->addWidget(l1); - layout->addWidget(l2); - layout->addWidget(l3); - layout->addWidget(l4); - layout->addWidget(box); - if (w->exec() != QDialog::Accepted) { - w->deleteLater(); - return; - } - // - if (l1->isChecked()) full_test_flags << "1"; - if (l2->isChecked()) full_test_flags << "2"; - if (l3->isChecked()) full_test_flags << "3"; - if (l4->isChecked()) full_test_flags << "4"; - // - w->deleteLater(); - if (full_test_flags.isEmpty()) return; - } - speedtesting = true; - - runOnNewThread([this, profiles, mode, full_test_flags]() { - QMutex lock_write; - QMutex lock_return; - int threadN = NekoGui::dataStore->test_concurrent; - int threadN_finished = 0; - auto profiles_test = profiles; // copy - - // Threads - lock_return.lock(); - for (int i = 0; i < threadN; i++) { - runOnNewThread([&] { - speedtesting_threads << QObject::thread(); - - forever { - // - lock_write.lock(); - if (profiles_test.isEmpty()) { - threadN_finished++; - if (threadN == threadN_finished) { - // quit control thread - lock_return.unlock(); - } - lock_write.unlock(); - // quit of this thread - speedtesting_threads.removeAll(QObject::thread()); - return; - } - auto profile = profiles_test.takeFirst(); - lock_write.unlock(); - - // - libcore::TestReq req; - req.set_mode((libcore::TestMode) mode); - req.set_timeout(10 * 1000); - req.set_url(NekoGui::dataStore->test_latency_url.toStdString()); - - // - std::list> extCs; - QSemaphore extSem; - - if (mode == libcore::TestMode::UrlTest || mode == libcore::FullTest) { - auto c = BuildConfig(profile, true, false); - if (!c->error.isEmpty()) { - profile->full_test_report = c->error; - profile->Save(); - auto profileId = profile->id; - runOnUiThread([this, profileId] { - refresh_proxy_list(profileId); - }); - continue; - } - // - if (!c->extRs.empty()) { - runOnUiThread( - [&] { - extCs = CreateExtCFromExtR(c->extRs, true); - QThread::msleep(500); - extSem.release(); - }, - DS_cores); - extSem.acquire(); - } - // - auto config = new libcore::LoadConfigReq; - config->set_core_config(QJsonObject2QString(c->coreConfig, false).toStdString()); - req.set_allocated_config(config); - req.set_in_address(profile->bean->serverAddress.toStdString()); - - req.set_full_latency(full_test_flags.contains("1")); - req.set_full_udp_latency(full_test_flags.contains("2")); - req.set_full_speed(full_test_flags.contains("3")); - req.set_full_in_out(full_test_flags.contains("4")); - - req.set_full_speed_url(NekoGui::dataStore->test_download_url.toStdString()); - req.set_full_speed_timeout(NekoGui::dataStore->test_download_timeout); - } else if (mode == libcore::TcpPing) { - req.set_address(profile->bean->DisplayAddress().toStdString()); - } - - bool rpcOK; - auto result = defaultClient->Test(&rpcOK, req); - // - if (!extCs.empty()) { - runOnUiThread( - [&] { - for (const auto &extC: extCs) { - extC->Kill(); - } - extSem.release(); - }, - DS_cores); - extSem.acquire(); - } - // - if (!rpcOK) return; - - if (result.error().empty()) { - profile->latency = result.ms(); - if (profile->latency == 0) profile->latency = 1; // nekoray use 0 to represents not tested - } else { - profile->latency = -1; - } - profile->full_test_report = result.full_report().c_str(); // higher priority - profile->Save(); - - if (!result.error().empty()) { - MW_show_log(tr("[%1] test error: %2").arg(profile->bean->DisplayTypeAndName(), result.error().c_str())); - } - - auto profileId = profile->id; - runOnUiThread([this, profileId] { - refresh_proxy_list(profileId); - }); - } - }); - } - - // Control - lock_return.lock(); - lock_return.unlock(); - speedtesting = false; - MW_show_log(QObject::tr("Speedtest finished.")); - }); -#endif -} - -void MainWindow::speedtest_current() { -#ifndef NKR_NO_GRPC - last_test_time = QTime::currentTime(); - ui->label_running->setText(tr("Testing")); - - runOnNewThread([=] { - libcore::TestReq req; - req.set_mode(libcore::UrlTest); - req.set_timeout(10 * 1000); - req.set_url(NekoGui::dataStore->test_latency_url.toStdString()); - - bool rpcOK; - auto result = defaultClient->Test(&rpcOK, req); - if (!rpcOK) return; - - auto latency = result.ms(); - last_test_time = QTime::currentTime(); - - runOnUiThread([=] { - if (!result.error().empty()) { - MW_show_log(QStringLiteral("UrlTest error: %1").arg(result.error().c_str())); - } - if (latency <= 0) { - ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable")); - } else if (latency > 0) { - ui->label_running->setText(tr("Test Result") + ": " + QStringLiteral("%1 ms").arg(latency)); - } - }); - }); -#endif -} - -void MainWindow::stop_core_daemon() { -#ifndef NKR_NO_GRPC - NekoGui_rpc::defaultClient->Exit(); -#endif -} - -void MainWindow::neko_start(int _id) { - if (NekoGui::dataStore->prepare_exit) return; - - auto ents = get_now_selected_list(); - auto ent = (_id < 0 && !ents.isEmpty()) ? ents.first() : NekoGui::profileManager->GetProfile(_id); - if (ent == nullptr) return; - - if (select_mode) { - emit profile_selected(ent->id); - select_mode = false; - refresh_status(); - return; - } - - auto group = NekoGui::profileManager->GetGroup(ent->gid); - if (group == nullptr || group->archive) return; - - auto result = BuildConfig(ent, false, false); - if (!result->error.isEmpty()) { - MessageBoxWarning("BuildConfig return error", result->error); - return; - } - - auto neko_start_stage2 = [=] { -#ifndef NKR_NO_GRPC - libcore::LoadConfigReq req; - req.set_core_config(QJsonObject2QString(result->coreConfig, false).toStdString()); - req.set_enable_nekoray_connections(NekoGui::dataStore->connection_statistics); - if (NekoGui::dataStore->traffic_loop_interval > 0) { - req.add_stats_outbounds("proxy"); - req.add_stats_outbounds("bypass"); - } - // - bool rpcOK; - QString error = defaultClient->Start(&rpcOK, req); - if (rpcOK && !error.isEmpty()) { - runOnUiThread([=] { MessageBoxWarning("LoadConfig return error", error); }); - return false; - } else if (!rpcOK) { - return false; - } - // - NekoGui_traffic::trafficLooper->proxy = result->outboundStat.get(); - NekoGui_traffic::trafficLooper->items = result->outboundStats; - NekoGui::dataStore->ignoreConnTag = result->ignoreConnTag; - NekoGui_traffic::trafficLooper->loop_enabled = true; -#endif - - runOnUiThread( - [=] { - auto extCs = CreateExtCFromExtR(result->extRs, true); - NekoGui_sys::running_ext.splice(NekoGui_sys::running_ext.end(), extCs); - }, - DS_cores); - - NekoGui::dataStore->UpdateStartedId(ent->id); - running = ent; - - runOnUiThread([=] { - refresh_status(); - refresh_proxy_list(ent->id); - }); - - return true; - }; - - if (!mu_starting.tryLock()) { - MessageBoxWarning(software_name, "Another profile is starting..."); - return; - } - if (!mu_stopping.tryLock()) { - MessageBoxWarning(software_name, "Another profile is stopping..."); - mu_starting.unlock(); - return; - } - mu_stopping.unlock(); - - // check core state - if (!NekoGui::dataStore->core_running) { - runOnUiThread( - [=] { - MW_show_log("Try to start the config, but the core has not listened to the grpc port, so restart it..."); - core_process->start_profile_when_core_is_up = ent->id; - core_process->Restart(); - }, - DS_cores); - mu_starting.unlock(); - return; // let CoreProcess call neko_start when core is up - } - - // timeout message - auto restartMsgbox = new QMessageBox(QMessageBox::Question, software_name, tr("If there is no response for a long time, it is recommended to restart the software."), - QMessageBox::Yes | QMessageBox::No, this); - connect(restartMsgbox, &QMessageBox::accepted, this, [=] { MW_dialog_message("", "RestartProgram"); }); - auto restartMsgboxTimer = new MessageBoxTimer(this, restartMsgbox, 5000); - - runOnNewThread([=] { - // stop current running - if (NekoGui::dataStore->started_id >= 0) { - runOnUiThread([=] { neko_stop(false, true); }); - sem_stopped.acquire(); - } - // do start - MW_show_log(">>>>>>>> " + tr("Starting profile %1").arg(ent->bean->DisplayTypeAndName())); - if (!neko_start_stage2()) { - MW_show_log("<<<<<<<< " + tr("Failed to start profile %1").arg(ent->bean->DisplayTypeAndName())); - } - mu_starting.unlock(); - // cancel timeout - runOnUiThread([=] { - restartMsgboxTimer->cancel(); - restartMsgboxTimer->deleteLater(); - restartMsgbox->deleteLater(); -#ifdef Q_OS_LINUX - // Check systemd-resolved - if (NekoGui::dataStore->spmode_vpn && NekoGui::dataStore->routing->direct_dns.startsWith("local") && ReadFileText("/etc/resolv.conf").contains("systemd-resolved")) { - MW_show_log("[Warning] The default Direct DNS may not works with systemd-resolved, you may consider change your DNS settings."); - } -#endif - }); - }); -} - -void MainWindow::neko_stop(bool crash, bool sem) { - auto id = NekoGui::dataStore->started_id; - if (id < 0) { - if (sem) sem_stopped.release(); - return; - } - - auto neko_stop_stage2 = [=] { - runOnUiThread( - [=] { - while (!NekoGui_sys::running_ext.empty()) { - auto extC = NekoGui_sys::running_ext.front(); - extC->Kill(); - NekoGui_sys::running_ext.pop_front(); - } - }, - DS_cores); - -#ifndef NKR_NO_GRPC - NekoGui_traffic::trafficLooper->loop_enabled = false; - NekoGui_traffic::trafficLooper->loop_mutex.lock(); - if (NekoGui::dataStore->traffic_loop_interval != 0) { - NekoGui_traffic::trafficLooper->UpdateAll(); - for (const auto &item: NekoGui_traffic::trafficLooper->items) { - NekoGui::profileManager->GetProfile(item->id)->Save(); - runOnUiThread([=] { refresh_proxy_list(item->id); }); - } - } - NekoGui_traffic::trafficLooper->loop_mutex.unlock(); - - if (!crash) { - bool rpcOK; - QString error = defaultClient->Stop(&rpcOK); - if (rpcOK && !error.isEmpty()) { - runOnUiThread([=] { MessageBoxWarning("Stop return error", error); }); - return false; - } else if (!rpcOK) { - return false; - } - } -#endif - - NekoGui::dataStore->UpdateStartedId(-1919); - NekoGui::dataStore->need_keep_vpn_off = false; - running = nullptr; - - runOnUiThread([=] { - refresh_status(); - refresh_proxy_list(id); - }); - - return true; - }; - - if (!mu_stopping.tryLock()) { - if (sem) sem_stopped.release(); - return; - } - - // timeout message - auto restartMsgbox = new QMessageBox(QMessageBox::Question, software_name, tr("If there is no response for a long time, it is recommended to restart the software."), - QMessageBox::Yes | QMessageBox::No, this); - connect(restartMsgbox, &QMessageBox::accepted, this, [=] { MW_dialog_message("", "RestartProgram"); }); - auto restartMsgboxTimer = new MessageBoxTimer(this, restartMsgbox, 5000); - - runOnNewThread([=] { - // do stop - MW_show_log(">>>>>>>> " + tr("Stopping profile %1").arg(running->bean->DisplayTypeAndName())); - if (!neko_stop_stage2()) { - MW_show_log("<<<<<<<< " + tr("Failed to stop, please restart the program.")); - } - mu_stopping.unlock(); - if (sem) sem_stopped.release(); - // cancel timeout - runOnUiThread([=] { - restartMsgboxTimer->cancel(); - restartMsgboxTimer->deleteLater(); - restartMsgbox->deleteLater(); - }); - }); -} - -void MainWindow::CheckUpdate() { - // on new thread... -#ifndef NKR_NO_GRPC - bool ok; - libcore::UpdateReq request; - request.set_action(libcore::UpdateAction::Check); - request.set_check_pre_release(NekoGui::dataStore->check_include_pre); - auto response = NekoGui_rpc::defaultClient->Update(&ok, request); - if (!ok) return; - - auto err = response.error(); - if (!err.empty()) { - runOnUiThread([=] { - MessageBoxWarning(QObject::tr("Update"), err.c_str()); - }); - return; - } - - if (response.release_download_url() == nullptr) { - runOnUiThread([=] { - MessageBoxInfo(QObject::tr("Update"), QObject::tr("No update")); - }); - return; - } - - runOnUiThread([=] { - auto allow_updater = !NekoGui::dataStore->flag_use_appdata; - auto note_pre_release = response.is_pre_release() ? " (Pre-release)" : ""; - QMessageBox box(QMessageBox::Question, QObject::tr("Update") + note_pre_release, - QObject::tr("Update found: %1\nRelease note:\n%2").arg(response.assets_name().c_str(), response.release_note().c_str())); - // - QAbstractButton *btn1 = nullptr; - if (allow_updater) { - btn1 = box.addButton(QObject::tr("Update"), QMessageBox::AcceptRole); - } - QAbstractButton *btn2 = box.addButton(QObject::tr("Open in browser"), QMessageBox::AcceptRole); - box.addButton(QObject::tr("Close"), QMessageBox::RejectRole); - box.exec(); - // - if (btn1 == box.clickedButton() && allow_updater) { - // Download Update - runOnNewThread([=] { - bool ok2; - libcore::UpdateReq request2; - request2.set_action(libcore::UpdateAction::Download); - auto response2 = NekoGui_rpc::defaultClient->Update(&ok2, request2); - runOnUiThread([=] { - if (response2.error().empty()) { - auto q = QMessageBox::question(nullptr, QObject::tr("Update"), - QObject::tr("Update is ready, restart to install?")); - if (q == QMessageBox::StandardButton::Yes) { - this->exit_reason = 1; - on_menu_exit_triggered(); - } - } else { - MessageBoxWarning(QObject::tr("Update"), response2.error().c_str()); - } - }); - }); - } else if (btn2 == box.clickedButton()) { - QDesktopServices::openUrl(QUrl(response.release_url().c_str())); - } - }); -#endif -} diff --git a/ui/mainwindow_interface.h b/ui/mainwindow_interface.h deleted file mode 100644 index 27157de..0000000 --- a/ui/mainwindow_interface.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define MW_INTERFACE - -#include "mainwindow.h" diff --git a/ui/widget/FloatCheckBox.h b/ui/widget/FloatCheckBox.h deleted file mode 100644 index 98ca3bd..0000000 --- a/ui/widget/FloatCheckBox.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -class FloatCheckBox : public QCheckBox { -public: - QWidget *parent; - QWidget *window; - - void refresh() { - setFixedSize(24, 24); - auto pos = parent->rect().topRight(); - pos = parent->mapTo(window, pos); - pos.setX(pos.x() - 48); // ? - move(pos); - raise(); - setVisible(parent->isVisible()); - }; - - bool eventFilter(QObject *obj, QEvent *e) override { - if (obj != window || e->type() != QEvent::Resize) return false; - refresh(); - return false; - }; - - explicit FloatCheckBox(QWidget *parent, QWidget *window) : QCheckBox(window) { - this->parent = parent; - this->window = window; - window->installEventFilter(this); - refresh(); - }; -}; diff --git a/ui/widget/GroupItem.cpp b/ui/widget/GroupItem.cpp deleted file mode 100644 index d3ff39d..0000000 --- a/ui/widget/GroupItem.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "GroupItem.h" -#include "ui_GroupItem.h" - -#include "ui/edit/dialog_edit_group.h" -#include "main/GuiUtils.hpp" -#include "sub/GroupUpdater.hpp" - -#include - -QString ParseSubInfo(const QString &info) { - if (info.trimmed().isEmpty()) return ""; - - QString result; - - long long used = 0; - long long total = 0; - long long expire = 0; - - auto re0m = QRegularExpression("total=([0-9]+)").match(info); - if (re0m.lastCapturedIndex() >= 1) { - total = re0m.captured(1).toLongLong(); - } else { - return ""; - } - auto re1m = QRegularExpression("upload=([0-9]+)").match(info); - if (re1m.lastCapturedIndex() >= 1) { - used += re1m.captured(1).toLongLong(); - } - auto re2m = QRegularExpression("download=([0-9]+)").match(info); - if (re2m.lastCapturedIndex() >= 1) { - used += re2m.captured(1).toLongLong(); - } - auto re3m = QRegularExpression("expire=([0-9]+)").match(info); - if (re3m.lastCapturedIndex() >= 1) { - expire = re3m.captured(1).toLongLong(); - } - - result = QObject::tr("Used: %1 Remain: %2 Expire: %3") - .arg(ReadableSize(used), ReadableSize(total - used), DisplayTime(expire, QLocale::ShortFormat)); - - return result; -} - -GroupItem::GroupItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item) : QWidget(parent), ui(new Ui::GroupItem) { - ui->setupUi(this); - this->setLayoutDirection(Qt::LeftToRight); - - this->parentWindow = parent; - this->ent = ent; - this->item = item; - if (ent == nullptr) return; - - connect(this, &GroupItem::edit_clicked, this, &GroupItem::on_edit_clicked); - connect(NekoGui_sub::groupUpdater, &NekoGui_sub::GroupUpdater::asyncUpdateCallback, this, [=](int gid) { if (gid == this->ent->id) refresh_data(); }); - - refresh_data(); -} - -GroupItem::~GroupItem() { - delete ui; -} - -void GroupItem::refresh_data() { - ui->name->setText(ent->name); - - auto type = ent->url.isEmpty() ? tr("Basic") : tr("Subscription"); - if (ent->archive) type = tr("Archive") + " " + type; - type += " (" + Int2String(ent->Profiles().length()) + ")"; - ui->type->setText(type); - - if (ent->url.isEmpty()) { - ui->url->hide(); - ui->subinfo->hide(); - ui->update_sub->hide(); - } else { - ui->url->setText(ent->url); - QStringList info; - if (ent->sub_last_update != 0) { - info << tr("Last update: %1").arg(DisplayTime(ent->sub_last_update, QLocale::ShortFormat)); - } - auto subinfo = ParseSubInfo(ent->info); - if (!ent->info.isEmpty()) { - info << subinfo; - } - if (info.isEmpty()) { - ui->subinfo->hide(); - } else { - ui->subinfo->show(); - ui->subinfo->setText(info.join(" | ")); - } - } - runOnUiThread( - [=] { - adjustSize(); - item->setSizeHint(sizeHint()); - dynamic_cast(parent())->adjustSize(); - }, - this); -} - -void GroupItem::on_update_sub_clicked() { - NekoGui_sub::groupUpdater->AsyncUpdate(ent->url, ent->id); -} - -void GroupItem::on_edit_clicked() { - auto dialog = new DialogEditGroup(ent, parentWindow); - connect(dialog, &QDialog::finished, this, [=] { - if (dialog->result() == QDialog::Accepted) { - ent->Save(); - refresh_data(); - MW_dialog_message(Dialog_DialogManageGroups, "refresh" + Int2String(ent->id)); - } - dialog->deleteLater(); - }); - dialog->show(); -} - -void GroupItem::on_remove_clicked() { - if (NekoGui::profileManager->groups.size() <= 1) return; - if (QMessageBox::question(this, tr("Confirmation"), tr("Remove %1?").arg(ent->name)) == - QMessageBox::StandardButton::Yes) { - NekoGui::profileManager->DeleteGroup(ent->id); - MW_dialog_message(Dialog_DialogManageGroups, "refresh-1"); - delete item; - } -} diff --git a/ui/widget/GroupItem.h b/ui/widget/GroupItem.h deleted file mode 100644 index 14aa864..0000000 --- a/ui/widget/GroupItem.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include - -#include "db/Database.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class GroupItem; -} -QT_END_NAMESPACE - -class GroupItem : public QWidget { - Q_OBJECT - -public: - explicit GroupItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item); - - ~GroupItem() override; - - void refresh_data(); - - std::shared_ptr ent; - QListWidgetItem *item; - -private: - Ui::GroupItem *ui; - - QWidget *parentWindow; - -signals: - - void edit_clicked(); - -private slots: - - void on_update_sub_clicked(); - - void on_edit_clicked(); - - void on_remove_clicked(); -}; diff --git a/ui/widget/GroupItem.ui b/ui/widget/GroupItem.ui deleted file mode 100644 index fd2f77f..0000000 --- a/ui/widget/GroupItem.ui +++ /dev/null @@ -1,128 +0,0 @@ - - - GroupItem - - - - 0 - 0 - 403 - 300 - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - - - - color: rgb(251, 114, 153); - - - Type - - - - - - - - 0 - 0 - - - - Name - - - - - - - - 0 - 0 - - - - Update Subscription - - - - - - - - 0 - 0 - - - - Edit - - - - - - - - 0 - 0 - - - - Remove - - - - - - - - - - 0 - 0 - - - - color: rgb(102, 102, 102); - - - Url - - - - - - - - 0 - 0 - - - - 订阅流量信息 - - - - - - - - diff --git a/ui/widget/MessageBoxTimer.h b/ui/widget/MessageBoxTimer.h deleted file mode 100644 index 7258770..0000000 --- a/ui/widget/MessageBoxTimer.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -class MessageBoxTimer : public QTimer { -public: - QMessageBox *msgbox = nullptr; - bool showed = false; - - explicit MessageBoxTimer(QObject *parent, QMessageBox *msgbox, int delayMs) : QTimer(parent) { - connect(this, &QTimer::timeout, this, &MessageBoxTimer::timeoutFunc, Qt::ConnectionType::QueuedConnection); - this->msgbox = msgbox; - setSingleShot(true); - setInterval(delayMs); - start(); - }; - - void cancel() { - QTimer::stop(); - if (msgbox != nullptr && showed) { - msgbox->reject(); // return the timeoutFunc - } - }; - -private: - void timeoutFunc() { - if (msgbox == nullptr) return; - showed = true; - msgbox->exec(); - msgbox = nullptr; - } -}; diff --git a/ui/widget/MyLineEdit.h b/ui/widget/MyLineEdit.h deleted file mode 100644 index 90dee9e..0000000 --- a/ui/widget/MyLineEdit.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -class MyLineEdit : public QLineEdit { -public slots: - - explicit MyLineEdit(QWidget *parent = nullptr) : QLineEdit(parent) { - } - - void setText(const QString &s) { - QLineEdit::setText(s); - QLineEdit::home(false); - } -}; diff --git a/ui/widget/MyTableWidget.h b/ui/widget/MyTableWidget.h deleted file mode 100644 index ff86256..0000000 --- a/ui/widget/MyTableWidget.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -class MyTableWidget : public QTableWidget { -public: - explicit MyTableWidget(QWidget *parent = nullptr) : QTableWidget(parent) { - // 拖拽设置 - this->setDragDropMode(QAbstractItemView::InternalMove); // 内部移动 - this->setDropIndicatorShown(true); // drop位置 提示 - this->setSelectionBehavior(QAbstractItemView::SelectRows); - }; - - QList order; // id sorted (save) - std::map id2Row; // id2Row - QList row2Id; // row2Id: use this to refresh data - - std::function callback_save_order; - std::function refresh_data; - - void _save_order(bool saveToFile) { - order.clear(); - id2Row.clear(); - for (int i = 0; i < this->rowCount(); i++) { - auto id = row2Id[i]; - order += id; - id2Row[id] = i; - } - if (callback_save_order != nullptr && saveToFile) - callback_save_order(); - } - - void update_order(bool saveToFile) { - if (order.isEmpty()) { - _save_order(false); - return; - } - - // 纠错: order 里面含有不在当前表格控件的 id - bool needSave = false; - auto deleted_profiles = order; - for (int i = 0; i < this->rowCount(); i++) { - auto id = row2Id[i]; - deleted_profiles.removeAll(id); - } - for (auto deleted_profile: deleted_profiles) { - needSave = true; - order.removeAll(deleted_profile); - } - - // map(dstRow -> srcId) - QMap newRows; - for (int i = 0; i < this->rowCount(); i++) { - auto id = row2Id[i]; - auto dst = order.indexOf(id); - if (dst == i) continue; - if (dst == -1) { - // 纠错: 新的profile不需要移动 - needSave = true; - continue; - } - newRows[dst] = id; - } - - for (int i = 0; i < this->rowCount(); i++) { - if (!newRows.contains(i)) continue; - row2Id[i] = newRows[i]; - } - - // Then save the order - _save_order(needSave || saveToFile); - }; - -protected: - /* - * 2021.7.6 by gy - * 拖拽 继承QTableWidget overwrite dropEvent事件 - * 功能:拖动一行到鼠标落下的位置 - * 注意:DragDropMode相关参数的设置 - */ - void dropEvent(QDropEvent *event) override { - if (order.isEmpty()) order = row2Id; - - // 原行号与目标行号的确定 - int row_src, row_dst; - row_src = this->currentRow(); // 原行号 可加if - auto id_src = row2Id[row_src]; // id_src - QTableWidgetItem *item = this->itemAt(event->pos()); // 获取落点的item - if (item != nullptr) { - // 判断是否为空 - row_dst = item->row(); // 不为空 获取其行号 - // Modify order - order.removeAt(row_src); - order.insert(row_dst, id_src); - } else { - // 落点没有item 说明拖动到了最下面 - return; - } - - // Do update order & refresh - clearSelection(); - update_order(true); - refresh_data(-1); - }; -}; diff --git a/ui/widget/ProxyItem.cpp b/ui/widget/ProxyItem.cpp deleted file mode 100644 index f8c1587..0000000 --- a/ui/widget/ProxyItem.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "ProxyItem.h" -#include "ui_ProxyItem.h" - -#include - -ProxyItem::ProxyItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item) - : QWidget(parent), ui(new Ui::ProxyItem) { - ui->setupUi(this); - this->setLayoutDirection(Qt::LeftToRight); - - this->item = item; - this->ent = ent; - if (ent == nullptr) return; - - refresh_data(); -} - -ProxyItem::~ProxyItem() { - delete ui; -} - -void ProxyItem::refresh_data() { - ui->type->setText(ent->bean->DisplayType()); - ui->name->setText(ent->bean->DisplayName()); - ui->address->setText(ent->bean->DisplayAddress()); - ui->traffic->setText(ent->traffic_data->DisplayTraffic()); - ui->test_result->setText(ent->DisplayLatency()); - - runOnUiThread( - [=] { - adjustSize(); - item->setSizeHint(sizeHint()); - dynamic_cast(parent())->adjustSize(); - }, - this); -} - -void ProxyItem::on_remove_clicked() { - if (!this->remove_confirm || - QMessageBox::question(this, tr("Confirmation"), tr("Remove %1?").arg(ent->bean->DisplayName())) == QMessageBox::StandardButton::Yes) { - // TODO do remove (or not) -> callback - delete item; - } -} - -QPushButton *ProxyItem::get_change_button() { - return ui->change; -} diff --git a/ui/widget/ProxyItem.h b/ui/widget/ProxyItem.h deleted file mode 100644 index ac8058f..0000000 --- a/ui/widget/ProxyItem.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -#include "db/ProxyEntity.hpp" - -QT_BEGIN_NAMESPACE -namespace Ui { - class ProxyItem; -} -QT_END_NAMESPACE - -class QPushButton; - -class ProxyItem : public QWidget { - Q_OBJECT - -public: - explicit ProxyItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item); - - ~ProxyItem() override; - - void refresh_data(); - - QPushButton *get_change_button(); - - std::shared_ptr ent; - QListWidgetItem *item; - bool remove_confirm = false; - -private: - Ui::ProxyItem *ui; - -private slots: - - void on_remove_clicked(); -}; diff --git a/ui/widget/ProxyItem.ui b/ui/widget/ProxyItem.ui deleted file mode 100644 index 351dbae..0000000 --- a/ui/widget/ProxyItem.ui +++ /dev/null @@ -1,146 +0,0 @@ - - - ProxyItem - - - - 0 - 0 - 400 - 300 - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - - - - 名称 - - - - - - - - 0 - 0 - - - - - :/icon/material/swap-horizontal.svg:/icon/material/swap-horizontal.svg - - - - - - - - 0 - 0 - - - - - :/icon/material/delete.svg:/icon/material/delete.svg - - - - - - - - - - - - 0 - 0 - - - - color: rgb(102, 102, 102); - - - 地址 - - - - - - - - 0 - 0 - - - - 测试结果 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 0 - 0 - - - - color: rgb(251, 114, 153); - - - 类型 - - - - - - - - 0 - 0 - - - - 流量 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - -