mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2026-11-02 17:52:19 +00:00
First public version
This commit is contained in:
26 files changed
+7941
No files matched your search
+2876
File diff suppressed because it is too large.
Load diff
+711
@@ -0,0 +1,711 @@
|
||||
#ifndef INI_READER_H_INCLUDED
|
||||
#define INI_READER_H_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#define MAX_LINE_LENGTH 4096
|
||||
|
||||
typedef std::map<std::string, std::multimap<std::string, std::string>> ini_data_struct;
|
||||
typedef std::multimap<std::string, std::string> string_multimap;
|
||||
|
||||
class INIReader
|
||||
{
|
||||
/**
|
||||
* @brief A simple INI reader which utilize map and vector
|
||||
* to store sections and items, allowing access in logarithmic time.
|
||||
*/
|
||||
private:
|
||||
bool parsed = false;
|
||||
std::string current_section;
|
||||
ini_data_struct ini_content;
|
||||
string_array exclude_sections, include_sections, read_sections;
|
||||
|
||||
std::string cached_section;
|
||||
string_multimap cached_section_content;
|
||||
|
||||
std::string isolated_items_section;
|
||||
|
||||
bool chkIgnore(std::string section)
|
||||
{
|
||||
bool excluded = false, included = false;
|
||||
if(count(exclude_sections.begin(), exclude_sections.end(), section) > 0)
|
||||
excluded = true;
|
||||
if(include_sections.size() != 0)
|
||||
{
|
||||
if(count(include_sections.begin(), include_sections.end(), section) > 0)
|
||||
included = true;
|
||||
}
|
||||
else
|
||||
included = true;
|
||||
|
||||
return excluded || !included;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Set this flag to true to do a UTF8-To-GBK conversion before parsing data. Only useful in Windows.
|
||||
*/
|
||||
bool do_utf8_to_gbk = false;
|
||||
|
||||
/**
|
||||
* @brief Set this flag to true so any line within the section will be stored even it doesn't follow the "name=value" format.
|
||||
* These lines will store as the name "{NONAME}".
|
||||
*/
|
||||
bool store_any_line = false;
|
||||
|
||||
/**
|
||||
* @brief Save isolated items before any section definitions.
|
||||
*/
|
||||
bool store_isolated_line = false;
|
||||
|
||||
/**
|
||||
* @brief Allow a section title to appear multiple times.
|
||||
*/
|
||||
bool allow_dup_section_titles = false;
|
||||
|
||||
/**
|
||||
* @brief Initialize the reader.
|
||||
*/
|
||||
INIReader()
|
||||
{
|
||||
parsed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a file during initialization.
|
||||
*/
|
||||
INIReader(std::string filePath)
|
||||
{
|
||||
parsed = false;
|
||||
ParseFile(filePath);
|
||||
}
|
||||
|
||||
~INIReader()
|
||||
{
|
||||
//nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exclude a section with the given name.
|
||||
*/
|
||||
void ExcludeSection(std::string section)
|
||||
{
|
||||
exclude_sections.emplace_back(section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Include a section with the given name.
|
||||
*/
|
||||
void IncludeSection(std::string section)
|
||||
{
|
||||
include_sections.emplace_back(section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set isolated items to given section.
|
||||
*/
|
||||
void SetIsolatedItemsSection(std::string section)
|
||||
{
|
||||
isolated_items_section = section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse INI content into mapped data structure.
|
||||
* If exclude sections are set, these sections will not be stored.
|
||||
* If include sections are set, only these sections will be stored.
|
||||
*/
|
||||
int Parse(std::string content) //parse content into mapped data
|
||||
{
|
||||
if(!content.size()) //empty content
|
||||
return -1;
|
||||
|
||||
//remove UTF-8 BOM
|
||||
removeUTF8BOM(content);
|
||||
|
||||
bool inExcludedSection = false;
|
||||
std::string strLine, thisSection, curSection, itemName, itemVal;
|
||||
string_multimap itemGroup, existItemGroup;
|
||||
std::stringstream strStrm;
|
||||
unsigned int lineSize = 0, epos = 0;
|
||||
char delimiter = count(content.begin(), content.end(), '\n') <= 1 ? '\r' : '\n';
|
||||
|
||||
EraseAll(); //first erase all data
|
||||
if(do_utf8_to_gbk && is_str_utf8(content))
|
||||
content = UTF8ToGBK(content); //do conversion if flag is set
|
||||
|
||||
if(store_isolated_line)
|
||||
curSection = isolated_items_section; //items before any section define will be store in this section
|
||||
strStrm<<content;
|
||||
while(getline(strStrm, strLine, delimiter)) //get one line of content
|
||||
{
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || lineSize > MAX_LINE_LENGTH || strLine[0] == ';' || strLine[0] == '#') //empty lines, lines longer than MAX_LINE_LENGTH and comments are ignored
|
||||
continue;
|
||||
if(strLine[lineSize - 1] == '\r') //remove line break
|
||||
{
|
||||
strLine.replace(lineSize - 1, 0, "");
|
||||
lineSize--;
|
||||
}
|
||||
epos = strLine.find("=");
|
||||
if(epos != strLine.npos) //is an item
|
||||
{
|
||||
if(inExcludedSection) //this section is excluded
|
||||
continue;
|
||||
if(!curSection.size()) //not in any section
|
||||
return -1;
|
||||
itemName = trim(strLine.substr(0, epos));
|
||||
itemVal = trim(strLine.substr(epos + 1));
|
||||
itemGroup.emplace(itemName, itemVal); //insert to current section
|
||||
}
|
||||
else if(strLine[0] == '[' && strLine[lineSize - 1] == ']') //is a section title
|
||||
{
|
||||
thisSection = strLine.substr(1, lineSize - 2); //save section title
|
||||
inExcludedSection = chkIgnore(thisSection); //check if this section is excluded
|
||||
|
||||
if(curSection.size() && itemGroup.size()) //just finished reading a section
|
||||
{
|
||||
if(ini_content.count(curSection)) //a section with the same name has been inserted
|
||||
{
|
||||
if(allow_dup_section_titles)
|
||||
{
|
||||
eraseElements(existItemGroup);
|
||||
existItemGroup = ini_content.at(curSection); //get the old items
|
||||
for(auto &x : existItemGroup)
|
||||
itemGroup.emplace(x); //insert them all into new section
|
||||
ini_content.erase(curSection); //remove the old section
|
||||
}
|
||||
else
|
||||
return -1; //not allowed, stop
|
||||
}
|
||||
ini_content.emplace(curSection, itemGroup); //insert previous section to content map
|
||||
read_sections.emplace_back(curSection); //add to read sections list
|
||||
}
|
||||
eraseElements(itemGroup); //reset section storage
|
||||
curSection = thisSection; //start a new section
|
||||
}
|
||||
else if(store_any_line && !inExcludedSection && curSection.size()) //store a line without name
|
||||
{
|
||||
itemGroup.emplace("{NONAME}", strLine);
|
||||
}
|
||||
if(include_sections.size() && include_sections == read_sections) //all included sections has been read
|
||||
break; //exit now
|
||||
}
|
||||
if(curSection.size() && itemGroup.size()) //final section
|
||||
{
|
||||
if(ini_content.count(curSection)) //a section with the same name has been inserted
|
||||
{
|
||||
if(allow_dup_section_titles)
|
||||
{
|
||||
eraseElements(existItemGroup);
|
||||
existItemGroup = ini_content.at(curSection); //get the old items
|
||||
for(auto &x : existItemGroup)
|
||||
itemGroup.emplace(x); //insert them all into new section
|
||||
ini_content.erase(curSection); //remove the old section
|
||||
}
|
||||
else
|
||||
return -1; //not allowed, stop
|
||||
}
|
||||
ini_content.emplace(curSection, itemGroup); //insert this section to content map
|
||||
read_sections.emplace_back(curSection); //add to read sections list
|
||||
}
|
||||
parsed = true;
|
||||
return 0; //all done
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse an INI file into mapped data structure.
|
||||
*/
|
||||
int ParseFile(std::string filePath)
|
||||
{
|
||||
if(!fileExist(filePath))
|
||||
return -1;
|
||||
return Parse(fileGet(filePath, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether a section exist.
|
||||
*/
|
||||
bool SectionExist(std::string section)
|
||||
{
|
||||
return ini_content.find(section) != ini_content.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Count of sections in the whole INI.
|
||||
*/
|
||||
unsigned int SectionCount()
|
||||
{
|
||||
return ini_content.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return all section names inside INI.
|
||||
*/
|
||||
string_array GetSections()
|
||||
{
|
||||
string_array retData;
|
||||
|
||||
for(auto &x : ini_content)
|
||||
{
|
||||
retData.emplace_back(x.first);
|
||||
}
|
||||
|
||||
return retData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enter a section with the given name. Section name and data will be cached to speed up the following reading process.
|
||||
*/
|
||||
int EnterSection(std::string section)
|
||||
{
|
||||
if(!SectionExist(section))
|
||||
return -1;
|
||||
current_section = cached_section = section;
|
||||
cached_section_content = ini_content.at(section);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set current section.
|
||||
*/
|
||||
void SetCurrentSection(std::string section)
|
||||
{
|
||||
current_section = section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether an item exist in the given section. Return false if the section does not exist.
|
||||
*/
|
||||
bool ItemExist(std::string section, std::string itemName)
|
||||
{
|
||||
if(!SectionExist(section))
|
||||
return false;
|
||||
|
||||
if(section != cached_section)
|
||||
{
|
||||
cached_section = section;
|
||||
cached_section_content= ini_content.at(section);
|
||||
}
|
||||
|
||||
return cached_section_content.find(itemName) != cached_section_content.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether an item exist in current section. Return false if the section does not exist.
|
||||
*/
|
||||
bool ItemExist(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? ItemExist(current_section, itemName) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether an item with the given name prefix exist in the given section. Return false if the section does not exist.
|
||||
*/
|
||||
bool ItemPrefixExist(std::string section, std::string itemName)
|
||||
{
|
||||
if(!SectionExist(section))
|
||||
return false;
|
||||
|
||||
if(section != cached_section)
|
||||
{
|
||||
cached_section = section;
|
||||
cached_section_content= ini_content.at(section);
|
||||
}
|
||||
|
||||
for(auto &x : cached_section_content)
|
||||
{
|
||||
if(x.first.find(itemName) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether an item with the given name prefix exist in current section. Return false if the section does not exist.
|
||||
*/
|
||||
bool ItemPrefixExist(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? ItemPrefixExist(current_section, itemName) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Count of items in the given section. Return 0 if the section does not exist.
|
||||
*/
|
||||
unsigned int ItemCount(std::string section)
|
||||
{
|
||||
if(!parsed || !SectionExist(section))
|
||||
return 0;
|
||||
|
||||
return ini_content.at(section).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase all data from the data structure and reset parser status.
|
||||
*/
|
||||
void EraseAll()
|
||||
{
|
||||
eraseElements(ini_content);
|
||||
parsed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve all items in the given section.
|
||||
*/
|
||||
int GetItems(std::string section, std::multimap<std::string, std::string> &data)
|
||||
{
|
||||
if(!parsed || !SectionExist(section))
|
||||
return -1;
|
||||
|
||||
if(cached_section != section)
|
||||
{
|
||||
cached_section = section;
|
||||
cached_section_content = ini_content.at(section);
|
||||
}
|
||||
|
||||
data = cached_section_content;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve all items in current section.
|
||||
*/
|
||||
int GetItems(string_multimap &data)
|
||||
{
|
||||
return current_section.size() ? GetItems(current_section, data) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve item(s) with the same name prefix in the given section.
|
||||
*/
|
||||
int GetAll(std::string section, std::string itemName, string_array &results) //retrieve item(s) with the same itemName prefix
|
||||
{
|
||||
if(!parsed)
|
||||
return -1;
|
||||
|
||||
string_multimap mapTemp;
|
||||
|
||||
if(GetItems(section, mapTemp) != 0)
|
||||
return -1;
|
||||
|
||||
for(auto &x : mapTemp)
|
||||
{
|
||||
if(x.first.find(itemName) == 0)
|
||||
results.emplace_back(x.second);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve item(s) with the same name prefix in current section.
|
||||
*/
|
||||
int GetAll(std::string itemName, string_array &results)
|
||||
{
|
||||
return current_section.size() ? GetAll(current_section, itemName, results) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one item with the exact same name in the given section.
|
||||
*/
|
||||
std::string Get(std::string section, std::string itemName) //retrieve one item with the exact same itemName
|
||||
{
|
||||
if(!parsed)
|
||||
return std::string();
|
||||
|
||||
string_multimap mapTemp;
|
||||
|
||||
if(GetItems(section, mapTemp) != 0)
|
||||
return std::string();
|
||||
|
||||
for(auto &x : mapTemp)
|
||||
{
|
||||
if(x.first == itemName)
|
||||
return x.second;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one item with the exact same name in current section.
|
||||
*/
|
||||
std::string Get(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? Get(current_section, itemName) : std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one boolean item value with the exact same name in the given section.
|
||||
*/
|
||||
bool GetBool(std::string section, std::string itemName)
|
||||
{
|
||||
return Get(section, itemName) == "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one boolean item value with the exact same name in current section.
|
||||
*/
|
||||
bool GetBool(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? Get(current_section, itemName) == "true" : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one integer item value with the exact same name in the given section.
|
||||
*/
|
||||
int GetInt(std::string section, std::string itemName)
|
||||
{
|
||||
return stoi(Get(section, itemName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve one integer item value with the exact same name in current section.
|
||||
*/
|
||||
int GetInt(std::string itemName)
|
||||
{
|
||||
return GetInt(current_section, itemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the first item found in the given section.
|
||||
*/
|
||||
std::string GetFirst(std::string section, std::string itemName) //return the first item value found in section
|
||||
{
|
||||
if(!parsed)
|
||||
return std::string();
|
||||
string_array result;
|
||||
if(GetAll(section, itemName, result) != -1)
|
||||
return result[0];
|
||||
else
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the first item found in current section.
|
||||
*/
|
||||
std::string GetFirst(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? GetFirst(current_section, itemName) : std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve a string style array with specific separator and write into integer array.
|
||||
*/
|
||||
template <typename T> void GetIntArray(std::string section, std::string itemName, std::string separator, T &Array)
|
||||
{
|
||||
string_array vArray;
|
||||
unsigned int index, UBound = sizeof(Array) / sizeof(Array[0]);
|
||||
vArray = split(Get(section, itemName), separator);
|
||||
for(index = 0; index < vArray.size() && index < UBound; index++)
|
||||
Array[index] = stoi(vArray[index]);
|
||||
for(; index < UBound; index++)
|
||||
Array[index] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve a string style array with specific separator and write into integer array.
|
||||
*/
|
||||
template <typename T> void GetIntArray(std::string itemName, std::string separator, T &Array)
|
||||
{
|
||||
if(current_section.size())
|
||||
GetIntArray(current_section, itemName, separator, Array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a std::string value with given values.
|
||||
*/
|
||||
int Set(std::string section, std::string itemName, std::string itemVal)
|
||||
{
|
||||
std::string value;
|
||||
|
||||
if(!section.size())
|
||||
return -1;
|
||||
|
||||
if(!parsed)
|
||||
parsed = true;
|
||||
|
||||
if(SectionExist(section))
|
||||
{
|
||||
string_multimap &mapTemp = ini_content.at(section);
|
||||
mapTemp.insert(std::pair<std::string, std::string>(itemName, itemVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
string_multimap mapTemp;
|
||||
mapTemp.insert(std::pair<std::string, std::string>(itemName, itemVal));
|
||||
ini_content.insert(std::pair<std::string, std::multimap<std::string, std::string>>(section, mapTemp));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a string value with given values.
|
||||
*/
|
||||
int Set(std::string itemName, std::string itemVal)
|
||||
{
|
||||
if(!current_section.size())
|
||||
return -1;
|
||||
return Set(current_section, itemName, itemVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a boolean value with given values.
|
||||
*/
|
||||
int SetBool(std::string section, std::string itemName, bool itemVal)
|
||||
{
|
||||
return Set(section, itemName, itemVal ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a boolean value with given values.
|
||||
*/
|
||||
int SetBool(std::string itemName, bool itemVal)
|
||||
{
|
||||
return SetBool(current_section, itemName, itemVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a double value with given values.
|
||||
*/
|
||||
int SetDouble(std::string section, std::string itemName, double itemVal)
|
||||
{
|
||||
return Set(section, itemName, std::__cxx11::to_string(itemVal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a double value with given values.
|
||||
*/
|
||||
int SetDouble(std::string itemName, double itemVal)
|
||||
{
|
||||
return SetDouble(current_section, itemName, itemVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a long value with given values.
|
||||
*/
|
||||
int SetLong(std::string section, std::string itemName, long itemVal)
|
||||
{
|
||||
return Set(section, itemName, std::__cxx11::to_string(itemVal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a long value with given values.
|
||||
*/
|
||||
int SetLong(std::string itemName, long itemVal)
|
||||
{
|
||||
return SetLong(current_section, itemName, itemVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an array with the given separator.
|
||||
*/
|
||||
template <typename T> int SetArray(std::string section, std::string itemName, std::string separator, T &Array)
|
||||
{
|
||||
std::string data;
|
||||
for(auto &x : Array)
|
||||
data += std::__cxx11::to_string(x) + separator;
|
||||
data = data.substr(0, data.size() - separator.size());
|
||||
return Set(section, itemName, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an array with the given separator.
|
||||
*/
|
||||
template <typename T> int SetArray(std::string itemName, std::string separator, T &Array)
|
||||
{
|
||||
return current_section.size() ? SetArray(current_section, itemName, separator, Array) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase all items with the given name.
|
||||
*/
|
||||
int Erase(std::string section, std::string itemName)
|
||||
{
|
||||
int retVal;
|
||||
if(!SectionExist(section))
|
||||
return -1;
|
||||
|
||||
retVal = ini_content.at(section).erase(itemName);
|
||||
if(retVal && cached_section == section)
|
||||
{
|
||||
cached_section_content = ini_content.at(section);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase all items with the given name.
|
||||
*/
|
||||
int Erase(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? Erase(current_section, itemName) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase the first item with the given name.
|
||||
*/
|
||||
int EraseFirst(std::string section, std::string itemName)
|
||||
{
|
||||
string_multimap &mapTemp = ini_content.at(section);
|
||||
string_multimap::iterator iter = mapTemp.find(itemName);
|
||||
if(iter != mapTemp.end())
|
||||
{
|
||||
mapTemp.erase(iter);
|
||||
if(cached_section == section)
|
||||
{
|
||||
cached_section_content = mapTemp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase the first item with the given name.
|
||||
*/
|
||||
int EraseFirst(std::string itemName)
|
||||
{
|
||||
return current_section.size() ? EraseFirst(current_section, itemName) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Export the whole INI data structure into a string.
|
||||
*/
|
||||
std::string ToString()
|
||||
{
|
||||
std::string content;
|
||||
|
||||
if(!parsed)
|
||||
return std::string();
|
||||
|
||||
for(auto &x : ini_content)
|
||||
{
|
||||
content += "[" + x.first + "]\n";
|
||||
for(auto &y : x.second)
|
||||
{
|
||||
if(y.first != "{NONAME}")
|
||||
content += y.first + " = ";
|
||||
content += y.second + "\n";
|
||||
}
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Export the whole INI data structure into a file.
|
||||
*/
|
||||
int ToFile(std::string filePath)
|
||||
{
|
||||
return fileWrite(filePath, ToString(), true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // INI_READER_H_INCLUDED
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
void writeLog(int type, std::string content)
|
||||
{
|
||||
//placeholder
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef LOGGER_H_INCLUDED
|
||||
#define LOGGER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#define LOG_TYPE_INFO 1
|
||||
#define LOG_TYPE_ERROR 2
|
||||
#define LOG_TYPE_RAW 3
|
||||
#define LOG_TYPE_WARN 4
|
||||
#define LOG_TYPE_TCPING 5
|
||||
#define LOG_TYPE_FILEDL 6
|
||||
#define LOG_TYPE_GEOIP 7
|
||||
#define LOG_TYPE_RULES 8
|
||||
#define LOG_TYPE_GPING 9
|
||||
#define LOG_TYPE_RENDER 10
|
||||
#define LOG_TYPE_FILEUL 11
|
||||
|
||||
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, std::string content);
|
||||
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
|
||||
@@ -0,0 +1,241 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "socket.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"
|
||||
|
||||
//common settings
|
||||
string_array def_exclude_remarks, def_include_remarks, rulesets;
|
||||
std::vector<ruleset_content> ruleset_content_array;
|
||||
std::string listen_address = "127.0.0.1", default_url;
|
||||
int listen_port = 25500, max_pending_connections = 10, max_concurrent_threads = 4;
|
||||
bool api_mode = true, update_ruleset_on_request = false, overwrite_original_rules = true;
|
||||
extern std::string custom_group;
|
||||
|
||||
//preferences
|
||||
string_array renames, emojis;
|
||||
bool add_emoji = false, remove_old_emoji = false;
|
||||
|
||||
//clash custom
|
||||
std::string clash_rule_base, clash_base_content;
|
||||
string_array clash_extra_group;
|
||||
|
||||
std::string refreshRulesets()
|
||||
{
|
||||
eraseElements(ruleset_content_array);
|
||||
string_array vArray;
|
||||
std::string rule_group, rule_url, proxy = getSystemProxy();
|
||||
ruleset_content rc;
|
||||
for(std::string &x : rulesets)
|
||||
{
|
||||
vArray = split(x, ",");
|
||||
if(vArray.size() != 2)
|
||||
continue;
|
||||
rule_group = trim(vArray[0]);
|
||||
rule_url = trim(vArray[1]);
|
||||
if(rule_url.find("[]") == 0)
|
||||
{
|
||||
std::cerr<<"Adding rule '"<<rule_url.substr(2)<<","<<rule_group<<"'."<<std::endl;
|
||||
rc = {rule_group, rule_url};
|
||||
ruleset_content_array.emplace_back(rc);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr<<"Updating ruleset url '"<<rule_url<<"' with group '"<<rule_group<<"'."<<std::endl;
|
||||
if(fileExist(rule_url))
|
||||
{
|
||||
rc = {rule_group, fileGet(rule_url, false)};
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = {rule_group, webGet(rule_url, proxy)};
|
||||
}
|
||||
}
|
||||
if(rc.rule_content.size())
|
||||
ruleset_content_array.emplace_back(rc);
|
||||
}
|
||||
|
||||
return "done";
|
||||
}
|
||||
|
||||
void readConf()
|
||||
{
|
||||
eraseElements(def_exclude_remarks);
|
||||
eraseElements(def_include_remarks);
|
||||
eraseElements(clash_extra_group);
|
||||
eraseElements(rulesets);
|
||||
eraseElements(renames);
|
||||
eraseElements(emojis);
|
||||
INIReader ini;
|
||||
//ini.do_utf8_to_gbk = true;
|
||||
ini.ParseFile("pref.ini");
|
||||
|
||||
ini.EnterSection("common");
|
||||
if(ini.ItemExist("api_mode"))
|
||||
api_mode = ini.GetBool("api_mode");
|
||||
if(ini.ItemExist("default_url"))
|
||||
default_url = ini.Get("default_url");
|
||||
if(ini.ItemPrefixExist("exclude_remarks"))
|
||||
ini.GetAll("exclude_remarks", def_exclude_remarks);
|
||||
if(ini.ItemPrefixExist("include_remarks"))
|
||||
ini.GetAll("include_remarks", def_exclude_remarks);
|
||||
if(ini.ItemExist("clash_rule_base"))
|
||||
clash_rule_base = ini.Get("clash_rule_base");
|
||||
if(ini.ItemPrefixExist("rename_node"))
|
||||
ini.GetAll("rename_node", renames);
|
||||
|
||||
ini.EnterSection("emojis");
|
||||
if(ini.ItemExist("add_emoji"))
|
||||
add_emoji = ini.GetBool("add_emoji");
|
||||
if(ini.ItemExist("remove_old_emoji"))
|
||||
remove_old_emoji = ini.GetBool("remove_old_emoji");
|
||||
if(ini.ItemPrefixExist("rule"))
|
||||
ini.GetAll("rule", emojis);
|
||||
|
||||
ini.EnterSection("ruleset");
|
||||
if(ini.GetBool("enabled"))
|
||||
{
|
||||
if(ini.ItemExist("overwrite_original_rules"))
|
||||
overwrite_original_rules = ini.GetBool("overwrite_original_rules");
|
||||
if(ini.ItemExist("update_ruleset_on_request"))
|
||||
update_ruleset_on_request = ini.GetBool("update_ruleset_on_request");
|
||||
if(ini.ItemPrefixExist("surge_ruleset"))
|
||||
ini.GetAll("surge_ruleset", rulesets);
|
||||
}
|
||||
else
|
||||
{
|
||||
overwrite_original_rules = false;
|
||||
update_ruleset_on_request = false;
|
||||
}
|
||||
|
||||
ini.EnterSection("clash_proxy_group");
|
||||
if(ini.ItemPrefixExist("custom_proxy_group"))
|
||||
ini.GetAll("custom_proxy_group", clash_extra_group);
|
||||
|
||||
ini.EnterSection("server");
|
||||
if(ini.ItemExist("listen"))
|
||||
listen_address = ini.Get("listen");
|
||||
if(ini.ItemExist("port"))
|
||||
listen_port = ini.GetInt("port");
|
||||
|
||||
ini.EnterSection("advanced");
|
||||
if(ini.ItemExist("max_pending_connections"))
|
||||
max_pending_connections = ini.GetInt("max_pending_connections");
|
||||
if(ini.ItemExist("max_concurrent_threads"))
|
||||
max_concurrent_threads = ini.GetInt("max_concurrent_threads");
|
||||
|
||||
remarksInit(def_exclude_remarks, def_include_remarks);
|
||||
}
|
||||
|
||||
std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, bool clashR);
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
|
||||
{
|
||||
fprintf(stderr, "WSAStartup failed.\n");
|
||||
return 1;
|
||||
}
|
||||
SetConsoleOutputCP(65001);
|
||||
#endif // _WIN32
|
||||
|
||||
readConf();
|
||||
if(!update_ruleset_on_request)
|
||||
refreshRulesets();
|
||||
|
||||
append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
return refreshRulesets();
|
||||
});
|
||||
|
||||
append_response("GET", "/clash", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
readConf();
|
||||
std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group"));
|
||||
if(!url.size()) url = default_url;
|
||||
string_array urls = split(url, "|");
|
||||
std::vector<nodeInfo> nodes;
|
||||
if(include.size())
|
||||
{
|
||||
eraseElements(def_include_remarks);
|
||||
def_include_remarks.emplace_back(include);
|
||||
}
|
||||
if(group.size()) custom_group = group;
|
||||
for(std::string &x : urls)
|
||||
{
|
||||
std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: Clash"<<std::endl;
|
||||
addNodes(x, nodes);
|
||||
}
|
||||
if(fileExist(clash_rule_base))
|
||||
clash_base_content = fileGet(clash_rule_base, false);
|
||||
else
|
||||
clash_base_content = webGet(clash_rule_base, getSystemProxy());
|
||||
|
||||
if(update_ruleset_on_request)
|
||||
refreshRulesets();
|
||||
return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, false);
|
||||
});
|
||||
|
||||
append_response("GET", "/clashr", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
readConf();
|
||||
std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group"));
|
||||
if(!url.size()) url = default_url;
|
||||
string_array urls = split(url, "|");
|
||||
std::vector<nodeInfo> nodes;
|
||||
if(include.size())
|
||||
{
|
||||
eraseElements(def_include_remarks);
|
||||
def_include_remarks.emplace_back(include);
|
||||
}
|
||||
if(group.size()) custom_group = group;
|
||||
for(std::string &x : urls)
|
||||
{
|
||||
std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: ClashR"<<std::endl;
|
||||
addNodes(x, nodes);
|
||||
}
|
||||
if(fileExist(clash_rule_base))
|
||||
clash_base_content = fileGet(clash_rule_base, false);
|
||||
else
|
||||
clash_base_content = webGet(clash_rule_base, getSystemProxy());
|
||||
|
||||
if(update_ruleset_on_request)
|
||||
refreshRulesets();
|
||||
return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, true);
|
||||
});
|
||||
|
||||
if(!api_mode)
|
||||
{
|
||||
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string url = UrlDecode(getUrlArg(argument, "url"));
|
||||
return webGet(url, "");
|
||||
});
|
||||
|
||||
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
return fileGet(UrlDecode(getUrlArg(argument, "path")));
|
||||
});
|
||||
}
|
||||
|
||||
listener_args args = {listen_address, listen_port, 10240, 4};
|
||||
std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
|
||||
start_web_server(&args);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif // _WIN32
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
#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 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 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] == '%')
|
||||
{
|
||||
assert(i + 2 < length);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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 regFind(std::string src, std::string target)
|
||||
{
|
||||
std::regex reg(target);
|
||||
return regex_search(src, reg);
|
||||
}
|
||||
|
||||
std::string regReplace(std::string src, std::string match, std::string rep)
|
||||
{
|
||||
std::string result = "";
|
||||
std::regex reg(match);
|
||||
regex_replace(back_inserter(result), src.begin(), src.end(), reg, rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool regMatch(std::string src, std::string match)
|
||||
{
|
||||
std::regex reg(match);
|
||||
return regex_match(src, reg);
|
||||
}
|
||||
|
||||
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 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
|
||||
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 = *str;
|
||||
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);
|
||||
}
|
||||
|
||||
std::string to_string(YAML::Node &node)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << node;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string getFormData(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;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef MISC_H_INCLUDED
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATH_SLASH "\\"
|
||||
#else
|
||||
#define PATH_SLASH "//"
|
||||
#endif // _WIN32
|
||||
|
||||
typedef std::vector<std::string> string_array;
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string UrlDecode(const std::string& str);
|
||||
std::string base64_decode(std::string encoded_string);
|
||||
std::string base64_encode(std::string string_to_encode);
|
||||
|
||||
std::vector<std::string> split(const std::string &s, const std::string &seperator);
|
||||
std::string getUrlArg(std::string url, std::string request);
|
||||
std::string replace_all_distinct(std::string str, std::string old_value, std::string new_value);
|
||||
std::string urlsafe_base64_reverse(std::string encoded_string);
|
||||
std::string urlsafe_base64_decode(std::string encoded_string);
|
||||
std::string UTF8ToGBK(std::string str_src);
|
||||
std::string GBKToUTF8(std::string str_src);
|
||||
std::string trim(const std::string& str);
|
||||
std::string getSystemProxy();
|
||||
std::string rand_str(const int len);
|
||||
bool is_str_utf8(std::string data);
|
||||
std::string getFormData(std::string &raw_data);
|
||||
|
||||
void sleep(int interval);
|
||||
bool regFind(std::string src, std::string target);
|
||||
std::string regReplace(std::string src, std::string match, std::string rep);
|
||||
bool regMatch(std::string src, std::string match);
|
||||
std::string speedCalc(double speed);
|
||||
std::string getMD5(std::string data);
|
||||
bool isIPv4(std::string address);
|
||||
bool isIPv6(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 to_string(YAML::Node &node);
|
||||
|
||||
std::string fileGet(std::string path, bool binary = true);
|
||||
int fileWrite(std::string path, std::string content, bool overwrite);
|
||||
bool fileExist(std::string path);
|
||||
bool fileCopy(std::string source,std::string dest);
|
||||
std::string fileToBase64(std::string filepath);
|
||||
std::string fileGetMD5(std::string filepath);
|
||||
|
||||
static inline bool strFind(std::string str, std::string target)
|
||||
{
|
||||
return str.find(target) != str.npos;
|
||||
}
|
||||
|
||||
template <typename T> static inline void eraseElements(T &target)
|
||||
{
|
||||
T().swap(target);
|
||||
}
|
||||
|
||||
#ifdef _MACOS
|
||||
namespace std
|
||||
{
|
||||
namespace __cxx11
|
||||
{
|
||||
template <typename T> std::string to_string(const T& n)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << n ;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _MACOS
|
||||
|
||||
#ifdef _WIN32
|
||||
void StringToWstring(std::wstring& szDst, std::string str);
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // MISC_H_INCLUDED
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#ifndef NODEINFO_H_INCLUDED
|
||||
#define NODEINFO_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
struct nodeInfo
|
||||
{
|
||||
int linkType = -1;
|
||||
int id = 0;
|
||||
std::string group;
|
||||
std::string remarks;
|
||||
std::string server;
|
||||
int port = 0;
|
||||
std::string proxyStr;
|
||||
};
|
||||
|
||||
#endif // NODEINFO_H_INCLUDED
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "nodeinfo.h"
|
||||
#include "printout.h"
|
||||
#include "logger.h"
|
||||
#include "webget.h"
|
||||
#include "speedtestutil.h"
|
||||
|
||||
std::string override_conf_port, custom_group;
|
||||
int socksport;
|
||||
bool ss_libev, ssr_libev;
|
||||
|
||||
void copyNodes(std::vector<nodeInfo> *source, std::vector<nodeInfo> *dest)
|
||||
{
|
||||
for(auto &x : *source)
|
||||
{
|
||||
dest->push_back(x);
|
||||
}
|
||||
}
|
||||
|
||||
void addNodes(std::string link, std::vector<nodeInfo> &allNodes)
|
||||
{
|
||||
int linkType = -1;
|
||||
std::vector<nodeInfo> nodes;
|
||||
nodeInfo node;
|
||||
std::string strSub, strInput, fileContent, strProxy;
|
||||
|
||||
link = replace_all_distinct(link, "\"", "");
|
||||
writeLog(LOG_TYPE_INFO, "Received Link.");
|
||||
if(strFind(link, "vmess://") || strFind(link, "vmess1://"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
|
||||
else if(strFind(link, "ss://"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSS;
|
||||
else if(strFind(link, "ssr://"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
|
||||
else if(strFind(link, "socks://") || strFind(link, "https://t.me/socks") || strFind(link, "tg://socks"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
|
||||
else if(strFind(link, "http://") || strFind(link, "https://") || strFind(link, "surge:///install-config"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSUB;
|
||||
else if(strFind(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(strFind(link, "surge:///install-config")) //surge config link
|
||||
link = UrlDecode(getUrlArg(link, "url"));
|
||||
strSub = webGet(link);
|
||||
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...");
|
||||
explodeConfContent(strSub, override_conf_port, socksport, ss_libev, ssr_libev, nodes);
|
||||
copyNodes(&nodes, &allNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Cannot download subscription data.");
|
||||
}
|
||||
break;
|
||||
case SPEEDTEST_MESSAGE_FOUNDLOCAL:
|
||||
writeLog(LOG_TYPE_INFO, "Parsing configuration file data...");
|
||||
if(explodeConf(link, override_conf_port, socksport, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE)
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "Invalid configuration file!");
|
||||
}
|
||||
else
|
||||
{
|
||||
copyNodes(&nodes, &allNodes);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(linkType > 0)
|
||||
{
|
||||
explode(link, ss_libev, ssr_libev, override_conf_port, socksport, node);
|
||||
if(custom_group.size() != 0)
|
||||
node.group = custom_group;
|
||||
if(node.server == "")
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "No valid link found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
allNodes.push_back(node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLog(LOG_TYPE_ERROR, "No valid link found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#ifndef NODEMANIP_H_INCLUDED
|
||||
#define NODEMANIP_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nodeinfo.h"
|
||||
|
||||
void addNodes(std::string link, std::vector<nodeInfo> &allNodes);
|
||||
|
||||
#endif // NODEMANIP_H_INCLUDED
|
||||
@@ -0,0 +1,139 @@
|
||||
[common]
|
||||
;Default URLs, used when no URL is provided in request, use "|" to separate multiple subscription links, supports local files/URL
|
||||
default_url=
|
||||
|
||||
;Exclude nodes which remarks match the following patterns. Supports regular expression.
|
||||
exclude_remarks=(剩余流量|到期时间|过期时间|官网地址|产品名称)
|
||||
;exclude_remarks=(other rule)
|
||||
|
||||
;Only include nodes which remarks match the following patterns. Supports regular expression.
|
||||
;include_remarks=V3.*港
|
||||
|
||||
;Clash config base used by the generator, supports local files/URL
|
||||
clash_rule_base=simple_base.yml
|
||||
|
||||
;Rename remarks with the following patterns. Supports regular expression.
|
||||
;Format: Search_Pattern@Replace_Pattern
|
||||
;rename_node=IPLC@专线
|
||||
;rename_node=RELAY@中转
|
||||
;rename_node=BGP-@
|
||||
;rename_node=\((.*?) 倍率\)@x$1
|
||||
;rename_node=\((.*?)\)$@ x$1
|
||||
|
||||
[emojis]
|
||||
add_emoji=true
|
||||
remove_old_emoji=true
|
||||
|
||||
;Rule to add emojis. Supports regular expression.
|
||||
;Format: Remark_Search_Pattern,emoji
|
||||
|
||||
rule=(流量|时间|应急),🏳️🌈
|
||||
rule=阿根廷,🇦🇷
|
||||
rule=(奥地利|维也纳),🇦🇹
|
||||
rule=(澳大利亚|悉尼),🇦🇺
|
||||
rule=(巴西|圣保罗),🇧🇷
|
||||
rule=(加拿大|蒙特利尔|温哥华),🇨🇦
|
||||
rule=(瑞士|苏黎世),🇨🇭
|
||||
rule=(中国|江苏|北京|上海|广州|深圳|杭州|徐州|青岛|宁波|镇江),🇨🇳
|
||||
rule=(德|德国|法兰克福),🇩🇪
|
||||
rule=(芬兰|赫尔辛基),🇫🇮
|
||||
rule=(法国|巴黎),🇫🇷
|
||||
rule=(英国|伦敦),🇬🇧
|
||||
rule=(港|香港|深港|沪港),🇭🇰
|
||||
rule=(印尼|印度尼西亚|雅加达),🇮🇩
|
||||
rule=(爱尔兰|都柏林),🇮🇪
|
||||
rule=(印度|孟买),🇮🇳
|
||||
rule=(意大利|米兰),🇮🇹
|
||||
rule=(日|日本|东京|大阪|埼玉|沪日),🇯🇵
|
||||
rule=朝鲜,🇰🇵
|
||||
rule=(韩国|首尔),🇰🇷
|
||||
rule=澳门,🇲🇴
|
||||
rule=马来西亚,🇲🇾
|
||||
rule=(荷兰|阿姆斯特丹),🇳🇱
|
||||
rule=菲律宾,🇵🇭
|
||||
rule=罗马尼亚,🇷🇴
|
||||
rule=(俄罗斯|伯力|莫斯科|圣彼得堡|西伯利亚|新西伯利亚),🇷🇺
|
||||
rule=(新|新加坡|狮城),🇸🇬
|
||||
rule=(泰国|曼谷),🇹🇭
|
||||
rule=(土耳其|伊斯坦布尔),🇹🇷
|
||||
rule=(台|台湾|台北|台中|新北|彰化),🇹🇼
|
||||
rule=(美|美国|波特兰|达拉斯|俄勒冈|凤凰城|费利蒙|硅谷|拉斯维加斯|洛杉矶|圣何塞|圣克拉拉|西雅图|芝加哥|沪美),🇺🇲
|
||||
rule=越南,🇻🇳
|
||||
rule=南非,🇿🇦
|
||||
|
||||
[ruleset]
|
||||
;Enable generating rules with rulesets
|
||||
enabled=true
|
||||
|
||||
;Overwrite the existing rules in rule_base
|
||||
overwrite_original_rules=false
|
||||
|
||||
;Perform a ruleset update on request
|
||||
update_ruleset_on_request=false
|
||||
|
||||
;Ruleset addresses, supports local files/URL
|
||||
;Format: Group name,URL
|
||||
; Group name,[]Rule
|
||||
|
||||
surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list
|
||||
surge_ruleset=Advertising,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Advertising.list
|
||||
surge_ruleset=Hijacking,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Hijacking.list
|
||||
surge_ruleset=YouTube,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/YouTube.list
|
||||
surge_ruleset=Netflix,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Netflix.list
|
||||
;surge_ruleset=HBO,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/HBO.list
|
||||
;surge_ruleset=Fox,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Fox.list
|
||||
surge_ruleset=GlobalMedia,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/GlobalMedia.list
|
||||
surge_ruleset=HKMTMedia,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/HKMTMedia.list
|
||||
surge_ruleset=Telegram,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Telegram.list
|
||||
surge_ruleset=PROXY,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Global.list
|
||||
surge_ruleset=Apple,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Apple.list
|
||||
surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/China.list
|
||||
surge_ruleset=Final,[]MATCH
|
||||
|
||||
[clash_proxy_group]
|
||||
;Generate Clash Proxy Group with the following patterns. Node filterting rule supports regular expression.
|
||||
;Format: Group_Name`select`Rule_1`Rule_2`...
|
||||
; Group_Name`url-test|fallback|load-balance`Rule_1`Rule_2`...`test_url`interval
|
||||
;Rule with "[]" prefix will be added directly.
|
||||
|
||||
;custom_proxy_group=UrlTest`url-test`.*`http://www.gstatic.com/generate_204`300
|
||||
;custom_proxy_group=FallBack`fallback`.*`http://www.gstatic.com/generate_204`300
|
||||
;custom_proxy_group=LoadBalance`load-balance`.*`http://www.gstatic.com/generate_204`300
|
||||
|
||||
;for forcerule.yml
|
||||
;custom_proxy_group=Proxy`select`.*
|
||||
;custom_proxy_group=google`select`.*
|
||||
;custom_proxy_group=netflix`select`.*
|
||||
;custom_proxy_group=动画疯`select`(深台|彰化|新北|台)
|
||||
;custom_proxy_group=fox+`select`(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|新加坡|sg|hk|tw)
|
||||
;custom_proxy_group=美区影视`select`(美|美国)
|
||||
;custom_proxy_group=Global_media`select`.*
|
||||
;custom_proxy_group=Domestic`select`[]DIRECT`[]Proxy
|
||||
;custom_proxy_group=Apple`select`[]DIRECT`[]Proxy
|
||||
;custom_proxy_group=Final`select`[]Proxy`[]DIRECT
|
||||
;custom_proxy_group=屏蔽广告`select`[]REJECT`[]DIRECT
|
||||
;custom_proxy_group=UnblockNeteaseMusic`select`云音乐解锁`[]DIRECT
|
||||
;custom_proxy_group=Telegram`select`新加坡`[]Proxy
|
||||
|
||||
;for DivineEngine_Profiles
|
||||
custom_proxy_group=PROXY`select`.*
|
||||
custom_proxy_group=YouTube`select`.*
|
||||
custom_proxy_group=Netflix`select`.*
|
||||
custom_proxy_group=Advertising`select`[]REJECT`[]DIRECT`[]PROXY
|
||||
custom_proxy_group=Hijacking`select`[]REJECT`[]DIRECT`[]PROXY
|
||||
custom_proxy_group=GlobalMedia`select`.*
|
||||
custom_proxy_group=HKMTMedia`select`(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|新加坡|sg|hk|tw)
|
||||
custom_proxy_group=Telegram`select`新加坡`[]PROXY
|
||||
custom_proxy_group=Apple`select`[]DIRECT`[]PROXY
|
||||
custom_proxy_group=Final`select`[]PROXY`[]DIRECT
|
||||
|
||||
[server]
|
||||
;Address to bind on for Web Server
|
||||
listen=0.0.0.0
|
||||
|
||||
;Port to bind on for Web Server
|
||||
port=25500
|
||||
|
||||
[advanced]
|
||||
max_pending_connections=10
|
||||
max_concurrent_threads=4
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
#ifndef PRINTOUT_H_INCLUDED
|
||||
#define PRINTOUT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "nodeinfo.h"
|
||||
|
||||
#define SPEEDTEST_ERROR_NONE 0
|
||||
#define SPEEDTEST_ERROR_UNDEFINED 100
|
||||
#define SPEEDTEST_ERROR_WSAERR 99
|
||||
#define SPEEDTEST_ERROR_SOCKETERR 98
|
||||
#define SPEEDTEST_ERROR_NORECOGLINK 97
|
||||
#define SPEEDTEST_ERROR_NOCONNECTION 96
|
||||
#define SPEEDTEST_ERROR_INVALIDSUB 95
|
||||
#define SPEEDTEST_ERROR_NONODES 94
|
||||
#define SPEEDTEST_ERROR_NORESOLVE 93
|
||||
#define SPEEDTEST_ERROR_RETEST 92
|
||||
#define SPEEDTEST_ERROR_NOSPEED 91
|
||||
#define SPEEDTEST_ERROR_UNRECOGFILE 90
|
||||
#define SPEEDTEST_ERROR_SUBFETCHERR 89
|
||||
#define SPEEDTEST_ERROR_GEOIPERR 88
|
||||
|
||||
#define SPEEDTEST_MESSAGE_WELCOME 0
|
||||
#define SPEEDTEST_MESSAGE_FOUNDVMESS 1
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSS 2
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSSR 3
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSUB 4
|
||||
#define SPEEDTEST_MESSAGE_GOTSERVER 5
|
||||
#define SPEEDTEST_MESSAGE_STARTPING 6
|
||||
#define SPEEDTEST_MESSAGE_GOTPING 7
|
||||
#define SPEEDTEST_MESSAGE_STARTSPEED 8
|
||||
#define SPEEDTEST_MESSAGE_GOTSPEED 9
|
||||
#define SPEEDTEST_MESSAGE_GOTRESULT 10
|
||||
#define SPEEDTEST_MESSAGE_TRAFFIC 11
|
||||
#define SPEEDTEST_MESSAGE_PICSAVING 12
|
||||
#define SPEEDTEST_MESSAGE_PICSAVED 13
|
||||
#define SPEEDTEST_MESSAGE_GROUP 14
|
||||
#define SPEEDTEST_MESSAGE_FETCHSUB 15
|
||||
#define SPEEDTEST_MESSAGE_BEGIN 16
|
||||
#define SPEEDTEST_MESSAGE_FOUNDLOCAL 17
|
||||
#define SPEEDTEST_MESSAGE_PARSING 18
|
||||
#define SPEEDTEST_MESSAGE_FOUNDUPD 19
|
||||
#define SPEEDTEST_MESSAGE_PICDATA 20
|
||||
#define SPEEDTEST_MESSAGE_STARTGPING 21
|
||||
#define SPEEDTEST_MESSAGE_GOTGPING 22
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSOCKS 23
|
||||
#define SPEEDTEST_MESSAGE_STARTGEOIP 24
|
||||
#define SPEEDTEST_MESSAGE_GOTGEOIP 25
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSSCONF 26
|
||||
#define SPEEDTEST_MESSAGE_MULTILINK 27
|
||||
#define SPEEDTEST_MESSAGE_PICSAVINGMULTI 28
|
||||
#define SPEEDTEST_MESSAGE_PICSAVEDMULTI 29
|
||||
#define SPEEDTEST_MESSAGE_STARTUPD 30
|
||||
#define SPEEDTEST_MESSAGE_GOTUPD 31
|
||||
#define SPEEDTEST_MESSAGE_FOUNDSSTAP 32
|
||||
#define SPEEDTEST_MESSAGE_FOUNDNETCH 33
|
||||
#define SPEEDTEST_MESSAGE_EOF 49
|
||||
|
||||
#define SS_DEFAULT_GROUP "SSProvider"
|
||||
#define SSR_DEFAULT_GROUP "SSRProvider"
|
||||
#define V2RAY_DEFAULT_GROUP "V2RayProvider"
|
||||
#define SOCKS_DEFAULT_GROUP "SocksProvider"
|
||||
|
||||
void printMsg(int index, nodeInfo *node, bool rpcmode);
|
||||
void printMsgWithDict(int index, bool rpcmode, std::vector<std::string> dict, std::vector<std::string> trans);
|
||||
void printMsgDirect(int index, bool rpcmode);
|
||||
|
||||
#endif // PRINTOUT_H_INCLUDED
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include "rapidjson_extra.h"
|
||||
|
||||
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.IsBool())
|
||||
i = value.GetBool() ? "true" : "false";
|
||||
else
|
||||
i = std::string();
|
||||
}
|
||||
|
||||
void operator >> (const rapidjson::Value& value, int& i)
|
||||
{
|
||||
if(value.IsNull())
|
||||
i = 0;
|
||||
else if(value.IsInt())
|
||||
i = value.GetInt();
|
||||
else if(value.IsString())
|
||||
i = std::stoi(value.GetString());
|
||||
else if(value.IsBool())
|
||||
i = value.GetBool() ? 1 : 0;
|
||||
else
|
||||
i = 0;
|
||||
}
|
||||
|
||||
std::string GetMember(const rapidjson::Value& value, std::string member)
|
||||
{
|
||||
std::string retStr;
|
||||
if(value.HasMember(member.data()))
|
||||
value[member.data()] >> retStr;
|
||||
return retStr;
|
||||
}
|
||||
|
||||
void GetMember(const rapidjson::Value& value, std::string member, std::string* target)
|
||||
{
|
||||
std::string retStr = GetMember(value, member);
|
||||
if(retStr.size())
|
||||
target->assign(retStr);
|
||||
}
|
||||
|
||||
std::string SerializeObject(const rapidjson::Value& value)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer_json(sb);
|
||||
value.Accept(writer_json);
|
||||
return sb.GetString();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef RAPIDJSON_EXTRA_H_INCLUDED
|
||||
#define RAPIDJSON_EXTRA_H_INCLUDED
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <string>
|
||||
|
||||
void operator >> (const rapidjson::Value& value, std::string& i);
|
||||
void operator >> (const rapidjson::Value& value, int& i);
|
||||
std::string GetMember(const rapidjson::Value& value, std::string member);
|
||||
void GetMember(const rapidjson::Value& value, std::string member, std::string* target);
|
||||
std::string SerializeObject(const rapidjson::Value& value);
|
||||
|
||||
#endif // RAPIDJSON_EXTRA_H_INCLUDED
|
||||
@@ -0,0 +1,15 @@
|
||||
port: 7890
|
||||
socks-port: 7891
|
||||
allow-lan: true
|
||||
mode: Rule
|
||||
log-level: info
|
||||
external-controller: 127.0.0.1:9090
|
||||
dns:
|
||||
enable: false
|
||||
listen: 0.0.0.0:53
|
||||
ipv6: false
|
||||
enhanced-mode: fake-ip
|
||||
nameserver:
|
||||
- 119.29.29.29
|
||||
- 114.114.114.114
|
||||
- 223.5.5.5
|
||||
@@ -0,0 +1,56 @@
|
||||
#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 <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
|
||||
#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(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
|
||||
+1558
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,27 @@
|
||||
#ifndef SPEEDTESTUTIL_H_INCLUDED
|
||||
#define SPEEDTESTUTIL_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "nodeinfo.h"
|
||||
|
||||
std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port);
|
||||
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev);
|
||||
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev);
|
||||
void explodeVmess(std::string vmess, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeSSR(std::string ssr, bool ss_libev, bool libev, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeSS(std::string ss, bool libev, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeQuan(std::string quan, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeKitsunebiNew(std::string kit, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, nodeInfo &node);
|
||||
void explodeSSD(std::string link, bool libev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes);
|
||||
void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes);
|
||||
int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
|
||||
int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
|
||||
void remarksInit(string_array &exclude, string_array &include);
|
||||
bool chkIgnore(nodeInfo &node);
|
||||
|
||||
#endif // SPEEDTESTUTIL_H_INCLUDED
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
#include "misc.h"
|
||||
#include "speedtestutil.h"
|
||||
#include "ini_reader.h"
|
||||
#include "rapidjson_extra.h"
|
||||
#include "yamlcpp_extra.h"
|
||||
#include "webget.h"
|
||||
#include "subexport.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
extern bool overwrite_original_rules;
|
||||
extern string_array renames, emojis;
|
||||
extern bool add_emoji, remove_old_emoji;
|
||||
|
||||
std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.StartObject();
|
||||
writer.Key("Type");
|
||||
writer.String("VMess");
|
||||
writer.Key("Remark");
|
||||
writer.String(std::string(add + ":" + port).data());
|
||||
writer.Key("Hostname");
|
||||
writer.String(add.data());
|
||||
writer.Key("Port");
|
||||
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
|
||||
writer.Key("UserID");
|
||||
writer.String(id.data());
|
||||
writer.Key("AlterID");
|
||||
writer.Int(stoi(aid));
|
||||
writer.Key("EncryptMethod");
|
||||
writer.String(cipher.data());
|
||||
writer.Key("TransferProtocol");
|
||||
writer.String(net.data());
|
||||
if(net == "ws")
|
||||
{
|
||||
writer.Key("Host");
|
||||
writer.String(host.data());
|
||||
writer.Key("Path");
|
||||
writer.String(path.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(net == "quic")
|
||||
{
|
||||
writer.Key("QUICSecure");
|
||||
writer.String(host.data());
|
||||
writer.Key("QUICSecret");
|
||||
writer.String(path.data());
|
||||
}
|
||||
writer.Key("FakeType");
|
||||
writer.String(type.data());
|
||||
}
|
||||
writer.Key("TLSSecure");
|
||||
writer.Bool(tls == "tls");
|
||||
writer.EndObject();
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.StartObject();
|
||||
writer.Key("Type");
|
||||
writer.String("SSR");
|
||||
writer.Key("Remark");
|
||||
writer.String(remarks.data());
|
||||
writer.Key("Hostname");
|
||||
writer.String(server.data());
|
||||
writer.Key("Port");
|
||||
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
|
||||
writer.Key("Password");
|
||||
writer.String(password.data());
|
||||
writer.Key("EncryptMethod");
|
||||
writer.String(method.data());
|
||||
writer.Key("Protocol");
|
||||
writer.String(protocol.data());
|
||||
writer.Key("ProtocolParam");
|
||||
writer.String(protoparam.data());
|
||||
writer.Key("OBFS");
|
||||
writer.String(obfs.data());
|
||||
writer.Key("OBFSParam");
|
||||
writer.String(obfsparam.data());
|
||||
writer.EndObject();
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev)
|
||||
{
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.StartObject();
|
||||
writer.Key("Type");
|
||||
writer.String("SS");
|
||||
writer.Key("Remark");
|
||||
writer.String(remarks.data());
|
||||
writer.Key("Hostname");
|
||||
writer.String(server.data());
|
||||
writer.Key("Port");
|
||||
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
|
||||
writer.Key("Password");
|
||||
writer.String(password.data());
|
||||
writer.Key("EncryptMethod");
|
||||
writer.String(method.data());
|
||||
writer.Key("Plugin");
|
||||
writer.String(plugin.data());
|
||||
writer.Key("PluginOption");
|
||||
writer.String(pluginopts.data());
|
||||
writer.EndObject();
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
std::string nodeRename(std::string remark)
|
||||
{
|
||||
string_array vArray;
|
||||
|
||||
for(std::string &x : renames)
|
||||
{
|
||||
vArray = split(x, "@");
|
||||
if(vArray.size() == 1)
|
||||
{
|
||||
remark = regReplace(remark, vArray[0], "");
|
||||
}
|
||||
else if(vArray.size() == 2)
|
||||
{
|
||||
remark = regReplace(remark, vArray[0], vArray[1]);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
return remark;
|
||||
}
|
||||
|
||||
std::string removeEmoji(std::string remark)
|
||||
{
|
||||
if(!remove_old_emoji)
|
||||
return remark;
|
||||
char emoji_id[2] = {(char)-16, (char)-97};
|
||||
while(true)
|
||||
{
|
||||
if(remark[0] == emoji_id[0] && remark[1] == emoji_id[1])
|
||||
remark = remark.substr(4);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return remark;
|
||||
}
|
||||
|
||||
std::string addEmoji(std::string remark)
|
||||
{
|
||||
if(!add_emoji)
|
||||
return remark;
|
||||
string_array vArray;
|
||||
for(std::string &x : emojis)
|
||||
{
|
||||
vArray = split(x, ",");
|
||||
if(vArray.size() != 2)
|
||||
continue;
|
||||
if(regFind(remark, vArray[0]))
|
||||
{
|
||||
remark = vArray[1] + " " + remark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return remark;
|
||||
}
|
||||
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array)
|
||||
{
|
||||
string_array allRules, vArray;
|
||||
std::string rule_group, rule_path, retrived_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
YAML::Node Rules;
|
||||
|
||||
if(!overwrite_original_rules)
|
||||
Rules = base_rule["Rule"];
|
||||
|
||||
for(ruleset_content &x : ruleset_content_array)
|
||||
{
|
||||
rule_group = x.rule_group;
|
||||
retrived_rules = x.rule_content;
|
||||
if(retrived_rules.find("[]") == 0)
|
||||
{
|
||||
allRules.emplace_back(retrived_rules.substr(2) + "," + rule_group);
|
||||
continue;
|
||||
}
|
||||
char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') <= 1 ? '\r' : '\n';
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrived_rules;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
strLine = replace_all_distinct(strLine, "\r", ""); //remove line break
|
||||
if(!strLine.size() || strLine.find("#") == 0 || strLine.find(";") == 0) //remove comments
|
||||
continue;
|
||||
if(strLine.find("USER-AGENT") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0) //remove unsupported types
|
||||
continue;
|
||||
if(strLine.find("IP-CIDR") == 0)
|
||||
strLine = replace_all_distinct(strLine, ",no-resolve", "");
|
||||
else if(strLine.find("DOMAIN-SUFFIX") == 0)
|
||||
strLine = replace_all_distinct(strLine, ",force-remote-dns", "");
|
||||
strLine += "," + rule_group;
|
||||
allRules.emplace_back(strLine);
|
||||
}
|
||||
}
|
||||
|
||||
for(std::string &x : allRules)
|
||||
{
|
||||
Rules.push_back(x);
|
||||
}
|
||||
base_rule["Rule"] = Rules;
|
||||
}
|
||||
|
||||
std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, bool clashR)
|
||||
{
|
||||
YAML::Node yamlnode, proxies, singleproxy, singlegroup, original_groups;
|
||||
rapidjson::Document json;
|
||||
std::string type, remark, hostname, port, username, password, method;
|
||||
std::string plugin, pluginopts;
|
||||
std::string protocol, protoparam, obfs, obfsparam;
|
||||
std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
|
||||
std::vector<std::string> nodelist;
|
||||
bool tlssecure, replace_flag;
|
||||
string_array vArray, filtered_nodelist;
|
||||
|
||||
try
|
||||
{
|
||||
yamlnode = YAML::Load(baseConf);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
for(nodeInfo &x : nodes)
|
||||
{
|
||||
singleproxy.reset();
|
||||
json.Parse(x.proxyStr.data());
|
||||
type = GetMember(json, "Type");
|
||||
remark = addEmoji(trim(removeEmoji(nodeRename(x.remarks))));
|
||||
hostname = GetMember(json, "Hostname");
|
||||
port = GetMember(json, "Port");
|
||||
username = GetMember(json, "Username");
|
||||
password = GetMember(json, "Password");
|
||||
method = GetMember(json, "EncryptMethod");
|
||||
|
||||
if(type == "SS")
|
||||
{
|
||||
plugin = GetMember(json, "Plugin");
|
||||
pluginopts = replace_all_distinct(GetMember(json, "PluginOption"), ";", "&");
|
||||
singleproxy["type"] = "ss";
|
||||
singleproxy["cipher"] = method;
|
||||
if(plugin == "simple-obfs" || plugin == "obfs-local")
|
||||
{
|
||||
singleproxy["plugin"] = "obfs";
|
||||
singleproxy["plugin-opts"]["mode"] = UrlDecode(getUrlArg(pluginopts, "obfs"));
|
||||
singleproxy["plugin-opts"]["host"] = UrlDecode(getUrlArg(pluginopts, "obfs-host"));
|
||||
}
|
||||
}
|
||||
else if(type == "VMess")
|
||||
{
|
||||
id = GetMember(json, "UserID");
|
||||
aid = GetMember(json, "AlterID");
|
||||
transproto = GetMember(json, "TransferProtocol");
|
||||
host = GetMember(json, "Host");
|
||||
path = GetMember(json, "Path");
|
||||
tlssecure = GetMember(json, "TLSSecure") == "true";
|
||||
singleproxy["type"] = "vmess";
|
||||
singleproxy["uuid"] = id;
|
||||
singleproxy["alterId"] = stoi(aid);
|
||||
singleproxy["cipher"] = method;
|
||||
singleproxy["tls"] = tlssecure;
|
||||
if(transproto == "ws")
|
||||
{
|
||||
singleproxy["network"] = transproto;
|
||||
singleproxy["ws-path"] = path;
|
||||
singleproxy["ws-headers"]["Host"] = host;
|
||||
}
|
||||
else if(transproto == "kcp" || transproto == "h2" || transproto == "quic")
|
||||
continue;
|
||||
}
|
||||
else if(type == "SSR" && clashR)
|
||||
{
|
||||
protocol = GetMember(json, "Protocol");
|
||||
protoparam = GetMember(json, "ProtocolParam");
|
||||
obfs = GetMember(json, "OBFS");
|
||||
obfsparam = GetMember(json, "OBFSParam");
|
||||
singleproxy["type"] = "ssr";
|
||||
singleproxy["cipher"] = method;
|
||||
singleproxy["protocol"] = protocol;
|
||||
singleproxy["protocolparam"] = protoparam;
|
||||
singleproxy["obfs"] = obfs;
|
||||
singleproxy["obfsparam"] = obfsparam;
|
||||
}
|
||||
else if(type == "Socks5")
|
||||
{
|
||||
singleproxy["type"] = "socks";
|
||||
singleproxy["username"] = username;
|
||||
}
|
||||
else if(type == "HTTP" || type == "HTTPS")
|
||||
{
|
||||
singleproxy["type"] = "http";
|
||||
singleproxy["username"] = username;
|
||||
singleproxy["tls"] = type == "HTTPS";
|
||||
}
|
||||
else
|
||||
continue;
|
||||
singleproxy["password"] = password;
|
||||
singleproxy["name"] = remark;
|
||||
singleproxy["server"] = hostname;
|
||||
singleproxy["port"] = (unsigned short)stoi(port);
|
||||
proxies.push_back(singleproxy);
|
||||
nodelist.emplace_back(remark);
|
||||
}
|
||||
|
||||
yamlnode["Proxy"] = proxies;
|
||||
std::string groupname;
|
||||
/*
|
||||
if(!overwrite_original_groups)
|
||||
{
|
||||
original_groups = yamlnode["Proxy Group"];
|
||||
for(unsigned int i = 0; i < original_groups.size(); i++)
|
||||
{
|
||||
groupname = original_groups[i]["name"].as<std::string>();
|
||||
if(groupname == "Proxy" || groupname == "PROXY")
|
||||
{
|
||||
original_groups[i]["name"] = "Proxy";
|
||||
original_groups[i]["proxies"] = vectorToNode(nodelist);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
singlegroup["name"] = "Proxy";
|
||||
singlegroup["type"] = "select";
|
||||
singlegroup["proxies"] = vectorToNode(nodelist);
|
||||
original_groups.push_back(singlegroup);
|
||||
}
|
||||
*/
|
||||
|
||||
for(std::string &x : extra_proxy_group)
|
||||
{
|
||||
singlegroup.reset();
|
||||
eraseElements(filtered_nodelist);
|
||||
replace_flag = false;
|
||||
unsigned int rules_upper_bound = 0;
|
||||
|
||||
vArray = split(x, "`");
|
||||
if(vArray.size() < 3)
|
||||
continue;
|
||||
|
||||
if(vArray[1] == "select")
|
||||
{
|
||||
rules_upper_bound = vArray.size();
|
||||
}
|
||||
else if(vArray[1] == "url-test" || vArray[1] == "fallback" || vArray[1] == "load-balance")
|
||||
{
|
||||
if(vArray.size() < 5)
|
||||
continue;
|
||||
rules_upper_bound = vArray.size() - 2;
|
||||
singlegroup["url"] = vArray[vArray.size() - 2];
|
||||
singlegroup["interval"] = stoi(vArray[vArray.size() - 1]);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
for(unsigned int i = 2; i < rules_upper_bound; i++)
|
||||
{
|
||||
if(vArray[i].find("[]") == 0)
|
||||
{
|
||||
filtered_nodelist.emplace_back(vArray[i].substr(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(std::string &y : nodelist)
|
||||
{
|
||||
if(regFind(y, vArray[i]) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y) == filtered_nodelist.end())
|
||||
filtered_nodelist.emplace_back(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!filtered_nodelist.size())
|
||||
filtered_nodelist.emplace_back("DIRECT");
|
||||
|
||||
singlegroup["name"] = vArray[0];
|
||||
singlegroup["type"] = vArray[1];
|
||||
singlegroup["proxies"] = vectorToNode(filtered_nodelist);
|
||||
|
||||
for(unsigned int i = 0; i < original_groups.size(); i++)
|
||||
{
|
||||
if(original_groups[i]["name"].as<std::string>() == vArray[0])
|
||||
{
|
||||
original_groups[i] = singlegroup;
|
||||
replace_flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!replace_flag)
|
||||
original_groups.push_back(singlegroup);
|
||||
}
|
||||
|
||||
yamlnode["Proxy Group"] = original_groups;
|
||||
|
||||
rulesetToClash(yamlnode, ruleset_content_array);
|
||||
|
||||
return to_string(yamlnode);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#ifndef SUBEXPORT_H_INCLUDED
|
||||
#define SUBEXPORT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
struct ruleset_content
|
||||
{
|
||||
std::string rule_group;
|
||||
std::string rule_content;
|
||||
};
|
||||
|
||||
#endif // SUBEXPORT_H_INCLUDED
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "webget.h"
|
||||
|
||||
std::string user_agent_str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
|
||||
|
||||
static int writer(char *data, size_t size, size_t nmemb, std::string *writerData)
|
||||
{
|
||||
if(writerData == NULL)
|
||||
return 0;
|
||||
|
||||
writerData->append(data, size*nmemb);
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
std::string curlGet(std::string url, std::string proxy)
|
||||
{
|
||||
CURL *curl_handle;
|
||||
std::string data;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl_handle = curl_easy_init();
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url.data());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
|
||||
if(proxy != "")
|
||||
curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data());
|
||||
|
||||
curl_easy_perform(curl_handle);
|
||||
curl_easy_cleanup(curl_handle);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string buildSocks5ProxyString(std::string addr, int port, std::string username, std::string password)
|
||||
{
|
||||
std::string authstr = username != "" && password != "" ? username + ":" + password + "@" : "";
|
||||
std::string proxystr = "socks5://" + authstr + addr + ":" + std::__cxx11::to_string(port);
|
||||
return proxystr;
|
||||
}
|
||||
|
||||
std::string webGet(std::string url, std::string proxy)
|
||||
{
|
||||
return curlGet(url, proxy);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef WEBGET_H_INCLUDED
|
||||
#define WEBGET_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
std::string webGet(std::string url, std::string proxy = "");
|
||||
std::string httpGet(std::string host, std::string addr, std::string uri);
|
||||
std::string httpsGet(std::string host, std::string addr, std::string uri);
|
||||
long curlPost(std::string url, std::string data, std::string proxy);
|
||||
std::string buildSocks5ProxyString(std::string addr, int port, std::string username, std::string password);
|
||||
|
||||
#endif // WEBGET_H_INCLUDED
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "socket.h"
|
||||
#include "webserver.h"
|
||||
|
||||
typedef std::vector<std::string> string_array;
|
||||
|
||||
struct responseRoute
|
||||
{
|
||||
std::string method;
|
||||
std::string path;
|
||||
std::string content_type;
|
||||
response_callback rc;
|
||||
};
|
||||
|
||||
std::vector<responseRoute> responses;
|
||||
|
||||
//for use of multi-thread socket test
|
||||
typedef std::lock_guard<std::mutex> guarded_mutex;
|
||||
int working_thread = 0, max_send_failure = 10;
|
||||
bool SERVER_EXIT_FLAG = false;
|
||||
std::mutex working_mutex, server_exit_flag_mutex;
|
||||
|
||||
static inline void working_acc()
|
||||
{
|
||||
guarded_mutex guard(working_mutex);
|
||||
working_thread++;
|
||||
}
|
||||
|
||||
static inline void working_dec()
|
||||
{
|
||||
guarded_mutex guard(working_mutex);
|
||||
working_thread--;
|
||||
}
|
||||
|
||||
static inline int safe_read_working()
|
||||
{
|
||||
int retVal;
|
||||
guarded_mutex guard(working_mutex);
|
||||
retVal = working_thread;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static inline bool safe_read_server_exit_flag()
|
||||
{
|
||||
bool retVal;
|
||||
guarded_mutex guard(server_exit_flag_mutex);
|
||||
retVal = SERVER_EXIT_FLAG;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static inline void safe_set_server_exit_flag()
|
||||
{
|
||||
guarded_mutex guard(server_exit_flag_mutex);
|
||||
SERVER_EXIT_FLAG = true;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void handle_req(std::string request, int client_sock)
|
||||
{
|
||||
working_acc();
|
||||
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;
|
||||
}
|
||||
|
||||
printf("handle_cmd: %s\n", command);
|
||||
printf("handle_path: %s\n", arguments);
|
||||
|
||||
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;
|
||||
sleep(1);
|
||||
closesocket(client_sock);
|
||||
working_dec();
|
||||
}
|
||||
|
||||
void append_response(std::string type, std::string request, 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(rr);
|
||||
}
|
||||
|
||||
void* start_web_server(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 NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
return NULL;
|
||||
}
|
||||
if (listen(server_socket, max_conn) == -1 )
|
||||
{
|
||||
//log listen error
|
||||
std::cerr<<"socket listen error!"<<std::endl;
|
||||
return NULL;
|
||||
}
|
||||
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(safe_read_working() >= max_workers)
|
||||
{
|
||||
sleep(10);
|
||||
if(safe_read_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;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#ifndef WEBSERVER_H_INCLUDED
|
||||
#define WEBSERVER_H_INCLUDED
|
||||
|
||||
typedef std::string (*response_callback)(std::string, std::string); //process arguments and POST data and return served-content
|
||||
|
||||
#define RESPONSE_CALLBACK_ARGS std::string argument, std::string postdata
|
||||
|
||||
struct listener_args
|
||||
{
|
||||
std::string listen_address;
|
||||
int port;
|
||||
int max_conn;
|
||||
int max_workers;
|
||||
};
|
||||
|
||||
void append_response(std::string type, std::string request, std::string content_type, response_callback response);
|
||||
int start_web_server(void *argv);
|
||||
int start_web_server_multi(void *argv);
|
||||
|
||||
#endif // WEBSERVER_H_INCLUDED
|
||||
@@ -0,0 +1,221 @@
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <evhttp.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "webserver.h"
|
||||
#include "socket.h"
|
||||
|
||||
struct responseRoute
|
||||
{
|
||||
std::string method;
|
||||
std::string path;
|
||||
std::string content_type;
|
||||
response_callback rc;
|
||||
};
|
||||
|
||||
std::vector<responseRoute> responses;
|
||||
|
||||
static inline int process_request(const char *method_str, std::string uri, std::string &postdata, std::string &content_type, std::string &return_data)
|
||||
{
|
||||
std::string path, arguments;
|
||||
//std::cerr << "handle_cmd: " << method_str << std::endl << "handle_uri: " << uri << std::endl;
|
||||
|
||||
if(strFind(uri, "?"))
|
||||
{
|
||||
path = uri.substr(0, uri.find("?"));
|
||||
arguments = uri.substr(uri.find("?") + 1);
|
||||
}
|
||||
else
|
||||
path = uri;
|
||||
|
||||
for(responseRoute &x : responses)
|
||||
{
|
||||
if(strcmp(method_str, "OPTIONS") == 0 && x.method == postdata && x.path == path)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if(x.method.compare(method_str) == 0 && x.path == path)
|
||||
{
|
||||
response_callback &rc = x.rc;
|
||||
return_data = rc(arguments, postdata);
|
||||
content_type = x.content_type;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void OnReq(evhttp_request *req, void *args)
|
||||
{
|
||||
const char *req_content_type = evhttp_find_header(req->input_headers, "Content-Type"), *req_ac_method = evhttp_find_header(req->input_headers, "Access-Control-Request-Method");
|
||||
const char *req_method = req_ac_method == NULL ? EVBUFFER_LENGTH(req->input_buffer) == 0 ? "GET" : "POST" : "OPTIONS", *uri = evhttp_request_get_uri(req);
|
||||
int retVal;
|
||||
std::string postdata, content_type, return_data;
|
||||
|
||||
if(EVBUFFER_LENGTH(req->input_buffer) != 0)
|
||||
{
|
||||
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)
|
||||
postdata = UrlDecode(postdata);
|
||||
}
|
||||
else if(req_ac_method != NULL)
|
||||
{
|
||||
postdata.assign(req_ac_method);
|
||||
}
|
||||
|
||||
retVal = process_request(req_method, uri, postdata, content_type, return_data);
|
||||
|
||||
auto *OutBuf = evhttp_request_get_output_buffer(req);
|
||||
if (!OutBuf)
|
||||
return;
|
||||
|
||||
switch(retVal)
|
||||
{
|
||||
case 1: //found OPTIONS
|
||||
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
|
||||
evhttp_add_header(req->output_headers, "Access-Control-Allow-Headers", "*");
|
||||
evhttp_send_reply(req, HTTP_OK, "", NULL);
|
||||
return;
|
||||
break;
|
||||
case 0: //found normal
|
||||
if(content_type.size())
|
||||
{
|
||||
if(content_type == "REDIRECT")
|
||||
{
|
||||
evhttp_add_header(req->output_headers, "Location", return_data.c_str());
|
||||
evhttp_send_reply(req, HTTP_MOVETEMP, "", NULL);
|
||||
return;
|
||||
}
|
||||
else
|
||||
evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str());
|
||||
}
|
||||
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
|
||||
evhttp_add_header(req->output_headers, "Connection", "close");
|
||||
evbuffer_add(OutBuf, return_data.data(), return_data.size());
|
||||
evhttp_send_reply(req, HTTP_OK, "", OutBuf);
|
||||
break;
|
||||
case -1: //not found
|
||||
evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found");
|
||||
break;
|
||||
default: //undefined behavior
|
||||
evhttp_send_error(req, HTTP_INTERNAL, "");
|
||||
}
|
||||
}
|
||||
|
||||
int start_web_server(void *argv)
|
||||
{
|
||||
struct listener_args *args = (listener_args*)argv;
|
||||
std::string listen_address = args->listen_address;
|
||||
int port = args->port;
|
||||
if (!event_init())
|
||||
{
|
||||
std::cerr << "Failed to init libevent." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
const char *SrvAddress = listen_address.c_str();
|
||||
std::uint16_t SrvPort = port;
|
||||
std::unique_ptr<evhttp, decltype(&evhttp_free)> Server(evhttp_start(SrvAddress, SrvPort), &evhttp_free);
|
||||
if (!Server)
|
||||
{
|
||||
std::cerr << "Failed to init http server." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
|
||||
evhttp_set_gencb(Server.get(), OnReq, nullptr);
|
||||
if (event_dispatch() == -1)
|
||||
{
|
||||
std::cerr << "Failed to run message loop." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* httpserver_dispatch(void *arg)
|
||||
{
|
||||
event_base_dispatch((struct event_base*)arg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog)
|
||||
{
|
||||
int ret;
|
||||
SOCKET nfd;
|
||||
nfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (nfd < 0)
|
||||
return -1;
|
||||
|
||||
int one = 1;
|
||||
ret = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr(listen_address.data());
|
||||
addr.sin_port = htons(listen_port);
|
||||
|
||||
ret = bind(nfd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
ret = listen(nfd, backlog);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
unsigned long ul = 1;
|
||||
ioctlsocket(nfd, FIONBIO, &ul); //set to non-blocking mode
|
||||
|
||||
return nfd;
|
||||
}
|
||||
|
||||
int start_web_server_multi(void *argv)
|
||||
{
|
||||
struct listener_args *args = (listener_args*)argv;
|
||||
std::string listen_address = args->listen_address;
|
||||
int port = args->port, nthreads = args->max_workers;
|
||||
int i, ret;
|
||||
|
||||
int nfd = httpserver_bindsocket(listen_address, port, args->max_conn);
|
||||
if (nfd < 0)
|
||||
return -1;
|
||||
|
||||
pthread_t ths[nthreads];
|
||||
for (i = 0; i < nthreads; i++)
|
||||
{
|
||||
struct event_base *base = event_init();
|
||||
if (base == NULL)
|
||||
return -1;
|
||||
struct evhttp *httpd = evhttp_new(base);
|
||||
if (httpd == NULL)
|
||||
return -1;
|
||||
ret = evhttp_accept_socket(httpd, nfd);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
|
||||
evhttp_set_gencb(httpd, OnReq, nullptr);
|
||||
ret = pthread_create(&ths[i], NULL, httpserver_dispatch, base);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void append_response(std::string type, std::string request, std::string content_type, response_callback response)
|
||||
{
|
||||
responseRoute rr;
|
||||
rr.method = type;
|
||||
rr.path = request;
|
||||
rr.content_type = content_type;
|
||||
rr.rc = response;
|
||||
responses.emplace_back(rr);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
YAML::Node vectorToNode(string_array data)
|
||||
{
|
||||
YAML::Node node;
|
||||
for(std::string &x : data)
|
||||
node.push_back(x);
|
||||
return node;
|
||||
}
|
||||
Reference in new issue
Block a user