mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2025-12-11 21:52:24 +00:00
Complete refactor of project file structure
Fix incorrect handing of default node parameters in SSD subscription. Fix unable to parse some Clash rule-providers (#289). Fix not filtering inline comments in some Surge rulesets. (#285) Add Cron task options. Add clean context option for persisting a JS context across the whole request. Replace JS engine with QuickJS.
This commit is contained in:
@@ -36,20 +36,30 @@ IF(USING_MALLOC_TRIM)
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(subconverter
|
||||
src/interfaces.cpp
|
||||
src/logger.cpp
|
||||
src/generator/config/nodemanip.cpp
|
||||
src/generator/config/ruleconvert.cpp
|
||||
src/generator/config/subexport.cpp
|
||||
src/generator/template/templates.cpp
|
||||
src/handler/interfaces.cpp
|
||||
src/handler/multithread.cpp
|
||||
src/handler/upload.cpp
|
||||
src/handler/webget.cpp
|
||||
src/main.cpp
|
||||
src/md5.cpp
|
||||
src/misc.cpp
|
||||
src/multithread.cpp
|
||||
src/nodemanip.cpp
|
||||
src/script.cpp
|
||||
src/speedtestutil.cpp
|
||||
src/subexport.cpp
|
||||
src/templates.cpp
|
||||
src/upload.cpp
|
||||
src/webget.cpp
|
||||
src/webserver_libevent.cpp)
|
||||
src/parser/infoparser.cpp
|
||||
src/parser/subparser.cpp
|
||||
src/script/cron.cpp
|
||||
src/script/script_quickjs.cpp
|
||||
src/server/webserver_libevent.cpp
|
||||
src/utils/base64/base64.cpp
|
||||
src/utils/codepage.cpp
|
||||
src/utils/file.cpp
|
||||
src/utils/logger.cpp
|
||||
src/utils/md5/md5.cpp
|
||||
src/utils/network.cpp
|
||||
src/utils/regexp.cpp
|
||||
src/utils/string.cpp
|
||||
src/utils/system.cpp
|
||||
src/utils/urlencode.cpp)
|
||||
INCLUDE_DIRECTORIES(src)
|
||||
LINK_DIRECTORIES(${CMAKE_SOURCE_DIR})
|
||||
|
||||
@@ -90,9 +100,13 @@ TARGET_LINK_LIBRARIES(subconverter ${YAML_CPP_LIBRARY})
|
||||
ADD_DEFINITIONS(-DPCRE2_STATIC)
|
||||
#ENDIF()
|
||||
|
||||
FIND_PACKAGE(Duktape REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${DUKTAPE_INCLUDE_DIRS})
|
||||
TARGET_LINK_LIBRARIES(subconverter ${DUKTAPE_LIBRARIES})
|
||||
FIND_PACKAGE(QuickJS REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${QUICKJS_INCLUDE_DIRS})
|
||||
TARGET_LINK_LIBRARIES(subconverter ${QUICKJS_LIBRARIES})
|
||||
|
||||
FIND_PACKAGE(libcron REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${LIBCRON_INCLUDE_DIRS})
|
||||
TARGET_LINK_LIBRARIES(subconverter ${LIBCRON_LIBRARIES})
|
||||
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(subconverter wsock32 ws2_32)
|
||||
|
||||
@@ -244,6 +244,11 @@ clash.log_level=info
|
||||
/v2ray=/sub?target=v2ray
|
||||
/trojan=/sub?target=trojan
|
||||
|
||||
[tasks]
|
||||
;Tasks to be run regularly during server execution.
|
||||
;Format: Name`Cron_Expression`JS_Path`Timeout_in_seconds
|
||||
;task=tick`0/10 * * * * ?`tick.js`3
|
||||
|
||||
[server]
|
||||
;Address to bind on for Web Server
|
||||
listen=0.0.0.0
|
||||
|
||||
@@ -123,6 +123,12 @@ aliases:
|
||||
- {uri: /v2ray, target: "/sub?target=v2ray"}
|
||||
- {uri: /trojan, target: "/sub?target=trojan"}
|
||||
|
||||
tasks:
|
||||
# - name: tick
|
||||
# cronexp: "0/10 * * * * ?"
|
||||
# path: tick.js
|
||||
# timeout: 3
|
||||
|
||||
server:
|
||||
listen: 0.0.0.0
|
||||
port: 25500
|
||||
|
||||
13
cmake/FindLibCron.cmake
Normal file
13
cmake/FindLibCron.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
find_path(LIBCRON_INCLUDE_DIR libcron/Cron.h)
|
||||
find_path(DATE_INCLUDE_DIR date/date.h)
|
||||
|
||||
find_library(LIBCRON_LIBRARY liblibcron)
|
||||
|
||||
set(LIBCRON_LIBRARIES "${LIBCRON_LIBRARY}")
|
||||
set(LIBCRON_INCLUDE_DIRS "${LIBCRON_INCLUDE_DIR} ${DATE_INCLUDE_DIR}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(libcron DEFAULT_MSG
|
||||
LIBCRON_INCLUDE_DIRS LIBCRON_LIBRARY)
|
||||
|
||||
mark_as_advanced(LIBCRON_INCLUDE_DIRS)
|
||||
11
cmake/FindQuickJS.cmake
Normal file
11
cmake/FindQuickJS.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
find_path(QUICKJS_INCLUDE_DIRS quickjs/quickjs.h)
|
||||
|
||||
find_library(QUICKJS_LIBRARY libquickjs)
|
||||
|
||||
set(QUICKJS_LIBRARIES "${QUICKJS_LIBRARY}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QuickJS DEFAULT_MSG
|
||||
QUICKJS_INCLUDE_DIRS QUICKJS_LIBRARY)
|
||||
|
||||
mark_as_advanced(QUICKJS_INCLUDE_DIRS QUICKJS_LIBRARY)
|
||||
1341
include/quickjspp.hpp
Normal file
1341
include/quickjspp.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,28 @@
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer "tindy.it@gmail.com"
|
||||
|
||||
# build minimized
|
||||
WORKDIR /
|
||||
RUN apk add --no-cache --virtual .build-tools git g++ build-base linux-headers cmake python2 nodejs npm && \
|
||||
RUN apk add --no-cache --virtual .build-tools git g++ build-base linux-headers cmake && \
|
||||
apk add --no-cache --virtual .build-deps curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev && \
|
||||
git clone https://github.com/svaarala/duktape --depth=1 && \
|
||||
cd duktape && \
|
||||
make -C src-tools && \
|
||||
python2 -m ensurepip && \
|
||||
pip2 install PyYAML --no-cache-dir && \
|
||||
python2 util/dist.py --output-directory dist && \
|
||||
cd dist/src && \
|
||||
cc -c -O3 -o duktape.o duktape.c && \
|
||||
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c && \
|
||||
ar cr libduktape.a duktape.o && \
|
||||
ar cr libduktape_module.a duk_module_node.o && \
|
||||
install -m0644 ./*.a /usr/lib && \
|
||||
install -m0644 duk*.h /usr/include && \
|
||||
install -m0644 ../extras/module-node/duk_module_node.h /usr/include && \
|
||||
cd ../../.. && \
|
||||
rm -rf duktape /usr/lib/python2.7 && \
|
||||
git clone https://github.com/ftk/quickjspp --depth=1 && \
|
||||
cd quickjspp && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release . && \
|
||||
make -j4 && \
|
||||
install -m644 quickjs/libquickjs.a /usr/lib && \
|
||||
install -m644 quickjs/quickjs.h quickjs/quickjs-libc.h /usr/include/quickjs && \
|
||||
install -m644 quickjspp.hpp /usr/include && \
|
||||
cd .. && \
|
||||
git clone https://github.com/PerMalmberg/libcron --depth=1 && \
|
||||
cd libcron && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release . && \
|
||||
make -j4 && \
|
||||
install -m644 libcron/out/Release/liblibcron.a /usr/lib && \
|
||||
install -d /usr/include/libcron/ && \
|
||||
install -m644 libcron/include/libcron/* /usr/include/libcron/ && \
|
||||
install -d /usr/include/date/ && \
|
||||
install -m644 libcron/externals/date/include/date/* /usr/include/date/ && \
|
||||
cd .. && \
|
||||
git clone https://github.com/tindy2013/subconverter --depth=1 && \
|
||||
cd subconverter && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release . && \
|
||||
|
||||
@@ -16,21 +16,25 @@ cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOO
|
||||
make install -j2 > /dev/null
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/svaarala/duktape --depth=1
|
||||
cd duktape
|
||||
make -C src-tools
|
||||
python2 -m ensurepip
|
||||
pip2 install PyYAML
|
||||
python2 util/dist.py --output-directory dist
|
||||
cd dist/src
|
||||
cc -c -O3 -o duktape.o duktape.c
|
||||
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
|
||||
ar cr libduktape.a duktape.o
|
||||
ar cr libduktape_module.a duk_module_node.o
|
||||
install -m0644 ./*.a /usr/lib
|
||||
install -m0644 ./duk*.h /usr/include
|
||||
install -m0644 ../extras/module-node/duk_module_node.h /usr/include
|
||||
cd ../../..
|
||||
git clone https://github.com/ftk/quickjspp --depth=1
|
||||
cd quickjspp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -m644 quickjs/libquickjs.a /usr/lib
|
||||
install -m644 quickjs/quickjs.h quickjs/quickjs-libc.h /usr/include/quickjs
|
||||
install -m644 quickjspp.hpp /usr/include
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/PerMalmberg/libcron --depth=1
|
||||
cd libcron
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -m644 libcron/out/Release/liblibcron.a /usr/lib
|
||||
install -d /usr/include/libcron/
|
||||
install -m644 libcron/include/libcron/* /usr/include/libcron/
|
||||
install -d /usr/include/date/
|
||||
install -m644 libcron/externals/date/include/date/* /usr/include/date/
|
||||
cd ..
|
||||
|
||||
export PKG_CONFIG_PATH=/usr/lib64/pkgconfig
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
|
||||
@@ -17,20 +17,23 @@ cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOO
|
||||
make install -j8 > /dev/null
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/svaarala/duktape --depth=1
|
||||
cd duktape
|
||||
make -C src-tools
|
||||
pip2 install PyYAML
|
||||
python2 util/dist.py --output-directory dist
|
||||
cd dist/src
|
||||
cc -c -O3 -o duktape.o duktape.c
|
||||
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
|
||||
ar cr libduktape.a duktape.o
|
||||
ar cr libduktape_module.a duk_module_node.o
|
||||
install -m0644 ./*.a /usr/local/lib
|
||||
install -m0644 ./duk*.h /usr/local/include
|
||||
install -m0644 ../extras/module-node/duk_module_node.h /usr/local/include
|
||||
cd ../../..
|
||||
git clone https://github.com/ftk/quickjspp --depth=1
|
||||
cd quickjspp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -m644 quickjs/quickjs.h quickjs/quickjs-libc.h /usr/local/include/quickjs
|
||||
install -m644 quickjspp.hpp /usr/local/include
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/PerMalmberg/libcron --depth=1
|
||||
cd libcron
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -d /usr/local/include/libcron/
|
||||
install -m644 libcron/include/libcron/* /usr/local/include/libcron/
|
||||
install -d /usr/local/include/date/
|
||||
install -m644 libcron/externals/date/include/date/* /usr/local/include/date/
|
||||
cd ..
|
||||
|
||||
cp /usr/local/lib/libevent.a .
|
||||
cp /usr/local/opt/zlib/lib/libz.a .
|
||||
|
||||
@@ -13,19 +13,26 @@ cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOO
|
||||
make install -j4
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/svaarala/duktape --depth=1
|
||||
cd duktape
|
||||
make -C src-tools
|
||||
node src-tools/index.js dist --output-directory dist
|
||||
cd dist/src
|
||||
gcc -c -O3 -o duktape.o duktape.c
|
||||
gcc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
|
||||
ar cr libduktape.a duktape.o
|
||||
ar cr libduktape_module.a duk_module_node.o
|
||||
install -m0644 ./*.a "$MINGW_PREFIX/lib"
|
||||
install -m0644 ./duk*.h "$MINGW_PREFIX/include"
|
||||
install -m0644 ../extras/module-node/duk_module_node.h "$MINGW_PREFIX/include"
|
||||
cd ../../..
|
||||
git clone https://github.com/ftk/quickjspp --depth=1
|
||||
cd quickjspp
|
||||
patch quickjs/quickjs-libc.c -i ../scripts/patches/0001-quickjs-libc-add-realpath-for-Windows.patch
|
||||
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -m644 quickjs/libquickjs.a "$MINGW_PREFIX/lib"
|
||||
install -m644 quickjs/quickjs.h quickjs/quickjs-libc.h "$MINGW_PREFIX/include/quickjs"
|
||||
install -m644 quickjspp.hpp "$MINGW_PREFIX/include"
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/PerMalmberg/libcron --depth=1
|
||||
cd libcron
|
||||
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j4
|
||||
install -m644 libcron/out/Release/liblibcron.a "$MINGW_PREFIX/lib"
|
||||
install -d "$MINGW_PREFIX/include/libcron/"
|
||||
install -m644 libcron/include/libcron/* "$MINGW_PREFIX/include/libcron/"
|
||||
install -d "$MINGW_PREFIX/include/date/"
|
||||
install -m644 libcron/externals/date/include/date/* "$MINGW_PREFIX/include/date/"
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/Tencent/rapidjson --depth=1
|
||||
cd rapidjson
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
--- quickjs-libc.c 2021-03-23 15:04:09.604314700 +0800
|
||||
+++ patched.c 2021-03-23 15:04:35.109289500 +0800
|
||||
@@ -132,6 +132,29 @@
|
||||
static uint64_t os_pending_signals;
|
||||
static int (*os_poll_func)(JSContext *ctx);
|
||||
|
||||
+static char *_realpath(const char *path, char *resolved_path)
|
||||
+{
|
||||
+#if defined(_WIN32)
|
||||
+ BOOL allocated = FALSE;
|
||||
+ if(resolved_path == NULL)
|
||||
+ {
|
||||
+ resolved_path = (char*) malloc(PATH_MAX);
|
||||
+ allocated = TRUE;
|
||||
+ }
|
||||
+ DWORD len = GetFullPathNameA(path, PATH_MAX, resolved_path, NULL);
|
||||
+ if(!len || len >= PATH_MAX)
|
||||
+ {
|
||||
+ if(allocated)
|
||||
+ free(resolved_path);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ resolved_path[len] = '\0';
|
||||
+ return resolved_path;
|
||||
+#else
|
||||
+ return realpath(path, resolved_path);
|
||||
+#endif // _WIN32
|
||||
+}
|
||||
+
|
||||
static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)
|
||||
{
|
||||
dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);
|
||||
@@ -530,19 +553,19 @@
|
||||
return -1;
|
||||
if (!strchr(module_name, ':')) {
|
||||
strcpy(buf, "file://");
|
||||
-#if !defined(_WIN32)
|
||||
+//#if !defined(_WIN32)
|
||||
/* realpath() cannot be used with modules compiled with qjsc
|
||||
because the corresponding module source code is not
|
||||
necessarily present */
|
||||
if (use_realpath) {
|
||||
- char *res = realpath(module_name, buf + strlen(buf));
|
||||
+ char *res = _realpath(module_name, buf + strlen(buf));
|
||||
if (!res) {
|
||||
JS_ThrowTypeError(ctx, "realpath failure");
|
||||
JS_FreeCString(ctx, module_name);
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
-#endif
|
||||
+//#endif
|
||||
{
|
||||
pstrcat(buf, sizeof(buf), module_name);
|
||||
}
|
||||
@@ -2588,8 +2611,6 @@
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
|
||||
-#if !defined(_WIN32)
|
||||
-
|
||||
/* return [path, errorcode] */
|
||||
static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
@@ -2601,7 +2622,7 @@
|
||||
path = JS_ToCString(ctx, argv[0]);
|
||||
if (!path)
|
||||
return JS_EXCEPTION;
|
||||
- res = realpath(path, buf);
|
||||
+ res = _realpath(path, buf);
|
||||
JS_FreeCString(ctx, path);
|
||||
if (!res) {
|
||||
buf[0] = '\0';
|
||||
@@ -2612,6 +2633,8 @@
|
||||
return make_string_error(ctx, buf, err);
|
||||
}
|
||||
|
||||
+#if !defined(_WIN32)
|
||||
+
|
||||
static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
@@ -3598,9 +3621,9 @@
|
||||
#endif
|
||||
JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
|
||||
JS_CFUNC_DEF("utimes", 3, js_os_utimes ),
|
||||
+ JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
|
||||
#if !defined(_WIN32)
|
||||
JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),
|
||||
- JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
|
||||
JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
|
||||
JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
|
||||
JS_CFUNC_DEF("exec", 1, js_os_exec ),
|
||||
476
src/generator/config/nodemanip.cpp
Normal file
476
src/generator/config/nodemanip.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../handler/webget.h"
|
||||
#include "../../parser/config/proxy.h"
|
||||
#include "../../parser/infoparser.h"
|
||||
#include "../../parser/subparser.h"
|
||||
#include "../../script/script_quickjs.h"
|
||||
#include "../../utils/file_extra.h"
|
||||
#include "../../utils/logger.h"
|
||||
#include "../../utils/map_extra.h"
|
||||
#include "../../utils/network.h"
|
||||
#include "../../utils/regexp.h"
|
||||
#include "../../utils/urlencode.h"
|
||||
#include "nodemanip.h"
|
||||
#include "subexport.h"
|
||||
|
||||
std::string override_conf_port;
|
||||
bool ss_libev, ssr_libev;
|
||||
extern int gCacheSubscription;
|
||||
extern bool gScriptCleanContext;
|
||||
|
||||
int explodeConf(const std::string &filepath, std::vector<Proxy> &nodes)
|
||||
{
|
||||
return explodeConfContent(fileGet(filepath), nodes);
|
||||
}
|
||||
|
||||
void copyNodes(std::vector<Proxy> &source, std::vector<Proxy> &dest)
|
||||
{
|
||||
std::move(source.begin(), source.end(), std::back_inserter(dest));
|
||||
}
|
||||
|
||||
int addNodes(std::string link, std::vector<Proxy> &allNodes, int groupID, parse_settings &parse_set)
|
||||
{
|
||||
std::string &proxy = *parse_set.proxy, subInfo = *parse_set.sub_info;
|
||||
string_array &exclude_remarks = *parse_set.exclude_remarks;
|
||||
string_array &include_remarks = *parse_set.include_remarks;
|
||||
string_array &stream_rules = *parse_set.stream_rules;
|
||||
string_array &time_rules = *parse_set.time_rules;
|
||||
string_icase_map &request_headers = *parse_set.request_header;
|
||||
bool &authorized = parse_set.authorized;
|
||||
|
||||
ConfType linkType = ConfType::Unknow;
|
||||
std::vector<Proxy> nodes;
|
||||
Proxy node;
|
||||
std::string strSub, extra_headers, custom_group;
|
||||
|
||||
// TODO: replace with startsWith if appropriate
|
||||
link = replaceAllDistinct(link, "\"", "");
|
||||
|
||||
/// script:filepath,arg1,arg2,...
|
||||
script_safe_runner(parse_set.js_runtime, parse_set.js_context, [&](qjs::Context &ctx)
|
||||
{
|
||||
if(startsWith(link, "script:")) /// process subscription with script
|
||||
{
|
||||
writeLog(0, "Found script link. Start running...", LOG_LEVEL_INFO);
|
||||
string_array args = split(link.substr(7), ",");
|
||||
if(args.size() >= 1)
|
||||
{
|
||||
std::string script = fileGet(args[0], false);
|
||||
try
|
||||
{
|
||||
ctx.eval(script);
|
||||
args.erase(args.begin()); /// remove script path
|
||||
auto parse = (std::function<std::string(const std::string&, const string_array&)>) ctx.eval("parse");
|
||||
switch(args.size())
|
||||
{
|
||||
case 0:
|
||||
link = parse(std::string(), string_array());
|
||||
break;
|
||||
case 1:
|
||||
link = parse(args[0], string_array());
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::string first = args[0];
|
||||
args.erase(args.begin());
|
||||
link = parse(first, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(qjs::exception)
|
||||
{
|
||||
script_print_stack(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, gScriptCleanContext);
|
||||
/*
|
||||
duk_context *ctx = duktape_init();
|
||||
defer(duk_destroy_heap(ctx);)
|
||||
duktape_peval(ctx, script);
|
||||
duk_get_global_string(ctx, "parse");
|
||||
for(size_t i = 1; i < args.size(); i++)
|
||||
duk_push_string(ctx, trim(args[i]).c_str());
|
||||
if(duk_pcall(ctx, args.size() - 1) == 0)
|
||||
link = duktape_get_res_str(ctx);
|
||||
else
|
||||
{
|
||||
writeLog(0, "Error when trying to evaluate script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
|
||||
duk_pop(ctx); /// pop err
|
||||
}
|
||||
*/
|
||||
|
||||
/// tag:group_name,link
|
||||
if(startsWith(link, "tag:"))
|
||||
{
|
||||
string_size pos = link.find(",");
|
||||
if(pos != link.npos)
|
||||
{
|
||||
custom_group = link.substr(4, pos - 4);
|
||||
link.erase(0, pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(link == "nullnode")
|
||||
{
|
||||
node.GroupId = 0;
|
||||
writeLog(0, "Adding node placeholder...");
|
||||
allNodes.emplace_back(std::move(node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeLog(LOG_TYPE_INFO, "Received Link.");
|
||||
if(startsWith(link, "https://t.me/socks") || startsWith(link, "tg://socks"))
|
||||
linkType = ConfType::SOCKS;
|
||||
else if(startsWith(link, "https://t.me/http") || startsWith(link, "tg://http"))
|
||||
linkType = ConfType::HTTP;
|
||||
else if(isLink(link) || startsWith(link, "surge:///install-config"))
|
||||
linkType = ConfType::SUB;
|
||||
else if(startsWith(link, "Netch://"))
|
||||
linkType = ConfType::Netch;
|
||||
else if(fileExist(link))
|
||||
linkType = ConfType::Local;
|
||||
|
||||
switch(linkType)
|
||||
{
|
||||
case ConfType::SUB:
|
||||
writeLog(LOG_TYPE_INFO, "Downloading subscription data...");
|
||||
if(startsWith(link, "surge:///install-config")) //surge config link
|
||||
link = urlDecode(getUrlArg(link, "url"));
|
||||
strSub = webGet(link, proxy, gCacheSubscription, &extra_headers, &request_headers);
|
||||
/*
|
||||
if(strSub.size() == 0)
|
||||
{
|
||||
//try to get it again with system proxy
|
||||
writeLog(LOG_TYPE_WARN, "Cannot download subscription directly. Using system proxy.");
|
||||
strProxy = getSystemProxy();
|
||||
if(strProxy != "")
|
||||
{
|
||||
strSub = webGet(link, strProxy);
|
||||
}
|
||||
else
|
||||
writeLog(LOG_TYPE_WARN, "No system proxy is set. Skipping.");
|
||||
}
|
||||
*/
|
||||
if(strSub.size())
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Parsing subscription data...");
|
||||
if(explodeConfContent(strSub, nodes) == 0)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Invalid subscription!");
|
||||
return -1;
|
||||
}
|
||||
if(startsWith(strSub, "ssd://"))
|
||||
{
|
||||
getSubInfoFromSSD(strSub, subInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!getSubInfoFromHeader(extra_headers, subInfo))
|
||||
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
|
||||
}
|
||||
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
|
||||
for(Proxy &x : nodes)
|
||||
{
|
||||
x.GroupId = groupID;
|
||||
if(custom_group.size())
|
||||
x.Group = custom_group;
|
||||
}
|
||||
copyNodes(nodes, allNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Cannot download subscription data.");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ConfType::Local:
|
||||
if(!authorized)
|
||||
return -1;
|
||||
writeLog(LOG_TYPE_INFO, "Parsing configuration file data...");
|
||||
if(explodeConf(link, nodes) == 0)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Invalid configuration file!");
|
||||
return -1;
|
||||
}
|
||||
if(startsWith(strSub, "ssd://"))
|
||||
{
|
||||
getSubInfoFromSSD(strSub, subInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
|
||||
}
|
||||
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
|
||||
for(Proxy &x : nodes)
|
||||
{
|
||||
x.GroupId = groupID;
|
||||
if(custom_group.size())
|
||||
x.Group = custom_group;
|
||||
}
|
||||
copyNodes(nodes, allNodes);
|
||||
break;
|
||||
default:
|
||||
explode(link, node);
|
||||
if(node.Type == -1)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "No valid link found.");
|
||||
return -1;
|
||||
}
|
||||
node.GroupId = groupID;
|
||||
if(custom_group.size())
|
||||
node.Group = custom_group;
|
||||
allNodes.emplace_back(std::move(node));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool chkIgnore(const Proxy &node, string_array &exclude_remarks, string_array &include_remarks)
|
||||
{
|
||||
bool excluded = false, included = false;
|
||||
//std::string remarks = UTF8ToACP(node.remarks);
|
||||
//std::string remarks = node.remarks;
|
||||
//writeLog(LOG_TYPE_INFO, "Comparing exclude remarks...");
|
||||
excluded = std::any_of(exclude_remarks.cbegin(), exclude_remarks.cend(), [&node](const auto &x)
|
||||
{
|
||||
std::string real_rule;
|
||||
if(applyMatcher(x, real_rule, node))
|
||||
{
|
||||
if(real_rule.empty()) return true;
|
||||
return regFind(node.Remark, real_rule);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
if(include_remarks.size() != 0)
|
||||
{
|
||||
//writeLog(LOG_TYPE_INFO, "Comparing include remarks...");
|
||||
included = std::any_of(include_remarks.cbegin(), include_remarks.cend(), [&node](const auto &x)
|
||||
{
|
||||
std::string real_rule;
|
||||
if(applyMatcher(x, real_rule, node))
|
||||
{
|
||||
if(real_rule.empty()) return true;
|
||||
return regFind(node.Remark, real_rule);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
included = true;
|
||||
}
|
||||
|
||||
return excluded || !included;
|
||||
}
|
||||
|
||||
void filterNodes(std::vector<Proxy> &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID)
|
||||
{
|
||||
int node_index = 0;
|
||||
std::vector<Proxy>::iterator iter = nodes.begin();
|
||||
while(iter != nodes.end())
|
||||
{
|
||||
if(chkIgnore(*iter, exclude_remarks, include_remarks))
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Node " + iter->Group + " - " + iter->Remark + " has been ignored and will not be added.");
|
||||
nodes.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Node " + iter->Group + " - " + iter->Remark + " has been added.");
|
||||
iter->Id = node_index;
|
||||
iter->GroupId = groupID;
|
||||
++node_index;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
/*
|
||||
std::vector<std::unique_ptr<pcre2_code, decltype(&pcre2_code_free)>> exclude_patterns, include_patterns;
|
||||
std::vector<std::unique_ptr<pcre2_match_data, decltype(&pcre2_match_data_free)>> exclude_match_data, include_match_data;
|
||||
unsigned int i = 0;
|
||||
PCRE2_SIZE erroroffset;
|
||||
int errornumber, rc;
|
||||
|
||||
for(i = 0; i < exclude_remarks.size(); i++)
|
||||
{
|
||||
std::unique_ptr<pcre2_code, decltype(&pcre2_code_free)> pattern(pcre2_compile(reinterpret_cast<const unsigned char*>(exclude_remarks[i].c_str()), exclude_remarks[i].size(), PCRE2_UTF | PCRE2_MULTILINE | PCRE2_ALT_BSUX, &errornumber, &erroroffset, NULL), &pcre2_code_free);
|
||||
if(!pattern)
|
||||
return;
|
||||
exclude_patterns.emplace_back(std::move(pattern));
|
||||
pcre2_jit_compile(exclude_patterns[i].get(), 0);
|
||||
std::unique_ptr<pcre2_match_data, decltype(&pcre2_match_data_free)> match_data(pcre2_match_data_create_from_pattern(exclude_patterns[i].get(), NULL), &pcre2_match_data_free);
|
||||
exclude_match_data.emplace_back(std::move(match_data));
|
||||
}
|
||||
for(i = 0; i < include_remarks.size(); i++)
|
||||
{
|
||||
std::unique_ptr<pcre2_code, decltype(&pcre2_code_free)> pattern(pcre2_compile(reinterpret_cast<const unsigned char*>(include_remarks[i].c_str()), include_remarks[i].size(), PCRE2_UTF | PCRE2_MULTILINE | PCRE2_ALT_BSUX, &errornumber, &erroroffset, NULL), &pcre2_code_free);
|
||||
if(!pattern)
|
||||
return;
|
||||
include_patterns.emplace_back(std::move(pattern));
|
||||
pcre2_jit_compile(include_patterns[i].get(), 0);
|
||||
std::unique_ptr<pcre2_match_data, decltype(&pcre2_match_data_free)> match_data(pcre2_match_data_create_from_pattern(include_patterns[i].get(), NULL), &pcre2_match_data_free);
|
||||
include_match_data.emplace_back(std::move(match_data));
|
||||
}
|
||||
writeLog(LOG_TYPE_INFO, "Filter started.");
|
||||
while(iter != nodes.end())
|
||||
{
|
||||
bool excluded = false, included = false;
|
||||
for(i = 0; i < exclude_patterns.size(); i++)
|
||||
{
|
||||
rc = pcre2_match(exclude_patterns[i].get(), reinterpret_cast<const unsigned char*>(iter->remarks.c_str()), iter->remarks.size(), 0, 0, exclude_match_data[i].get(), NULL);
|
||||
if (rc < 0)
|
||||
{
|
||||
switch(rc)
|
||||
{
|
||||
case PCRE2_ERROR_NOMATCH:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
excluded = true;
|
||||
}
|
||||
if(include_patterns.size() > 0)
|
||||
for(i = 0; i < include_patterns.size(); i++)
|
||||
{
|
||||
rc = pcre2_match(include_patterns[i].get(), reinterpret_cast<const unsigned char*>(iter->remarks.c_str()), iter->remarks.size(), 0, 0, include_match_data[i].get(), NULL);
|
||||
if (rc < 0)
|
||||
{
|
||||
switch(rc)
|
||||
{
|
||||
case PCRE2_ERROR_NOMATCH:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
included = true;
|
||||
}
|
||||
else
|
||||
included = true;
|
||||
if(excluded || !included)
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been ignored and will not be added.");
|
||||
nodes.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been added.");
|
||||
iter->id = node_index;
|
||||
iter->groupID = groupID;
|
||||
++node_index;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
*/
|
||||
writeLog(LOG_TYPE_INFO, "Filter done.");
|
||||
}
|
||||
|
||||
bool matchRange(const std::string &range, int target)
|
||||
{
|
||||
string_array vArray = split(range, ",");
|
||||
bool match = false;
|
||||
std::string range_begin_str, range_end_str;
|
||||
int range_begin, range_end;
|
||||
static const std::string reg_num = "-?\\d+", reg_range = "(\\d+)-(\\d+)", reg_not = "\\!-?(\\d+)", reg_not_range = "\\!(\\d+)-(\\d+)", reg_less = "(\\d+)-", reg_more = "(\\d+)\\+";
|
||||
for(std::string &x : vArray)
|
||||
{
|
||||
if(regMatch(x, reg_num))
|
||||
{
|
||||
if(to_int(x, INT_MAX) == target)
|
||||
match = true;
|
||||
}
|
||||
else if(regMatch(x, reg_range))
|
||||
{
|
||||
/*
|
||||
range_begin = to_int(regReplace(x, reg_range, "$1"), INT_MAX);
|
||||
range_end = to_int(regReplace(x, reg_range, "$2"), INT_MIN);
|
||||
*/
|
||||
regGetMatch(x, reg_range, 3, 0, &range_begin_str, &range_end_str);
|
||||
range_begin = to_int(range_begin_str, INT_MAX);
|
||||
range_end = to_int(range_end_str, INT_MIN);
|
||||
if(target >= range_begin && target <= range_end)
|
||||
match = true;
|
||||
}
|
||||
else if(regMatch(x, reg_not))
|
||||
{
|
||||
if(to_int(regReplace(x, reg_not, "$1"), INT_MAX) == target)
|
||||
match = false;
|
||||
}
|
||||
else if(regMatch(x, reg_not_range))
|
||||
{
|
||||
/*
|
||||
range_begin = to_int(regReplace(x, reg_range, "$1"), INT_MAX);
|
||||
range_end = to_int(regReplace(x, reg_range, "$2"), INT_MIN);
|
||||
*/
|
||||
regGetMatch(x, reg_range, 3, 0, &range_begin_str, &range_end_str);
|
||||
range_begin = to_int(range_begin_str, INT_MAX);
|
||||
range_end = to_int(range_end_str, INT_MIN);
|
||||
if(target >= range_begin && target <= range_end)
|
||||
match = false;
|
||||
}
|
||||
else if(regMatch(x, reg_less))
|
||||
{
|
||||
if(to_int(regReplace(x, reg_less, "$1"), INT_MAX) >= target)
|
||||
match = true;
|
||||
}
|
||||
else if(regMatch(x, reg_more))
|
||||
{
|
||||
if(to_int(regReplace(x, reg_more, "$1"), INT_MIN) <= target)
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node)
|
||||
{
|
||||
std::string target, ret_real_rule;
|
||||
static const std::string groupid_regex = R"(^!!(?:GROUPID|INSERT)=([\d\-+!,]+)(?:!!(.*))?$)", group_regex = R"(^!!(?:GROUP)=(.+?)(?:!!(.*))?$)";
|
||||
static const std::string type_regex = R"(^!!(?:TYPE)=(.+?)(?:!!(.*))?$)", port_regex = R"(^!!(?:PORT)=(.+?)(?:!!(.*))?$)", server_regex = R"(^!!(?:SERVER)=(.+?)(?:!!(.*))?$)";
|
||||
static const string_array types = {"", "SS", "SSR", "VMESS", "TROJAN", "SNELL", "HTTP", "HTTPS", "SOCKS5"};
|
||||
if(startsWith(rule, "!!GROUP="))
|
||||
{
|
||||
regGetMatch(rule, group_regex, 3, 0, &target, &ret_real_rule);
|
||||
real_rule = ret_real_rule;
|
||||
return regFind(node.Group, target);
|
||||
}
|
||||
else if(startsWith(rule, "!!GROUPID=") || startsWith(rule, "!!INSERT="))
|
||||
{
|
||||
int dir = startsWith(rule, "!!INSERT=") ? -1 : 1;
|
||||
regGetMatch(rule, groupid_regex, 3, 0, &target, &ret_real_rule);
|
||||
real_rule = ret_real_rule;
|
||||
return matchRange(target, dir * node.GroupId);
|
||||
}
|
||||
else if(startsWith(rule, "!!TYPE="))
|
||||
{
|
||||
regGetMatch(rule, type_regex, 3, 0, &target, &ret_real_rule);
|
||||
real_rule = ret_real_rule;
|
||||
if(node.Type == ProxyType::Unknow)
|
||||
return false;
|
||||
return regMatch(types[node.Type], target);
|
||||
}
|
||||
else if(startsWith(rule, "!!PORT="))
|
||||
{
|
||||
regGetMatch(rule, port_regex, 3, 0, &target, &ret_real_rule);
|
||||
real_rule = ret_real_rule;
|
||||
return matchRange(target, node.Port);
|
||||
}
|
||||
else if(startsWith(rule, "!!SERVER="))
|
||||
{
|
||||
regGetMatch(rule, server_regex, 3, 0, &target, &ret_real_rule);
|
||||
real_rule = ret_real_rule;
|
||||
return regFind(node.Hostname, target);
|
||||
}
|
||||
else
|
||||
real_rule = rule;
|
||||
return true;
|
||||
}
|
||||
30
src/generator/config/nodemanip.h
Normal file
30
src/generator/config/nodemanip.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef NODEMANIP_H_INCLUDED
|
||||
#define NODEMANIP_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <quickjspp.hpp>
|
||||
|
||||
#include "../../parser/config/proxy.h"
|
||||
#include "../../utils/map_extra.h"
|
||||
#include "../../utils/string.h"
|
||||
|
||||
struct parse_settings
|
||||
{
|
||||
std::string *proxy = nullptr;
|
||||
string_array *exclude_remarks = nullptr;
|
||||
string_array *include_remarks = nullptr;
|
||||
string_array *stream_rules = nullptr;
|
||||
string_array *time_rules = nullptr;
|
||||
std::string *sub_info = nullptr;
|
||||
bool authorized = false;
|
||||
string_icase_map *request_header = nullptr;
|
||||
qjs::Runtime *js_runtime = nullptr;
|
||||
qjs::Context *js_context = nullptr;
|
||||
};
|
||||
|
||||
int addNodes(std::string link, std::vector<Proxy> &allNodes, int groupID, parse_settings &parse_set);
|
||||
void filterNodes(std::vector<Proxy> &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID);
|
||||
bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node);
|
||||
|
||||
#endif // NODEMANIP_H_INCLUDED
|
||||
456
src/generator/config/ruleconvert.cpp
Normal file
456
src/generator/config/ruleconvert.cpp
Normal file
@@ -0,0 +1,456 @@
|
||||
#include <string>
|
||||
|
||||
#include "../../utils/logger.h"
|
||||
#include "../../utils/network.h"
|
||||
#include "../../utils/regexp.h"
|
||||
#include "../../utils/string.h"
|
||||
#include "subexport.h"
|
||||
|
||||
extern size_t gMaxAllowedRules;
|
||||
|
||||
/// rule type lists
|
||||
#define basic_types "DOMAIN", "DOMAIN-SUFFIX", "DOMAIN-KEYWORD", "IP-CIDR", "SRC-IP-CIDR", "GEOIP", "MATCH", "FINAL"
|
||||
string_array ClashRuleTypes = {basic_types, "IP-CIDR6", "SRC-PORT", "DST-PORT", "PROCESS-NAME"};
|
||||
string_array Surge2RuleTypes = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
string_array SurgeRuleTypes = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "AND", "OR", "NOT", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
string_array QuanXRuleTypes = {basic_types, "USER-AGENT", "HOST", "HOST-SUFFIX", "HOST-KEYWORD"};
|
||||
string_array SurfRuleTypes = {basic_types, "IP-CIDR6", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
|
||||
std::string convertRuleset(const std::string &content, int type)
|
||||
{
|
||||
/// Target: Surge type,pattern[,flag]
|
||||
/// Source: QuanX type,pattern[,group]
|
||||
/// Clash payload:\n - 'ipcidr/domain/classic(Surge-like)'
|
||||
|
||||
std::string output, strLine;
|
||||
|
||||
if(type == RULESET_SURGE)
|
||||
return content;
|
||||
|
||||
if(regFind(content, "^payload:\\r?\\n")) /// Clash
|
||||
{
|
||||
output = regReplace(regReplace(content, "payload:\\r?\\n", "", true), "\\s?^\\s*-\\s+('|\"?)(.*)\\1$", "\n$2", true);
|
||||
if(type == RULESET_CLASH_CLASSICAL) /// classical type
|
||||
return output;
|
||||
std::stringstream ss;
|
||||
ss << output;
|
||||
char delimiter = getLineBreak(output);
|
||||
output.clear();
|
||||
string_size pos, lineSize;
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
strLine = trim(strLine);
|
||||
lineSize = strLine.size();
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
strLine.erase(--lineSize);
|
||||
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
{
|
||||
pos = strLine.find("/");
|
||||
if(pos != strLine.npos) /// ipcidr
|
||||
{
|
||||
if(isIPv4(strLine.substr(0, pos)))
|
||||
output += "IP-CIDR,";
|
||||
else
|
||||
output += "IP-CIDR6,";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strLine[0] == '.' || (lineSize >= 2 && strLine[0] == '+' && strLine[1] == '.')) /// suffix
|
||||
{
|
||||
bool keyword_flag = false;
|
||||
while(endsWith(strLine, ".*"))
|
||||
{
|
||||
keyword_flag = true;
|
||||
strLine.erase(strLine.size() - 2);
|
||||
}
|
||||
output += "DOMAIN-";
|
||||
if(keyword_flag)
|
||||
output += "KEYWORD,";
|
||||
else
|
||||
output += "SUFFIX,";
|
||||
strLine.erase(0, 2 - (strLine[0] == '.'));
|
||||
}
|
||||
else
|
||||
output += "DOMAIN,";
|
||||
}
|
||||
}
|
||||
output += strLine;
|
||||
output += '\n';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
else /// QuanX
|
||||
{
|
||||
output = regReplace(regReplace(content, "^(?i:host)", "DOMAIN", true), "^(?i:ip6-cidr)", "IP-CIDR6", true); //translate type
|
||||
output = regReplace(output, "^((?i:DOMAIN(?:-(?:SUFFIX|KEYWORD))?|IP-CIDR6?|USER-AGENT),)\\s*?(\\S*?)(?:,(?!no-resolve).*?)(,no-resolve)?$", "\\U$1\\E$2${3:-}", true); //remove group
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name)
|
||||
{
|
||||
string_array allRules;
|
||||
std::string rule_group, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
const std::string field_name = new_field_name ? "rules" : "Rule";
|
||||
YAML::Node Rules;
|
||||
size_t total_rules = 0;
|
||||
|
||||
if(!overwrite_original_rules && base_rule[field_name].IsDefined())
|
||||
Rules = base_rule[field_name];
|
||||
|
||||
for(ruleset_content &x : ruleset_content_array)
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
if(startsWith(retrieved_rules, "[]"))
|
||||
{
|
||||
strLine = retrieved_rules.substr(2);
|
||||
if(startsWith(strLine, "FINAL"))
|
||||
strLine.replace(0, 5, "MATCH");
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
strLine = trimWhitespace(strLine, true, true); //remove whitespaces
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
//Rules.push_back(strLine);
|
||||
}
|
||||
}
|
||||
|
||||
for(std::string &x : allRules)
|
||||
{
|
||||
Rules.push_back(x);
|
||||
}
|
||||
|
||||
base_rule[field_name] = Rules;
|
||||
}
|
||||
|
||||
std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name)
|
||||
{
|
||||
std::string rule_group, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
const std::string field_name = new_field_name ? "rules" : "Rule";
|
||||
std::string output_content = "\n" + field_name + ":\n";
|
||||
size_t total_rules = 0;
|
||||
|
||||
if(!overwrite_original_rules && base_rule[field_name].IsDefined())
|
||||
{
|
||||
for(size_t i = 0; i < base_rule[field_name].size(); i++)
|
||||
output_content += " - " + safe_as<std::string>(base_rule[field_name][i]) + "\n";
|
||||
}
|
||||
base_rule.remove(field_name);
|
||||
|
||||
for(ruleset_content &x : ruleset_content_array)
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
if(startsWith(retrieved_rules, "[]"))
|
||||
{
|
||||
strLine = retrieved_rules.substr(2);
|
||||
if(startsWith(strLine, "FINAL"))
|
||||
strLine.replace(0, 5, "MATCH");
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
output_content += " - " + strLine + "\n";
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
strLine = trimWhitespace(strLine, true, true); //remove whitespaces
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](std::string type){ return startsWith(strLine, type); }))
|
||||
continue;
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
output_content += " - " + strLine + "\n";
|
||||
total_rules++;
|
||||
}
|
||||
}
|
||||
return output_content;
|
||||
}
|
||||
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix)
|
||||
{
|
||||
string_array allRules;
|
||||
std::string rule_group, rule_path, rule_path_typed, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
size_t total_rules = 0;
|
||||
|
||||
switch(surge_ver) //other version: -3 for Surfboard, -4 for Loon
|
||||
{
|
||||
case 0:
|
||||
base_rule.SetCurrentSection("RoutingRule"); //Mellow
|
||||
break;
|
||||
case -1:
|
||||
base_rule.SetCurrentSection("filter_local"); //Quantumult X
|
||||
break;
|
||||
case -2:
|
||||
base_rule.SetCurrentSection("TCP"); //Quantumult
|
||||
break;
|
||||
default:
|
||||
base_rule.SetCurrentSection("Rule");
|
||||
}
|
||||
|
||||
if(overwrite_original_rules)
|
||||
{
|
||||
base_rule.EraseSection();
|
||||
switch(surge_ver)
|
||||
{
|
||||
case -1:
|
||||
base_rule.EraseSection("filter_remote");
|
||||
break;
|
||||
case -4:
|
||||
base_rule.EraseSection("Remote Rule");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
|
||||
for(ruleset_content &x : ruleset_content_array)
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
rule_path = x.rule_path;
|
||||
rule_path_typed = x.rule_path_typed;
|
||||
if(rule_path.empty())
|
||||
{
|
||||
strLine = x.rule_content.get().substr(2);
|
||||
if(strLine == "MATCH")
|
||||
strLine = "FINAL";
|
||||
strLine += "," + rule_group;
|
||||
if(surge_ver == -1 || surge_ver == -2)
|
||||
{
|
||||
if(count_least(strLine, ',', 3) && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve")
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
else
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!startsWith(strLine, "AND") && !startsWith(strLine, "OR") && !startsWith(strLine, "NOT") && count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
}
|
||||
strLine = replaceAllDistinct(strLine, ",,", ",");
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(surge_ver == -1 && x.rule_type == RULESET_QUANX && isLink(rule_path))
|
||||
{
|
||||
strLine = rule_path + ", tag=" + rule_group + ", force-policy=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
if(fileExist(rule_path))
|
||||
{
|
||||
if(surge_ver > 2 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
if(x.update_interval)
|
||||
strLine += ",update-interval=" + std::to_string(x.update_interval);
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlSafeBase64Encode(rule_path_typed) + "&group=" + urlSafeBase64Encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -4 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
base_rule.Set("Remote Rule", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(isLink(rule_path))
|
||||
{
|
||||
if(surge_ver > 2)
|
||||
{
|
||||
if(x.rule_type != RULESET_SURGE)
|
||||
{
|
||||
if(remote_path_prefix.size())
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
strLine = "RULE-SET," + rule_path + "," + rule_group;
|
||||
|
||||
if(x.update_interval)
|
||||
strLine += ",update-interval=" + std::to_string(x.update_interval);
|
||||
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlSafeBase64Encode(rule_path_typed) + "&group=" + urlSafeBase64Encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -4)
|
||||
{
|
||||
strLine = rule_path + "," + rule_group;
|
||||
base_rule.Set("Remote Rule", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
if(gMaxAllowedRules && total_rules > gMaxAllowedRules)
|
||||
break;
|
||||
strLine = trimWhitespace(strLine, true, true);
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
|
||||
/// remove unsupported types
|
||||
switch(surge_ver)
|
||||
{
|
||||
case -2:
|
||||
if(startsWith(strLine, "IP-CIDR6"))
|
||||
continue;
|
||||
[[fallthrough]];
|
||||
case -1:
|
||||
if(!std::any_of(QuanXRuleTypes.begin(), QuanXRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
break;
|
||||
case -3:
|
||||
if(!std::any_of(SurfRuleTypes.begin(), SurfRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
if(surge_ver > 2)
|
||||
{
|
||||
if(!std::any_of(SurgeRuleTypes.begin(), SurgeRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!std::any_of(Surge2RuleTypes.begin(), Surge2RuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
|
||||
strLine += "," + rule_group;
|
||||
if(surge_ver == -1 || surge_ver == -2)
|
||||
{
|
||||
if(startsWith(strLine, "IP-CIDR6"))
|
||||
strLine.replace(0, 8, "IP6-CIDR");
|
||||
if(count_least(strLine, ',', 3) && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve")
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
else
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!startsWith(strLine, "AND") && !startsWith(strLine, "OR") && !startsWith(strLine, "NOT") && count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
}
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
total_rules++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::string &x : allRules)
|
||||
{
|
||||
base_rule.Set("{NONAME}", x);
|
||||
}
|
||||
}
|
||||
36
src/generator/config/ruleconvert.h
Normal file
36
src/generator/config/ruleconvert.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef RULECONVERT_H_INCLUDED
|
||||
#define RULECONVERT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "../../utils/ini_reader/ini_reader.h"
|
||||
|
||||
enum ruleset_type
|
||||
{
|
||||
RULESET_SURGE,
|
||||
RULESET_QUANX,
|
||||
RULESET_CLASH_DOMAIN,
|
||||
RULESET_CLASH_IPCIDR,
|
||||
RULESET_CLASH_CLASSICAL
|
||||
};
|
||||
|
||||
struct ruleset_content
|
||||
{
|
||||
std::string rule_group;
|
||||
std::string rule_path;
|
||||
std::string rule_path_typed;
|
||||
int rule_type = RULESET_SURGE;
|
||||
std::shared_future<std::string> rule_content;
|
||||
int update_interval = 0;
|
||||
};
|
||||
|
||||
std::string convertRuleset(const std::string &content, int type);
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
|
||||
|
||||
#endif // RULECONVERT_H_INCLUDED
|
||||
2087
src/generator/config/subexport.cpp
Normal file
2087
src/generator/config/subexport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
69
src/generator/config/subexport.h
Normal file
69
src/generator/config/subexport.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef SUBEXPORT_H_INCLUDED
|
||||
#define SUBEXPORT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <quickjspp.hpp>
|
||||
|
||||
#include "../../parser/config/proxy.h"
|
||||
#include "../../utils/ini_reader/ini_reader.h"
|
||||
#include "../../utils/string.h"
|
||||
#include "../../utils/yamlcpp_extra.h"
|
||||
#include "ruleconvert.h"
|
||||
|
||||
struct extra_settings
|
||||
{
|
||||
bool enable_rule_generator = true;
|
||||
bool overwrite_original_rules = true;
|
||||
string_array rename_array;
|
||||
string_array emoji_array;
|
||||
bool add_emoji = false;
|
||||
bool remove_emoji = false;
|
||||
bool append_proxy_type = false;
|
||||
bool nodelist = false;
|
||||
bool sort_flag = false;
|
||||
bool filter_deprecated = false;
|
||||
bool clash_new_field_name = false;
|
||||
bool clash_script = false;
|
||||
std::string surge_ssr_path;
|
||||
std::string managed_config_prefix;
|
||||
std::string quanx_dev_id;
|
||||
tribool udp = tribool();
|
||||
tribool tfo = tribool();
|
||||
tribool skip_cert_verify = tribool();
|
||||
tribool tls13 = tribool();
|
||||
bool clash_classical_ruleset = false;
|
||||
std::string sort_script = "";
|
||||
std::string clash_proxies_style = "flow";
|
||||
|
||||
qjs::Runtime *js_runtime = nullptr;
|
||||
qjs::Context *js_context = nullptr;
|
||||
extra_settings() {};
|
||||
extra_settings(const extra_settings&) = delete;
|
||||
extra_settings(extra_settings&&) = delete;
|
||||
~extra_settings()
|
||||
{
|
||||
delete js_context;
|
||||
delete js_runtime;
|
||||
}
|
||||
};
|
||||
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
|
||||
void preprocessNodes(std::vector<Proxy> &nodes, extra_settings &ext);
|
||||
|
||||
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, extra_settings &ext);
|
||||
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, extra_settings &ext);
|
||||
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, extra_settings &ext);
|
||||
std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
std::string proxyToSSSub(std::string &base_conf, std::vector<Proxy> &nodes, extra_settings &ext);
|
||||
std::string proxyToSingle(std::vector<Proxy> &nodes, int types, extra_settings &ext);
|
||||
std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
|
||||
std::string proxyToSSD(std::vector<Proxy> &nodes, std::string &group, std::string &userinfo, extra_settings &ext);
|
||||
|
||||
#endif // SUBEXPORT_H_INCLUDED
|
||||
@@ -5,14 +5,13 @@
|
||||
#include <jinja2cpp/template.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "../../handler/interfaces.h"
|
||||
#include "../../utils/regexp.h"
|
||||
#include "templates.h"
|
||||
#include "webget.h"
|
||||
#include "misc.h"
|
||||
|
||||
static inline void parse_json_pointer(nlohmann::json &json, const std::string &path, const std::string &value)
|
||||
{
|
||||
std::string pointer = "/" + replace_all_distinct(path, ".", "/");
|
||||
std::string pointer = "/" + replaceAllDistinct(path, ".", "/");
|
||||
json[nlohmann::json::json_pointer(pointer)] = value;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
#include <inja.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "yamlcpp_extra.h"
|
||||
#include "interfaces.h"
|
||||
#include "../../utils/yamlcpp_extra.h"
|
||||
#include "../../utils/urlencode.h"
|
||||
#include "../../utils/regexp.h"
|
||||
#include "../../handler/interfaces.h"
|
||||
#include "../../utils/logger.h"
|
||||
#include "../../utils/network.h"
|
||||
#include "templates.h"
|
||||
#include "logger.h"
|
||||
#include "misc.h"
|
||||
#include "webget.h"
|
||||
|
||||
extern std::string gManagedConfigPrefix;
|
||||
|
||||
@@ -68,19 +69,19 @@ int render_template(const std::string &content, const template_args &vars, std::
|
||||
m_callbacks.add_callback("UrlEncode", 1, [](inja::Arguments &args)
|
||||
{
|
||||
std::string data = args.at(0)->get<std::string>();
|
||||
return UrlEncode(data);
|
||||
return urlEncode(data);
|
||||
});
|
||||
m_callbacks.add_callback("UrlDecode", 1, [](inja::Arguments &args)
|
||||
{
|
||||
std::string data = args.at(0)->get<std::string>();
|
||||
return UrlDecode(data);
|
||||
return urlDecode(data);
|
||||
});
|
||||
m_callbacks.add_callback("trim_of", 2, [](inja::Arguments &args)
|
||||
{
|
||||
std::string data = args.at(0)->get<std::string>(), target = args.at(1)->get<std::string>();
|
||||
if(target.empty())
|
||||
return data;
|
||||
return trim_of(data, target[0]);
|
||||
return trimOf(data, target[0]);
|
||||
});
|
||||
m_callbacks.add_callback("trim", 1, [](inja::Arguments &args)
|
||||
{
|
||||
@@ -421,7 +422,7 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][yaml_key]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlSafeBase64Encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["path"] = "./providers/rule-provider_" + yaml_key + ".yaml";
|
||||
if(interval)
|
||||
base_rule["rule-providers"][yaml_key]["interval"] = interval;
|
||||
@@ -436,7 +437,7 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][yaml_key]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlSafeBase64Encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["path"] = "./providers/rule-provider_" + yaml_key + ".yaml";
|
||||
if(interval)
|
||||
base_rule["rule-providers"][yaml_key]["interval"] = interval;
|
||||
@@ -449,7 +450,7 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][yaml_key]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=6&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["url"] = remote_path_prefix + "/getruleset?type=6&url=" + urlSafeBase64Encode(url);
|
||||
base_rule["rule-providers"][yaml_key]["path"] = "./providers/rule-provider_" + yaml_key + ".yaml";
|
||||
if(interval)
|
||||
base_rule["rule-providers"][yaml_key]["interval"] = interval;
|
||||
@@ -4,15 +4,15 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "subexport.h"
|
||||
|
||||
typedef std::map<std::string, std::string> string_map;
|
||||
#include "../../generator/config/subexport.h"
|
||||
#include "../../utils/string.h"
|
||||
|
||||
struct template_args
|
||||
{
|
||||
string_map global_vars;
|
||||
string_map request_params;
|
||||
string_map local_vars;
|
||||
string_map node_list;
|
||||
};
|
||||
|
||||
int render_template(const std::string &content, const template_args &vars, std::string &output, const std::string &include_scope = "template");
|
||||
@@ -6,21 +6,30 @@
|
||||
#include <inja.hpp>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "yamlcpp_extra.h"
|
||||
#include "misc.h"
|
||||
#include "nodeinfo.h"
|
||||
#include "speedtestutil.h"
|
||||
#include "nodemanip.h"
|
||||
#include "ini_reader.h"
|
||||
#include "webget.h"
|
||||
#include "webserver.h"
|
||||
#include "subexport.h"
|
||||
#include "../generator/config/ruleconvert.h"
|
||||
#include "../generator/config/nodemanip.h"
|
||||
#include "../generator/config/ruleconvert.h"
|
||||
#include "../generator/config/subexport.h"
|
||||
#include "../generator/template/templates.h"
|
||||
#include "../script/cron.h"
|
||||
#include "../script/script_quickjs.h"
|
||||
#include "../server/webserver.h"
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/file_extra.h"
|
||||
#include "../utils/ini_reader/ini_reader.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/network.h"
|
||||
#include "../utils/regexp.h"
|
||||
#include "../utils/stl_extra.h"
|
||||
#include "../utils/string.h"
|
||||
#include "../utils/string_hash.h"
|
||||
#include "../utils/system.h"
|
||||
#include "../utils/system.h"
|
||||
#include "../utils/urlencode.h"
|
||||
#include "../utils/yamlcpp_extra.h"
|
||||
#include "multithread.h"
|
||||
#include "logger.h"
|
||||
#include "string_hash.h"
|
||||
#include "templates.h"
|
||||
#include "upload.h"
|
||||
#include "script_duktape.h"
|
||||
#include "webget.h"
|
||||
|
||||
//common settings
|
||||
std::string gPrefPath = "pref.ini", gDefaultExtConfig;
|
||||
@@ -73,6 +82,11 @@ int gCacheSubscription = 60, gCacheConfig = 300, gCacheRuleset = 21600;
|
||||
|
||||
//limits
|
||||
size_t gMaxAllowedRulesets = 64, gMaxAllowedRules = 32768;
|
||||
bool gScriptCleanContext = false;
|
||||
|
||||
//cron system
|
||||
bool gEnableCron = false;
|
||||
string_array gCronTasks;
|
||||
|
||||
string_array gRegexBlacklist = {"(.*)*"};
|
||||
|
||||
@@ -182,81 +196,9 @@ void matchUserAgent(const std::string &user_agent, std::string &target, tribool
|
||||
return;
|
||||
}
|
||||
|
||||
std::string convertRuleset(const std::string &content, int type)
|
||||
{
|
||||
/// Target: Surge type,pattern[,flag]
|
||||
/// Source: QuanX type,pattern[,group]
|
||||
/// Clash payload:\n - 'ipcidr/domain/classic(Surge-like)'
|
||||
|
||||
std::string output, strLine;
|
||||
|
||||
if(type == RULESET_SURGE)
|
||||
return content;
|
||||
|
||||
if(startsWith(content, "payload:")) /// Clash
|
||||
{
|
||||
output = regReplace(regReplace(content, "payload:\\r?\\n", "", true), "\\s?^\\s*-\\s+('?)(.*)\\1$", "\n$2", true);
|
||||
if(type == RULESET_CLASH_CLASSICAL) /// classical type
|
||||
return output;
|
||||
std::stringstream ss;
|
||||
ss << output;
|
||||
char delimiter = getLineBreak(output);
|
||||
output.clear();
|
||||
string_size pos, lineSize;
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
strLine = trim(strLine);
|
||||
lineSize = strLine.size();
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
strLine.erase(--lineSize);
|
||||
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
{
|
||||
pos = strLine.find("/");
|
||||
if(pos != strLine.npos) /// ipcidr
|
||||
{
|
||||
if(isIPv4(strLine.substr(0, pos)))
|
||||
output += "IP-CIDR,";
|
||||
else
|
||||
output += "IP-CIDR6,";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strLine[0] == '.' || (lineSize >= 2 && strLine[0] == '+' && strLine[1] == '.')) /// suffix
|
||||
{
|
||||
bool keyword_flag = false;
|
||||
while(endsWith(strLine, ".*"))
|
||||
{
|
||||
keyword_flag = true;
|
||||
strLine.erase(strLine.size() - 2);
|
||||
}
|
||||
output += "DOMAIN-";
|
||||
if(keyword_flag)
|
||||
output += "KEYWORD,";
|
||||
else
|
||||
output += "SUFFIX,";
|
||||
strLine.erase(0, 2 - (strLine[0] == '.'));
|
||||
}
|
||||
else
|
||||
output += "DOMAIN,";
|
||||
}
|
||||
}
|
||||
output += strLine;
|
||||
output += '\n';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
else /// QuanX
|
||||
{
|
||||
output = regReplace(regReplace(content, "^(?i:host)", "DOMAIN", true), "^(?i:ip6-cidr)", "IP-CIDR6", true); //translate type
|
||||
output = regReplace(output, "^((?i:DOMAIN(?:-(?:SUFFIX|KEYWORD))?|IP-CIDR6?|USER-AGENT),)\\s*?(\\S*?)(?:,(?!no-resolve).*?)(,no-resolve)?$", "\\U$1\\E$2${3:-}", true); //remove group
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getConvertedRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string url = UrlDecode(getUrlArg(request.argument, "url")), type = getUrlArg(request.argument, "type");
|
||||
std::string url = urlDecode(getUrlArg(request.argument, "url")), type = getUrlArg(request.argument, "type");
|
||||
return convertRuleset(fetchFile(url, parseProxy(gProxyRuleset), gCacheRuleset), to_int(type));
|
||||
}
|
||||
|
||||
@@ -265,7 +207,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
/// type: 1 for Surge, 2 for Quantumult X, 3 for Clash domain rule-provider, 4 for Clash ipcidr rule-provider, 5 for Surge DOMAIN-SET, 6 for Clash classical ruleset
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlsafe_base64_decode(getUrlArg(argument, "group"));
|
||||
std::string url = urlSafeBase64Decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlSafeBase64Decode(getUrlArg(argument, "group"));
|
||||
std::string output_content, dummy;
|
||||
int type_int = to_int(type, 0);
|
||||
|
||||
@@ -328,6 +270,11 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
switch(type_int)
|
||||
{
|
||||
case 2:
|
||||
@@ -541,8 +488,8 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout;
|
||||
object["name"] >>= name;
|
||||
object["type"] >>= type;
|
||||
tempArray.emplace_back(std::move(name));
|
||||
tempArray.emplace_back(type);
|
||||
tempArray.emplace_back("name=" + name);
|
||||
tempArray.emplace_back("type=" + type);
|
||||
object["url"] >>= url;
|
||||
object["interval"] >>= interval;
|
||||
object["tolerance"] >>= tolerance;
|
||||
@@ -562,13 +509,13 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
default:
|
||||
if(tempArray.size() < 3)
|
||||
continue;
|
||||
tempArray.emplace_back(std::move(url));
|
||||
tempArray.emplace_back(interval + "," + timeout + "," + tolerance);
|
||||
tempArray.emplace_back("url=" + url);
|
||||
tempArray.emplace_back("interval=" + interval + ",timeout=" + timeout + ",tolerance=" + tolerance);
|
||||
}
|
||||
|
||||
strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) -> std::string
|
||||
{
|
||||
return std::move(a) + "`" + std::move(b);
|
||||
return std::move(a) + "," + std::move(b);
|
||||
});
|
||||
dest.emplace_back(std::move(strLine));
|
||||
}
|
||||
@@ -693,8 +640,6 @@ void readYAMLConf(YAML::Node &node)
|
||||
if(section["include_remarks"].IsSequence())
|
||||
section["include_remarks"] >> gIncludeRemarks;
|
||||
gFilterScript = safe_as<bool>(section["enable_filter"]) ? safe_as<std::string>(section["filter_script"]) : "";
|
||||
if(startsWith(gFilterScript, "path:"))
|
||||
gFilterScript = fileGet(gFilterScript.substr(5), false);
|
||||
section["base_path"] >> gBasePath;
|
||||
section["clash_rule_base"] >> gClashBase;
|
||||
section["surge_rule_base"] >> gSurgeBase;
|
||||
@@ -836,6 +781,30 @@ void readYAMLConf(YAML::Node &node)
|
||||
}
|
||||
}
|
||||
|
||||
if(node["tasks"].IsSequence())
|
||||
{
|
||||
gCronTasks.clear();
|
||||
for(size_t i = 0; i < node["tasks"].size(); i++)
|
||||
{
|
||||
std::string name, exp, path, timeout;
|
||||
node["tasks"][i]["import"] >> name;
|
||||
if(name.size())
|
||||
{
|
||||
gCronTasks.emplace_back("!!import:" + name);
|
||||
continue;
|
||||
}
|
||||
node["tasks"][i]["name"] >> name;
|
||||
node["tasks"][i]["cronexp"] >> exp;
|
||||
node["tasks"][i]["path"] >> path;
|
||||
node["tasks"][i]["timeout"] >> timeout;
|
||||
strLine = name + "`" + exp + "`" + path + "`" + timeout;
|
||||
gCronTasks.push_back(std::move(strLine));
|
||||
}
|
||||
importItems(gCronTasks, false);
|
||||
gEnableCron = !gCronTasks.empty();
|
||||
refresh_schedule();
|
||||
}
|
||||
|
||||
if(node["server"].IsDefined())
|
||||
{
|
||||
node["server"]["listen"] >> gListenAddress;
|
||||
@@ -891,6 +860,7 @@ void readYAMLConf(YAML::Node &node)
|
||||
else
|
||||
gCacheSubscription = gCacheConfig = gCacheRuleset = 0; //disable cache
|
||||
}
|
||||
node["advanced"]["script_clean_context"] >> gScriptCleanContext;
|
||||
node["advanced"]["async_fetch_ruleset"] >> gAsyncFetchRuleset;
|
||||
node["advanced"]["skip_failed_links"] >> gSkipFailedLinks;
|
||||
}
|
||||
@@ -1090,6 +1060,16 @@ void readConf()
|
||||
append_redirect(x.first, x.second);
|
||||
}
|
||||
|
||||
if(ini.SectionExist("tasks"))
|
||||
{
|
||||
gCronTasks.clear();
|
||||
ini.EnterSection("tasks");
|
||||
ini.GetAll("task", gCronTasks);
|
||||
importItems(gCronTasks, false);
|
||||
gEnableCron = !gCronTasks.empty();
|
||||
refresh_schedule();
|
||||
}
|
||||
|
||||
ini.EnterSection("server");
|
||||
ini.GetIfExist("listen", gListenAddress);
|
||||
ini.GetIntIfExist("port", gListenPort);
|
||||
@@ -1145,6 +1125,7 @@ void readConf()
|
||||
gServeCacheOnFetchFail = false;
|
||||
}
|
||||
}
|
||||
ini.GetBoolIfExist("script_clean_context", gScriptCleanContext);
|
||||
ini.GetBoolIfExist("async_fetch_ruleset", gAsyncFetchRuleset);
|
||||
ini.GetBoolIfExist("skip_failed_links", gSkipFailedLinks);
|
||||
|
||||
@@ -1357,12 +1338,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
readConf();
|
||||
|
||||
/// string values
|
||||
std::string argUrl = UrlDecode(getUrlArg(argument, "url"));
|
||||
std::string argGroupName = UrlDecode(getUrlArg(argument, "group")), argUploadPath = getUrlArg(argument, "upload_path");
|
||||
std::string argIncludeRemark = UrlDecode(getUrlArg(argument, "include")), argExcludeRemark = UrlDecode(getUrlArg(argument, "exclude"));
|
||||
std::string argCustomGroups = urlsafe_base64_decode(getUrlArg(argument, "groups")), argCustomRulesets = urlsafe_base64_decode(getUrlArg(argument, "ruleset")), argExternalConfig = UrlDecode(getUrlArg(argument, "config"));
|
||||
std::string argUrl = urlDecode(getUrlArg(argument, "url"));
|
||||
std::string argGroupName = urlDecode(getUrlArg(argument, "group")), argUploadPath = getUrlArg(argument, "upload_path");
|
||||
std::string argIncludeRemark = urlDecode(getUrlArg(argument, "include")), argExcludeRemark = urlDecode(getUrlArg(argument, "exclude"));
|
||||
std::string argCustomGroups = urlSafeBase64Decode(getUrlArg(argument, "groups")), argCustomRulesets = urlSafeBase64Decode(getUrlArg(argument, "ruleset")), argExternalConfig = urlDecode(getUrlArg(argument, "config"));
|
||||
std::string argDeviceID = getUrlArg(argument, "dev_id"), argFilename = getUrlArg(argument, "filename"), argUpdateInterval = getUrlArg(argument, "interval"), argUpdateStrict = getUrlArg(argument, "strict");
|
||||
std::string argRenames = UrlDecode(getUrlArg(argument, "rename")), argFilterScript = UrlDecode(getUrlArg(argument, "filter_script"));
|
||||
std::string argRenames = urlDecode(getUrlArg(argument, "rename")), argFilterScript = urlDecode(getUrlArg(argument, "filter_script"));
|
||||
|
||||
/// switches with default value
|
||||
tribool argUpload = getUrlArg(argument, "upload"), argEmoji = getUrlArg(argument, "emoji"), argAddEmoji = getUrlArg(argument, "add_emoji"), argRemoveEmoji = getUrlArg(argument, "remove_emoji");
|
||||
@@ -1429,10 +1410,10 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
ext.clash_proxies_style = gClashProxiesStyle;
|
||||
|
||||
/// read preference from argument, assign global var if not in argument
|
||||
ext.tfo.parse(argTFO).parse(gTFO);
|
||||
ext.udp.parse(argUDP).parse(gUDP);
|
||||
ext.skip_cert_verify.parse(argSkipCertVerify).parse(gSkipCertVerify);
|
||||
ext.tls13.parse(argTLS13).parse(gTLS13);
|
||||
ext.tfo.define(argTFO).define(gTFO);
|
||||
ext.udp.define(argUDP).define(gUDP);
|
||||
ext.skip_cert_verify.define(argSkipCertVerify).define(gSkipCertVerify);
|
||||
ext.tls13.define(argTLS13).define(gTLS13);
|
||||
|
||||
ext.sort_flag = argSort.get(gEnableSort);
|
||||
argUseSortScript.define(gSortScript.size() != 0);
|
||||
@@ -1455,7 +1436,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
if(!argExpandRulesets)
|
||||
ext.managed_config_prefix = gManagedConfigPrefix;
|
||||
|
||||
//load external configuration
|
||||
/// load external configuration
|
||||
if(argExternalConfig.empty())
|
||||
argExternalConfig = gDefaultExtConfig;
|
||||
if(argExternalConfig.size())
|
||||
@@ -1498,17 +1479,16 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
argAddEmoji.define(extconf.add_emoji);
|
||||
argRemoveEmoji.define(extconf.remove_old_emoji);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!lSimpleSubscription)
|
||||
{
|
||||
//loading custom groups
|
||||
/// loading custom groups
|
||||
if(argCustomGroups.size() && !ext.nodelist)
|
||||
lCustomProxyGroups = split(argCustomGroups, "@");
|
||||
|
||||
//loading custom rulesets
|
||||
/// loading custom rulesets
|
||||
if(argCustomRulesets.size() && !ext.nodelist)
|
||||
lCustomRulesets = split(argCustomRulesets, "@");
|
||||
}
|
||||
@@ -1539,19 +1519,44 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
else if(ext.rename_array.empty())
|
||||
ext.rename_array = safe_get_renames();
|
||||
|
||||
//check custom include/exclude settings
|
||||
/// check custom include/exclude settings
|
||||
if(argIncludeRemark.size() && regValid(argIncludeRemark))
|
||||
lIncludeRemarks = string_array{argIncludeRemark};
|
||||
if(argExcludeRemark.size() && regValid(argExcludeRemark))
|
||||
lExcludeRemarks = string_array{argExcludeRemark};
|
||||
|
||||
/// initialize script runtime
|
||||
if(authorized)
|
||||
{
|
||||
ext.js_runtime = new qjs::Runtime();
|
||||
script_runtime_init(*ext.js_runtime);
|
||||
if(!gScriptCleanContext)
|
||||
{
|
||||
ext.js_context = new qjs::Context(*ext.js_runtime);
|
||||
script_context_init(*ext.js_context);
|
||||
}
|
||||
}
|
||||
|
||||
//start parsing urls
|
||||
string_array stream_temp = safe_get_streams(), time_temp = safe_get_times();
|
||||
|
||||
//loading urls
|
||||
string_array urls;
|
||||
std::vector<nodeInfo> nodes, insert_nodes;
|
||||
std::vector<Proxy> nodes, insert_nodes;
|
||||
int groupID = 0;
|
||||
|
||||
parse_settings parse_set;
|
||||
parse_set.proxy = &proxy;
|
||||
parse_set.exclude_remarks = &lExcludeRemarks;
|
||||
parse_set.include_remarks = &lIncludeRemarks;
|
||||
parse_set.stream_rules = &stream_temp;
|
||||
parse_set.time_rules = &time_temp;
|
||||
parse_set.sub_info = &subInfo;
|
||||
parse_set.authorized = authorized;
|
||||
parse_set.request_header = &request.headers;
|
||||
parse_set.js_runtime = ext.js_runtime;
|
||||
parse_set.js_context = ext.js_context;
|
||||
|
||||
if(gInsertUrls.size() && argEnableInsert)
|
||||
{
|
||||
groupID = -1;
|
||||
@@ -1561,7 +1566,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
x = regTrim(x);
|
||||
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
|
||||
if(addNodes(x, insert_nodes, groupID, proxy, lExcludeRemarks, lIncludeRemarks, stream_temp, time_temp, subInfo, authorized, request.headers) == -1)
|
||||
if(addNodes(x, insert_nodes, groupID, parse_set) == -1)
|
||||
{
|
||||
if(gSkipFailedLinks)
|
||||
writeLog(0, "The following link doesn't contain any valid node info: " + x, LOG_LEVEL_WARNING);
|
||||
@@ -1582,7 +1587,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
x = regTrim(x);
|
||||
//std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
|
||||
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
|
||||
if(addNodes(x, nodes, groupID, proxy, lExcludeRemarks, lIncludeRemarks, stream_temp, time_temp, subInfo, authorized, request.headers) == -1)
|
||||
if(addNodes(x, nodes, groupID, parse_set) == -1)
|
||||
{
|
||||
if(gSkipFailedLinks)
|
||||
writeLog(0, "The following link doesn't contain any valid node info: " + x, LOG_LEVEL_WARNING);
|
||||
@@ -1618,16 +1623,17 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
if(startsWith(filterScript, "path:"))
|
||||
filterScript = fileGet(filterScript.substr(5), false);
|
||||
/*
|
||||
duk_context *ctx = duktape_init();
|
||||
if(ctx)
|
||||
{
|
||||
defer(duk_destroy_heap(ctx);)
|
||||
if(duktape_peval(ctx, filterScript) == 0)
|
||||
{
|
||||
auto filter = [&](const nodeInfo &x)
|
||||
auto filter = [&](const Proxy &x)
|
||||
{
|
||||
duk_get_global_string(ctx, "filter");
|
||||
duktape_push_nodeinfo(ctx, x);
|
||||
duktape_push_Proxy(ctx, x);
|
||||
duk_pcall(ctx, 1);
|
||||
return !duktape_get_res_bool(ctx);
|
||||
};
|
||||
@@ -1639,12 +1645,25 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
duk_pop(ctx); /// pop err
|
||||
}
|
||||
}
|
||||
*/
|
||||
script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx){
|
||||
try
|
||||
{
|
||||
ctx.eval(filterScript);
|
||||
auto filter = (std::function<bool(const Proxy&)>) ctx.eval("filter");
|
||||
nodes.erase(std::remove_if(nodes.begin(), nodes.end(), filter), nodes.end());
|
||||
}
|
||||
catch(qjs::exception)
|
||||
{
|
||||
script_print_stack(ctx);
|
||||
}
|
||||
}, gScriptCleanContext);
|
||||
}
|
||||
|
||||
//check custom group name
|
||||
if(argGroupName.size())
|
||||
for(nodeInfo &x : nodes)
|
||||
x.group = argGroupName;
|
||||
for(Proxy &x : nodes)
|
||||
x.Group = argGroupName;
|
||||
|
||||
if(subInfo.size() && argAppendUserinfo.get(gAppendUserinfo))
|
||||
response.headers.emplace("Subscription-UserInfo", subInfo);
|
||||
@@ -1652,9 +1671,23 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
//do pre-process now
|
||||
preprocessNodes(nodes, ext);
|
||||
|
||||
/*
|
||||
//insert node info to template
|
||||
int index = 0;
|
||||
std::string template_node_prefix;
|
||||
for(Proxy &x : nodes)
|
||||
{
|
||||
template_node_prefix = std::to_string(index) + ".";
|
||||
tpl_args.node_list[template_node_prefix + "remarks"] = x.remarks;
|
||||
tpl_args.node_list[template_node_prefix + "group"] = x.Group;
|
||||
tpl_args.node_list[template_node_prefix + "groupid"] = std::to_string(x.GroupId);
|
||||
index++;
|
||||
}
|
||||
*/
|
||||
|
||||
string_array dummy_group;
|
||||
std::vector<ruleset_content> dummy_ruleset;
|
||||
std::string managed_url = base64_decode(UrlDecode(getUrlArg(argument, "profile_data")));
|
||||
std::string managed_url = base64Decode(urlDecode(getUrlArg(argument, "profile_data")));
|
||||
if(managed_url.empty())
|
||||
managed_url = gManagedConfigPrefix + "/sub?" + argument;
|
||||
|
||||
@@ -1669,7 +1702,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
if(ext.nodelist)
|
||||
{
|
||||
YAML::Node yamlnode;
|
||||
netchToClash(nodes, yamlnode, dummy_group, argTarget == "clashr", ext);
|
||||
proxyToClash(nodes, yamlnode, dummy_group, argTarget == "clashr", ext);
|
||||
output_content = YAML::Dump(yamlnode);
|
||||
}
|
||||
else
|
||||
@@ -1679,7 +1712,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
output_content = netchToClash(nodes, base_content, lRulesetContent, lCustomProxyGroups, argTarget == "clashr", ext);
|
||||
output_content = proxyToClash(nodes, base_content, lRulesetContent, lCustomProxyGroups, argTarget == "clashr", ext);
|
||||
}
|
||||
|
||||
if(argUpload)
|
||||
@@ -1691,7 +1724,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
if(ext.nodelist)
|
||||
{
|
||||
output_content = netchToSurge(nodes, base_content, dummy_ruleset, dummy_group, intSurgeVer, ext);
|
||||
output_content = proxyToSurge(nodes, base_content, dummy_ruleset, dummy_group, intSurgeVer, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("surge" + argSurgeVer + "list", argUploadPath, output_content, true);
|
||||
@@ -1703,7 +1736,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
output_content = netchToSurge(nodes, base_content, lRulesetContent, lCustomProxyGroups, intSurgeVer, ext);
|
||||
output_content = proxyToSurge(nodes, base_content, lRulesetContent, lCustomProxyGroups, intSurgeVer, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("surge" + argSurgeVer, argUploadPath, output_content, true);
|
||||
@@ -1721,7 +1754,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
output_content = netchToSurge(nodes, base_content, lRulesetContent, lCustomProxyGroups, -3, ext);
|
||||
output_content = proxyToSurge(nodes, base_content, lRulesetContent, lCustomProxyGroups, -3, ext);
|
||||
if(argUpload)
|
||||
uploadGist("surfboard", argUploadPath, output_content, true);
|
||||
|
||||
@@ -1737,7 +1770,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
output_content = netchToMellow(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
output_content = proxyToMellow(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("mellow", argUploadPath, output_content, true);
|
||||
@@ -1750,37 +1783,37 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
output_content = netchToSSSub(base_content, nodes, ext);
|
||||
output_content = proxyToSSSub(base_content, nodes, ext);
|
||||
if(argUpload)
|
||||
uploadGist("sssub", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "ss"_hash:
|
||||
writeLog(0, "Generate target: SS", LOG_LEVEL_INFO);
|
||||
output_content = netchToSingle(nodes, 1, ext);
|
||||
output_content = proxyToSingle(nodes, 1, ext);
|
||||
if(argUpload)
|
||||
uploadGist("ss", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "ssr"_hash:
|
||||
writeLog(0, "Generate target: SSR", LOG_LEVEL_INFO);
|
||||
output_content = netchToSingle(nodes, 2, ext);
|
||||
output_content = proxyToSingle(nodes, 2, ext);
|
||||
if(argUpload)
|
||||
uploadGist("ssr", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "v2ray"_hash:
|
||||
writeLog(0, "Generate target: v2rayN", LOG_LEVEL_INFO);
|
||||
output_content = netchToSingle(nodes, 4, ext);
|
||||
output_content = proxyToSingle(nodes, 4, ext);
|
||||
if(argUpload)
|
||||
uploadGist("v2ray", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "trojan"_hash:
|
||||
writeLog(0, "Generate target: Trojan", LOG_LEVEL_INFO);
|
||||
output_content = netchToSingle(nodes, 8, ext);
|
||||
output_content = proxyToSingle(nodes, 8, ext);
|
||||
if(argUpload)
|
||||
uploadGist("trojan", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "mixed"_hash:
|
||||
writeLog(0, "Generate target: Standard Subscription", LOG_LEVEL_INFO);
|
||||
output_content = netchToSingle(nodes, 15, ext);
|
||||
output_content = proxyToSingle(nodes, 15, ext);
|
||||
if(argUpload)
|
||||
uploadGist("sub", argUploadPath, output_content, false);
|
||||
break;
|
||||
@@ -1795,7 +1828,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
output_content = netchToQuan(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
output_content = proxyToQuan(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("quan", argUploadPath, output_content, false);
|
||||
@@ -1811,7 +1844,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
output_content = netchToQuanX(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
output_content = proxyToQuanX(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("quanx", argUploadPath, output_content, false);
|
||||
@@ -1827,14 +1860,14 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
output_content = netchToLoon(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
output_content = proxyToLoon(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("loon", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "ssd"_hash:
|
||||
writeLog(0, "Generate target: SSD", LOG_LEVEL_INFO);
|
||||
output_content = netchToSSD(nodes, argGroupName, subInfo, ext);
|
||||
output_content = proxyToSSD(nodes, argGroupName, subInfo, ext);
|
||||
if(argUpload)
|
||||
uploadGist("ssd", argUploadPath, output_content, false);
|
||||
break;
|
||||
@@ -1865,7 +1898,7 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return "Please insert your subscription link instead of clicking the default link.";
|
||||
}
|
||||
request.argument = "target=clashr&url=" + UrlEncode(url);
|
||||
request.argument = "target=clashr&url=" + urlEncode(url);
|
||||
return subconverter(request, response);
|
||||
}
|
||||
|
||||
@@ -1876,7 +1909,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
INIReader ini;
|
||||
string_array dummy_str_array;
|
||||
std::vector<nodeInfo> nodes;
|
||||
std::vector<Proxy> nodes;
|
||||
std::string base_content, url = argument.size() <= 5 ? "" : argument.substr(5);
|
||||
const std::string proxygroup_name = gClashUseNewField ? "proxy-groups" : "Proxy Group", rule_name = gClashUseNewField ? "rules" : "Rule";
|
||||
|
||||
@@ -1968,11 +2001,17 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
eraseElements(dummy_str_array);
|
||||
|
||||
std::string subInfo;
|
||||
parse_settings parse_set;
|
||||
parse_set.proxy = &proxy;
|
||||
parse_set.exclude_remarks = parse_set.include_remarks = parse_set.stream_rules = parse_set.time_rules = &dummy_str_array;
|
||||
parse_set.request_header = &request.headers;
|
||||
parse_set.sub_info = &subInfo;
|
||||
parse_set.authorized = !gAPIMode;
|
||||
for(std::string &x : links)
|
||||
{
|
||||
//std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
|
||||
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
|
||||
if(addNodes(x, nodes, 0, proxy, dummy_str_array, dummy_str_array, dummy_str_array, dummy_str_array, subInfo, !gAPIMode, request.headers) == -1)
|
||||
if(addNodes(x, nodes, 0, parse_set) == -1)
|
||||
{
|
||||
if(gSkipFailedLinks)
|
||||
writeLog(0, "The following link doesn't contain any valid node info: " + x, LOG_LEVEL_WARNING);
|
||||
@@ -1991,10 +2030,17 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
return "No nodes were found!";
|
||||
}
|
||||
|
||||
extra_settings ext = {true, true, dummy_str_array, dummy_str_array, false, false, false, false, gEnableSort, gFilterDeprecated, gClashUseNewField, false, "", "", "", gUDP, gTFO, gSkipCertVerify, gTLS13};
|
||||
extra_settings ext;
|
||||
ext.sort_flag = gEnableSort;
|
||||
ext.filter_deprecated = gFilterDeprecated;
|
||||
ext.clash_new_field_name = gClashUseNewField;
|
||||
ext.udp = gUDP;
|
||||
ext.tfo = gTFO;
|
||||
ext.skip_cert_verify = gSkipCertVerify;
|
||||
ext.tls13 = gTLS13;
|
||||
ext.clash_proxies_style = gClashProxiesStyle;
|
||||
|
||||
netchToClash(nodes, clash, dummy_str_array, false, ext);
|
||||
proxyToClash(nodes, clash, dummy_str_array, false, ext);
|
||||
|
||||
section.clear();
|
||||
ini.GetItems("Proxy", section);
|
||||
@@ -2080,7 +2126,7 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string name = UrlDecode(getUrlArg(argument, "name")), token = UrlDecode(getUrlArg(argument, "token"));
|
||||
std::string name = urlDecode(getUrlArg(argument, "name")), token = urlDecode(getUrlArg(argument, "token"));
|
||||
string_array profiles = split(name, "|");
|
||||
name = profiles[0];
|
||||
if(token.empty() || name.empty())
|
||||
@@ -2088,7 +2134,16 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 403;
|
||||
return "Forbidden";
|
||||
}
|
||||
if(!fileExist(name))
|
||||
std::string profile_content;
|
||||
/*if(vfs::vfs_exist(name))
|
||||
{
|
||||
profile_content = vfs::vfs_get(name);
|
||||
}
|
||||
else */if(fileExist(name))
|
||||
{
|
||||
profile_content = fileGet(name, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
*status_code = 404;
|
||||
return "Profile not found";
|
||||
@@ -2096,7 +2151,7 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
//std::cerr<<"Trying to load profile '" + name + "'.\n";
|
||||
writeLog(0, "Trying to load profile '" + name + "'.", LOG_LEVEL_INFO);
|
||||
INIReader ini;
|
||||
if(ini.ParseFile(name) != INIREADER_EXCEPTION_NONE && !ini.SectionExist("Profile"))
|
||||
if(ini.Parse(profile_content) != INIREADER_EXCEPTION_NONE && !ini.SectionExist("Profile"))
|
||||
{
|
||||
//std::cerr<<"Load profile failed! Reason: "<<ini.GetLastError()<<"\n";
|
||||
writeLog(0, "Load profile failed! Reason: " + ini.GetLastError(), LOG_LEVEL_ERROR);
|
||||
@@ -2168,8 +2223,8 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
}
|
||||
|
||||
contents.emplace("token", token);
|
||||
contents.emplace("profile_data", base64_encode(gManagedConfigPrefix + "/getprofile?" + argument));
|
||||
std::string query = std::accumulate(contents.begin(), contents.end(), std::string(), [](const std::string &x, auto y){ return x + y.first + "=" + UrlEncode(y.second) + "&"; });
|
||||
contents.emplace("profile_data", base64Encode(gManagedConfigPrefix + "/getprofile?" + argument));
|
||||
std::string query = std::accumulate(contents.begin(), contents.end(), std::string(), [](const std::string &x, auto y){ return x + y.first + "=" + urlEncode(y.second) + "&"; });
|
||||
query += argument;
|
||||
request.argument = query;
|
||||
return subconverter(request, response);
|
||||
@@ -2179,7 +2234,7 @@ std::string getScript(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string url = urlSafeBase64Decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string output_content;
|
||||
|
||||
std::string proxy = parseProxy(gProxyConfig);
|
||||
@@ -2204,7 +2259,7 @@ std::string getRewriteRemote(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string url = urlSafeBase64Decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string output_content;
|
||||
|
||||
std::string proxy = parseProxy(gProxyConfig);
|
||||
@@ -2232,7 +2287,7 @@ std::string getRewriteRemote(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
if(!strLine.empty() && regMatch(strLine, pattern))
|
||||
{
|
||||
url = gManagedConfigPrefix + "/qx-script?id=" + dev_id + "&url=" + urlsafe_base64_encode(regReplace(strLine, pattern, "$2"));
|
||||
url = gManagedConfigPrefix + "/qx-script?id=" + dev_id + "&url=" + urlSafeBase64Encode(regReplace(strLine, pattern, "$2"));
|
||||
strLine = regReplace(strLine, pattern, "$1") + url;
|
||||
}
|
||||
output_content.append(strLine + "\n");
|
||||
@@ -2285,7 +2340,7 @@ std::string jinja2_webGet(const std::string &url)
|
||||
return webGet(url, proxy, gCacheConfig);
|
||||
}
|
||||
|
||||
static inline std::string intToStream(unsigned long long stream)
|
||||
inline std::string intToStream(unsigned long long stream)
|
||||
{
|
||||
char chrs[16] = {}, units[6] = {' ', 'K', 'M', 'G', 'T', 'P'};
|
||||
double streamval = stream;
|
||||
@@ -2303,8 +2358,8 @@ static inline std::string intToStream(unsigned long long stream)
|
||||
|
||||
std::string subInfoToMessage(std::string subinfo)
|
||||
{
|
||||
typedef unsigned long long ull;
|
||||
subinfo = replace_all_distinct(subinfo, "; ", "&");
|
||||
using ull = unsigned long long;
|
||||
subinfo = replaceAllDistinct(subinfo, "; ", "&");
|
||||
std::string retdata, useddata = "N/A", totaldata = "N/A", expirydata = "N/A";
|
||||
std::string upload = getUrlArg(subinfo, "upload"), download = getUrlArg(subinfo, "download"), total = getUrlArg(subinfo, "total"), expire = getUrlArg(subinfo, "expire");
|
||||
ull used = to_number<ull>(upload, 0) + to_number<ull>(download, 0), tot = to_number<ull>(total, 0);
|
||||
@@ -2396,7 +2451,7 @@ int simpleGenerator()
|
||||
if(ini.ItemExist("profile"))
|
||||
{
|
||||
profile = ini.Get("profile");
|
||||
request.argument = "name=" + UrlEncode(profile) + "&token=" + gAccessToken + "&expand=true";
|
||||
request.argument = "name=" + urlEncode(profile) + "&token=" + gAccessToken + "&expand=true";
|
||||
content = getProfile(request, response);
|
||||
}
|
||||
else
|
||||
@@ -2422,7 +2477,7 @@ int simpleGenerator()
|
||||
{
|
||||
if(y.first == "path")
|
||||
continue;
|
||||
arguments += y.first + "=" + UrlEncode(y.second) + "&";
|
||||
arguments += y.first + "=" + urlEncode(y.second) + "&";
|
||||
}
|
||||
arguments.erase(arguments.size() - 1);
|
||||
request.argument = arguments;
|
||||
@@ -2454,7 +2509,7 @@ std::string renderTemplate(RESPONSE_CALLBACK_ARGS)
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string path = UrlDecode(getUrlArg(argument, "path"));
|
||||
std::string path = urlDecode(getUrlArg(argument, "path"));
|
||||
writeLog(0, "Trying to render template '" + path + "'...", LOG_LEVEL_INFO);
|
||||
|
||||
if(!startsWith(path, gTemplatePath) || !fileExist(path))
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <map>
|
||||
#include <inja.hpp>
|
||||
|
||||
#include "subexport.h"
|
||||
#include "webserver.h"
|
||||
#include "../generator/config/subexport.h"
|
||||
#include "../server/webserver.h"
|
||||
|
||||
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca);
|
||||
void readConf();
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <thread>
|
||||
#include "webget.h"
|
||||
#include "multithread.h"
|
||||
//#include "vfs.h"
|
||||
|
||||
#include "../utils/network.h"
|
||||
|
||||
//safety lock for multi-thread
|
||||
std::mutex on_emoji, on_rename, on_stream, on_time;
|
||||
@@ -60,7 +63,9 @@ void safe_set_times(string_array &data)
|
||||
std::shared_future<std::string> fetchFileAsync(const std::string &path, const std::string &proxy, int cache_ttl, bool async)
|
||||
{
|
||||
std::shared_future<std::string> retVal;
|
||||
if(fileExist(path, true))
|
||||
/*if(vfs::vfs_exist(path))
|
||||
retVal = std::async(std::launch::async, [path](){return vfs::vfs_get(path);});
|
||||
else */if(fileExist(path, true))
|
||||
retVal = std::async(std::launch::async, [path](){return fileGet(path, true);});
|
||||
else if(isLink(path))
|
||||
retVal = std::async(std::launch::async, [path, proxy, cache_ttl](){return webGet(path, proxy, cache_ttl);});
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "ini_reader.h"
|
||||
#include "../utils/ini_reader/ini_reader.h"
|
||||
#include "../utils/string.h"
|
||||
|
||||
typedef std::lock_guard<std::mutex> guarded_mutex;
|
||||
using guarded_mutex = std::lock_guard<std::mutex>;
|
||||
|
||||
string_array safe_get_emojis();
|
||||
string_array safe_get_renames();
|
||||
@@ -1,9 +1,10 @@
|
||||
#include <string>
|
||||
|
||||
#include "../utils/ini_reader/ini_reader.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/rapidjson_extra.h"
|
||||
#include "../utils/system.h"
|
||||
#include "webget.h"
|
||||
#include "ini_reader.h"
|
||||
#include "logger.h"
|
||||
#include "rapidjson_extra.h"
|
||||
|
||||
std::string buildGistData(std::string name, std::string content)
|
||||
{
|
||||
@@ -70,7 +71,7 @@ int uploadGist(std::string name, std::string path, std::string content, bool wri
|
||||
{
|
||||
//std::cerr<<"No gist id is provided. Creating new gist...\n";
|
||||
writeLog(0, "No Gist id is provided. Creating new Gist...", LOG_LEVEL_ERROR);
|
||||
retVal = webPost("https://api.github.com/gists", buildGistData(path, content), getSystemProxy(), {"Authorization: token " + token}, &retData);
|
||||
retVal = webPost("https://api.github.com/gists", buildGistData(path, content), getSystemProxy(), {{"Authorization", "token " + token}}, &retData);
|
||||
if(retVal != 201)
|
||||
{
|
||||
//std::cerr<<"Create new Gist failed! Return data:\n"<<retData<<"\n";
|
||||
@@ -85,7 +86,7 @@ int uploadGist(std::string name, std::string path, std::string content, bool wri
|
||||
writeLog(0, "Gist id provided. Modifying Gist...", LOG_LEVEL_INFO);
|
||||
if(writeManageURL)
|
||||
content = "#!MANAGED-CONFIG " + url + "\n" + content;
|
||||
retVal = webPatch("https://api.github.com/gists/" + id, buildGistData(path, content), getSystemProxy(), {"Authorization: token " + token}, &retData);
|
||||
retVal = webPatch("https://api.github.com/gists/" + id, buildGistData(path, content), getSystemProxy(), {{"Authorization", "token " + token}}, &retData);
|
||||
if(retVal != 200)
|
||||
{
|
||||
//std::cerr<<"Modify gist failed! Return data:\n"<<retData<<"\n";
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/defer.h"
|
||||
#include "../utils/file_extra.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/urlencode.h"
|
||||
#include "../version.h"
|
||||
#include "webget.h"
|
||||
#include "version.h"
|
||||
#include "misc.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _stat
|
||||
@@ -22,7 +25,7 @@ extern bool gPrintDbgInfo, gServeCacheOnFetchFail;
|
||||
extern int gLogLevel;
|
||||
|
||||
/*
|
||||
typedef std::lock_guard<std::mutex> guarded_mutex;
|
||||
using guarded_mutex = std::lock_guard<std::mutex>;
|
||||
std::mutex cache_rw_lock;
|
||||
*/
|
||||
|
||||
@@ -137,7 +140,7 @@ static int size_checker(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cur
|
||||
curl_progress_data *data = reinterpret_cast<curl_progress_data*>(clientp);
|
||||
if(data->size_limit)
|
||||
{
|
||||
if(dltotal > data->size_limit || dlnow > data->size_limit)
|
||||
if(dlnow > data->size_limit)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -156,6 +159,7 @@ static inline void curl_set_common_options(CURL *curl_handle, const char *url, c
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 15L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "");
|
||||
if(data)
|
||||
{
|
||||
if(data->size_limit)
|
||||
@@ -190,7 +194,7 @@ static int curlGet(const FetchArgument &argument, FetchResult &result)
|
||||
curl_progress_data limit;
|
||||
limit.size_limit = gMaxAllowedDownloadSize;
|
||||
curl_set_common_options(curl_handle, new_url.data(), &limit);
|
||||
|
||||
list = curl_slist_append(list, "Content-Type: application/json;charset='utf-8'");
|
||||
if(argument.request_headers)
|
||||
{
|
||||
for(auto &x : *argument.request_headers)
|
||||
@@ -216,6 +220,32 @@ static int curlGet(const FetchArgument &argument, FetchResult &result)
|
||||
else
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dummy_writer);
|
||||
|
||||
if(argument.cookies)
|
||||
{
|
||||
string_array cookies = split(*argument.cookies, "\r\n");
|
||||
for(auto &x : cookies)
|
||||
curl_easy_setopt(curl_handle, CURLOPT_COOKIELIST, x.c_str());
|
||||
}
|
||||
|
||||
switch(argument.method)
|
||||
{
|
||||
case HTTP_POST:
|
||||
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, argument.post_data.data());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, argument.post_data.size());
|
||||
break;
|
||||
case HTTP_PATCH:
|
||||
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "PATCH");
|
||||
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, argument.post_data.data());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, argument.post_data.size());
|
||||
break;
|
||||
case HTTP_HEAD:
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L);
|
||||
break;
|
||||
case HTTP_GET:
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int fail_count = 0, max_fails = 1;
|
||||
while(true)
|
||||
{
|
||||
@@ -227,6 +257,24 @@ static int curlGet(const FetchArgument &argument, FetchResult &result)
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &retVal);
|
||||
|
||||
if(result.cookies)
|
||||
{
|
||||
curl_slist *cookies = nullptr;
|
||||
curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies);
|
||||
if(cookies)
|
||||
{
|
||||
auto each = cookies;
|
||||
while(each)
|
||||
{
|
||||
result.cookies->append(each->data);
|
||||
*result.cookies += "\r\n";
|
||||
each = each->next;
|
||||
}
|
||||
}
|
||||
curl_slist_free_all(cookies);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl_handle);
|
||||
|
||||
if(data)
|
||||
@@ -248,9 +296,9 @@ static std::string dataGet(const std::string &url)
|
||||
if (comma == std::string::npos || comma == url.size() - 1)
|
||||
return std::string();
|
||||
|
||||
std::string data = UrlDecode(url.substr(comma + 1));
|
||||
std::string data = urlDecode(url.substr(comma + 1));
|
||||
if (endsWith(url.substr(0, comma), ";base64")) {
|
||||
return urlsafe_base64_decode(data);
|
||||
return urlSafeBase64Decode(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
@@ -263,13 +311,13 @@ std::string buildSocks5ProxyString(const std::string &addr, int port, const std:
|
||||
return proxystr;
|
||||
}
|
||||
|
||||
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl, std::string *response_headers, string_map *request_headers)
|
||||
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl, std::string *response_headers, string_icase_map *request_headers)
|
||||
{
|
||||
int return_code = 0;
|
||||
std::string content;
|
||||
|
||||
FetchArgument argument {url, proxy, request_headers, cache_ttl};
|
||||
FetchResult fetch_res {&return_code, &content, response_headers};
|
||||
FetchArgument argument {HTTP_GET, url, proxy, "", request_headers, nullptr, cache_ttl};
|
||||
FetchResult fetch_res {&return_code, &content, response_headers, nullptr};
|
||||
|
||||
if (startsWith(url, "data:"))
|
||||
return dataGet(url);
|
||||
@@ -375,9 +423,13 @@ int curlPost(const std::string &url, const std::string &data, const std::string
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
|
||||
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_icase_map &request_headers, std::string *retData)
|
||||
{
|
||||
return curlPost(url, data, proxy, request_headers, retData);
|
||||
//return curlPost(url, data, proxy, request_headers, retData);
|
||||
int return_code = 0;
|
||||
FetchArgument argument {HTTP_POST, url, proxy, "", &request_headers, nullptr, 0};
|
||||
FetchResult fetch_res {&return_code, retData, nullptr, nullptr};
|
||||
return webGet(argument, fetch_res);
|
||||
}
|
||||
|
||||
int curlPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
|
||||
@@ -418,9 +470,13 @@ int curlPatch(const std::string &url, const std::string &data, const std::string
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
|
||||
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_icase_map &request_headers, std::string *retData)
|
||||
{
|
||||
return curlPatch(url, data, proxy, request_headers, retData);
|
||||
//return curlPatch(url, data, proxy, request_headers, retData);
|
||||
int return_code = 0;
|
||||
FetchArgument argument {HTTP_PATCH, url, proxy, "", &request_headers, nullptr, 0};
|
||||
FetchResult fetch_res {&return_code, retData, nullptr, nullptr};
|
||||
return webGet(argument, fetch_res);
|
||||
}
|
||||
|
||||
int curlHead(const std::string &url, const std::string &proxy, const string_array &request_headers, std::string &response_headers)
|
||||
@@ -457,7 +513,59 @@ int curlHead(const std::string &url, const std::string &proxy, const string_arra
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int webHead(const std::string &url, const std::string &proxy, const string_array &request_headers, std::string &response_headers)
|
||||
int webHead(const std::string &url, const std::string &proxy, const string_icase_map &request_headers, std::string &response_headers)
|
||||
{
|
||||
return curlHead(url, proxy, request_headers, response_headers);
|
||||
//return curlHead(url, proxy, request_headers, response_headers);
|
||||
int return_code = 0;
|
||||
FetchArgument argument {HTTP_HEAD, url, proxy, "", &request_headers, nullptr, 0};
|
||||
FetchResult fetch_res {&return_code, nullptr, &response_headers, nullptr};
|
||||
return webGet(argument, fetch_res);
|
||||
}
|
||||
|
||||
string_array headers_map_to_array(const string_map &headers)
|
||||
{
|
||||
string_array result;
|
||||
for(auto &kv : headers)
|
||||
result.push_back(kv.first + ": " + kv.second);
|
||||
return result;
|
||||
}
|
||||
|
||||
int webGet(const FetchArgument& argument, FetchResult &result)
|
||||
{
|
||||
return curlGet(argument, result);
|
||||
/*
|
||||
switch(argument.method)
|
||||
{
|
||||
case HTTP_GET:
|
||||
return curlGet(argument, result);
|
||||
case HTTP_POST:
|
||||
{
|
||||
string_array request_header;
|
||||
if(argument.request_headers != nullptr)
|
||||
request_header = headers_map_to_array(*argument.request_headers);
|
||||
int res = curlPost(argument.url, argument.post_data, argument.proxy, request_header, result.content);
|
||||
*result.status_code = res;
|
||||
return res;
|
||||
}
|
||||
case HTTP_PATCH:
|
||||
{
|
||||
string_array request_header;
|
||||
if(argument.request_headers != nullptr)
|
||||
request_header = headers_map_to_array(*argument.request_headers);
|
||||
int res = curlPatch(argument.url, argument.post_data, argument.proxy, request_header, result.content);
|
||||
*result.status_code = res;
|
||||
return res;
|
||||
}
|
||||
case HTTP_HEAD:
|
||||
{
|
||||
string_array request_header;
|
||||
if(argument.request_headers != nullptr)
|
||||
request_header = headers_map_to_array(*argument.request_headers);
|
||||
int res = curlHead(argument.url, argument.proxy, request_header, *result.response_headers);
|
||||
*result.status_code = res;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
*/
|
||||
}
|
||||
@@ -4,36 +4,45 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "misc.h"
|
||||
#include "../utils/map_extra.h"
|
||||
#include "../utils/string.h"
|
||||
|
||||
enum http_method
|
||||
{
|
||||
HTTP_GET,
|
||||
HTTP_HEAD,
|
||||
HTTP_POST,
|
||||
HTTP_PATCH
|
||||
};
|
||||
|
||||
struct FetchArgument
|
||||
{
|
||||
const http_method method;
|
||||
const std::string url;
|
||||
const std::string proxy;
|
||||
string_map *request_headers = NULL;
|
||||
const std::string post_data;
|
||||
const string_icase_map *request_headers = nullptr;
|
||||
std::string *cookies = nullptr;
|
||||
const unsigned int cache_ttl = 0;
|
||||
};
|
||||
|
||||
struct FetchResult
|
||||
{
|
||||
int *status_code;
|
||||
std::string *content = NULL;
|
||||
std::string *response_headers = NULL;
|
||||
std::string *content = nullptr;
|
||||
std::string *response_headers = nullptr;
|
||||
std::string *cookies = nullptr;
|
||||
};
|
||||
|
||||
std::string webGet(const std::string &url, const std::string &proxy = "", unsigned int cache_ttl = 0, std::string *response_headers = NULL, string_map *request_headers = NULL);
|
||||
int webGet(const FetchArgument& argument, FetchResult &result);
|
||||
std::string webGet(const std::string &url, const std::string &proxy = "", unsigned int cache_ttl = 0, std::string *response_headers = nullptr, string_icase_map *request_headers = nullptr);
|
||||
void flushCache();
|
||||
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
|
||||
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
|
||||
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_icase_map &request_headers, std::string *retData);
|
||||
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_icase_map &request_headers, std::string *retData);
|
||||
std::string buildSocks5ProxyString(const std::string &addr, int port, const std::string &username, const std::string &password);
|
||||
|
||||
// Unimplemented: (CURLOPT_HTTPHEADER: Host:)
|
||||
std::string httpGet(const std::string &host, const std::string &addr, const std::string &uri);
|
||||
std::string httpsGet(const std::string &host, const std::string &addr, const std::string &uri);
|
||||
|
||||
static inline bool isLink(const std::string &url)
|
||||
{
|
||||
return startsWith(url, "https://") || startsWith(url, "http://") || startsWith(url, "data:");
|
||||
}
|
||||
|
||||
#endif // WEBGET_H_INCLUDED
|
||||
43
src/main.cpp
43
src/main.cpp
@@ -3,18 +3,31 @@
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "handler/interfaces.h"
|
||||
#include "handler/webget.h"
|
||||
#include "script/cron.h"
|
||||
#include "server/socket.h"
|
||||
#include "server/webserver.h"
|
||||
#include "utils/defer.h"
|
||||
#include "utils/file_extra.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/network.h"
|
||||
#include "utils/rapidjson_extra.h"
|
||||
#include "utils/system.h"
|
||||
#include "utils/urlencode.h"
|
||||
#include "version.h"
|
||||
#include "misc.h"
|
||||
#include "socket.h"
|
||||
#include "webget.h"
|
||||
#include "logger.h"
|
||||
|
||||
//#include "vfs.h"
|
||||
|
||||
extern std::string gPrefPath, gAccessToken, gListenAddress, gGenerateProfiles, gManagedConfigPrefix;
|
||||
extern bool gAPIMode, gGeneratorMode, gCFWChildProcess, gUpdateRulesetOnRequest;
|
||||
extern int gListenPort, gMaxConcurThreads, gMaxPendingConns;
|
||||
extern string_array gCustomRulesets;
|
||||
extern std::vector<ruleset_content> gRulesetContent;
|
||||
extern bool gEnableCron;
|
||||
|
||||
#ifndef _WIN32
|
||||
void SetConsoleTitle(const std::string &title)
|
||||
@@ -97,6 +110,12 @@ void signal_handler(int sig)
|
||||
}
|
||||
}
|
||||
|
||||
void cron_tick_caller()
|
||||
{
|
||||
if(gEnableCron)
|
||||
cron_tick();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifndef _DEBUG
|
||||
@@ -143,7 +162,7 @@ int main(int argc, char *argv[])
|
||||
if(!gUpdateRulesetOnRequest)
|
||||
refreshRulesets(gCustomRulesets, gRulesetContent);
|
||||
|
||||
std::string env_api_mode = GetEnv("API_MODE"), env_managed_prefix = GetEnv("MANAGED_PREFIX"), env_token = GetEnv("API_TOKEN");
|
||||
std::string env_api_mode = getEnv("API_MODE"), env_managed_prefix = getEnv("MANAGED_PREFIX"), env_token = getEnv("API_TOKEN");
|
||||
gAPIMode = tribool().parse(toLower(env_api_mode)).get(gAPIMode);
|
||||
if(env_managed_prefix.size())
|
||||
gManagedConfigPrefix = env_managed_prefix;
|
||||
@@ -258,20 +277,24 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string url = UrlDecode(getUrlArg(request.argument, "url"));
|
||||
std::string url = urlDecode(getUrlArg(request.argument, "url"));
|
||||
return webGet(url, "");
|
||||
});
|
||||
|
||||
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
return fileGet(UrlDecode(getUrlArg(request.argument, "path")));
|
||||
return fileGet(urlDecode(getUrlArg(request.argument, "path")));
|
||||
});
|
||||
}
|
||||
|
||||
std::string env_port = GetEnv("PORT");
|
||||
//append_response("POST", "/create-profile", "text/plain;charset=utf-8", createProfile);
|
||||
|
||||
//append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles);
|
||||
|
||||
std::string env_port = getEnv("PORT");
|
||||
if(env_port.size())
|
||||
gListenPort = to_int(env_port, gListenPort);
|
||||
listener_args args = {gListenAddress, gListenPort, gMaxPendingConns, gMaxConcurThreads};
|
||||
listener_args args = {gListenAddress, gListenPort, gMaxPendingConns, gMaxConcurThreads, cron_tick_caller, 200};
|
||||
//std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
|
||||
writeLog(0, "Startup completed. Serving HTTP @ http://" + gListenAddress + ":" + std::to_string(gListenPort), LOG_LEVEL_INFO);
|
||||
start_web_server_multi(&args);
|
||||
|
||||
1253
src/misc.cpp
1253
src/misc.cpp
File diff suppressed because it is too large
Load Diff
302
src/misc.h
302
src/misc.h
@@ -1,302 +0,0 @@
|
||||
#ifndef MISC_H_INCLUDED
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "string_hash.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <unistd.h>
|
||||
#define PATH_SLASH "\\"
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#define PATH_SLASH "//"
|
||||
#endif // _WIN32
|
||||
|
||||
#define CONCAT(a,b) a ## b
|
||||
#define DO_CONCAT(a,b) CONCAT(a,b)
|
||||
template <typename T> class __defer_struct final {private: T fn; bool __cancelled = false; public: explicit __defer_struct(T func) : fn(std::move(func)) {} ~__defer_struct() {if(!__cancelled) fn();} void cancel() {__cancelled = true;} };
|
||||
//#define defer(x) std::unique_ptr<void> DO_CONCAT(__defer_deleter_,__LINE__) (nullptr, [&](...){x});
|
||||
#define defer(x) __defer_struct DO_CONCAT(__defer_deleter,__LINE__) ([&](...){x;});
|
||||
|
||||
#define GETBIT(x,n) (((int)x < 1) ? 0 : ((x >> (n - 1)) & 1))
|
||||
#define SETBIT(x,n,v) x ^= (-v ^ x) & (1UL << (n - 1))
|
||||
|
||||
typedef std::string::size_type string_size;
|
||||
typedef std::vector<std::string> string_array;
|
||||
typedef std::map<std::string, std::string> string_map;
|
||||
typedef const std::string &refCnstStr;
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string UrlEncode(const std::string& str);
|
||||
std::string UrlDecode(const std::string& str);
|
||||
std::string base64_decode(const std::string &encoded_string, bool accept_urlsafe = false);
|
||||
std::string base64_encode(const std::string &string_to_encode);
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &seperator);
|
||||
std::string getUrlArg(const std::string &url, const std::string &request);
|
||||
std::string replace_all_distinct(std::string str, const std::string &old_value, const std::string &new_value);
|
||||
std::string urlsafe_base64(const std::string &encoded_string);
|
||||
std::string urlsafe_base64_reverse(const std::string &encoded_string);
|
||||
std::string urlsafe_base64_decode(const std::string &encoded_string);
|
||||
std::string urlsafe_base64_encode(const std::string &string_to_encode);
|
||||
std::string UTF8ToACP(const std::string &str_src);
|
||||
std::string ACPToUTF8(const std::string &str_src);
|
||||
std::string trim_of(const std::string& str, char target, bool before = true, bool after = true);
|
||||
std::string trim(const std::string& str, bool before = true, bool after = true);
|
||||
std::string trim_quote(const std::string &str, bool before = true, bool after = true);
|
||||
void trim_self_of(std::string &str, char target, bool before = true, bool after = true);
|
||||
std::string getSystemProxy();
|
||||
std::string rand_str(const int len);
|
||||
bool is_str_utf8(const std::string &data);
|
||||
std::string getFormData(const std::string &raw_data);
|
||||
|
||||
void sleep(int interval);
|
||||
bool regValid(const std::string ®);
|
||||
bool regFind(const std::string &src, const std::string &match);
|
||||
std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global = true, bool multiline = true);
|
||||
bool regMatch(const std::string &src, const std::string &match);
|
||||
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...);
|
||||
std::string regTrim(const std::string &src);
|
||||
std::string speedCalc(double speed);
|
||||
std::string getMD5(const std::string &data);
|
||||
bool isIPv4(const std::string &address);
|
||||
bool isIPv6(const std::string &address);
|
||||
void urlParse(std::string &url, std::string &host, std::string &path, int &port, bool &isTLS);
|
||||
void removeUTF8BOM(std::string &data);
|
||||
int shortAssemble(unsigned short num_a, unsigned short num_b);
|
||||
void shortDisassemble(int source, unsigned short &num_a, unsigned short &num_b);
|
||||
std::string UTF8ToCodePoint(const std::string &data);
|
||||
std::string GetEnv(const std::string &name);
|
||||
std::string toLower(const std::string &str);
|
||||
std::string toUpper(const std::string &str);
|
||||
void ProcessEscapeChar(std::string &str);
|
||||
void ProcessEscapeCharReverse(std::string &str);
|
||||
|
||||
std::string fileGet(const std::string &path, bool scope_limit = false);
|
||||
int fileWrite(const std::string &path, const std::string &content, bool overwrite);
|
||||
bool fileExist(const std::string &path, bool scope_limit = false);
|
||||
bool fileCopy(const std::string &source, const std::string &dest);
|
||||
std::string fileToBase64(const std::string &filepath);
|
||||
std::string fileGetMD5(const std::string &filepath);
|
||||
|
||||
template<typename F>
|
||||
int operateFiles(const std::string &path, F &&op)
|
||||
{
|
||||
DIR* dir = opendir(path.data());
|
||||
if(!dir)
|
||||
return -1;
|
||||
struct dirent* dp;
|
||||
while((dp = readdir(dir)) != NULL)
|
||||
{
|
||||
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
|
||||
{
|
||||
if(op(dp->d_name))
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool strFind(const std::string &str, const std::string &target)
|
||||
{
|
||||
return str.find(target) != str.npos;
|
||||
}
|
||||
|
||||
static inline bool startsWith(const std::string &hay, const std::string &needle)
|
||||
{
|
||||
return hay.substr(0, needle.length()) == needle;
|
||||
}
|
||||
|
||||
static inline bool endsWith(const std::string &hay, const std::string &needle)
|
||||
{
|
||||
std::string::size_type hl = hay.length(), nl = needle.length();
|
||||
return hl >= nl && hay.substr(hl - nl, nl) == needle;
|
||||
}
|
||||
|
||||
template <typename T> static inline void eraseElements(std::vector<T> &target)
|
||||
{
|
||||
target.clear();
|
||||
target.shrink_to_fit();
|
||||
}
|
||||
|
||||
template <typename T> static inline void eraseElements(T &target)
|
||||
{
|
||||
T().swap(target);
|
||||
}
|
||||
|
||||
template <typename T, typename U> static inline T to_number(const U &value, T def_value = T())
|
||||
{
|
||||
T retval = 0.0;
|
||||
char c;
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
if(!(ss >> retval))
|
||||
return def_value;
|
||||
else if(ss >> c)
|
||||
return def_value;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
int to_int(const std::string &str, int def_value = 0);
|
||||
|
||||
static inline bool count_least(const std::string &hay, const char needle, size_t cnt)
|
||||
{
|
||||
string_size pos = hay.find(needle);
|
||||
while(pos != hay.npos)
|
||||
{
|
||||
cnt--;
|
||||
if(!cnt)
|
||||
return true;
|
||||
pos = hay.find(needle, pos + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline char getLineBreak(const std::string &str)
|
||||
{
|
||||
return count_least(str, '\n', 1) ? '\n' : '\r';
|
||||
}
|
||||
|
||||
class tribool
|
||||
{
|
||||
private:
|
||||
|
||||
char _M_VALUE = 0;
|
||||
|
||||
public:
|
||||
|
||||
tribool() { clear(); }
|
||||
|
||||
template <typename T> tribool(const T &value) { set(value); }
|
||||
|
||||
tribool(const tribool &value) { _M_VALUE = value._M_VALUE; }
|
||||
|
||||
~tribool() = default;
|
||||
|
||||
tribool& operator=(const tribool &src)
|
||||
{
|
||||
_M_VALUE = src._M_VALUE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& operator=(const T &value)
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return _M_VALUE == 3; }
|
||||
|
||||
bool is_undef() const { return _M_VALUE <= 1; }
|
||||
|
||||
template <typename T> tribool& define(const T &value)
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
*this = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& parse(const T &value)
|
||||
{
|
||||
return define(value);
|
||||
}
|
||||
|
||||
tribool reverse()
|
||||
{
|
||||
if(_M_VALUE > 1)
|
||||
_M_VALUE = _M_VALUE > 2 ? 2 : 3;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool get(const bool &def_value = false) const
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
return def_value;
|
||||
return _M_VALUE == 3;
|
||||
}
|
||||
|
||||
std::string get_str() const
|
||||
{
|
||||
switch(_M_VALUE)
|
||||
{
|
||||
case 2:
|
||||
return "false";
|
||||
case 3:
|
||||
return "true";
|
||||
}
|
||||
return "undef";
|
||||
}
|
||||
|
||||
template <typename T> bool set(const T &value)
|
||||
{
|
||||
_M_VALUE = (bool)value + 2;
|
||||
return _M_VALUE > 2;
|
||||
}
|
||||
|
||||
bool set(const std::string &str)
|
||||
{
|
||||
switch(hash_(str))
|
||||
{
|
||||
case "true"_hash:
|
||||
case "1"_hash:
|
||||
_M_VALUE = 3;
|
||||
break;
|
||||
case "false"_hash:
|
||||
case "0"_hash:
|
||||
_M_VALUE = 2;
|
||||
break;
|
||||
default:
|
||||
if(to_int(str, 0) > 1)
|
||||
_M_VALUE = 3;
|
||||
else
|
||||
_M_VALUE = 0;
|
||||
break;
|
||||
}
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
void clear() { _M_VALUE = 0; }
|
||||
};
|
||||
|
||||
#ifndef HAVE_TO_STRING
|
||||
namespace std
|
||||
{
|
||||
template <typename T> std::string to_string(const T& n)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
#endif // HAVE_TO_STRING
|
||||
|
||||
static inline int md(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return mkdir(path);
|
||||
#else
|
||||
return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void StringToWstring(std::wstring& szDst, const std::string &str);
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // MISC_H_INCLUDED
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef NODEINFO_H_INCLUDED
|
||||
#define NODEINFO_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
struct nodeInfo
|
||||
{
|
||||
int linkType = -1;
|
||||
int id = -1;
|
||||
int groupID = -1;
|
||||
std::string group;
|
||||
std::string remarks;
|
||||
std::string server;
|
||||
int port = 0;
|
||||
std::string proxyStr;
|
||||
};
|
||||
|
||||
#endif // NODEINFO_H_INCLUDED
|
||||
@@ -1,179 +0,0 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "nodeinfo.h"
|
||||
#include "printout.h"
|
||||
#include "logger.h"
|
||||
#include "webget.h"
|
||||
#include "speedtestutil.h"
|
||||
#include "script_duktape.h"
|
||||
|
||||
std::string override_conf_port;
|
||||
bool ss_libev, ssr_libev;
|
||||
extern int gCacheSubscription;
|
||||
|
||||
void copyNodes(std::vector<nodeInfo> &source, std::vector<nodeInfo> &dest)
|
||||
{
|
||||
std::move(source.begin(), source.end(), std::back_inserter(dest));
|
||||
}
|
||||
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, const std::string &proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized, string_map &request_headers)
|
||||
{
|
||||
int linkType = -1;
|
||||
std::vector<nodeInfo> nodes;
|
||||
nodeInfo node;
|
||||
std::string strSub, extra_headers, custom_group;
|
||||
|
||||
// TODO: replace with startsWith if appropriate
|
||||
link = replace_all_distinct(link, "\"", "");
|
||||
|
||||
/// script:filepath,arg1,arg2,...
|
||||
if(startsWith(link, "script:") && authorized) /// process subscription with script
|
||||
{
|
||||
writeLog(0, "Found script link. Start running...", LOG_LEVEL_INFO);
|
||||
string_array args = split(link.substr(7), ",");
|
||||
if(args.size() >= 1)
|
||||
{
|
||||
std::string script = fileGet(args[0], false);
|
||||
duk_context *ctx = duktape_init();
|
||||
defer(duk_destroy_heap(ctx);)
|
||||
duktape_peval(ctx, script);
|
||||
duk_get_global_string(ctx, "parse");
|
||||
for(size_t i = 1; i < args.size(); i++)
|
||||
duk_push_string(ctx, trim(args[i]).c_str());
|
||||
if(duk_pcall(ctx, args.size() - 1) == 0)
|
||||
link = duktape_get_res_str(ctx);
|
||||
else
|
||||
{
|
||||
writeLog(0, "Error when trying to evaluate script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
|
||||
duk_pop(ctx); /// pop err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// tag:group_name,link
|
||||
if(startsWith(link, "tag:"))
|
||||
{
|
||||
string_size pos = link.find(",");
|
||||
if(pos != link.npos)
|
||||
{
|
||||
custom_group = link.substr(4, pos - 4);
|
||||
link.erase(0, pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(link == "nullnode")
|
||||
{
|
||||
node.groupID = 0;
|
||||
writeLog(0, "Adding node placeholder...");
|
||||
allNodes.emplace_back(std::move(node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeLog(LOG_TYPE_INFO, "Received Link.");
|
||||
if(startsWith(link, "https://t.me/socks") || startsWith(link, "tg://socks"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
|
||||
else if(startsWith(link, "https://t.me/http") || startsWith(link, "tg://http"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
|
||||
else if(isLink(link) || startsWith(link, "surge:///install-config"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSUB;
|
||||
else if(startsWith(link, "Netch://"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDNETCH;
|
||||
else if(fileExist(link))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDLOCAL;
|
||||
|
||||
switch(linkType)
|
||||
{
|
||||
case SPEEDTEST_MESSAGE_FOUNDSUB:
|
||||
writeLog(LOG_TYPE_INFO, "Downloading subscription data...");
|
||||
if(startsWith(link, "surge:///install-config")) //surge config link
|
||||
link = UrlDecode(getUrlArg(link, "url"));
|
||||
strSub = webGet(link, proxy, gCacheSubscription, &extra_headers, &request_headers);
|
||||
/*
|
||||
if(strSub.size() == 0)
|
||||
{
|
||||
//try to get it again with system proxy
|
||||
writeLog(LOG_TYPE_WARN, "Cannot download subscription directly. Using system proxy.");
|
||||
strProxy = getSystemProxy();
|
||||
if(strProxy != "")
|
||||
{
|
||||
strSub = webGet(link, strProxy);
|
||||
}
|
||||
else
|
||||
writeLog(LOG_TYPE_WARN, "No system proxy is set. Skipping.");
|
||||
}
|
||||
*/
|
||||
if(strSub.size())
|
||||
{
|
||||
writeLog(LOG_TYPE_INFO, "Parsing subscription data...");
|
||||
if(explodeConfContent(strSub, override_conf_port, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Invalid subscription!");
|
||||
return -1;
|
||||
}
|
||||
if(startsWith(strSub, "ssd://"))
|
||||
{
|
||||
getSubInfoFromSSD(strSub, subInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!getSubInfoFromHeader(extra_headers, subInfo))
|
||||
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
|
||||
}
|
||||
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
|
||||
for(nodeInfo &x : nodes)
|
||||
{
|
||||
x.groupID = groupID;
|
||||
if(custom_group.size())
|
||||
x.group = custom_group;
|
||||
}
|
||||
copyNodes(nodes, allNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Cannot download subscription data.");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SPEEDTEST_MESSAGE_FOUNDLOCAL:
|
||||
if(!authorized)
|
||||
return -1;
|
||||
writeLog(LOG_TYPE_INFO, "Parsing configuration file data...");
|
||||
if(explodeConf(link, override_conf_port, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Invalid configuration file!");
|
||||
return -1;
|
||||
}
|
||||
if(startsWith(strSub, "ssd://"))
|
||||
{
|
||||
getSubInfoFromSSD(strSub, subInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
|
||||
}
|
||||
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
|
||||
for(nodeInfo &x : nodes)
|
||||
{
|
||||
x.groupID = groupID;
|
||||
if(custom_group.size())
|
||||
x.group = custom_group;
|
||||
}
|
||||
copyNodes(nodes, allNodes);
|
||||
break;
|
||||
default:
|
||||
explode(link, ss_libev, ssr_libev, override_conf_port, node);
|
||||
if(node.linkType == -1)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "No valid link found.");
|
||||
return -1;
|
||||
}
|
||||
node.groupID = groupID;
|
||||
if(custom_group.size())
|
||||
node.group = custom_group;
|
||||
allNodes.emplace_back(std::move(node));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef NODEMANIP_H_INCLUDED
|
||||
#define NODEMANIP_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nodeinfo.h"
|
||||
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, const std::string &proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized, string_map &request_headers);
|
||||
|
||||
#endif // NODEMANIP_H_INCLUDED
|
||||
94
src/parser/config/proxy.h
Normal file
94
src/parser/config/proxy.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef PROXY_H_INCLUDED
|
||||
#define PROXY_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../../utils/tribool.h"
|
||||
|
||||
using String = std::string;
|
||||
|
||||
enum ProxyType
|
||||
{
|
||||
Unknow,
|
||||
Shadowsocks,
|
||||
ShadowsocksR,
|
||||
VMess,
|
||||
Trojan,
|
||||
Snell,
|
||||
HTTP,
|
||||
HTTPS,
|
||||
SOCKS5
|
||||
};
|
||||
|
||||
inline String getProxyTypeName(int type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ProxyType::Shadowsocks:
|
||||
return "SS";
|
||||
case ProxyType::ShadowsocksR:
|
||||
return "SSR";
|
||||
case ProxyType::VMess:
|
||||
return "VMess";
|
||||
case ProxyType::Trojan:
|
||||
return "Trojan";
|
||||
case ProxyType::Snell:
|
||||
return "Snell";
|
||||
case ProxyType::HTTP:
|
||||
return "HTTP";
|
||||
case ProxyType::HTTPS:
|
||||
return "HTTPS";
|
||||
case ProxyType::SOCKS5:
|
||||
return "SOCKS5";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct Proxy
|
||||
{
|
||||
int Type = ProxyType::Unknow;
|
||||
uint32_t Id = 0;
|
||||
uint32_t GroupId = 0;
|
||||
String Group;
|
||||
String Remark;
|
||||
String Hostname;
|
||||
uint16_t Port = 0;
|
||||
|
||||
String Username;
|
||||
String Password;
|
||||
String EncryptMethod;
|
||||
String Plugin;
|
||||
String PluginOption;
|
||||
String Protocol;
|
||||
String ProtocolParam;
|
||||
String OBFS;
|
||||
String OBFSParam;
|
||||
String UserId;
|
||||
uint8_t AlterId = 0;
|
||||
String TransferProtocol;
|
||||
String FakeType;
|
||||
bool TLSSecure = false;
|
||||
|
||||
String Host;
|
||||
String Path;
|
||||
String Edge;
|
||||
|
||||
String QUICSecure;
|
||||
String QUICSecret;
|
||||
|
||||
tribool UDP;
|
||||
tribool TCPFastOpen;
|
||||
tribool AllowInsecure;
|
||||
tribool TLS13;
|
||||
};
|
||||
|
||||
#define SS_DEFAULT_GROUP "SSProvider"
|
||||
#define SSR_DEFAULT_GROUP "SSRProvider"
|
||||
#define V2RAY_DEFAULT_GROUP "V2RayProvider"
|
||||
#define SOCKS_DEFAULT_GROUP "SocksProvider"
|
||||
#define HTTP_DEFAULT_GROUP "HTTPProvider"
|
||||
#define TROJAN_DEFAULT_GROUP "TrojanProvider"
|
||||
#define SNELL_DEFAULT_GROUP "SnellProvider"
|
||||
|
||||
#endif // PROXY_H_INCLUDED
|
||||
206
src/parser/infoparser.cpp
Normal file
206
src/parser/infoparser.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <time.h>
|
||||
|
||||
#include "../parser/config/proxy.h"
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/rapidjson_extra.h"
|
||||
#include "../utils/regexp.h"
|
||||
#include "../utils/string.h"
|
||||
|
||||
unsigned long long streamToInt(const std::string &stream)
|
||||
{
|
||||
if(!stream.size())
|
||||
return 0;
|
||||
double streamval = 1.0;
|
||||
std::vector<std::string> units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"};
|
||||
size_t index = units.size();
|
||||
do
|
||||
{
|
||||
index--;
|
||||
if(endsWith(stream, units[index]))
|
||||
{
|
||||
streamval = std::pow(1024, index) * to_number<float>(stream.substr(0, stream.size() - units[index].size()), 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(index != 0);
|
||||
return (unsigned long long)streamval;
|
||||
}
|
||||
|
||||
static inline double percentToDouble(const std::string &percent)
|
||||
{
|
||||
return stof(percent.substr(0, percent.size() - 1)) / 100.0;
|
||||
}
|
||||
|
||||
time_t dateStringToTimestamp(std::string date)
|
||||
{
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
if(startsWith(date, "left="))
|
||||
{
|
||||
time_t seconds_left = 0;
|
||||
date.erase(0, 5);
|
||||
if(endsWith(date, "d"))
|
||||
{
|
||||
date.erase(date.size() - 1);
|
||||
seconds_left = to_number<double>(date, 0.0) * 86400.0;
|
||||
}
|
||||
return rawtime + seconds_left;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct tm *expire_time;
|
||||
std::vector<std::string> date_array = split(date, ":");
|
||||
if(date_array.size() != 6)
|
||||
return 0;
|
||||
|
||||
expire_time = localtime(&rawtime);
|
||||
expire_time->tm_year = to_int(date_array[0], 1900) - 1900;
|
||||
expire_time->tm_mon = to_int(date_array[1], 1) - 1;
|
||||
expire_time->tm_mday = to_int(date_array[2]);
|
||||
expire_time->tm_hour = to_int(date_array[3]);
|
||||
expire_time->tm_min = to_int(date_array[4]);
|
||||
expire_time->tm_sec = to_int(date_array[5]);
|
||||
return mktime(expire_time);
|
||||
}
|
||||
}
|
||||
|
||||
bool getSubInfoFromHeader(const std::string &header, std::string &result)
|
||||
{
|
||||
std::string pattern = R"(^(?i:Subscription-UserInfo): (.*?)\s*?$)", retStr;
|
||||
if(regFind(header, pattern))
|
||||
{
|
||||
regGetMatch(header, pattern, 2, 0, &retStr);
|
||||
if(retStr.size())
|
||||
{
|
||||
result = retStr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result)
|
||||
{
|
||||
std::string remarks, pattern, target, stream_info, time_info, retStr;
|
||||
string_size spos;
|
||||
|
||||
for(const Proxy &x : nodes)
|
||||
{
|
||||
remarks = x.Remark;
|
||||
if(!stream_info.size())
|
||||
{
|
||||
for(const std::string &y : stream_rules)
|
||||
{
|
||||
spos = y.rfind("|");
|
||||
if(spos == y.npos)
|
||||
continue;
|
||||
pattern = y.substr(0, spos);
|
||||
target = y.substr(spos + 1);
|
||||
if(regMatch(remarks, pattern))
|
||||
{
|
||||
retStr = regReplace(remarks, pattern, target);
|
||||
if(retStr != remarks)
|
||||
{
|
||||
stream_info = retStr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
remarks = x.Remark;
|
||||
if(!time_info.size())
|
||||
{
|
||||
for(const std::string &y : time_rules)
|
||||
{
|
||||
spos = y.rfind("|");
|
||||
if(spos == y.npos)
|
||||
continue;
|
||||
pattern = y.substr(0, spos);
|
||||
target = y.substr(spos + 1);
|
||||
if(regMatch(remarks, pattern))
|
||||
{
|
||||
retStr = regReplace(remarks, pattern, target);
|
||||
if(retStr != remarks)
|
||||
{
|
||||
time_info = retStr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(stream_info.size() && time_info.size())
|
||||
break;
|
||||
}
|
||||
|
||||
if(!stream_info.size() && !time_info.size())
|
||||
return false;
|
||||
|
||||
//calculate how much stream left
|
||||
unsigned long long total = 0, left, used = 0, expire = 0;
|
||||
std::string total_str = getUrlArg(stream_info, "total"), left_str = getUrlArg(stream_info, "left"), used_str = getUrlArg(stream_info, "used");
|
||||
if(strFind(total_str, "%"))
|
||||
{
|
||||
if(used_str.size())
|
||||
{
|
||||
used = streamToInt(used_str);
|
||||
total = used / (1 - percentToDouble(total_str));
|
||||
}
|
||||
else if(left_str.size())
|
||||
{
|
||||
left = streamToInt(left_str);
|
||||
total = left / percentToDouble(total_str);
|
||||
used = total - left;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
total = streamToInt(total_str);
|
||||
if(used_str.size())
|
||||
{
|
||||
used = streamToInt(used_str);
|
||||
}
|
||||
else if(left_str.size())
|
||||
{
|
||||
left = streamToInt(left_str);
|
||||
used = total - left;
|
||||
}
|
||||
}
|
||||
|
||||
result = "upload=0; download=" + std::to_string(used) + "; total=" + std::to_string(total) + ";";
|
||||
|
||||
//calculate expire time
|
||||
expire = dateStringToTimestamp(time_info);
|
||||
if(expire)
|
||||
result += " expire=" + std::to_string(expire) + ";";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSubInfoFromSSD(const std::string &sub, std::string &result)
|
||||
{
|
||||
rapidjson::Document json;
|
||||
json.Parse(urlSafeBase64Decode(sub.substr(6)).data());
|
||||
if(json.HasParseError())
|
||||
return false;
|
||||
|
||||
std::string used_str = GetMember(json, "traffic_used"), total_str = GetMember(json, "traffic_total"), expire_str = GetMember(json, "expiry");
|
||||
if(!used_str.size() || !total_str.size())
|
||||
return false;
|
||||
unsigned long long used = stod(used_str) * std::pow(1024, 3), total = stod(total_str) * std::pow(1024, 3), expire;
|
||||
result = "upload=0; download=" + std::to_string(used) + "; total=" + std::to_string(total) + ";";
|
||||
|
||||
expire = dateStringToTimestamp(regReplace(expire_str, "(\\d+)-(\\d+)-(\\d+) (.*)", "$1:$2:$3:$4"));
|
||||
if(expire)
|
||||
result += " expire=" + std::to_string(expire) + ";";
|
||||
|
||||
return true;
|
||||
}
|
||||
15
src/parser/infoparser.h
Normal file
15
src/parser/infoparser.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef INFOPARSER_H_INCLUDED
|
||||
#define INFOPARSER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../utils/string.h"
|
||||
#include "config/proxy.h"
|
||||
|
||||
bool getSubInfoFromHeader(const std::string &header, std::string &result);
|
||||
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result);
|
||||
bool getSubInfoFromSSD(const std::string &sub, std::string &result);
|
||||
unsigned long long streamToInt(const std::string &stream);
|
||||
|
||||
|
||||
#endif // INFOPARSER_H_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
45
src/parser/subparser.h
Normal file
45
src/parser/subparser.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef SUBPARSER_H_INCLUDED
|
||||
#define SUBPARSER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "config/proxy.h"
|
||||
|
||||
enum class ConfType
|
||||
{
|
||||
Unknow,
|
||||
SS,
|
||||
SSR,
|
||||
V2Ray,
|
||||
SSConf,
|
||||
SSTap,
|
||||
Netch,
|
||||
SOCKS,
|
||||
HTTP,
|
||||
SUB,
|
||||
Local
|
||||
};
|
||||
|
||||
void vmessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &id, const std::string &aid, const std::string &net, const std::string &cipher, const std::string &path, const std::string &host, const std::string &edge, const std::string &tls, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
void ssrConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &protocol, const std::string &method, const std::string &obfs, const std::string &password, const std::string &obfsparam, const std::string &protoparam, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
void ssConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &method, const std::string &plugin, const std::string &pluginopts, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
void socksConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
void httpConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, bool tls, tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
void explodeVmess(std::string vmess, Proxy &node);
|
||||
void explodeSSR(std::string ssr, Proxy &node);
|
||||
void explodeSS(std::string ss, Proxy &node);
|
||||
void explodeTrojan(std::string trojan, Proxy &node);
|
||||
void explodeQuan(const std::string &quan, Proxy &node);
|
||||
void explodeStdVMess(std::string vmess, Proxy &node);
|
||||
void explodeShadowrocket(std::string kit, Proxy &node);
|
||||
void explodeKitsunebi(std::string kit, Proxy &node);
|
||||
/// Parse a link
|
||||
void explode(const std::string &link, Proxy &node);
|
||||
void explodeSSD(std::string link, std::vector<Proxy> &nodes);
|
||||
void explodeSub(std::string sub, std::vector<Proxy> &nodes);
|
||||
int explodeConf(const std::string &filepath, std::vector<Proxy> &nodes);
|
||||
int explodeConfContent(const std::string &content, std::vector<Proxy> &nodes);
|
||||
|
||||
#endif // SUBPARSER_H_INCLUDED
|
||||
135
src/script/cron.cpp
Normal file
135
src/script/cron.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <libcron/Cron.h>
|
||||
|
||||
#include "../handler/interfaces.h"
|
||||
#include "../handler/multithread.h"
|
||||
#include "../server/webserver.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/rapidjson_extra.h"
|
||||
#include "../utils/system.h"
|
||||
#include "script_quickjs.h"
|
||||
|
||||
extern bool gEnableCron;
|
||||
extern string_array gCronTasks;
|
||||
extern std::string gProxyConfig, gAccessToken;
|
||||
extern int gCacheConfig;
|
||||
|
||||
libcron::Cron cron;
|
||||
|
||||
static std::string parseProxy(const std::string &source)
|
||||
{
|
||||
std::string proxy = source;
|
||||
if(source == "SYSTEM")
|
||||
proxy = getSystemProxy();
|
||||
else if(source == "NONE")
|
||||
proxy = "";
|
||||
return proxy;
|
||||
}
|
||||
|
||||
struct script_info
|
||||
{
|
||||
std::string name;
|
||||
time_t begin_time = 0;
|
||||
time_t timeout = 0;
|
||||
};
|
||||
|
||||
int timeout_checker(JSRuntime *rt, void *opaque)
|
||||
{
|
||||
script_info info = *((script_info*)opaque);
|
||||
if(info.timeout != 0 && time(NULL) >= info.begin_time + info.timeout) /// timeout reached
|
||||
{
|
||||
writeLog(0, "Script '" + info.name + "' has exceeded timeout " + std::to_string(info.timeout) + ", terminate now.", LOG_LEVEL_WARNING);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void refresh_schedule()
|
||||
{
|
||||
cron.clear_schedules();
|
||||
for(std::string &x : gCronTasks)
|
||||
{
|
||||
string_array arguments = split(x, "`");
|
||||
if(arguments.size() < 3)
|
||||
continue;
|
||||
std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2];
|
||||
cron.add_schedule(name, cronexp, [=](auto &)
|
||||
{
|
||||
qjs::Runtime runtime;
|
||||
qjs::Context context(runtime);
|
||||
try
|
||||
{
|
||||
script_runtime_init(runtime);
|
||||
script_context_init(context);
|
||||
defer(script_cleanup(context);)
|
||||
std::string proxy = parseProxy(gProxyConfig);
|
||||
std::string script = fetchFile(path, proxy, gCacheConfig);
|
||||
if(script.empty())
|
||||
{
|
||||
writeLog(0, "Script '" + name + "' run failed: file is empty or not exist!", LOG_LEVEL_WARNING);
|
||||
return;
|
||||
}
|
||||
script_info info;
|
||||
if(arguments.size() >= 4 && !arguments[3].empty())
|
||||
{
|
||||
info.begin_time = time(NULL);
|
||||
info.timeout = to_int(arguments[3], 0);
|
||||
info.name = name;
|
||||
JS_SetInterruptHandler(JS_GetRuntime(context.ctx), timeout_checker, &info);
|
||||
}
|
||||
context.eval(script);
|
||||
}
|
||||
catch (qjs::exception)
|
||||
{
|
||||
script_print_stack(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::string list_cron_schedule(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
std::string token = getUrlArg(argument, "token");
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.StartObject();
|
||||
if(token != gAccessToken)
|
||||
{
|
||||
response.status_code = 403;
|
||||
writer.Key("code");
|
||||
writer.Int(403);
|
||||
writer.Key("data");
|
||||
writer.String("Unauthorized");
|
||||
writer.EndObject();
|
||||
return sb.GetString();
|
||||
}
|
||||
writer.Key("code");
|
||||
writer.Int(200);
|
||||
writer.Key("tasks");
|
||||
writer.StartArray();
|
||||
for(std::string &x : gCronTasks)
|
||||
{
|
||||
string_array arguments = split(x, "`");
|
||||
if(arguments.size() < 3)
|
||||
continue;
|
||||
writer.StartObject();
|
||||
std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2];
|
||||
writer.Key("name");
|
||||
writer.String(name.data());
|
||||
writer.Key("cronexp");
|
||||
writer.String(cronexp.data());
|
||||
writer.Key("path");
|
||||
writer.String(path.data());
|
||||
writer.EndObject();
|
||||
}
|
||||
writer.EndArray();
|
||||
writer.EndObject();
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
size_t cron_tick()
|
||||
{
|
||||
return cron.tick();
|
||||
}
|
||||
7
src/script/cron.h
Normal file
7
src/script/cron.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef CRON_H_INCLUDED
|
||||
#define CRON_H_INCLUDED
|
||||
|
||||
void refresh_schedule();
|
||||
size_t cron_tick();
|
||||
|
||||
#endif // CRON_H_INCLUDED
|
||||
@@ -3,11 +3,12 @@
|
||||
#include <duktape.h>
|
||||
#include <duk_module_node.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "multithread.h"
|
||||
#include "nodeinfo.h"
|
||||
#include "socket.h"
|
||||
#include "webget.h"
|
||||
#include "../utils/string.h"
|
||||
#include "../utils/string_hash.h"
|
||||
#include "../handler/webget.h"
|
||||
#include "../handler/multithread.h"
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/network.h"
|
||||
|
||||
extern int gCacheConfig;
|
||||
extern std::string gProxyConfig;
|
||||
@@ -132,14 +133,14 @@ static duk_ret_t fetch(duk_context *ctx)
|
||||
static duk_ret_t atob(duk_context *ctx)
|
||||
{
|
||||
std::string data = duk_safe_to_string(ctx, -1);
|
||||
duk_push_string(ctx, base64_encode(data).c_str());
|
||||
duk_push_string(ctx, base64Encode(data).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t btoa(duk_context *ctx)
|
||||
{
|
||||
std::string data = duk_safe_to_string(ctx, -1);
|
||||
data = base64_decode(data, true);
|
||||
data = base64Decode(data, true);
|
||||
duk_push_lstring(ctx, data.c_str(), data.size());
|
||||
return 1;
|
||||
}
|
||||
22
src/script/script.h
Normal file
22
src/script/script.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef SCRIPT_H_INCLUDED
|
||||
#define SCRIPT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <chaiscript/chaiscript.hpp>
|
||||
|
||||
template <typename... input_type, typename return_type> int evalScript(const std::string &script, return_type &return_value, input_type&... input_value)
|
||||
{
|
||||
chaiscript::ChaiScript chai;
|
||||
try
|
||||
{
|
||||
auto fun = chai.eval<std::function<return_type (input_type...)>>(script);
|
||||
return_value = fun(input_value...);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SCRIPT_H_INCLUDED
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <duktape.h>
|
||||
|
||||
#include "nodeinfo.h"
|
||||
#include "misc.h"
|
||||
|
||||
duk_context *duktape_init();
|
||||
int duktape_push_nodeinfo(duk_context *ctx, const nodeInfo &node);
|
||||
@@ -16,4 +17,8 @@ std::string duktape_get_res_str(duk_context *ctx);
|
||||
bool duktape_get_res_bool(duk_context *ctx);
|
||||
std::string duktape_get_err_stack(duk_context *ctx);
|
||||
|
||||
#define SCRIPT_ENGINE_INIT(name) \
|
||||
duk_context* name = duktape_init(); \
|
||||
defer(duk_destroy_heap(name);)
|
||||
|
||||
#endif // SCRIPT_DUKTAPE_H_INCLUDED
|
||||
551
src/script/script_quickjs.cpp
Normal file
551
src/script/script_quickjs.cpp
Normal file
@@ -0,0 +1,551 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <quickjspp.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include "../handler/multithread.h"
|
||||
#include "../handler/webget.h"
|
||||
#include "../parser/config/proxy.h"
|
||||
#include "../utils/map_extra.h"
|
||||
#include "../utils/system.h"
|
||||
#include "script_quickjs.h"
|
||||
|
||||
extern int gCacheConfig;
|
||||
extern std::string gProxyConfig;
|
||||
|
||||
std::string parseProxy(const std::string &source);
|
||||
|
||||
static const std::string qjs_require_module {R"(import * as std from 'std'
|
||||
import * as os from 'os'
|
||||
|
||||
let modules = {}
|
||||
|
||||
let debug = console.log
|
||||
{
|
||||
let _debugOptions = std.getenv('DEBUG')
|
||||
if (typeof _debugOptions == 'undefined' || _debugOptions.split(',').indexOf('require') === -1) {
|
||||
debug = function () {}
|
||||
}
|
||||
}
|
||||
|
||||
class CJSModule {
|
||||
constructor (id) {
|
||||
this.id = id
|
||||
this._failed = null
|
||||
this._loaded = false
|
||||
this.exports = {}
|
||||
}
|
||||
|
||||
load () {
|
||||
const __file = this.id
|
||||
const __dir = _basename(this.id)
|
||||
const _require = require
|
||||
|
||||
let ctx = { exports: {} }
|
||||
// Prevents modules from changing exports
|
||||
Object.seal(ctx)
|
||||
|
||||
const _mark = '<<SCRIPT>>'
|
||||
let _loaderTemplate = `(function _loader (exports, require, module, __filename, __dirname) {${_mark}})(ctx.exports, _require, ctx, __file, __dir)`
|
||||
|
||||
let _script = std.loadFile(__file)
|
||||
this._failed = _script === null
|
||||
if (this._failed) {
|
||||
return new Error(`Can't load script ${__file}`)
|
||||
}
|
||||
|
||||
_script = _loaderTemplate.replace('<<SCRIPT>>', _script)
|
||||
eval(_script)
|
||||
|
||||
this.exports = ctx.exports
|
||||
this._loaded = true
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _basename (path) {
|
||||
let idx = path.lastIndexOf('/')
|
||||
if (idx === 0)
|
||||
return '/'
|
||||
return path.substring(0, idx)
|
||||
}
|
||||
|
||||
function _statPath (path) {
|
||||
const [fstat, err] = os.stat(path)
|
||||
return {
|
||||
errno: err,
|
||||
isFile: fstat && (fstat.mode & os.S_IFREG) && true,
|
||||
isDir: fstat && (fstat.mode & os.S_IFDIR) && true
|
||||
}
|
||||
}
|
||||
|
||||
function _loadModule (path) {
|
||||
debug(`_loadModule# Module ${path}`)
|
||||
const [id, err] = os.realpath(path)
|
||||
if (err) {
|
||||
throw new Error(`Module require error: Can't get real module path for ${path}`)
|
||||
return
|
||||
}
|
||||
|
||||
debug(`_loadModule# id ${id}`)
|
||||
if (modules.hasOwnProperty(id)) {
|
||||
return modules[id]
|
||||
}
|
||||
|
||||
let _module = new CJSModule(id)
|
||||
modules[id] = _module
|
||||
|
||||
let _result = _module.load()
|
||||
if (_result !== true) {
|
||||
throw _result
|
||||
return
|
||||
}
|
||||
return _module
|
||||
}
|
||||
|
||||
function _lookupModule (path) {
|
||||
let fstat = _statPath(path)
|
||||
|
||||
debug(`_lookupModule# Looking for ${path}`)
|
||||
// Path found
|
||||
if (fstat.isFile) {
|
||||
debug(`_lookupModule# Found module file`)
|
||||
return path
|
||||
}
|
||||
|
||||
// Path not found
|
||||
if (fstat.errno) {
|
||||
debug(`_lookupModule# Not found module file`)
|
||||
// Try with '.js' extension
|
||||
if (!path.endsWith('.js') && _statPath(`${path}.js`).isFile) {
|
||||
debug(`_lookupModule# Found appending .js to file name`)
|
||||
return `${path}.js`
|
||||
}
|
||||
return new Error(`Error: Module ${path} not found!`)
|
||||
}
|
||||
|
||||
// Path found and it isn't a dir
|
||||
if (!fstat.isDir) {
|
||||
return new Error(`Error: Module file type not supported for ${path}`)
|
||||
}
|
||||
|
||||
// Path it's a dir
|
||||
let _path = null // Real path to module
|
||||
let _tryOthers = true // Keep trying?
|
||||
|
||||
debug(`_lookupModule# Path is a directory, trying options...`)
|
||||
// Try with package.json for NPM or YARN modules
|
||||
if (_statPath(`${path}/package.json`).isFile) {
|
||||
debug(`_lookupModule# It has package.json, looking for main script...`)
|
||||
let _pkg = JSON.parse(std.loadFile(`${path}/package.json`))
|
||||
if (_pkg && Object.keys(_pkg).indexOf('main') !== -1 && _pkg.main !== '' && _statPath(`${path}/${_pkg.main}`).isFile) {
|
||||
_tryOthers = false
|
||||
_path = `${path}/${_pkg.main}`
|
||||
debug(`_lookupModule# Found package main script!`)
|
||||
}
|
||||
}
|
||||
// Try other options
|
||||
if (_tryOthers && _statPath(`${path}/index.js`).isFile) {
|
||||
_tryOthers = false
|
||||
_path = `${path}/index.js`
|
||||
debug(`_lookupModule# Found package index.js file`)
|
||||
}
|
||||
if (_tryOthers && _statPath(`${path}/main.js`).isFile) {
|
||||
_tryOthers = false
|
||||
_path = `${path}/main.js`
|
||||
debug(`_lookupModule# Found package main.js file`)
|
||||
}
|
||||
|
||||
if (_path === null) {
|
||||
return new Error(`Error: Module ${path} is a directory, but not a package`)
|
||||
}
|
||||
|
||||
debug(`_lookupModule# Found module file: ${_path}`)
|
||||
// Returns what it founded
|
||||
return _path
|
||||
}
|
||||
|
||||
export function require (path) {
|
||||
if (typeof __filename == 'undefined') {
|
||||
debug('require# Calling from main script')
|
||||
} else {
|
||||
debug(`require# Calling from ${__filename} parent module`)
|
||||
}
|
||||
let _path = _lookupModule(path)
|
||||
|
||||
// Module not found
|
||||
if (_path instanceof Error) {
|
||||
throw _path
|
||||
return
|
||||
}
|
||||
|
||||
let _module = _loadModule(_path)
|
||||
|
||||
return _module.exports
|
||||
})"};
|
||||
|
||||
class qjs_fetch_Headers
|
||||
{
|
||||
public:
|
||||
qjs_fetch_Headers() {}
|
||||
|
||||
string_icase_map headers;
|
||||
|
||||
void append(const std::string &key, const std::string &value)
|
||||
{
|
||||
headers[key] = value;
|
||||
}
|
||||
void parse_from_string(const std::string &data)
|
||||
{
|
||||
headers.clear();
|
||||
string_array all_kv = split(data, "\r\n");
|
||||
for(std::string &x : all_kv)
|
||||
{
|
||||
size_t pos_colon = x.find(':');
|
||||
if(pos_colon == std::string::npos)
|
||||
continue;
|
||||
else if(pos_colon >= x.size() - 1)
|
||||
headers[x.substr(0, pos_colon)] = "";
|
||||
else
|
||||
headers[x.substr(0, pos_colon)] = x.substr(pos_colon + 2, x.size() - pos_colon);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class qjs_fetch_Request
|
||||
{
|
||||
public:
|
||||
qjs_fetch_Request() {}
|
||||
std::string method = "GET";
|
||||
std::string url;
|
||||
std::string proxy;
|
||||
qjs_fetch_Headers headers;
|
||||
std::string cookies;
|
||||
std::string postdata;
|
||||
qjs_fetch_Request(const std::string &url) : url(url) {}
|
||||
};
|
||||
|
||||
class qjs_fetch_Response
|
||||
{
|
||||
public:
|
||||
qjs_fetch_Response() {}
|
||||
int status_code = 200;
|
||||
std::string content;
|
||||
std::string cookies;
|
||||
qjs_fetch_Headers headers;
|
||||
};
|
||||
|
||||
namespace qjs
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
using string_map = std::map<std::string, std::string>;
|
||||
using string_icase_map = std::map<std::string, std::string, strICaseComp>;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct js_traits<detail::string_icase_map>
|
||||
{
|
||||
static detail::string_icase_map unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
string_icase_map res;
|
||||
JSPropertyEnum *props = nullptr, *props_begin;
|
||||
uint32_t len = 0;
|
||||
JS_GetOwnPropertyNames(ctx, &props, &len, v, 1);
|
||||
props_begin = props;
|
||||
while(len > 0)
|
||||
{
|
||||
auto key = JS_AtomToCString(ctx, props->atom);
|
||||
auto val = JS_GetProperty(ctx, v, props->atom);
|
||||
auto valData = JS_ToCString(ctx, val);
|
||||
res[key] = valData;
|
||||
JS_FreeCString(ctx, valData);
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_FreeCString(ctx, key);
|
||||
JS_FreeAtom(ctx, props->atom);
|
||||
props++;
|
||||
len--;
|
||||
}
|
||||
js_free(ctx, props_begin);
|
||||
return res;
|
||||
}
|
||||
static JSValue wrap(JSContext *ctx, const detail::string_icase_map &m) noexcept
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
|
||||
for(auto &kv : m)
|
||||
{
|
||||
auto value = JS_NewStringLen(ctx, kv.second.c_str(), kv.second.size());
|
||||
JS_SetPropertyStr(ctx, obj, kv.first.c_str(), value);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<qjs_fetch_Headers>
|
||||
{
|
||||
static qjs_fetch_Headers unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
qjs_fetch_Headers result;
|
||||
auto headers = JS_GetPropertyStr(ctx, v, "headers");
|
||||
result.headers = js_traits<detail::string_icase_map>::unwrap(ctx, headers);
|
||||
JS_FreeValue(ctx, headers);
|
||||
return result;
|
||||
}
|
||||
static JSValue wrap(JSContext *ctx, const qjs_fetch_Headers &h)
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "headers", js_traits<detail::string_icase_map>::wrap(ctx, h.headers));
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<qjs_fetch_Request>
|
||||
{
|
||||
static qjs_fetch_Request unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
qjs_fetch_Request request;
|
||||
auto headers = JS_GetPropertyStr(ctx, v, "headers");
|
||||
request.method = JS_GetPropertyToString(ctx, v, "method");
|
||||
request.url = JS_GetPropertyToString(ctx, v, "url");
|
||||
request.postdata = JS_GetPropertyToString(ctx, v, "data");
|
||||
request.proxy = JS_GetPropertyToString(ctx, v, "proxy");
|
||||
request.cookies = JS_GetPropertyToString(ctx, v, "cookies");
|
||||
request.headers = js_traits<qjs_fetch_Headers>::unwrap(ctx, headers);
|
||||
JS_FreeValue(ctx, headers);
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<qjs_fetch_Response>
|
||||
{
|
||||
static JSValue wrap(JSContext *ctx, const qjs_fetch_Response &r) noexcept
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "status_code", JS_NewInt32(ctx, r.status_code));
|
||||
JS_SetPropertyStr(ctx, obj, "headers", js_traits<qjs_fetch_Headers>::wrap(ctx, r.headers));
|
||||
JS_SetPropertyStr(ctx, obj, "data", JS_NewStringLen(ctx, r.content.c_str(), r.content.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "cookies", JS_NewStringLen(ctx, r.cookies.c_str(), r.cookies.size()));
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static std::string makeDataURI(const std::string &content, bool shouldBase64 = false)
|
||||
{
|
||||
if(shouldBase64)
|
||||
return "data:text/plain;base64," + base64Encode(content);
|
||||
else
|
||||
return "data:text/plain," + content;
|
||||
}
|
||||
|
||||
static qjs_fetch_Response qjs_fetch(qjs_fetch_Request request)
|
||||
{
|
||||
qjs_fetch_Response response;
|
||||
http_method method;
|
||||
switch(hash_(toUpper(request.method)))
|
||||
{
|
||||
case "GET"_hash:
|
||||
method = request.postdata.empty() ? HTTP_GET : HTTP_POST;
|
||||
break;
|
||||
case "POST"_hash:
|
||||
method = HTTP_POST;
|
||||
break;
|
||||
case "PATCH"_hash:
|
||||
method = HTTP_PATCH;
|
||||
break;
|
||||
case "HEAD"_hash:
|
||||
method = HTTP_HEAD;
|
||||
break;
|
||||
default:
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string response_headers;
|
||||
FetchArgument argument {method, request.url, request.proxy, request.postdata, &request.headers.headers, &request.cookies, 0};
|
||||
FetchResult result {&response.status_code, &response.content, &response_headers, &response.cookies};
|
||||
|
||||
webGet(argument, result);
|
||||
response.headers.parse_from_string(response_headers);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string getGeoIP(const std::string &address, const std::string &proxy)
|
||||
{
|
||||
return fetchFile("https://api.ip.sb/geoip/" + address, parseProxy(proxy), gCacheConfig);
|
||||
}
|
||||
|
||||
void script_runtime_init(qjs::Runtime &runtime)
|
||||
{
|
||||
js_std_init_handlers(runtime.rt);
|
||||
JS_SetModuleLoaderFunc(runtime.rt, nullptr, js_module_loader, nullptr);
|
||||
}
|
||||
|
||||
int ShowMsgbox(std::string title, std::string content, uint16_t type = 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(!type)
|
||||
type = MB_ICONINFORMATION;
|
||||
title = utf8ToACP(title);
|
||||
content = utf8ToACP(content);
|
||||
return MessageBoxA(NULL, content.c_str(), title.c_str(), type);
|
||||
#else
|
||||
return -1;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
template<typename... Targs>
|
||||
struct Lambda {
|
||||
template<typename Tret, typename T>
|
||||
static Tret lambda_ptr_exec(Targs... args) {
|
||||
return (Tret) (*(T*)fn<T>())(args...);
|
||||
}
|
||||
|
||||
template<typename Tret = void, typename Tfp = Tret(*)(Targs...), typename T>
|
||||
static Tfp ptr(T& t) {
|
||||
fn<T>(&t);
|
||||
return (Tfp) lambda_ptr_exec<Tret, T>;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void* fn(void* new_fn = nullptr) {
|
||||
static void* fn;
|
||||
if (new_fn != nullptr)
|
||||
fn = new_fn;
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t currentTime()
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
int script_context_init(qjs::Context &context)
|
||||
{
|
||||
try
|
||||
{
|
||||
js_init_module_os(context.ctx, "os");
|
||||
js_init_module_std(context.ctx, "std");
|
||||
js_std_add_helpers(context.ctx, 0, nullptr);
|
||||
context.eval(qjs_require_module, "<require>", JS_EVAL_TYPE_MODULE);
|
||||
auto &module = context.addModule("interUtils");
|
||||
module.class_<qjs_fetch_Headers>("Headers")
|
||||
.constructor<>()
|
||||
.fun<&qjs_fetch_Headers::headers>("headers")
|
||||
.fun<&qjs_fetch_Headers::append>("append")
|
||||
.fun<&qjs_fetch_Headers::parse_from_string>("parse");
|
||||
module.class_<qjs_fetch_Request>("Request")
|
||||
.constructor<>()
|
||||
.constructor<const std::string&>("Request")
|
||||
.fun<&qjs_fetch_Request::method>("method")
|
||||
.fun<&qjs_fetch_Request::url>("url")
|
||||
.fun<&qjs_fetch_Request::proxy>("proxy")
|
||||
.fun<&qjs_fetch_Request::postdata>("data")
|
||||
.fun<&qjs_fetch_Request::headers>("headers")
|
||||
.fun<&qjs_fetch_Request::cookies>("cookies");
|
||||
module.class_<qjs_fetch_Response>("Response")
|
||||
.constructor<>()
|
||||
.fun<&qjs_fetch_Response::status_code>("code")
|
||||
.fun<&qjs_fetch_Response::content>("data")
|
||||
.fun<&qjs_fetch_Response::cookies>("cookies")
|
||||
.fun<&qjs_fetch_Response::headers>("headers");
|
||||
/*
|
||||
module.class_<nodeInfo>("NodeInfo")
|
||||
.constructor<>()
|
||||
.fun<&nodeInfo::linkType>("LinkType")
|
||||
.fun<&nodeInfo::id>("ID")
|
||||
.fun<&nodeInfo::groupID>("GroupID")
|
||||
.fun<&nodeInfo::group>("Group")
|
||||
.fun<&nodeInfo::remarks>("Remark")
|
||||
.fun<&nodeInfo::server>("Hostname")
|
||||
.fun<&nodeInfo::port>("Port")
|
||||
.fun<&nodeInfo::proxyStr>("ProxyInfo");
|
||||
*/
|
||||
module.class_<Proxy>("Proxy")
|
||||
.constructor<>()
|
||||
.fun<&Proxy::Type>("Type")
|
||||
.fun<&Proxy::Id>("Id")
|
||||
.fun<&Proxy::GroupId>("GroupId")
|
||||
.fun<&Proxy::Group>("Group")
|
||||
.fun<&Proxy::Remark>("Remark")
|
||||
.fun<&Proxy::Hostname>("Hostname")
|
||||
.fun<&Proxy::Port>("Port")
|
||||
.fun<&Proxy::Username>("Username")
|
||||
.fun<&Proxy::Password>("Password")
|
||||
.fun<&Proxy::EncryptMethod>("EncryptMethod")
|
||||
.fun<&Proxy::Plugin>("Plugin")
|
||||
.fun<&Proxy::PluginOption>("PluginOption")
|
||||
.fun<&Proxy::Protocol>("Protocol")
|
||||
.fun<&Proxy::ProtocolParam>("ProtocolParam")
|
||||
.fun<&Proxy::OBFS>("OBFS")
|
||||
.fun<&Proxy::OBFSParam>("OBFSParam")
|
||||
.fun<&Proxy::UserId>("UserId")
|
||||
.fun<&Proxy::AlterId>("AlterId")
|
||||
.fun<&Proxy::TransferProtocol>("TransferProtocol")
|
||||
.fun<&Proxy::FakeType>("FakeType")
|
||||
.fun<&Proxy::TLSSecure>("TLSSecure")
|
||||
.fun<&Proxy::Host>("Host")
|
||||
.fun<&Proxy::Path>("Path")
|
||||
.fun<&Proxy::Edge>("Edge")
|
||||
.fun<&Proxy::QUICSecure>("QUICSecure")
|
||||
.fun<&Proxy::QUICSecret>("QUICSecret")
|
||||
.fun<&Proxy::UDP>("UDP")
|
||||
.fun<&Proxy::TCPFastOpen>("TCPFastOpen")
|
||||
.fun<&Proxy::AllowInsecure>("AllowInsecure")
|
||||
.fun<&Proxy::TLS13>("TLS13");
|
||||
context.global().add<&makeDataURI>("makeDataURI")
|
||||
.add<&qjs_fetch>("fetch")
|
||||
.add<&base64Encode>("atob")
|
||||
.add<&base64Decode>("btoa")
|
||||
.add<¤tTime>("time")
|
||||
.add<&sleepMs>("sleep")
|
||||
.add<&ShowMsgbox>("msgbox")
|
||||
.add<&getUrlArg>("getUrlArg")
|
||||
.add<&fileGet>("fileGet")
|
||||
.add<&fileWrite>("fileWrite");
|
||||
context.eval(R"(
|
||||
import * as interUtils from 'interUtils'
|
||||
globalThis.Request = interUtils.Request
|
||||
globalThis.Response = interUtils.Response
|
||||
globalThis.Headers = interUtils.Headers
|
||||
globalThis.NodeInfo = interUtils.NodeInfo
|
||||
import * as std from 'std'
|
||||
import * as os from 'os'
|
||||
globalThis.std = std
|
||||
globalThis.os = os
|
||||
import { require } from '<require>'
|
||||
globalThis.require = require
|
||||
)", "<import>", JS_EVAL_TYPE_MODULE);
|
||||
return 0;
|
||||
}
|
||||
catch(qjs::exception)
|
||||
{
|
||||
script_print_stack(context);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int script_cleanup(qjs::Context &context)
|
||||
{
|
||||
js_std_loop(context.ctx);
|
||||
js_std_free_handlers(JS_GetRuntime(context.ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void script_print_stack(qjs::Context &context)
|
||||
{
|
||||
auto exc = context.getException();
|
||||
std::cerr << (std::string) exc << std::endl;
|
||||
if((bool) exc["stack"])
|
||||
std::cerr << (std::string) exc["stack"] << std::endl;
|
||||
}
|
||||
213
src/script/script_quickjs.h
Normal file
213
src/script/script_quickjs.h
Normal file
@@ -0,0 +1,213 @@
|
||||
#ifndef SCRIPT_QUICKJS_H_INCLUDED
|
||||
#define SCRIPT_QUICKJS_H_INCLUDED
|
||||
|
||||
#include <quickjspp.hpp>
|
||||
|
||||
#include "../parser/config/proxy.h"
|
||||
#include "../utils/defer.h"
|
||||
|
||||
void script_runtime_init(qjs::Runtime &runtime);
|
||||
int script_context_init(qjs::Context &context);
|
||||
int script_cleanup(qjs::Context &context);
|
||||
void script_print_stack(qjs::Context &context);
|
||||
|
||||
inline std::string JS_GetPropertyToString(JSContext *ctx, JSValue v, const char* prop)
|
||||
{
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
auto valData = JS_ToCString(ctx, val);
|
||||
std::string result = valData;
|
||||
JS_FreeCString(ctx, valData);
|
||||
JS_FreeValue(ctx, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int JS_GetPropertyToInt32(JSContext *ctx, JSValue v, const char* prop, int32_t def_value = 0)
|
||||
{
|
||||
int32_t result = def_value;
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
int32_t ret = JS_ToInt32(ctx, &result, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if(ret != 0) return def_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int JS_GetPropertyToUInt32(JSContext *ctx, JSValue v, const char* prop, uint32_t def_value = 0)
|
||||
{
|
||||
uint32_t result = def_value;
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
int ret = JS_ToUint32(ctx, &result, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if(ret != 0) return def_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool JS_GetPropertyToBool(JSContext *ctx, JSValue v, const char* prop, bool def_value = false)
|
||||
{
|
||||
bool result = def_value;
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
int ret = JS_ToBool(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if(ret != 0) return def_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace qjs
|
||||
{
|
||||
template<>
|
||||
struct js_traits<tribool>
|
||||
{
|
||||
static JSValue wrap(JSContext *ctx, const tribool &t) noexcept
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "value", JS_NewBool(ctx, t.get()));
|
||||
JS_SetPropertyStr(ctx, obj, "isDefined", JS_NewBool(ctx, !t.is_undef()));
|
||||
return obj;
|
||||
}
|
||||
static tribool unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
tribool t;
|
||||
bool defined = JS_GetPropertyToBool(ctx, v, "isDefined");
|
||||
if(defined)
|
||||
{
|
||||
bool value = JS_GetPropertyToBool(ctx, v, "value");
|
||||
t.set(value);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
static tribool JS_GetPropertyToTriBool(JSContext *ctx, JSValue v, const char* prop)
|
||||
{
|
||||
auto obj = JS_GetPropertyStr(ctx, v, prop);
|
||||
auto tb = unwrap(ctx, obj);
|
||||
JS_FreeValue(ctx, obj);
|
||||
return tb;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<Proxy>
|
||||
{
|
||||
static JSValue wrap(JSContext *ctx, const Proxy &n) noexcept
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
/*
|
||||
JS_SetPropertyStr(ctx, obj, "LinkType", JS_NewInt32(ctx, n.linkType));
|
||||
JS_SetPropertyStr(ctx, obj, "ID", JS_NewInt32(ctx, n.id));
|
||||
JS_SetPropertyStr(ctx, obj, "GroupID", JS_NewInt32(ctx, n.groupID));
|
||||
JS_SetPropertyStr(ctx, obj, "Group", JS_NewStringLen(ctx, n.group.c_str(), n.group.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Remark", JS_NewStringLen(ctx, n.remarks.c_str(), n.remarks.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Server", JS_NewStringLen(ctx, n.server.c_str(), n.server.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Port", JS_NewInt32(ctx, n.port));
|
||||
JS_SetPropertyStr(ctx, obj, "ProxyInfo", JS_NewStringLen(ctx, n.proxyStr.c_str(), n.proxyStr.size()));
|
||||
*/
|
||||
JS_SetPropertyStr(ctx, obj, "Type", JS_NewInt32(ctx, n.Type));
|
||||
JS_SetPropertyStr(ctx, obj, "ID", JS_NewInt32(ctx, n.Id));
|
||||
JS_SetPropertyStr(ctx, obj, "GroupID", JS_NewInt32(ctx, n.GroupId));
|
||||
JS_SetPropertyStr(ctx, obj, "Group", JS_NewStringLen(ctx, n.Group.c_str(), n.Group.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Remark", JS_NewStringLen(ctx, n.Remark.c_str(), n.Remark.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Server", JS_NewStringLen(ctx, n.Hostname.c_str(), n.Hostname.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Port", JS_NewInt32(ctx, n.Port));
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "Username", JS_NewStringLen(ctx, n.Username.c_str(), n.Username.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Password", JS_NewStringLen(ctx, n.Password.c_str(), n.Password.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "EncryptMethod", JS_NewStringLen(ctx, n.EncryptMethod.c_str(), n.EncryptMethod.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Plugin", JS_NewStringLen(ctx, n.Plugin.c_str(), n.Plugin.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "PluginOption", JS_NewStringLen(ctx, n.PluginOption.c_str(), n.PluginOption.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Protocol", JS_NewStringLen(ctx, n.Protocol.c_str(), n.Protocol.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "ProtocolParam", JS_NewStringLen(ctx, n.ProtocolParam.c_str(), n.ProtocolParam.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "OBFS", JS_NewStringLen(ctx, n.OBFS.c_str(), n.OBFS.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "OBFSParam", JS_NewStringLen(ctx, n.OBFSParam.c_str(), n.OBFSParam.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "UserId", JS_NewStringLen(ctx, n.UserId.c_str(), n.UserId.size()));
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "AlterId", JS_NewInt32(ctx, n.AlterId));
|
||||
JS_SetPropertyStr(ctx, obj, "TransferProtocol", JS_NewStringLen(ctx, n.TransferProtocol.c_str(), n.TransferProtocol.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "FakeType", JS_NewStringLen(ctx, n.FakeType.c_str(), n.FakeType.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "TLSSecure", JS_NewBool(ctx, n.TLSSecure));
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "Host", JS_NewStringLen(ctx, n.Host.c_str(), n.Host.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Path", JS_NewStringLen(ctx, n.Path.c_str(), n.Path.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Edge", JS_NewStringLen(ctx, n.Edge.c_str(), n.Edge.size()));
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "QUICSecure", JS_NewStringLen(ctx, n.QUICSecure.c_str(), n.QUICSecure.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "QUICSecret", JS_NewStringLen(ctx, n.QUICSecret.c_str(), n.QUICSecret.size()));
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "UDP", js_traits<tribool>::wrap(ctx, n.UDP));
|
||||
JS_SetPropertyStr(ctx, obj, "TCPFastOpen", js_traits<tribool>::wrap(ctx, n.TCPFastOpen));
|
||||
JS_SetPropertyStr(ctx, obj, "AllowInsecure", js_traits<tribool>::wrap(ctx, n.AllowInsecure));
|
||||
JS_SetPropertyStr(ctx, obj, "TLS13", js_traits<tribool>::wrap(ctx, n.TLS13));
|
||||
|
||||
return obj;
|
||||
}
|
||||
static Proxy unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
Proxy node;
|
||||
/*
|
||||
node.linkType = JS_GetPropertyToInt32(ctx, v, "LinkType");
|
||||
node.id = JS_GetPropertyToInt32(ctx, v, "ID");
|
||||
node.groupID = JS_GetPropertyToInt32(ctx, v, "GroupID");
|
||||
node.group = JS_GetPropertyToString(ctx, v, "Group");
|
||||
node.remarks = JS_GetPropertyToString(ctx, v, "Remark");
|
||||
node.server = JS_GetPropertyToString(ctx, v, "Server");
|
||||
node.port = JS_GetPropertyToInt32(ctx, v, "Port");
|
||||
node.proxyStr = JS_GetPropertyToString(ctx, v, "ProxyInfo");
|
||||
*/
|
||||
node.Id = JS_GetPropertyToUInt32(ctx, v, "Id");
|
||||
node.GroupId = JS_GetPropertyToUInt32(ctx, v, "GroupId");
|
||||
node.Group = JS_GetPropertyToString(ctx, v, "Group");
|
||||
node.Remark = JS_GetPropertyToString(ctx, v, "Remark");
|
||||
node.Hostname = JS_GetPropertyToString(ctx, v, "Hostname");
|
||||
node.Port = JS_GetPropertyToUInt32(ctx, v, "Port");
|
||||
|
||||
node.Username = JS_GetPropertyToString(ctx, v, "Username");
|
||||
node.Password = JS_GetPropertyToString(ctx, v, "Password");
|
||||
node.EncryptMethod = JS_GetPropertyToString(ctx, v, "EncryptMethod");
|
||||
node.Plugin = JS_GetPropertyToString(ctx, v, "Plugin");
|
||||
node.PluginOption = JS_GetPropertyToString(ctx, v, "PluginOption");
|
||||
node.Protocol = JS_GetPropertyToString(ctx, v, "Protocol");
|
||||
node.ProtocolParam = JS_GetPropertyToString(ctx, v, "ProtocolParam");
|
||||
node.OBFS = JS_GetPropertyToString(ctx, v, "OBFS");
|
||||
node.OBFSParam = JS_GetPropertyToString(ctx, v, "OBFSParam");
|
||||
node.UserId = JS_GetPropertyToString(ctx, v, "UserId");
|
||||
node.AlterId = JS_GetPropertyToUInt32(ctx, v, "AlterId");
|
||||
node.TransferProtocol = JS_GetPropertyToString(ctx, v, "TransferProtocol");
|
||||
node.FakeType = JS_GetPropertyToString(ctx, v, "FakeType");
|
||||
node.TLSSecure = JS_GetPropertyToBool(ctx, v, "TLSSecure");
|
||||
|
||||
node.Host = JS_GetPropertyToString(ctx, v, "Host");
|
||||
node.Path = JS_GetPropertyToString(ctx, v, "Path");
|
||||
node.Edge = JS_GetPropertyToString(ctx, v, "Edge");
|
||||
|
||||
node.QUICSecure = JS_GetPropertyToString(ctx, v, "QUICSecure");
|
||||
node.QUICSecret = JS_GetPropertyToString(ctx, v, "QUICSecret");
|
||||
|
||||
node.UDP = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "UDP");
|
||||
node.TCPFastOpen = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "TCPFastOpen");
|
||||
node.AllowInsecure = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "AllowInsecure");
|
||||
node.TLS13 = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "TLS13");
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void script_safe_runner(qjs::Runtime *runtime, qjs::Context *context, Fn runnable, bool clean_context = false)
|
||||
{
|
||||
if(runtime != nullptr)
|
||||
{
|
||||
qjs::Context *internal_context = context;
|
||||
bool use_internal = false;
|
||||
defer(if(use_internal && internal_context) delete internal_context;)
|
||||
if(clean_context)
|
||||
{
|
||||
internal_context = new qjs::Context(*runtime);
|
||||
use_internal = true;
|
||||
script_context_init(*internal_context);
|
||||
}
|
||||
if(internal_context != nullptr)
|
||||
runnable(*internal_context);
|
||||
if(clean_context)
|
||||
delete internal_context;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SCRIPT_QUICKJS_H_INCLUDED
|
||||
38
src/server/socket.h
Normal file
38
src/server/socket.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef SOCKET_H_INCLUDED
|
||||
#define SOCKET_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0501
|
||||
#endif // WINVER
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
//translate windows functions to linux functions
|
||||
#define SOCKET int
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define closesocket close
|
||||
#define SOCKADDR_IN sockaddr_in
|
||||
#define ZeroMemory(d,l) memset((d), 0, (l))
|
||||
#define ioctlsocket ioctl
|
||||
#ifndef SA_INTERRUPT
|
||||
#define SA_INTERRUPT 0 //ignore this setting
|
||||
#endif
|
||||
#define SD_BOTH SHUT_RDWR
|
||||
#ifndef __hpux
|
||||
#include <sys/select.h>
|
||||
#endif /* __hpux */
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
typedef sockaddr *LPSOCKADDR;
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // SOCKET_H_INCLUDED
|
||||
372
src/server/webserver.cpp
Normal file
372
src/server/webserver.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "../utils/file.h"
|
||||
#include "../utils/string.h"
|
||||
#include "../utils/system.h"
|
||||
#include "socket.h"
|
||||
#include "webserver.h"
|
||||
|
||||
using string_array = std::vector<std::string>;
|
||||
int def_timeout = 5;
|
||||
|
||||
struct responseRoute
|
||||
{
|
||||
std::string method;
|
||||
std::string path;
|
||||
std::string content_type;
|
||||
response_callback rc;
|
||||
};
|
||||
|
||||
std::vector<responseRoute> responses;
|
||||
|
||||
//for use of multi-thread
|
||||
int max_send_failure = 10;
|
||||
std::atomic_bool SERVER_EXIT_FLAG(false);
|
||||
std::atomic_int working_thread(0);
|
||||
|
||||
int sendall(SOCKET sock, std::string data)
|
||||
{
|
||||
unsigned int total = 0, bytesleft = data.size();
|
||||
int sent = 0;
|
||||
const char* datastr = data.data();
|
||||
while(total < bytesleft)
|
||||
{
|
||||
sent = send(sock, datastr + total, bytesleft, 0);
|
||||
if(sent < 0)
|
||||
{
|
||||
std::cerr<<strerror(errno)<<std::endl;
|
||||
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
total += sent;
|
||||
bytesleft -= sent;
|
||||
}
|
||||
return sent == -1 ? -1 : 0;
|
||||
}
|
||||
|
||||
void wrong_req(SOCKET sock)
|
||||
{
|
||||
std::string response = "HTTP/1.1 501 Not Implemented\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"The command is not yet completed\r\n";
|
||||
|
||||
if (sendall(sock, response) == -1)
|
||||
{
|
||||
std::cerr << "Sending failed!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void file_not_found(std::string arguments, int sock)
|
||||
{
|
||||
std::string prompt_info = "Not found: " + arguments;
|
||||
std::string response = "HTTP/1.1 404 Not Found\r\n"
|
||||
"Content-Type: text/plain\r\nConnection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
|
||||
"Content-Length: " + std::__cxx11::to_string(prompt_info.size()) + "\r\n\r\n" + prompt_info + "\r\n";
|
||||
|
||||
if (sendall(sock, response) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void send_header(SOCKET send_to, std::string content_type)
|
||||
{
|
||||
std::string header = "HTTP/1.1 200 OK\r\nConnection: close\r\nCache-Control: no-cache, no-store, must-revalidate\r\nAccess-Control-Allow-Origin: *\r\n";
|
||||
if(content_type.size())
|
||||
header += "Content-Type: " + content_type + "\r\n";
|
||||
if(sendall(send_to, header) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
}
|
||||
}
|
||||
|
||||
void send_options_header(SOCKET send_to)
|
||||
{
|
||||
std::string header = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n";
|
||||
if(sendall(send_to, header) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
}
|
||||
}
|
||||
|
||||
char* file_type(const char* arg)
|
||||
{
|
||||
char * temp;
|
||||
if ((temp=strrchr(arg,'.')) != NULL)
|
||||
{
|
||||
return temp+1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void serve_options(SOCKET sock)
|
||||
{
|
||||
send_options_header(sock);
|
||||
std::string extra_header = "Content-Length: 0\r\n\r\n";
|
||||
sendall(sock, extra_header);
|
||||
sendall(sock, "\r\n\r\n");
|
||||
}
|
||||
|
||||
void serve_content(SOCKET sock, std::string type, std::string content)
|
||||
{
|
||||
send_header(sock, type.data());
|
||||
std::string extra_header = "Content-Length: " + std::__cxx11::to_string(content.size()) + "\r\n";
|
||||
sendall(sock, extra_header);
|
||||
send(sock, "\r\n", 2, 0);
|
||||
if (sendall(sock, content) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
}
|
||||
sendall(sock, "\r\n\r\n");
|
||||
}
|
||||
|
||||
void send_file(std::string arguments, int sock)
|
||||
{
|
||||
char* extension = file_type(arguments.data());
|
||||
std::string content_type = "text/plain", data;
|
||||
char sizestr[16] = {};
|
||||
int len;
|
||||
|
||||
if (strcmp(extension, "html") == 0)
|
||||
{
|
||||
content_type = "text/html";
|
||||
}
|
||||
if (strcmp(extension, "gif") == 0)
|
||||
{
|
||||
content_type = "image/gif";
|
||||
}
|
||||
if (strcmp(extension, "jpg") == 0)
|
||||
{
|
||||
content_type = "image/jpg";
|
||||
}
|
||||
|
||||
send_header(sock, content_type);
|
||||
sendall(sock, "Transfer-Encoding: chunked\r\n\r\n");
|
||||
data = fileGet(arguments);
|
||||
len = data.size();
|
||||
sprintf(sizestr, "%x\r\n", len);
|
||||
if (sendall(sock, sizestr) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
}
|
||||
if (sendall(sock, data) == -1)
|
||||
{
|
||||
printf("Sending error!");
|
||||
}
|
||||
len = 2;
|
||||
sendall(sock, "\r\n");
|
||||
|
||||
len = 7;
|
||||
sendall(sock, "0\r\n\r\n");
|
||||
}
|
||||
|
||||
int setTimeout(SOCKET s, int timeout)
|
||||
{
|
||||
int ret = -1;
|
||||
#ifdef _WIN32
|
||||
ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(int));
|
||||
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(int));
|
||||
#else
|
||||
struct timeval timeo = {timeout / 1000, (timeout % 1000) * 1000};
|
||||
ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeo, sizeof(timeo));
|
||||
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeo, sizeof(timeo));
|
||||
#endif
|
||||
def_timeout = timeout;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void handle_req(std::string request, int client_sock)
|
||||
{
|
||||
working_thread++;
|
||||
std::cerr<<"worker startup"<<std::endl;
|
||||
string_array vArray;
|
||||
char command[16] = {};
|
||||
char arguments[BUFSIZ] = {};
|
||||
std::string uri, args, target, postdata;
|
||||
|
||||
if (sscanf(request.data(), "%s%s", command, arguments) != 2)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
std::cerr<<"handle_cmd: "<<command<<"\n"<<"handle_path: "<<arguments<<"\n";
|
||||
|
||||
vArray = split(arguments, "?");
|
||||
|
||||
if(vArray.size() > 2)
|
||||
{
|
||||
wrong_req(client_sock);
|
||||
goto end;
|
||||
}
|
||||
else if(vArray.size() > 1)
|
||||
{
|
||||
uri = vArray[0];
|
||||
args = vArray[1];
|
||||
}
|
||||
else
|
||||
uri = arguments;
|
||||
|
||||
if(strcmp(command, "POST") == 0)
|
||||
{
|
||||
if(request.find("\r\n\r\n") != request.npos)
|
||||
postdata = request.substr(request.find("\r\n\r\n") + 4);
|
||||
}
|
||||
else if(strcmp(command, "OPTIONS") == 0)
|
||||
{
|
||||
serve_options(client_sock);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for(std::vector<responseRoute>::iterator iter = responses.begin(); iter != responses.end(); ++iter)
|
||||
{
|
||||
if(iter->method.compare(command) == 0 && iter->path == uri)
|
||||
{
|
||||
response_callback &rc = iter->rc;
|
||||
serve_content(client_sock, iter->content_type, rc(args, postdata));
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
file_not_found(uri, client_sock);
|
||||
|
||||
end:
|
||||
std::cerr<<"worker stop"<<std::endl;
|
||||
sleepMs(1);
|
||||
closesocket(client_sock);
|
||||
working_thread--;
|
||||
}
|
||||
|
||||
void append_response(const std::string &type, const std::string &request, const std::string &content_type, response_callback response)
|
||||
{
|
||||
responseRoute rr;
|
||||
rr.method = type;
|
||||
rr.path = request;
|
||||
rr.content_type = content_type;
|
||||
rr.rc = response;
|
||||
responses.push_back(std::move(rr));
|
||||
}
|
||||
|
||||
void stop_web_server()
|
||||
{
|
||||
SERVER_EXIT_FLAG = true;
|
||||
}
|
||||
|
||||
int start_web_server(void *argv)
|
||||
{
|
||||
struct listener_args *args = (listener_args*)argv;
|
||||
args->max_workers = 1;
|
||||
return start_web_server_multi(args);
|
||||
}
|
||||
|
||||
int start_web_server_multi(void *argv)
|
||||
{
|
||||
//log startup
|
||||
struct listener_args *args = (listener_args*)argv;
|
||||
std::string listen_address = args->listen_address, request;
|
||||
int port = args->port, max_conn = args->max_conn, max_workers = args->max_workers, numbytes, worker_index = 0;
|
||||
socklen_t sock_size = sizeof(struct sockaddr_in);
|
||||
char buf[BUFSIZ];
|
||||
struct sockaddr_in user_socket, server_addr;
|
||||
SOCKET acc_socket;
|
||||
int server_socket, fail_counter = 0;
|
||||
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
std::thread workers[max_workers];
|
||||
|
||||
if (server_socket == -1)
|
||||
{
|
||||
//log socket error
|
||||
std::cerr<<"socket build error!"<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZeroMemory(&server_addr, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons((short)port);
|
||||
server_addr.sin_addr.s_addr = inet_addr(listen_address.data());
|
||||
if (::bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
|
||||
{
|
||||
//log bind error
|
||||
std::cerr<<"socket bind error!"<<std::endl;
|
||||
closesocket(server_socket);
|
||||
return 0;
|
||||
}
|
||||
if (listen(server_socket, max_conn) == -1 )
|
||||
{
|
||||
//log listen error
|
||||
std::cerr<<"socket listen error!"<<std::endl;
|
||||
closesocket(server_socket);
|
||||
return 0;
|
||||
}
|
||||
setTimeout(server_socket, 500);
|
||||
|
||||
while(true)
|
||||
{
|
||||
acc_socket = accept(server_socket, (struct sockaddr *)&user_socket, &sock_size); //wait for connection
|
||||
if(acc_socket < 0)
|
||||
{
|
||||
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
{
|
||||
fail_counter++;
|
||||
if(fail_counter > max_send_failure)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
request = "";
|
||||
while(true) //receive the complete request
|
||||
{
|
||||
numbytes = recv(acc_socket, buf, BUFSIZ - 1, 0);
|
||||
if(numbytes > 0) //received
|
||||
request.append(buf);
|
||||
if(numbytes < 0)
|
||||
{
|
||||
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(numbytes == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
//handle_req(buf, acc_socket);
|
||||
|
||||
while(working_thread >= max_workers)
|
||||
{
|
||||
sleepMs(10);
|
||||
if(SERVER_EXIT_FLAG)
|
||||
break;
|
||||
}
|
||||
while(workers[worker_index].get_id() != std::thread::id())
|
||||
{
|
||||
worker_index++;
|
||||
}
|
||||
workers[worker_index] = std::thread(handle_req, request, acc_socket);
|
||||
workers[worker_index].detach();
|
||||
worker_index++;
|
||||
if(worker_index > max_workers)
|
||||
worker_index = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -4,12 +4,14 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "../utils/map_extra.h"
|
||||
|
||||
struct Request
|
||||
{
|
||||
std::string method;
|
||||
std::string url;
|
||||
std::string argument;
|
||||
std::map<std::string, std::string> headers;
|
||||
string_icase_map headers;
|
||||
std::string postdata;
|
||||
};
|
||||
|
||||
@@ -17,10 +19,10 @@ struct Response
|
||||
{
|
||||
int status_code = 200;
|
||||
std::string content_type;
|
||||
std::map<std::string, std::string> headers;
|
||||
string_icase_map headers;
|
||||
};
|
||||
|
||||
typedef std::string (*response_callback)(Request&, Response&); //process arguments and POST data and return served-content
|
||||
using response_callback = std::string (*)(Request&, Response&); //process arguments and POST data and return served-content
|
||||
|
||||
#define RESPONSE_CALLBACK_ARGS Request &request, Response &response
|
||||
|
||||
@@ -30,6 +32,8 @@ struct listener_args
|
||||
int port;
|
||||
int max_conn;
|
||||
int max_workers;
|
||||
void (*looper_callback)() = nullptr;
|
||||
uint32_t looper_interval = 200;
|
||||
};
|
||||
|
||||
void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response);
|
||||
@@ -10,13 +10,18 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <thread>
|
||||
|
||||
#include "misc.h"
|
||||
#include "webserver.h"
|
||||
#include "../utils/file_extra.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/stl_extra.h"
|
||||
#include "../utils/string.h"
|
||||
#include "../utils/urlencode.h"
|
||||
#include "socket.h"
|
||||
#include "logger.h"
|
||||
#include "webserver.h"
|
||||
|
||||
extern std::string user_agent_str;
|
||||
std::atomic_bool SERVER_EXIT_FLAG(false);
|
||||
@@ -122,7 +127,7 @@ static inline int process_request(Request &request, Response &response, std::str
|
||||
|
||||
for(responseRoute &x : responses)
|
||||
{
|
||||
if(request.method == "OPTIONS" && matchSpaceSeparatedList(replace_all_distinct(request.postdata, ",", ""), x.method) && x.path == request.url)
|
||||
if(request.method == "OPTIONS" && matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -171,7 +176,7 @@ void OnReq(evhttp_request *req, void *args)
|
||||
//std::cerr<<"Accept connection from client "<<client_ip<<":"<<client_port<<"\n";
|
||||
writeLog(0, "Accept connection from client " + std::string(client_ip) + ":" + std::to_string(client_port), LOG_LEVEL_DEBUG);
|
||||
|
||||
if(internal_flag != NULL) //check for flag existence
|
||||
if(internal_flag != NULL)
|
||||
{
|
||||
evhttp_send_error(req, 500, "Loop request detected!");
|
||||
return;
|
||||
@@ -183,7 +188,7 @@ void OnReq(evhttp_request *req, void *args)
|
||||
{
|
||||
request.postdata.assign((char *)EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
|
||||
if(req_content_type != NULL && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0)
|
||||
request.postdata = UrlDecode(request.postdata);
|
||||
request.postdata = urlDecode(request.postdata);
|
||||
}
|
||||
else if(req_ac_method != NULL)
|
||||
{
|
||||
@@ -371,7 +376,11 @@ int start_web_server_multi(void *argv)
|
||||
return -1;
|
||||
}
|
||||
while (!SERVER_EXIT_FLAG)
|
||||
sleep(200); //block forever until receive stop signal
|
||||
{
|
||||
if(args->looper_callback != nullptr)
|
||||
args->looper_callback();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(args->looper_interval)); //block forever until receive stop signal
|
||||
}
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
event_base_loopbreak(base[i]); //stop the loop
|
||||
58
src/socket.h
58
src/socket.h
@@ -1,58 +0,0 @@
|
||||
#ifndef SOCKET_H_INCLUDED
|
||||
#define SOCKET_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0501
|
||||
#endif // WINVER
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
//translate windows functions to linux functions
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#define SOCKET int
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define closesocket close
|
||||
#define SOCKADDR_IN sockaddr_in
|
||||
#define ZeroMemory(d,l) memset((d), 0, (l))
|
||||
#define ioctlsocket ioctl
|
||||
#ifndef SA_INTERRUPT
|
||||
#define SA_INTERRUPT 0 //ignore this setting
|
||||
#endif
|
||||
#define SD_BOTH SHUT_RDWR
|
||||
#ifndef __hpux
|
||||
#include <sys/select.h>
|
||||
#endif /* __hpux */
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
typedef sockaddr *LPSOCKADDR;
|
||||
#endif // _WIN32
|
||||
|
||||
#define BUF_SIZE 8192
|
||||
|
||||
int getNetworkType(std::string addr);
|
||||
int Send(SOCKET sHost, const char* data, int len, int flags);
|
||||
int Recv(SOCKET sHost, char* data, int len, int flags);
|
||||
int socks5_do_auth_userpass(SOCKET sHost, std::string user, std::string pass);
|
||||
int setTimeout(SOCKET s, int timeout);
|
||||
int startConnect(SOCKET sHost, std::string addr, int port);
|
||||
int simpleSend(std::string addr, int port, std::string data);
|
||||
int send_simple(SOCKET sHost, std::string data);
|
||||
std::string hostnameToIPAddr(const std::string &host);
|
||||
int connectSocks5(SOCKET sHost, std::string username, std::string password);
|
||||
int connectThruSocks(SOCKET sHost, std::string host, int port);
|
||||
int connectThruHTTP(SOCKET sHost, std::string username, std::string password, std::string dsthost, int dstport);
|
||||
int checkPort(int startport);
|
||||
|
||||
#endif // SOCKET_H_INCLUDED
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef SPEEDTESTUTIL_H_INCLUDED
|
||||
#define SPEEDTESTUTIL_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "misc.h"
|
||||
#include "nodeinfo.h"
|
||||
|
||||
std::string vmessConstruct(const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &id, const std::string &aid, const std::string &net, const std::string &cipher, const std::string &path, const std::string &host, const std::string &edge, const std::string &tls, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
std::string ssrConstruct(const std::string &group, const std::string &remarks, const std::string &remarks_base64, const std::string &server, const std::string &port, const std::string &protocol, const std::string &method, const std::string &obfs, const std::string &password, const std::string &obfsparam, const std::string &protoparam, bool libev, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
std::string ssConstruct(const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &method, const std::string &plugin, const std::string &pluginopts, bool libev, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
std::string socksConstruct(const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
std::string httpConstruct(const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, bool tls, tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
std::string trojanConstruct(const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
|
||||
std::string snellConstruct(const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
|
||||
void explodeVmess(std::string vmess, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeSSR(std::string ssr, bool ss_libev, bool libev, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeSS(std::string ss, bool libev, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeTrojan(std::string trojan, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeQuan(const std::string &quan, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeStdVMess(std::string vmess, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeShadowrocket(std::string kit, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeKitsunebi(std::string kit, const std::string &custom_port, nodeInfo &node);
|
||||
/// Parse a link
|
||||
void explode(const std::string &link, bool sslibev, bool ssrlibev, const std::string &custom_port, nodeInfo &node);
|
||||
void explodeSSD(std::string link, bool libev, const std::string &custom_port, std::vector<nodeInfo> &nodes);
|
||||
void explodeSub(std::string sub, bool sslibev, bool ssrlibev, const std::string &custom_port, std::vector<nodeInfo> &nodes);
|
||||
int explodeConf(std::string filepath, const std::string &custom_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
|
||||
int explodeConfContent(const std::string &content, const std::string &custom_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
|
||||
bool chkIgnore(const nodeInfo &node, string_array &exclude_remarks, string_array &include_remarks);
|
||||
void filterNodes(std::vector<nodeInfo> &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID);
|
||||
bool getSubInfoFromHeader(const std::string &header, std::string &result);
|
||||
bool getSubInfoFromNodes(const std::vector<nodeInfo> &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result);
|
||||
bool getSubInfoFromSSD(const std::string &sub, std::string &result);
|
||||
unsigned long long streamToInt(const std::string &stream);
|
||||
|
||||
#endif // SPEEDTESTUTIL_H_INCLUDED
|
||||
3095
src/subexport.cpp
3095
src/subexport.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
#ifndef SUBEXPORT_H_INCLUDED
|
||||
#define SUBEXPORT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
|
||||
#include "misc.h"
|
||||
#include "ini_reader.h"
|
||||
#include "nodeinfo.h"
|
||||
|
||||
enum ruleset_type
|
||||
{
|
||||
RULESET_SURGE,
|
||||
RULESET_QUANX,
|
||||
RULESET_CLASH_DOMAIN,
|
||||
RULESET_CLASH_IPCIDR,
|
||||
RULESET_CLASH_CLASSICAL
|
||||
};
|
||||
|
||||
struct ruleset_content
|
||||
{
|
||||
std::string rule_group;
|
||||
std::string rule_path;
|
||||
std::string rule_path_typed;
|
||||
int rule_type = RULESET_SURGE;
|
||||
std::shared_future<std::string> rule_content;
|
||||
int update_interval = 0;
|
||||
};
|
||||
|
||||
struct extra_settings
|
||||
{
|
||||
bool enable_rule_generator = true;
|
||||
bool overwrite_original_rules = true;
|
||||
string_array rename_array;
|
||||
string_array emoji_array;
|
||||
bool add_emoji = false;
|
||||
bool remove_emoji = false;
|
||||
bool append_proxy_type = false;
|
||||
bool nodelist = false;
|
||||
bool sort_flag = false;
|
||||
bool filter_deprecated = false;
|
||||
bool clash_new_field_name = false;
|
||||
bool clash_script = false;
|
||||
std::string surge_ssr_path;
|
||||
std::string managed_config_prefix;
|
||||
std::string quanx_dev_id;
|
||||
tribool udp = tribool();
|
||||
tribool tfo = tribool();
|
||||
tribool skip_cert_verify = tribool();
|
||||
tribool tls13 = tribool();
|
||||
bool clash_classical_ruleset = false;
|
||||
std::string sort_script = "";
|
||||
std::string clash_proxies_style = "flow";
|
||||
};
|
||||
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
|
||||
void preprocessNodes(std::vector<nodeInfo> &nodes, const extra_settings &ext);
|
||||
|
||||
std::string netchToClash(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, const extra_settings &ext);
|
||||
void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, const extra_settings &ext);
|
||||
std::string netchToSurge(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, const extra_settings &ext);
|
||||
std::string netchToMellow(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
void netchToMellow(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
std::string netchToLoon(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
std::string netchToSSSub(std::string &base_conf, std::vector<nodeInfo> &nodes, const extra_settings &ext);
|
||||
std::string netchToSingle(std::vector<nodeInfo> &nodes, int types, const extra_settings &ext);
|
||||
std::string netchToQuanX(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
std::string netchToQuan(std::vector<nodeInfo> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, const extra_settings &ext);
|
||||
std::string netchToSSD(std::vector<nodeInfo> &nodes, std::string &group, std::string &userinfo, const extra_settings &ext);
|
||||
|
||||
#endif // SUBEXPORT_H_INCLUDED
|
||||
149
src/utils/base64/base64.cpp
Normal file
149
src/utils/base64/base64.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <string>
|
||||
|
||||
#include "../string.h"
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string base64Encode(const std::string &string_to_encode)
|
||||
{
|
||||
char const* bytes_to_encode = string_to_encode.data();
|
||||
unsigned int in_len = string_to_encode.size();
|
||||
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--)
|
||||
{
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3)
|
||||
{
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
int j;
|
||||
for(j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
std::string base64Decode(const std::string &encoded_string, bool accept_urlsafe)
|
||||
{
|
||||
string_size in_len = encoded_string.size();
|
||||
string_size i = 0;
|
||||
string_size in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3], uchar;
|
||||
static unsigned char dtable[256], itable[256], table_ready = 0;
|
||||
std::string ret;
|
||||
|
||||
// Should not need thread_local with the flag...
|
||||
if (!table_ready)
|
||||
{
|
||||
// No memset needed for static/TLS
|
||||
for (string_size k = 0; k < base64_chars.length(); k++)
|
||||
{
|
||||
uchar = base64_chars[k]; // make compiler happy
|
||||
dtable[uchar] = k; // decode (find)
|
||||
itable[uchar] = 1; // is_base64
|
||||
}
|
||||
const unsigned char dash = '-', add = '+', under = '_', slash = '/';
|
||||
// Add urlsafe table
|
||||
dtable[dash] = dtable[add]; itable[dash] = 2;
|
||||
dtable[under] = dtable[slash]; itable[under] = 2;
|
||||
table_ready = 1;
|
||||
}
|
||||
|
||||
while (in_len-- && (encoded_string[in_] != '='))
|
||||
{
|
||||
uchar = encoded_string[in_]; // make compiler happy
|
||||
if (!(accept_urlsafe ? itable[uchar] : (itable[uchar] == 1))) // break away from the while condition
|
||||
{
|
||||
ret += uchar; // not base64 encoded data, copy to result
|
||||
in_++;
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
char_array_4[i++] = uchar;
|
||||
in_++;
|
||||
if (i == 4)
|
||||
{
|
||||
for (string_size j = 0; j < 4; j++)
|
||||
char_array_4[j] = dtable[char_array_4[j]];
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for (string_size j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for (string_size j = 0; j <4; j++)
|
||||
char_array_4[j] = dtable[char_array_4[j]];
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (string_size j = 0; (j < i - 1); j++)
|
||||
ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string urlSafeBase64Reverse(const std::string &encoded_string)
|
||||
{
|
||||
return replaceAllDistinct(replaceAllDistinct(encoded_string, "-", "+"), "_", "/");
|
||||
}
|
||||
|
||||
std::string urlSafeBase64Apply(const std::string &encoded_string)
|
||||
{
|
||||
return replaceAllDistinct(replaceAllDistinct(replaceAllDistinct(encoded_string, "+", "-"), "/", "_"), "=", "");
|
||||
}
|
||||
|
||||
std::string urlSafeBase64Decode(const std::string &encoded_string)
|
||||
{
|
||||
return base64Decode(encoded_string, true);
|
||||
}
|
||||
|
||||
std::string urlSafeBase64Encode(const std::string &string_to_encode)
|
||||
{
|
||||
return urlSafeBase64Apply(base64Encode(string_to_encode));
|
||||
}
|
||||
14
src/utils/base64/base64.h
Normal file
14
src/utils/base64/base64.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef BASE64_H_INCLUDED
|
||||
#define BASE64_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string base64Decode(const std::string &encoded_string, bool accept_urlsafe = false);
|
||||
std::string base64Encode(const std::string &string_to_encode);
|
||||
|
||||
std::string urlSafeBase64Apply(const std::string &encoded_string);
|
||||
std::string urlSafeBase64Reverse(const std::string &encoded_string);
|
||||
std::string urlSafeBase64Decode(const std::string &encoded_string);
|
||||
std::string urlSafeBase64Encode(const std::string &string_to_encode);
|
||||
|
||||
#endif // BASE64_H_INCLUDED
|
||||
7
src/utils/bitwise.h
Normal file
7
src/utils/bitwise.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef BITWISE_H_INCLUDED
|
||||
#define BITWISE_H_INCLUDED
|
||||
|
||||
#define GETBIT(x,n) (((int)x < 1) ? 0 : ((x >> (n - 1)) & 1))
|
||||
#define SETBIT(x,n,v) x ^= (-v ^ x) & (1UL << (n - 1))
|
||||
|
||||
#endif // BITWISE_H_INCLUDED
|
||||
22
src/utils/checkpoint.h
Normal file
22
src/utils/checkpoint.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef CHECKPOINT_H_INCLUDED
|
||||
#define CHECKPOINT_H_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
inline std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> start_time;
|
||||
|
||||
inline void checkpoint()
|
||||
{
|
||||
if(start_time == std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds>())
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
else
|
||||
{
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
std::chrono::duration duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
|
||||
std::cerr<<duration.count()<<"\n";
|
||||
start_time = end_time;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CHECKPOINT_H_INCLUDED
|
||||
100
src/utils/codepage.cpp
Normal file
100
src/utils/codepage.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif // _WIN32
|
||||
|
||||
// ANSI code page (GBK on 936) to UTF8
|
||||
std::string acpToUTF8(const std::string &str_src)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const char* strGBK = str_src.c_str();
|
||||
int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0);
|
||||
wchar_t* wstr = new wchar_t[len + 1];
|
||||
memset(wstr, 0, len + 1);
|
||||
MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len);
|
||||
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
|
||||
char* str = new char[len + 1];
|
||||
memset(str, 0, len + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
|
||||
std::string strTemp = str;
|
||||
delete[] wstr;
|
||||
delete[] str;
|
||||
return strTemp;
|
||||
#else
|
||||
return str_src;
|
||||
#endif // _WIN32
|
||||
/*
|
||||
std::vector<wchar_t> buffer(str_src.size());
|
||||
#ifdef _MSC_VER
|
||||
std::locale loc("zh-CN");
|
||||
#else
|
||||
std::locale loc{"zh_CN.GB2312"};
|
||||
#endif // _MSC_VER
|
||||
wchar_t *pwszNew = nullptr;
|
||||
const char *pszNew = nullptr;
|
||||
mbstate_t state = {};
|
||||
int res = std::use_facet<std::codecvt<wchar_t, char, mbstate_t> >
|
||||
(loc).in(state, str_src.data(), str_src.data() + str_src.size(), pszNew,
|
||||
buffer.data(), buffer.data() + buffer.size(), pwszNew);
|
||||
|
||||
if(res == std::codecvt_base::ok)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> cutf8;
|
||||
return cutf8.to_bytes(std::wstring(buffer.data(), pwszNew));
|
||||
}
|
||||
return str_src;
|
||||
*/
|
||||
}
|
||||
|
||||
// UTF8 to ANSI code page (GBK on 936)
|
||||
std::string utf8ToACP(const std::string &str_src)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const char* strUTF8 = str_src.data();
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
|
||||
wchar_t* wszGBK = new wchar_t[len + 1];
|
||||
memset(wszGBK, 0, len * 2 + 2);
|
||||
MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
|
||||
len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
|
||||
char* szGBK = new char[len + 1];
|
||||
memset(szGBK, 0, len + 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
|
||||
std::string strTemp(szGBK);
|
||||
if (wszGBK)
|
||||
delete[] wszGBK;
|
||||
if (szGBK)
|
||||
delete[] szGBK;
|
||||
return strTemp;
|
||||
#else
|
||||
return str_src;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// std::string to wstring
|
||||
void stringToWstring(std::wstring& szDst, const std::string &str)
|
||||
{
|
||||
std::string temp = str;
|
||||
int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, NULL,0);
|
||||
wchar_t* wszUtf8 = new wchar_t[len + 1];
|
||||
memset(wszUtf8, 0, len * 2 + 2);
|
||||
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, (LPWSTR)wszUtf8, len);
|
||||
szDst = wszUtf8;
|
||||
//std::wstring r = wszUtf8;
|
||||
delete[] wszUtf8;
|
||||
}
|
||||
|
||||
std::string wstringToString(LPWSTR str)
|
||||
{
|
||||
std::string result;
|
||||
size_t srclen = wcslen(str);
|
||||
size_t len = WideCharToMultiByte(CP_ACP, 0, str, srclen, 0, 0, 0, 0);
|
||||
result.resize(len);
|
||||
WideCharToMultiByte(CP_ACP, 0, str, srclen, result.data(), len, 0, 0);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
/* Unimplemented: std::codecvt_utf8 */
|
||||
#endif // _WIN32
|
||||
9
src/utils/codepage.h
Normal file
9
src/utils/codepage.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef CODEPAGE_H_INCLUDED
|
||||
#define CODEPAGE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string utf8ToACP(const std::string &str_src);
|
||||
std::string acpToUTF8(const std::string &str_src);
|
||||
|
||||
#endif // CODEPAGE_H_INCLUDED
|
||||
10
src/utils/defer.h
Normal file
10
src/utils/defer.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef DEFER_H_INCLUDED
|
||||
#define DEFER_H_INCLUDED
|
||||
|
||||
#define CONCAT(a,b) a ## b
|
||||
#define DO_CONCAT(a,b) CONCAT(a,b)
|
||||
template <typename T> class __defer_struct final {private: T fn; bool __cancelled = false; public: explicit __defer_struct(T func) : fn(std::move(func)) {} ~__defer_struct() {if(!__cancelled) fn();} void cancel() {__cancelled = true;} };
|
||||
//#define defer(x) std::unique_ptr<void> DO_CONCAT(__defer_deleter_,__LINE__) (nullptr, [&](...){x});
|
||||
#define defer(x) __defer_struct DO_CONCAT(__defer_deleter,__LINE__) ([&](...){x;});
|
||||
|
||||
#endif // DEFER_H_INCLUDED
|
||||
108
src/utils/file.cpp
Normal file
108
src/utils/file.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
bool isInScope(const std::string &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(path.find(":\\") != path.npos || path.find("..") != path.npos)
|
||||
return false;
|
||||
#else
|
||||
if(startsWith(path, "/") || path.find("..") != path.npos)
|
||||
return false;
|
||||
#endif // _WIN32
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Add preprocessor option to disable (open web service safety)
|
||||
std::string fileGet(const std::string &path, bool scope_limit)
|
||||
{
|
||||
std::string content;
|
||||
|
||||
if(scope_limit && !isInScope(path))
|
||||
return std::string();
|
||||
|
||||
std::FILE *fp = std::fopen(path.c_str(), "rb");
|
||||
if(fp)
|
||||
{
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
long tot = std::ftell(fp);
|
||||
/*
|
||||
char *data = new char[tot + 1];
|
||||
data[tot] = '\0';
|
||||
std::rewind(fp);
|
||||
std::fread(&data[0], 1, tot, fp);
|
||||
std::fclose(fp);
|
||||
content.assign(data, tot);
|
||||
delete[] data;
|
||||
*/
|
||||
content.resize(tot);
|
||||
std::rewind(fp);
|
||||
std::fread(&content[0], 1, tot, fp);
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
std::stringstream sstream;
|
||||
std::ifstream infile;
|
||||
infile.open(path, std::ios::binary);
|
||||
if(infile)
|
||||
{
|
||||
sstream<<infile.rdbuf();
|
||||
infile.close();
|
||||
content = sstream.str();
|
||||
}
|
||||
*/
|
||||
return content;
|
||||
}
|
||||
|
||||
bool fileExist(const std::string &path, bool scope_limit)
|
||||
{
|
||||
//using c++17 standard, but may cause problem on clang
|
||||
//return std::filesystem::exists(path);
|
||||
if(scope_limit && !isInScope(path))
|
||||
return false;
|
||||
struct stat st;
|
||||
return stat(path.data(), &st) == 0 && S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
bool fileCopy(const std::string &source, const std::string &dest)
|
||||
{
|
||||
std::ifstream infile;
|
||||
std::ofstream outfile;
|
||||
infile.open(source, std::ios::binary);
|
||||
if(!infile)
|
||||
return false;
|
||||
outfile.open(dest, std::ios::binary);
|
||||
if(!outfile)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
outfile<<infile.rdbuf();
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
infile.close();
|
||||
outfile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
int fileWrite(const std::string &path, const std::string &content, bool overwrite)
|
||||
{
|
||||
/*
|
||||
std::fstream outfile;
|
||||
std::ios_base::openmode mode = overwrite ? std::ios_base::out : std::ios_base::app;
|
||||
mode |= std::ios_base::binary;
|
||||
outfile.open(path, mode);
|
||||
outfile << content;
|
||||
outfile.close();
|
||||
return 0;
|
||||
*/
|
||||
const char *mode = overwrite ? "wb" : "ab";
|
||||
std::FILE *fp = std::fopen(path.c_str(), mode);
|
||||
std::fwrite(content.c_str(), 1, content.size(), fp);
|
||||
std::fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
51
src/utils/file.h
Normal file
51
src/utils/file.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef FILE_H_INCLUDED
|
||||
#define FILE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <unistd.h>
|
||||
#define PATH_SLASH "\\"
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#define PATH_SLASH "//"
|
||||
#endif // _WIN32
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
std::string fileGet(const std::string &path, bool scope_limit = false);
|
||||
bool fileExist(const std::string &path, bool scope_limit = false);
|
||||
bool fileCopy(const std::string &source, const std::string &dest);
|
||||
int fileWrite(const std::string &path, const std::string &content, bool overwrite);
|
||||
|
||||
template<typename F>
|
||||
int operateFiles(const std::string &path, F &&op)
|
||||
{
|
||||
DIR* dir = opendir(path.data());
|
||||
if(!dir)
|
||||
return -1;
|
||||
struct dirent* dp;
|
||||
while((dp = readdir(dir)) != NULL)
|
||||
{
|
||||
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
|
||||
{
|
||||
if(op(dp->d_name))
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int md(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return mkdir(path);
|
||||
#else
|
||||
return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
#endif // FILE_H_INCLUDED
|
||||
18
src/utils/file_extra.h
Normal file
18
src/utils/file_extra.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef FILE_EXTRA_H_INCLUDED
|
||||
#define FILE_EXTRA_H_INCLUDED
|
||||
|
||||
#include "base64/base64.h"
|
||||
#include "file.h"
|
||||
#include "md5/md5_interface.h"
|
||||
|
||||
inline std::string fileToBase64(const std::string &filepath)
|
||||
{
|
||||
return base64Encode(fileGet(filepath));
|
||||
}
|
||||
|
||||
inline std::string fileGetMD5(const std::string &filepath)
|
||||
{
|
||||
return getMD5(fileGet(filepath));
|
||||
}
|
||||
|
||||
#endif // FILE_EXTRA_H_INCLUDED
|
||||
@@ -2,12 +2,14 @@
|
||||
#define INI_READER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
|
||||
#include "misc.h"
|
||||
#include "../codepage.h"
|
||||
#include "../file_extra.h"
|
||||
#include "../string.h"
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -19,10 +21,10 @@ enum
|
||||
INIREADER_EXCEPTION_NONE
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::multimap<std::string, std::string>> ini_data_struct;
|
||||
typedef std::multimap<std::string, std::string> string_multimap;
|
||||
typedef std::vector<std::string> string_array;
|
||||
typedef std::string::size_type string_size;
|
||||
using ini_data_struct = std::map<std::string, std::multimap<std::string, std::string>>;
|
||||
using string_multimap = std::multimap<std::string, std::string>;
|
||||
using string_array = std::vector<std::string>;
|
||||
using string_size = std::string::size_type;
|
||||
|
||||
class INIReader
|
||||
{
|
||||
@@ -58,9 +60,9 @@ private:
|
||||
inline bool __priv_chk_ignore(const std::string §ion)
|
||||
{
|
||||
bool excluded = false, included = false;
|
||||
excluded = std::find(exclude_sections.cbegin(), exclude_sections.cend(), section) != exclude_sections.cend();
|
||||
excluded = std::find(exclude_sections.begin(), exclude_sections.end(), section) != exclude_sections.end();
|
||||
if(include_sections.size())
|
||||
included = std::find(include_sections.cbegin(), include_sections.cend(), section) != include_sections.cend();
|
||||
included = std::find(include_sections.begin(), include_sections.end(), section) != include_sections.end();
|
||||
else
|
||||
included = true;
|
||||
|
||||
@@ -90,6 +92,17 @@ private:
|
||||
return "Undefined";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> inline void eraseElements(std::vector<T> &target)
|
||||
{
|
||||
target.clear();
|
||||
target.shrink_to_fit();
|
||||
}
|
||||
|
||||
template <typename T> inline void eraseElements(T &target)
|
||||
{
|
||||
T().swap(target);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Set this flag to true to do a UTF8-To-GBK conversion before parsing data. Only useful in Windows.
|
||||
@@ -220,8 +233,8 @@ public:
|
||||
char delimiter = getLineBreak(content);
|
||||
|
||||
EraseAll(); //first erase all data
|
||||
if(do_utf8_to_gbk && is_str_utf8(content))
|
||||
content = UTF8ToACP(content); //do conversion if flag is set
|
||||
if(do_utf8_to_gbk && isStrUTF8(content))
|
||||
content = utf8ToACP(content); //do conversion if flag is set
|
||||
|
||||
if(store_isolated_line && isolated_items_section.size())
|
||||
{
|
||||
@@ -236,12 +249,11 @@ public:
|
||||
while(getline(strStrm, strLine, delimiter)) //get one line of content
|
||||
{
|
||||
last_error_index++;
|
||||
strLine = trimWhitespace(strLine);
|
||||
string_size lineSize = strLine.size(), pos_equal = strLine.find("=");
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
strLine.erase(--lineSize);
|
||||
if((!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) && !inDirectSaveSection) //empty lines and comments are ignored
|
||||
continue;
|
||||
ProcessEscapeChar(strLine);
|
||||
processEscapeChar(strLine);
|
||||
if(strLine[0] == '[' && strLine[lineSize - 1] == ']') //is a section title
|
||||
{
|
||||
thisSection = strLine.substr(1, lineSize - 2); //save section title
|
||||
@@ -954,7 +966,7 @@ public:
|
||||
if(iter->first != "{NONAME}")
|
||||
content += iter->first + "=";
|
||||
itemVal = iter->second;
|
||||
ProcessEscapeCharReverse(itemVal);
|
||||
processEscapeCharReverse(itemVal);
|
||||
content += itemVal + "\n";
|
||||
if(std::next(iter) == section.end())
|
||||
strsize = itemVal.size();
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
enum
|
||||
{
|
||||
LOG_TYPE_INFO,
|
||||
@@ -30,19 +28,7 @@ enum
|
||||
LOG_LEVEL_VERBOSE
|
||||
};
|
||||
|
||||
extern std::string resultPath, logPath;
|
||||
|
||||
int makeDir(const char *path);
|
||||
std::string getTime(int type);
|
||||
void logInit(bool rpcmode);
|
||||
void resultInit();
|
||||
void writeLog(int type, const std::string &content, int level = LOG_LEVEL_VERBOSE);
|
||||
void logEOF();
|
||||
|
||||
/*
|
||||
void resultInit(bool export_with_maxspeed);
|
||||
void writeResult(nodeInfo *node, bool export_with_maxspeed);
|
||||
void resultEOF(std::string traffic, int worknodes, int totnodes);
|
||||
void exportResult(std::string outpath, std::string utiljspath, std::string stylepath, bool export_with_maxspeed);
|
||||
*/
|
||||
#endif // LOGGER_H_INCLUDED
|
||||
18
src/utils/map_extra.h
Normal file
18
src/utils/map_extra.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef MAP_EXTRA_H_INCLUDED
|
||||
#define MAP_EXTRA_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
|
||||
struct strICaseComp
|
||||
{
|
||||
bool operator() (const std::string &lhs, const std::string &rhs) const
|
||||
{
|
||||
return strcasecmp(lhs.c_str(), rhs.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
using string_icase_map = std::map<std::string, std::string, strICaseComp>;
|
||||
|
||||
#endif // MAP_EXTRA_H_INCLUDED
|
||||
50
src/utils/md5/md5_interface.h
Normal file
50
src/utils/md5/md5_interface.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef MD5_INTERFACE_H_INCLUDED
|
||||
#define MD5_INTERFACE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
inline std::string getMD5(const std::string &data)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
/*
|
||||
unsigned int i = 0;
|
||||
unsigned char digest[16] = {};
|
||||
|
||||
#ifdef USE_MBEDTLS
|
||||
mbedtls_md5_context ctx;
|
||||
|
||||
mbedtls_md5_init(&ctx);
|
||||
mbedtls_md5_starts_ret(&ctx);
|
||||
mbedtls_md5_update_ret(&ctx, reinterpret_cast<const unsigned char*>(data.data()), data.size());
|
||||
mbedtls_md5_finish_ret(&ctx, reinterpret_cast<unsigned char*>(&digest));
|
||||
mbedtls_md5_free(&ctx);
|
||||
#else
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, data.data(), data.size());
|
||||
MD5_Final((unsigned char *)&digest, &ctx);
|
||||
#endif // USE_MBEDTLS
|
||||
|
||||
char tmp[3] = {};
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
snprintf(tmp, 3, "%02x", digest[i]);
|
||||
result += tmp;
|
||||
}
|
||||
*/
|
||||
|
||||
char result_str[MD5_STRING_SIZE];
|
||||
md5::md5_t md5;
|
||||
md5.process(data.data(), data.size());
|
||||
md5.finish();
|
||||
md5.get_string(result_str);
|
||||
result.assign(result_str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // MD5_INTERFACE_H_INCLUDED
|
||||
154
src/utils/network.cpp
Normal file
154
src/utils/network.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include "../server/socket.h"
|
||||
#include "string.h"
|
||||
#include "regexp.h"
|
||||
|
||||
std::string hostnameToIPAddr(const std::string &host)
|
||||
{
|
||||
int retVal;
|
||||
std::string retAddr;
|
||||
char cAddr[128] = {};
|
||||
struct sockaddr_in *target;
|
||||
struct sockaddr_in6 *target6;
|
||||
struct addrinfo hint = {}, *retAddrInfo, *cur;
|
||||
retVal = getaddrinfo(host.data(), NULL, &hint, &retAddrInfo);
|
||||
if(retVal != 0)
|
||||
{
|
||||
freeaddrinfo(retAddrInfo);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
for(cur = retAddrInfo; cur != NULL; cur = cur->ai_next)
|
||||
{
|
||||
if(cur->ai_family == AF_INET)
|
||||
{
|
||||
target = reinterpret_cast<struct sockaddr_in *>(cur->ai_addr);
|
||||
inet_ntop(AF_INET, &target->sin_addr, cAddr, sizeof(cAddr));
|
||||
break;
|
||||
}
|
||||
else if(cur->ai_family == AF_INET6)
|
||||
{
|
||||
target6 = reinterpret_cast<struct sockaddr_in6 *>(cur->ai_addr);
|
||||
inet_ntop(AF_INET6, &target6->sin6_addr, cAddr, sizeof(cAddr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
retAddr.assign(cAddr);
|
||||
freeaddrinfo(retAddrInfo);
|
||||
return retAddr;
|
||||
}
|
||||
|
||||
bool isIPv4(const std::string &address)
|
||||
{
|
||||
return regMatch(address, "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
|
||||
}
|
||||
|
||||
bool isIPv6(const std::string &address)
|
||||
{
|
||||
std::vector<std::string> regLists = {"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$", "^((?:[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((?:([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$", "^(::(?:[0-9A-Fa-f]{1,4})(?::[0-9A-Fa-f]{1,4}){5})|((?:[0-9A-Fa-f]{1,4})(?::[0-9A-Fa-f]{1,4}){5}::)$"};
|
||||
for(unsigned int i = 0; i < regLists.size(); i++)
|
||||
{
|
||||
if(regMatch(address, regLists[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void urlParse(std::string &url, std::string &host, std::string &path, int &port, bool &isTLS)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
string_size pos;
|
||||
|
||||
if(regMatch(url, "^https://(.*)"))
|
||||
isTLS = true;
|
||||
url = regReplace(url, "^(http|https)://", "");
|
||||
pos = url.find("/");
|
||||
if(pos == url.npos)
|
||||
{
|
||||
host = url;
|
||||
path = "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
host = url.substr(0, pos);
|
||||
path = url.substr(pos);
|
||||
}
|
||||
pos = host.rfind(":");
|
||||
if(regFind(host, "\\[(.*)\\]")) //IPv6
|
||||
{
|
||||
args = split(regReplace(host, "\\[(.*)\\](.*)", "$1,$2"), ",");
|
||||
if(args.size() == 2) //with port
|
||||
port = to_int(args[1].substr(1));
|
||||
host = args[0];
|
||||
}
|
||||
else if(pos != host.npos)
|
||||
{
|
||||
port = to_int(host.substr(pos + 1));
|
||||
host = host.substr(0, pos);
|
||||
}
|
||||
if(port == 0)
|
||||
{
|
||||
if(isTLS)
|
||||
port = 443;
|
||||
else
|
||||
port = 80;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getFormData(const std::string &raw_data)
|
||||
{
|
||||
std::stringstream strstrm;
|
||||
std::string line;
|
||||
|
||||
std::string boundary;
|
||||
std::string file; /* actual file content */
|
||||
|
||||
int i = 0;
|
||||
|
||||
strstrm<<raw_data;
|
||||
|
||||
while (std::getline(strstrm, line))
|
||||
{
|
||||
if(i == 0)
|
||||
boundary = line.substr(0, line.length() - 1); // Get boundary
|
||||
else if(startsWith(line, boundary))
|
||||
break; // The end
|
||||
else if(line.length() == 1)
|
||||
{
|
||||
// Time to get raw data
|
||||
char c;
|
||||
int bl = boundary.length();
|
||||
bool endfile = false;
|
||||
char buffer[256];
|
||||
while(!endfile)
|
||||
{
|
||||
int j = 0;
|
||||
while(j < 256 && strstrm.get(c) && !endfile)
|
||||
{
|
||||
buffer[j] = c;
|
||||
int k = 0;
|
||||
// Verify if we are at the end
|
||||
while(boundary[bl - 1 - k] == buffer[j - k])
|
||||
{
|
||||
if(k >= bl - 1)
|
||||
{
|
||||
// We are at the end of the file
|
||||
endfile = true;
|
||||
break;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
file.append(buffer, j);
|
||||
j = 0;
|
||||
};
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
20
src/utils/network.h
Normal file
20
src/utils/network.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef NETWORK_H_INCLUDED
|
||||
#define NETWORK_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "string.h"
|
||||
|
||||
std::string getFormData(const std::string &raw_data);
|
||||
std::string getUrlArg(const std::string &url, const std::string &request);
|
||||
bool isIPv4(const std::string &address);
|
||||
bool isIPv6(const std::string &address);
|
||||
void urlParse(std::string &url, std::string &host, std::string &path, int &port, bool &isTLS);
|
||||
std::string hostnameToIPAddr(const std::string &host);
|
||||
|
||||
inline bool isLink(const std::string &url)
|
||||
{
|
||||
return startsWith(url, "https://") || startsWith(url, "http://") || startsWith(url, "data:");
|
||||
}
|
||||
|
||||
#endif // NETWORK_H_INCLUDED
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef RAPIDJSON_EXTRA_H_INCLUDED
|
||||
#define RAPIDJSON_EXTRA_H_INCLUDED
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
template <typename T> void exception_thrower(T e)
|
||||
{
|
||||
@@ -17,23 +17,23 @@ template <typename T> void exception_thrower(T e)
|
||||
#include <rapidjson/writer.h>
|
||||
#include <string>
|
||||
|
||||
static inline void operator >> (const rapidjson::Value& value, std::string& i)
|
||||
inline void operator >> (const rapidjson::Value& value, std::string& i)
|
||||
{
|
||||
if(value.IsNull())
|
||||
i = std::string();
|
||||
else if(value.IsInt64())
|
||||
i = std::to_string(value.GetInt64());
|
||||
else if(value.IsDouble())
|
||||
i = std::to_string(value.GetDouble());
|
||||
else if(value.IsString())
|
||||
i = std::string(value.GetString());
|
||||
else if(value.IsInt64())
|
||||
i = std::to_string(value.GetInt64());
|
||||
else if(value.IsBool())
|
||||
i = value.GetBool() ? "true" : "false";
|
||||
else if(value.IsDouble())
|
||||
i = std::to_string(value.GetDouble());
|
||||
else
|
||||
i = std::string();
|
||||
}
|
||||
|
||||
static inline void operator >> (const rapidjson::Value& value, int& i)
|
||||
inline void operator >> (const rapidjson::Value& value, int& i)
|
||||
{
|
||||
if(value.IsNull())
|
||||
i = 0;
|
||||
@@ -47,7 +47,7 @@ static inline void operator >> (const rapidjson::Value& value, int& i)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
static inline std::string GetMember(const rapidjson::Value& value, const std::string &member)
|
||||
inline std::string GetMember(const rapidjson::Value& value, const std::string &member)
|
||||
{
|
||||
std::string retStr;
|
||||
if(value.IsObject() && value.HasMember(member.data()))
|
||||
@@ -55,14 +55,14 @@ static inline std::string GetMember(const rapidjson::Value& value, const std::st
|
||||
return retStr;
|
||||
}
|
||||
|
||||
static inline void GetMember(const rapidjson::Value& value, const std::string &member, std::string& target)
|
||||
inline void GetMember(const rapidjson::Value& value, const std::string &member, std::string& target)
|
||||
{
|
||||
std::string retStr = GetMember(value, member);
|
||||
if(retStr.size())
|
||||
target.assign(retStr);
|
||||
}
|
||||
|
||||
static inline std::string SerializeObject(const rapidjson::Value& value)
|
||||
inline std::string SerializeObject(const rapidjson::Value& value)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer_json(sb);
|
||||
202
src/utils/regexp.cpp
Normal file
202
src/utils/regexp.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
#ifdef USE_STD_REGEX
|
||||
#include <regex>
|
||||
#else
|
||||
*/
|
||||
#include <jpcre2.hpp>
|
||||
using jp = jpcre2::select<char>;
|
||||
//#endif // USE_STD_REGEX
|
||||
|
||||
/*
|
||||
#ifdef USE_STD_REGEX
|
||||
bool regValid(const std::string ®)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::regex r(reg, std::regex::ECMAScript);
|
||||
return true;
|
||||
}
|
||||
catch (std::regex_error &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool regFind(const std::string &src, const std::string &match)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::regex::flag_type flags = std::regex::extended | std::regex::ECMAScript;
|
||||
std::string target = match;
|
||||
if(match.find("(?i)") == 0)
|
||||
{
|
||||
target.erase(0, 4);
|
||||
flags |= std::regex::icase;
|
||||
}
|
||||
std::regex reg(target, flags);
|
||||
return regex_search(src, reg);
|
||||
}
|
||||
catch (std::regex_error &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string regReplace(const std::string &src, const std::string &match, const std::string &rep)
|
||||
{
|
||||
std::string result = "";
|
||||
try
|
||||
{
|
||||
std::regex::flag_type flags = std::regex::extended | std::regex::ECMAScript;
|
||||
std::string target = match;
|
||||
if(match.find("(?i)") == 0)
|
||||
{
|
||||
target.erase(0, 4);
|
||||
flags |= std::regex::icase;
|
||||
}
|
||||
std::regex reg(target, flags);
|
||||
regex_replace(back_inserter(result), src.begin(), src.end(), reg, rep);
|
||||
}
|
||||
catch (std::regex_error &e)
|
||||
{
|
||||
result = src;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool regMatch(const std::string &src, const std::string &match)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::regex::flag_type flags = std::regex::extended | std::regex::ECMAScript;
|
||||
std::string target = match;
|
||||
if(match.find("(?i)") == 0)
|
||||
{
|
||||
target.erase(0, 4);
|
||||
flags |= std::regex::icase;
|
||||
}
|
||||
std::regex reg(target, flags);
|
||||
return regex_match(src, reg);
|
||||
}
|
||||
catch (std::regex_error &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::regex::flag_type flags = std::regex::extended | std::regex::ECMAScript;
|
||||
std::string target = match;
|
||||
if(match.find("(?i)") == 0)
|
||||
{
|
||||
target.erase(0, 4);
|
||||
flags |= std::regex::icase;
|
||||
}
|
||||
std::regex reg(target, flags);
|
||||
std::smatch result;
|
||||
if(regex_search(src.cbegin(), src.cend(), result, reg))
|
||||
{
|
||||
if(result.size() < group_count - 1)
|
||||
return -1;
|
||||
va_list vl;
|
||||
va_start(vl, group_count);
|
||||
size_t index = 0;
|
||||
while(group_count)
|
||||
{
|
||||
std::string* arg = va_arg(vl, std::string*);
|
||||
if(arg != NULL)
|
||||
*arg = std::move(result[index]);
|
||||
index++;
|
||||
group_count--;
|
||||
}
|
||||
va_end(vl);
|
||||
}
|
||||
else
|
||||
return -2;
|
||||
return 0;
|
||||
}
|
||||
catch (std::regex_error&)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
*/
|
||||
bool regMatch(const std::string &src, const std::string &match)
|
||||
{
|
||||
jp::Regex reg;
|
||||
reg.setPattern(match).addModifier("m").addPcre2Option(PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_UTF).compile();
|
||||
if(!reg)
|
||||
return false;
|
||||
return reg.match(src, "g");
|
||||
}
|
||||
|
||||
bool regFind(const std::string &src, const std::string &match)
|
||||
{
|
||||
jp::Regex reg;
|
||||
reg.setPattern(match).addModifier("m").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile();
|
||||
if(!reg)
|
||||
return false;
|
||||
return reg.match(src, "g");
|
||||
}
|
||||
|
||||
std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global, bool multiline)
|
||||
{
|
||||
jp::Regex reg;
|
||||
reg.setPattern(match).addModifier(multiline ? "m" : "").addPcre2Option(PCRE2_UTF|PCRE2_MULTILINE|PCRE2_ALT_BSUX).compile();
|
||||
if(!reg)
|
||||
return src;
|
||||
return reg.replace(src, rep, global ? "gEx" : "Ex");
|
||||
}
|
||||
|
||||
bool regValid(const std::string ®)
|
||||
{
|
||||
jp::Regex r;
|
||||
r.setPattern(reg).addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile();
|
||||
return !!r;
|
||||
}
|
||||
|
||||
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...)
|
||||
{
|
||||
jp::Regex reg;
|
||||
reg.setPattern(match).addModifier("m").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile();
|
||||
jp::VecNum vec_num;
|
||||
jp::RegexMatch rm;
|
||||
size_t count = rm.setRegexObject(®).setSubject(src).setNumberedSubstringVector(&vec_num).setModifier("g").match();
|
||||
if(!count)
|
||||
return -1;
|
||||
va_list vl;
|
||||
va_start(vl, group_count);
|
||||
size_t index = 0, match_index = 0;
|
||||
while(group_count)
|
||||
{
|
||||
std::string* arg = va_arg(vl, std::string*);
|
||||
if(arg != NULL)
|
||||
*arg = std::move(vec_num[match_index][index]);
|
||||
index++;
|
||||
group_count--;
|
||||
if(vec_num[match_index].size() <= index)
|
||||
{
|
||||
match_index++;
|
||||
index = 0;
|
||||
}
|
||||
if(vec_num.size() <= match_index)
|
||||
break;
|
||||
}
|
||||
va_end(vl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//#endif // USE_STD_REGEX
|
||||
|
||||
std::string regTrim(const std::string &src)
|
||||
{
|
||||
return regReplace(src, "^\\s*([\\s\\S]*)\\s*$", "$1", false, false);
|
||||
}
|
||||
13
src/utils/regexp.h
Normal file
13
src/utils/regexp.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef REGEXP_H_INCLUDED
|
||||
#define REGEXP_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
bool regValid(const std::string ®);
|
||||
bool regFind(const std::string &src, const std::string &match);
|
||||
std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global = true, bool multiline = true);
|
||||
bool regMatch(const std::string &src, const std::string &match);
|
||||
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...);
|
||||
std::string regTrim(const std::string &src);
|
||||
|
||||
#endif // REGEXP_H_INCLUDED
|
||||
17
src/utils/stl_extra.h
Normal file
17
src/utils/stl_extra.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef STL_EXTRA_H_INCLUDED
|
||||
#define STL_EXTRA_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
template <typename T> inline void eraseElements(std::vector<T> &target)
|
||||
{
|
||||
target.clear();
|
||||
target.shrink_to_fit();
|
||||
}
|
||||
|
||||
template <typename T> inline void eraseElements(T &target)
|
||||
{
|
||||
T().swap(target);
|
||||
}
|
||||
|
||||
#endif // STL_EXTRA_H_INCLUDED
|
||||
396
src/utils/string.cpp
Normal file
396
src/utils/string.cpp
Normal file
@@ -0,0 +1,396 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "string.h"
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &seperator)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
string_size i = 0;
|
||||
|
||||
while(i != s.size())
|
||||
{
|
||||
int flag = 0;
|
||||
while(i != s.size() && flag == 0)
|
||||
{
|
||||
flag = 1;
|
||||
for(string_size x = 0; x < seperator.size(); ++x)
|
||||
if(s[i] == seperator[x])
|
||||
{
|
||||
++i;
|
||||
flag = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flag = 0;
|
||||
string_size j = i;
|
||||
while(j != s.size() && flag == 0)
|
||||
{
|
||||
for(string_size x = 0; x < seperator.size(); ++x)
|
||||
if(s[j] == seperator[x])
|
||||
{
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
if(flag == 0)
|
||||
++j;
|
||||
}
|
||||
if(i != j)
|
||||
{
|
||||
result.push_back(s.substr(i, j-i));
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UTF8ToCodePoint(const std::string &data)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for(string_size i = 0; i < data.size(); i++)
|
||||
{
|
||||
int charcode = data[i] & 0xff;
|
||||
if((charcode >> 7) == 0)
|
||||
{
|
||||
ss<<data[i];
|
||||
}
|
||||
else if((charcode >> 5) == 6)
|
||||
{
|
||||
ss<<"\\u"<<std::hex<<((data[i + 1] & 0x3f) | (data[i] & 0x1f) << 6);
|
||||
i++;
|
||||
}
|
||||
else if((charcode >> 4) == 14)
|
||||
{
|
||||
ss<<"\\u"<<std::hex<<((data[i + 2] & 0x3f) | (data[i + 1] & 0x3f) << 6 | (data[i] & 0xf) << 12);
|
||||
i += 2;
|
||||
}
|
||||
else if((charcode >> 3) == 30)
|
||||
{
|
||||
ss<<"\\u"<<std::hex<<((data[i + 3] & 0x3f) | (data[i + 2] & 0x3f) << 6 | (data[i + 1] & 0x3f) << 12 | (data[i] & 0x7) << 18);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toLower(const std::string &str)
|
||||
{
|
||||
std::string result;
|
||||
std::transform(str.begin(), str.end(), std::back_inserter(result), [](unsigned char c) { return std::tolower(c); });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toUpper(const std::string &str)
|
||||
{
|
||||
std::string result;
|
||||
std::transform(str.begin(), str.end(), std::back_inserter(result), [](unsigned char c) { return std::toupper(c); });
|
||||
return result;
|
||||
}
|
||||
|
||||
void processEscapeChar(std::string &str)
|
||||
{
|
||||
string_size pos = str.find('\\');
|
||||
while(pos != str.npos)
|
||||
{
|
||||
if(pos == str.size())
|
||||
break;
|
||||
switch(str[pos + 1])
|
||||
{
|
||||
case 'n':
|
||||
str.replace(pos, 2, "\n");
|
||||
break;
|
||||
case 'r':
|
||||
str.replace(pos, 2, "\r");
|
||||
break;
|
||||
case 't':
|
||||
str.replace(pos, 2, "\t");
|
||||
break;
|
||||
default:
|
||||
/// ignore others for backward compatibility
|
||||
//str.erase(pos, 1);
|
||||
break;
|
||||
}
|
||||
pos = str.find('\\', pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void processEscapeCharReverse(std::string &str)
|
||||
{
|
||||
string_size pos = 0;
|
||||
while(pos < str.size())
|
||||
{
|
||||
switch(str[pos])
|
||||
{
|
||||
case '\n':
|
||||
str.replace(pos, 1, "\\n");
|
||||
break;
|
||||
case '\r':
|
||||
str.replace(pos, 1, "\\r");
|
||||
break;
|
||||
case '\t':
|
||||
str.replace(pos, 1, "\\t");
|
||||
break;
|
||||
default:
|
||||
/// ignore others for backward compatibility
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
int parseCommaKeyValue(const std::string &input, const std::string &separator, string_pair_array &result)
|
||||
{
|
||||
string_size bpos = 0, epos = input.find(',');
|
||||
std::string kv;
|
||||
while(bpos < input.size())
|
||||
{
|
||||
if(epos == std::string::npos)
|
||||
epos = input.size();
|
||||
else if(epos && input[epos - 1] == '\\')
|
||||
{
|
||||
kv += input.substr(bpos, epos - bpos - 1);
|
||||
kv += ',';
|
||||
bpos = epos + 1;
|
||||
epos = input.find(',', bpos);
|
||||
continue;
|
||||
}
|
||||
kv += input.substr(bpos, epos - bpos);
|
||||
string_size eqpos = kv.find('=');
|
||||
if(eqpos == std::string::npos)
|
||||
result.emplace_back("{NONAME}", kv);
|
||||
else
|
||||
result.emplace_back(kv.substr(0, eqpos), kv.substr(eqpos + 1));
|
||||
kv.clear();
|
||||
bpos = epos + 1;
|
||||
epos = input.find(',', bpos);
|
||||
}
|
||||
if(kv.size())
|
||||
{
|
||||
string_size eqpos = kv.find('=');
|
||||
if(eqpos == std::string::npos)
|
||||
result.emplace_back("{NONAME}", kv);
|
||||
else
|
||||
result.emplace_back(kv.substr(0, eqpos), kv.substr(eqpos + 1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trimSelfOf(std::string &str, char target, bool before, bool after)
|
||||
{
|
||||
if (!before && !after)
|
||||
return;
|
||||
std::string::size_type pos = str.size() - 1;
|
||||
if (after)
|
||||
pos = str.find_last_not_of(target);
|
||||
if (pos != std::string::npos)
|
||||
str.erase(pos + 1);
|
||||
if (before)
|
||||
pos = str.find_first_not_of(target);
|
||||
str.erase(0, pos);
|
||||
}
|
||||
|
||||
std::string trimOf(const std::string& str, char target, bool before, bool after)
|
||||
{
|
||||
if (!before && !after)
|
||||
return str;
|
||||
std::string::size_type pos = 0;
|
||||
if (before)
|
||||
pos = str.find_first_not_of(target);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
std::string::size_type pos2 = str.size() - 1;
|
||||
if (after)
|
||||
pos2 = str.find_last_not_of(target);
|
||||
if (pos2 != std::string::npos)
|
||||
{
|
||||
return str.substr(pos, pos2 - pos + 1);
|
||||
}
|
||||
return str.substr(pos);
|
||||
}
|
||||
|
||||
std::string trim(const std::string& str, bool before, bool after)
|
||||
{
|
||||
return trimOf(str, ' ', before, after);
|
||||
}
|
||||
|
||||
std::string trimQuote(const std::string &str, bool before, bool after)
|
||||
{
|
||||
return trimOf(str, '\"', before, after);
|
||||
}
|
||||
|
||||
std::string trimWhitespace(const std::string &str, bool before, bool after)
|
||||
{
|
||||
static std::string whitespaces(" \t\f\v\n\r");
|
||||
string_size bpos = 0, epos = str.size();
|
||||
if(after)
|
||||
{
|
||||
epos = str.find_last_not_of(whitespaces);
|
||||
if(epos == std::string::npos)
|
||||
return std::string();
|
||||
}
|
||||
if(before)
|
||||
{
|
||||
bpos = str.find_first_not_of(whitespaces);
|
||||
if(bpos == std::string::npos)
|
||||
return std::string();
|
||||
}
|
||||
return str.substr(bpos, epos - bpos + 1);
|
||||
}
|
||||
|
||||
std::string getUrlArg(const std::string &url, const std::string &request)
|
||||
{
|
||||
//std::smatch result;
|
||||
/*
|
||||
if (regex_search(url.cbegin(), url.cend(), result, std::regex(request + "=(.*?)&")))
|
||||
{
|
||||
return result[1];
|
||||
}
|
||||
else if (regex_search(url.cbegin(), url.cend(), result, std::regex(request + "=(.*)")))
|
||||
{
|
||||
return result[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
*/
|
||||
/*
|
||||
std::string::size_type spos = url.find("?");
|
||||
if(spos != url.npos)
|
||||
url.erase(0, spos + 1);
|
||||
|
||||
string_array vArray, arglist = split(url, "&");
|
||||
for(std::string &x : arglist)
|
||||
{
|
||||
std::string::size_type epos = x.find("=");
|
||||
if(epos != x.npos)
|
||||
{
|
||||
if(x.substr(0, epos) == request)
|
||||
return x.substr(epos + 1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
std::string pattern = request + "=";
|
||||
std::string::size_type pos = url.size();
|
||||
while(pos)
|
||||
{
|
||||
pos = url.rfind(pattern, pos);
|
||||
if(pos != url.npos)
|
||||
{
|
||||
if(pos == 0 || url[pos - 1] == '&' || url[pos - 1] == '?')
|
||||
{
|
||||
pos += pattern.size();
|
||||
return url.substr(pos, url.find("&", pos) - pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
pos--;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string replaceAllDistinct(std::string str, const std::string &old_value, const std::string &new_value)
|
||||
{
|
||||
for(std::string::size_type pos(0); pos != std::string::npos; pos += new_value.length())
|
||||
{
|
||||
if((pos = str.find(old_value, pos)) != std::string::npos)
|
||||
str.replace(pos, old_value.length(), new_value);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void removeUTF8BOM(std::string &data)
|
||||
{
|
||||
if(data.compare(0, 3, "\xEF\xBB\xBF") == 0)
|
||||
data = data.substr(3);
|
||||
}
|
||||
|
||||
bool isStrUTF8(const std::string &data)
|
||||
{
|
||||
const char *str = data.c_str();
|
||||
unsigned int nBytes = 0;
|
||||
for (unsigned int i = 0; str[i] != '\0'; ++i)
|
||||
{
|
||||
unsigned char chr = *(str + i);
|
||||
if (nBytes == 0)
|
||||
{
|
||||
if (chr >= 0x80)
|
||||
{
|
||||
if (chr >= 0xFC && chr <= 0xFD)
|
||||
nBytes = 6;
|
||||
else if (chr >= 0xF8)
|
||||
nBytes = 5;
|
||||
else if (chr >= 0xF0)
|
||||
nBytes = 4;
|
||||
else if (chr >= 0xE0)
|
||||
nBytes = 3;
|
||||
else if (chr >= 0xC0)
|
||||
nBytes = 2;
|
||||
else
|
||||
return false;
|
||||
nBytes--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((chr & 0xC0) != 0x80)
|
||||
return false;
|
||||
nBytes--;
|
||||
}
|
||||
}
|
||||
if (nBytes != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string randomStr(const int len)
|
||||
{
|
||||
std::string retData;
|
||||
srand(time(NULL));
|
||||
int cnt = 0;
|
||||
while(cnt < len)
|
||||
{
|
||||
switch((rand() % 3))
|
||||
{
|
||||
case 1:
|
||||
retData += ('A' + rand() % 26);
|
||||
break;
|
||||
case 2:
|
||||
retData += ('a' + rand() % 26);
|
||||
break;
|
||||
default:
|
||||
retData += ('0' + rand() % 10);
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
return retData;
|
||||
}
|
||||
|
||||
int to_int(const std::string &str, int def_value)
|
||||
{
|
||||
if(str.empty())
|
||||
return def_value;
|
||||
/*
|
||||
int retval = 0;
|
||||
char c;
|
||||
std::stringstream ss(str);
|
||||
if(!(ss >> retval))
|
||||
return def_value;
|
||||
else if(ss >> c)
|
||||
return def_value;
|
||||
else
|
||||
return retval;
|
||||
*/
|
||||
return std::atoi(str.data());
|
||||
}
|
||||
95
src/utils/string.h
Normal file
95
src/utils/string.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef STRING_H_INCLUDED
|
||||
#define STRING_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
using string_size = std::string::size_type;
|
||||
using string_array = std::vector<std::string>;
|
||||
using string_map = std::map<std::string, std::string>;
|
||||
using string_pair_array = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &seperator);
|
||||
std::string getUrlArg(const std::string &url, const std::string &request);
|
||||
std::string replaceAllDistinct(std::string str, const std::string &old_value, const std::string &new_value);
|
||||
std::string trimOf(const std::string& str, char target, bool before = true, bool after = true);
|
||||
std::string trim(const std::string& str, bool before = true, bool after = true);
|
||||
std::string trimQuote(const std::string &str, bool before = true, bool after = true);
|
||||
void trimSelfOf(std::string &str, char target, bool before = true, bool after = true);
|
||||
std::string trimWhitespace(const std::string &str, bool before = false, bool after = true);
|
||||
std::string randomStr(const int len);
|
||||
bool isStrUTF8(const std::string &data);
|
||||
|
||||
void removeUTF8BOM(std::string &data);
|
||||
std::string UTF8ToCodePoint(const std::string &data);
|
||||
std::string toLower(const std::string &str);
|
||||
std::string toUpper(const std::string &str);
|
||||
void processEscapeChar(std::string &str);
|
||||
void processEscapeCharReverse(std::string &str);
|
||||
int parseCommaKeyValue(const std::string &input, const std::string &separator, string_pair_array &result);
|
||||
|
||||
inline bool strFind(const std::string &str, const std::string &target)
|
||||
{
|
||||
return str.find(target) != str.npos;
|
||||
}
|
||||
|
||||
inline bool startsWith(const std::string &hay, const std::string &needle)
|
||||
{
|
||||
return hay.substr(0, needle.length()) == needle;
|
||||
}
|
||||
|
||||
inline bool endsWith(const std::string &hay, const std::string &needle)
|
||||
{
|
||||
std::string::size_type hl = hay.length(), nl = needle.length();
|
||||
return hl >= nl && hay.substr(hl - nl, nl) == needle;
|
||||
}
|
||||
|
||||
inline bool count_least(const std::string &hay, const char needle, size_t cnt)
|
||||
{
|
||||
string_size pos = hay.find(needle);
|
||||
while(pos != hay.npos)
|
||||
{
|
||||
cnt--;
|
||||
if(!cnt)
|
||||
return true;
|
||||
pos = hay.find(needle, pos + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline char getLineBreak(const std::string &str)
|
||||
{
|
||||
return count_least(str, '\n', 1) ? '\n' : '\r';
|
||||
}
|
||||
|
||||
template <typename T, typename U> static inline T to_number(const U &value, T def_value = T())
|
||||
{
|
||||
T retval = 0.0;
|
||||
char c;
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
if(!(ss >> retval))
|
||||
return def_value;
|
||||
else if(ss >> c)
|
||||
return def_value;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
int to_int(const std::string &str, int def_value = 0);
|
||||
|
||||
#ifndef HAVE_TO_STRING
|
||||
namespace std
|
||||
{
|
||||
template <typename T> std::string to_string(const T& n)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
#endif // HAVE_TO_STRING
|
||||
|
||||
#endif // STRING_H_INCLUDED
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
typedef uint64_t hash_t;
|
||||
using hash_t = uint64_t;
|
||||
|
||||
constexpr hash_t prime = 0x100000001B3ull;
|
||||
constexpr hash_t basis = 0xCBF29CE484222325ull;
|
||||
114
src/utils/system.cpp
Normal file
114
src/utils/system.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif // _WIN32
|
||||
|
||||
void sleepMs(int interval)
|
||||
{
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
Sleep(interval);
|
||||
#else
|
||||
// Portable sleep for platforms other than Windows.
|
||||
struct timeval wait = { 0, interval * 1000 };
|
||||
select(0, NULL, NULL, NULL, &wait);
|
||||
#endif
|
||||
*/
|
||||
//upgrade to c++11 standard
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
|
||||
}
|
||||
|
||||
std::string getEnv(const std::string &name)
|
||||
{
|
||||
std::string retVal;
|
||||
#ifdef _WIN32
|
||||
char chrData[1024] = {};
|
||||
if(GetEnvironmentVariable(name.c_str(), chrData, 1023))
|
||||
retVal.assign(chrData);
|
||||
#else
|
||||
char *env = getenv(name.c_str());
|
||||
if(env != NULL)
|
||||
retVal.assign(env);
|
||||
#endif // _WIN32
|
||||
return retVal;
|
||||
}
|
||||
|
||||
std::string getSystemProxy()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HKEY key;
|
||||
auto ret = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings)", 0, KEY_ALL_ACCESS, &key);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
//std::cout << "open failed: " << ret << std::endl;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
DWORD values_count, max_value_name_len, max_value_len;
|
||||
ret = RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
&values_count, &max_value_name_len, &max_value_len, NULL, NULL);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
//std::cout << "query failed" << std::endl;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::shared_ptr<char>, DWORD, std::shared_ptr<BYTE>>> values;
|
||||
for(DWORD i = 0; i < values_count; i++)
|
||||
{
|
||||
std::shared_ptr<char> value_name(new char[max_value_name_len + 1],
|
||||
std::default_delete<char[]>());
|
||||
DWORD value_name_len = max_value_name_len + 1;
|
||||
DWORD value_type, value_len;
|
||||
RegEnumValue(key, i, value_name.get(), &value_name_len, NULL, &value_type, NULL, &value_len);
|
||||
std::shared_ptr<BYTE> value(new BYTE[value_len],
|
||||
std::default_delete<BYTE[]>());
|
||||
value_name_len = max_value_name_len + 1;
|
||||
RegEnumValue(key, i, value_name.get(), &value_name_len, NULL, &value_type, value.get(), &value_len);
|
||||
values.push_back(std::make_tuple(value_name, value_type, value));
|
||||
}
|
||||
|
||||
DWORD ProxyEnable = 0;
|
||||
for (auto x : values)
|
||||
{
|
||||
if (strcmp(std::get<0>(x).get(), "ProxyEnable") == 0)
|
||||
{
|
||||
ProxyEnable = *(DWORD*)(std::get<2>(x).get());
|
||||
}
|
||||
}
|
||||
|
||||
if (ProxyEnable)
|
||||
{
|
||||
for (auto x : values)
|
||||
{
|
||||
if (strcmp(std::get<0>(x).get(), "ProxyServer") == 0)
|
||||
{
|
||||
//std::cout << "ProxyServer: " << (char*)(std::get<2>(x).get()) << std::endl;
|
||||
return std::string((char*)(std::get<2>(x).get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
else {
|
||||
//std::cout << "Proxy not Enabled" << std::endl;
|
||||
}
|
||||
*/
|
||||
//return 0;
|
||||
return std::string();
|
||||
#else
|
||||
string_array proxy_env = {"all_proxy", "ALL_PROXY", "http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY"};
|
||||
for(std::string &x : proxy_env)
|
||||
{
|
||||
char* proxy = getenv(x.c_str());
|
||||
if(proxy != NULL)
|
||||
return std::string(proxy);
|
||||
}
|
||||
return std::string();
|
||||
#endif // _WIN32
|
||||
}
|
||||
10
src/utils/system.h
Normal file
10
src/utils/system.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SYSTEM_H_INCLUDED
|
||||
#define SYSTEM_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
void sleepMs(int interval);
|
||||
std::string getEnv(const std::string &name);
|
||||
std::string getSystemProxy();
|
||||
|
||||
#endif // SYSTEM_H_INCLUDED
|
||||
110
src/utils/tribool.h
Normal file
110
src/utils/tribool.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#ifndef TRIBOOL_H_INCLUDED
|
||||
#define TRIBOOL_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "string.h"
|
||||
#include "string_hash.h"
|
||||
|
||||
class tribool
|
||||
{
|
||||
private:
|
||||
|
||||
char _M_VALUE = 0;
|
||||
|
||||
public:
|
||||
|
||||
tribool() { clear(); }
|
||||
|
||||
template <typename T> tribool(const T &value) { set(value); }
|
||||
|
||||
tribool(const tribool &value) { _M_VALUE = value._M_VALUE; }
|
||||
|
||||
~tribool() = default;
|
||||
|
||||
tribool& operator=(const tribool &src)
|
||||
{
|
||||
_M_VALUE = src._M_VALUE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& operator=(const T &value)
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return _M_VALUE == 3; }
|
||||
|
||||
bool is_undef() const { return _M_VALUE <= 1; }
|
||||
|
||||
template <typename T> tribool& define(const T &value)
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
*this = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& parse(const T &value)
|
||||
{
|
||||
return define(value);
|
||||
}
|
||||
|
||||
tribool reverse()
|
||||
{
|
||||
if(_M_VALUE > 1)
|
||||
_M_VALUE = _M_VALUE > 2 ? 2 : 3;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool get(const bool &def_value = false) const
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
return def_value;
|
||||
return _M_VALUE == 3;
|
||||
}
|
||||
|
||||
std::string get_str() const
|
||||
{
|
||||
switch(_M_VALUE)
|
||||
{
|
||||
case 2:
|
||||
return "false";
|
||||
case 3:
|
||||
return "true";
|
||||
}
|
||||
return "undef";
|
||||
}
|
||||
|
||||
template <typename T> bool set(const T &value)
|
||||
{
|
||||
_M_VALUE = (bool)value + 2;
|
||||
return _M_VALUE > 2;
|
||||
}
|
||||
|
||||
bool set(const std::string &str)
|
||||
{
|
||||
switch(hash_(str))
|
||||
{
|
||||
case "true"_hash:
|
||||
case "1"_hash:
|
||||
_M_VALUE = 3;
|
||||
break;
|
||||
case "false"_hash:
|
||||
case "0"_hash:
|
||||
_M_VALUE = 2;
|
||||
break;
|
||||
default:
|
||||
if(to_int(str, 0) > 1)
|
||||
_M_VALUE = 3;
|
||||
else
|
||||
_M_VALUE = 0;
|
||||
break;
|
||||
}
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
void clear() { _M_VALUE = 0; }
|
||||
};
|
||||
|
||||
#endif // TRIBOOL_H_INCLUDED
|
||||
71
src/utils/urlencode.cpp
Normal file
71
src/utils/urlencode.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <string>
|
||||
|
||||
#include "string.h"
|
||||
|
||||
unsigned char toHex(unsigned char x)
|
||||
{
|
||||
return x > 9 ? x + 55 : x + 48;
|
||||
}
|
||||
|
||||
unsigned char fromHex(unsigned char x)
|
||||
{
|
||||
unsigned char y;
|
||||
if (x >= 'A' && x <= 'Z')
|
||||
y = x - 'A' + 10;
|
||||
else if (x >= 'a' && x <= 'z')
|
||||
y = x - 'a' + 10;
|
||||
else if (x >= '0' && x <= '9')
|
||||
y = x - '0';
|
||||
else
|
||||
y = x;
|
||||
return y;
|
||||
}
|
||||
|
||||
std::string urlEncode(const std::string& str)
|
||||
{
|
||||
std::string strTemp = "";
|
||||
string_size length = str.length();
|
||||
for (string_size i = 0; i < length; i++)
|
||||
{
|
||||
if (isalnum((unsigned char)str[i]) ||
|
||||
(str[i] == '-') ||
|
||||
(str[i] == '_') ||
|
||||
(str[i] == '.') ||
|
||||
(str[i] == '~'))
|
||||
strTemp += str[i];
|
||||
else
|
||||
{
|
||||
strTemp += '%';
|
||||
strTemp += toHex((unsigned char)str[i] >> 4);
|
||||
strTemp += toHex((unsigned char)str[i] % 16);
|
||||
}
|
||||
}
|
||||
return strTemp;
|
||||
}
|
||||
|
||||
std::string urlDecode(const std::string& str)
|
||||
{
|
||||
std::string strTemp;
|
||||
string_size length = str.length();
|
||||
for (string_size i = 0; i < length; i++)
|
||||
{
|
||||
if (str[i] == '+')
|
||||
strTemp += ' ';
|
||||
else if (str[i] == '%')
|
||||
{
|
||||
if(i + 2 >= length)
|
||||
return strTemp;
|
||||
if(isalnum(str[i + 1]) && isalnum(str[i + 2]))
|
||||
{
|
||||
unsigned char high = fromHex((unsigned char)str[++i]);
|
||||
unsigned char low = fromHex((unsigned char)str[++i]);
|
||||
strTemp += high * 16 + low;
|
||||
}
|
||||
else
|
||||
strTemp += str[i];
|
||||
}
|
||||
else
|
||||
strTemp += str[i];
|
||||
}
|
||||
return strTemp;
|
||||
}
|
||||
9
src/utils/urlencode.h
Normal file
9
src/utils/urlencode.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef URLENCODE_H_INCLUDED
|
||||
#define URLENCODE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string urlEncode(const std::string& str);
|
||||
std::string urlDecode(const std::string& str);
|
||||
|
||||
#endif // URLENCODE_H_INCLUDED
|
||||
44
src/utils/yamlcpp_extra.h
Normal file
44
src/utils/yamlcpp_extra.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef YAMLCPP_EXTRA_H_INCLUDED
|
||||
#define YAMLCPP_EXTRA_H_INCLUDED
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
template <typename T> void operator >> (const YAML::Node& node, T& i)
|
||||
{
|
||||
if(node.IsDefined() && !node.IsNull()) //fail-safe
|
||||
i = node.as<T>();
|
||||
};
|
||||
|
||||
template <typename T> T safe_as (const YAML::Node& node)
|
||||
{
|
||||
if(node.IsDefined() && !node.IsNull())
|
||||
return node.as<T>();
|
||||
return T();
|
||||
};
|
||||
|
||||
template <typename T> void operator >>= (const YAML::Node& node, T& i)
|
||||
{
|
||||
i = safe_as<T>(node);
|
||||
};
|
||||
|
||||
using string_array = std::vector<std::string>;
|
||||
|
||||
inline std::string dump_to_pairs (const YAML::Node &node, const string_array &exclude = string_array())
|
||||
{
|
||||
std::string result;
|
||||
for(auto iter = node.begin(); iter != node.end(); iter++)
|
||||
{
|
||||
if(iter->second.Type() != YAML::NodeType::Scalar)
|
||||
continue;
|
||||
std::string key = iter->first.as<std::string>();
|
||||
if(std::find(exclude.cbegin(), exclude.cend(), key) != exclude.cend())
|
||||
continue;
|
||||
std::string value = iter->second.as<std::string>();
|
||||
result += key + "=" + value + ",";
|
||||
}
|
||||
return result.erase(result.size() - 1);
|
||||
}
|
||||
|
||||
#endif // YAMLCPP_EXTRA_H_INCLUDED
|
||||
@@ -1,19 +0,0 @@
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
template <typename T> void operator >> (const YAML::Node& node, T& i)
|
||||
{
|
||||
if(node.IsDefined() && !node.IsNull()) //fail-safe
|
||||
i = node.as<T>();
|
||||
};
|
||||
|
||||
template <typename T> T safe_as (const YAML::Node& node)
|
||||
{
|
||||
if(node.IsDefined() && !node.IsNull())
|
||||
return node.as<T>();
|
||||
return T();
|
||||
};
|
||||
|
||||
template <typename T> void operator >>= (const YAML::Node& node, T& i)
|
||||
{
|
||||
i = safe_as<T>(node);
|
||||
};
|
||||
Reference in New Issue
Block a user