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:
Tindy X
2021-03-23 18:09:00 +08:00
parent ba058e4e4b
commit 3d67406af6
88 changed files with 9404 additions and 6356 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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 . && \

View File

@@ -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 .

View File

@@ -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 .

View File

@@ -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

View File

@@ -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 ),

View 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;
}

View 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

View 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);
}
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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))

View File

@@ -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();

View File

@@ -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);});

View File

@@ -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();

View File

@@ -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";

View File

@@ -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;
*/
}

View File

@@ -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

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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 &reg);
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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
#ifndef CRON_H_INCLUDED
#define CRON_H_INCLUDED
void refresh_schedule();
size_t cron_tick();
#endif // CRON_H_INCLUDED

View File

@@ -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
View 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

View File

@@ -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

View 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<&currentTime>("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
View 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
View 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
View 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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -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 &section)
{
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();

View File

@@ -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
View 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

View 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
View 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
View 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

View File

@@ -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
View 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 &reg)
{
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 &reg)
{
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(&reg).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
View File

@@ -0,0 +1,13 @@
#ifndef REGEXP_H_INCLUDED
#define REGEXP_H_INCLUDED
#include <string>
bool regValid(const std::string &reg);
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
View 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
View 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
View 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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -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);
};