Files
subconverter/misc.cpp
Tindy X 1039d44259 Bug fixes
Fix incorrect URL Enocde process.
Fix Emoji switch in URL argument.
Remove module detection for importing Surge 2 subscription.
Add timestamp in debug info.
2019-12-08 22:22:33 +08:00

870 lines
21 KiB
C++

#include <chrono>
#include <regex>
#include <fstream>
#include <thread>
#include <sstream>
#include <iosfwd>
//#include <filesystem>
#include <unistd.h>
#include <rapidjson/document.h>
#include <openssl/md5.h>
#include "misc.h"
#ifdef _WIN32
//#include <io.h>
#include <windows.h>
#include <winreg.h>
#else
#ifndef __hpux
#include <sys/select.h>
#endif /* __hpux */
#ifndef _access
#define _access access
#endif // _access
#include <sys/socket.h>
#endif // _WIN32
using namespace std::__cxx11;
void sleep(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 GBKToUTF8(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;
if(wstr)
delete[] wstr;
if(str)
delete[] str;
return strTemp;
#else
return str_src;
#endif // _WIN32
}
std::string UTF8ToGBK(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, 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;
}
#endif // _WIN32
unsigned char ToHex(unsigned char x)
{
return x > 9 ? x + 55 : x + 48;
}
unsigned char FromHex(unsigned char x)
{
unsigned char y = '\0';
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
assert(0);
return y;
}
std::string UrlEncode(const std::string& str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (isalnum((unsigned char)str[i]) ||
(str[i] == '-') ||
(str[i] == '_') ||
(str[i] == '.') ||
(str[i] == '~'))
strTemp += str[i];
//else if (str[i] == ' ')
// strTemp += "+";
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 = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (str[i] == '+')
strTemp += ' ';
else if (str[i] == '%')
{
if(i + 2 >= length)
return strTemp;
unsigned char high = FromHex((unsigned char)str[++i]);
unsigned char low = FromHex((unsigned char)str[++i]);
strTemp += high * 16 + low;
}
else
strTemp += str[i];
}
return strTemp;
}
static inline bool is_base64(unsigned char c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(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;
int j = 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)
{
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 base64_decode(std::string encoded_string)
{
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
{
char_array_4[i++] = encoded_string[in_];
in_++;
if (i ==4)
{
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
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 (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(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 (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}
std::vector<std::string> split(const std::string &s, const std::string &seperator)
{
std::vector<std::string> result;
typedef std::string::size_type string_size;
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 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
char* proxy = getenv("ALL_PROXY");
if(proxy != NULL)
return std::string(proxy);
else
return std::string();
#endif // _WIN32
}
std::string trim(const std::string& str)
{
std::string::size_type pos = str.find_first_not_of(' ');
if (pos == std::string::npos)
{
return str;
}
std::string::size_type pos2 = str.find_last_not_of(' ');
if (pos2 != std::string::npos)
{
return str.substr(pos, pos2 - pos + 1);
}
return str.substr(pos);
}
std::string getUrlArg(std::string url, 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();
}
*/
string_array vArray, arglist = split(url, "&");
for(std::string &x : arglist)
{
/*
if(regex_search(x.cbegin(), x.cend(), result, std::regex("^" + request + "=(.*)$")))
return result[1];
*/
std::string::size_type epos = x.find("=");
if(epos != x.npos)
{
if(x.substr(0, epos) == request)
return x.substr(epos + 1);
}
}
return std::string();
}
std::string replace_all_distinct(std::string str, std::string old_value, 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;
}
bool regValid(std::string &reg)
{
try
{
std::regex r(reg);
return true;
}
catch (std::regex_error &e)
{
return false;
}
}
bool regFind(std::string src, std::string target)
{
try
{
std::regex reg(target);
return regex_search(src, reg);
}
catch (std::regex_error &e)
{
return false;
}
}
std::string regReplace(std::string src, std::string match, std::string rep)
{
std::string result = "";
try
{
std::regex reg(match);
regex_replace(back_inserter(result), src.begin(), src.end(), reg, rep);
}
catch (std::regex_error &e)
{
result = src;
}
return result;
}
bool regMatch(std::string src, std::string match)
{
try
{
std::regex reg(match);
return regex_match(src, reg);
}
catch (std::regex_error &e)
{
return false;
}
}
std::string speedCalc(double speed)
{
if(speed == 0.0)
return std::string("0.00B");
char str[10];
std::string retstr;
if(speed >= 1073741824.0)
sprintf(str, "%.2fGB", speed / 1073741824.0);
else if(speed >= 1048576.0)
sprintf(str, "%.2fMB", speed / 1048576.0);
else if(speed >= 1024.0)
sprintf(str, "%.2fKB", speed / 1024.0);
else
sprintf(str, "%.2fB", speed);
retstr = str;
return retstr;
}
std::string urlsafe_base64_reverse(std::string encoded_string)
{
return replace_all_distinct(replace_all_distinct(encoded_string, "-", "+"), "_", "/");
}
std::string urlsafe_base64_decode(std::string encoded_string)
{
return base64_decode(urlsafe_base64_reverse(encoded_string));
}
std::string urlsafe_base64_encode(std::string string_to_encode)
{
return replace_all_distinct(replace_all_distinct(replace_all_distinct(base64_encode(string_to_encode), "+", "-"), "/", "_"), "=", "");
}
std::string getMD5(std::string data)
{
MD5_CTX ctx;
std::string result;
unsigned int i = 0;
unsigned char digest[16] = {};
MD5_Init(&ctx);
MD5_Update(&ctx, data.data(), data.size());
MD5_Final((unsigned char *)&digest, &ctx);
char tmp[3] = {};
for(i = 0; i < 16; i++)
{
snprintf(tmp, 3, "%02x", digest[i]);
result += tmp;
}
return result;
}
std::string fileGet(std::string path, bool binary)
{
std::ifstream infile;
std::stringstream strstrm;
std::ios::openmode mode = binary ? std::ios::binary : std::ios::in;
infile.open(path, mode);
if(infile)
{
strstrm<<infile.rdbuf();
infile.close();
return strstrm.str();
}
return std::string();
}
bool fileExist(std::string path)
{
//using c++17 standard, but may cause problem on clang
//return std::filesystem::exists(path);
return _access(path.data(), 4) != -1;
}
bool fileCopy(std::string source, 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;
}
std::string fileToBase64(std::string filepath)
{
return base64_encode(fileGet(filepath));
}
std::string fileGetMD5(std::string filepath)
{
return getMD5(fileGet(filepath));
}
int fileWrite(std::string path, std::string content, bool overwrite)
{
std::fstream outfile;
std::ios::openmode mode = overwrite ? std::ios::out : std::ios::app;
outfile.open(path, mode);
outfile << content << std::endl;
outfile.close();
return 0;
}
bool isIPv4(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(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;
}
std::string rand_str(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;
}
void urlParse(std::string url, std::string &host, std::string &path, int &port, bool &isTLS)
{
std::vector<std::string> args;
if(regMatch(url, "^https://(.*)"))
isTLS = true;
url = regReplace(url, "^(http|https)://", "");
if(url.find("/") == url.npos)
{
host = url;
path = "/";
}
else
{
host = url.substr(0, url.find("/"));
path = url.substr(url.find("/"));
}
if(regFind(host, "\\[(.*)\\]")) //IPv6
{
args = split(regReplace(host, "\\[(.*)\\](.*)", "$1,$2"), ",");
if(args.size() == 2) //with port
port = stoi(args[1].substr(1));
host = args[0];
}
else if(strFind(host, ":"))
{
port = stoi(host.substr(host.rfind(":") + 1));
host = host.substr(0, host.rfind(":"));
}
if(port == 0)
{
if(isTLS)
port = 443;
else
port = 80;
}
}
bool is_str_utf8(std::string data)
{
const char *str = data.c_str();
unsigned int nBytes = 0;
unsigned char chr;
bool bAllAscii = true;
for (unsigned int i = 0; str[i] != '\0'; ++i)
{
chr = *(str + i);
if (nBytes == 0 && (chr & 0x80) != 0)
{
bAllAscii = false;
}
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;
}
if (bAllAscii)
{
return true;
}
return true;
}
void removeUTF8BOM(std::string &data)
{
int BOM[3] = {0xef, 0xbb, 0xbf};
if(data.compare(0, 3, (char*)BOM) == 0)
data = data.substr(3);
}
int shortAssemble(unsigned short num_a, unsigned short num_b)
{
return (int)num_b << 16 | num_a;
}
void shortDisassemble(int source, unsigned short &num_a, unsigned short &num_b)
{
num_a = (unsigned short)source;
num_b = (unsigned short)(source >> 16);
}
int to_int(std::string &s, int def_vaule)
{
int retval = 0;
char c;
std::stringstream ss(s);
if(!(ss >> retval))
return def_vaule;
else if(ss >> c)
return def_vaule;
else
return retval;
}
std::string getFormData(const std::string &raw_data)
{
std::stringstream strstrm;
std::string line;
std::string boundary;
std::string disp; /* content disposition string */
std::string type; /* content type string */
std::string file; /* actual file content */
int i = 0;
strstrm<<raw_data;
while (std::getline(strstrm, line))
{
if(i == 0)
{
// Get boundary
boundary = line.substr(0, line.length() - 1);
}
else if(line.find("Content-Disposition") == 0)
{
disp = line;
}
else if(line.find("Content-Type") == 0)
{
type = line;
}
else if(line.find(boundary) == 0)
{
// The end
type = line;
break;
}
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;
int k;
while(j < 256 && strstrm.get(c) && !endfile)
{
buffer[j] = c;
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;
}