initial import
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
#include "compiler.h"
|
||||
#include <sys/file.h>
|
||||
|
||||
String process_html_literal(Request* context, SharedUnit* su, String content)
|
||||
{
|
||||
String pc;
|
||||
String HT_START = "print(R\"(";
|
||||
String HT_END = ")\");";
|
||||
|
||||
u8 mode = 0;
|
||||
char quote_char;
|
||||
bool inside_quote = false;
|
||||
String code_buffer = "";
|
||||
bool is_field = false;
|
||||
|
||||
for(u32 i = 0; i < content.length(); i++)
|
||||
{
|
||||
char c = content[i];
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(0):
|
||||
if(c == '<' && content[i+1] == '?')
|
||||
{
|
||||
code_buffer = "";
|
||||
if(content[i+2] == '=')
|
||||
{
|
||||
is_field = true;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_field = false;
|
||||
i += 1;
|
||||
}
|
||||
mode = 1; // code-parsing mode
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(1, c);
|
||||
}
|
||||
break;
|
||||
case(1):
|
||||
if(inside_quote)
|
||||
{
|
||||
if(quote_char == c && content[i-1] != '\\')
|
||||
inside_quote = false;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c == '\"' || c == '\'')
|
||||
{
|
||||
inside_quote = true;
|
||||
quote_char = c;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
else if(c == '?' && content[i+1] == '>')
|
||||
{
|
||||
mode = 0;
|
||||
i += 1;
|
||||
if(is_field)
|
||||
{
|
||||
pc.append(
|
||||
HT_END +
|
||||
"print(html_escape( " +
|
||||
code_buffer +
|
||||
" )); " +
|
||||
HT_START
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(HT_END + code_buffer + HT_START);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(HT_START + pc + HT_END);
|
||||
}
|
||||
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su)
|
||||
{
|
||||
String content = file_get_contents(su->file_name);
|
||||
printf("(c) compiling with root dir %s\n", context->server->config.COMPILER_SYS_PATH.c_str());
|
||||
String pc = ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n";
|
||||
String token = "";
|
||||
String html_buffer = "";
|
||||
u8 mode = 0;
|
||||
bool inside_quote = false;
|
||||
for(u32 i = 0; i < content.length(); i++)
|
||||
{
|
||||
char c = content[i];
|
||||
if(mode == 2)
|
||||
{
|
||||
auto end_pos = content.find(String("</")+token+">", i);
|
||||
if(end_pos != String::npos)
|
||||
{
|
||||
u32 len = token.length() + 3 + end_pos - i;
|
||||
html_buffer.append(content.substr(i, len - (token.length() == 0 ? 3 : 0)));
|
||||
i += len - 1;
|
||||
pc.append(process_html_literal(context, su, html_buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("(!) unterminated HTML literal <%s> in %s", token.c_str(), su->file_name.c_str());
|
||||
}
|
||||
mode = 0;
|
||||
}
|
||||
else if(mode == 1)
|
||||
{
|
||||
if(isspace(c) || c == '>')
|
||||
{
|
||||
mode = 2;
|
||||
if(token.length() > 0)
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
token.append(1, c);
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(!inside_quote && c == '<' && (content[i+1] == '>'/* || isalpha(content[i+1])*/))
|
||||
{
|
||||
mode = 1;
|
||||
token = "";
|
||||
html_buffer = "";
|
||||
if(content[i+1] != '>')
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
else if(c == '\"')
|
||||
{
|
||||
inside_quote = !inside_quote;
|
||||
pc.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(1, c);
|
||||
}
|
||||
}
|
||||
return(pc);
|
||||
}
|
||||
|
||||
void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
su->file_name = file_name;
|
||||
|
||||
if(su->src_path.length() > 0) // we did this already
|
||||
return;
|
||||
|
||||
su->src_path = dirname(file_name);
|
||||
su->bin_path = context->server->config.BIN_DIRECTORY + su->src_path;
|
||||
su->pre_path = context->server->config.BIN_DIRECTORY + su->src_path;
|
||||
|
||||
su->src_file_name = basename(file_name);
|
||||
su->bin_file_name = su->src_file_name + ".so";
|
||||
su->pre_file_name = su->src_file_name + ".cpp";
|
||||
|
||||
su->so_name = su->bin_path + "/" + su->bin_file_name;
|
||||
}
|
||||
|
||||
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
|
||||
su->on_render = 0;
|
||||
su->on_setup = 0;
|
||||
su->compiler_messages = "";
|
||||
|
||||
if(!file_exists(su->so_name))
|
||||
{
|
||||
printf("(i) unit file not found: %s\n", su->so_name.c_str());
|
||||
su->compiler_messages = "unit file not found";
|
||||
return;
|
||||
}
|
||||
|
||||
su->so_handle = dlopen(su->so_name.c_str(), RTLD_NOW);
|
||||
if(su->so_handle)
|
||||
{
|
||||
su->last_compiled = file_mtime(su->so_name);
|
||||
char *error;
|
||||
su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request");
|
||||
su->on_render = (call_handler)dlsym(su->so_handle, "render");
|
||||
if ((error = dlerror()) != NULL)
|
||||
printf("Error - %s in %s\n", error, su->file_name.c_str());
|
||||
else
|
||||
printf("(i) loaded unit %s\n", su->file_name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
|
||||
if(!file_exists(su->file_name))
|
||||
{
|
||||
su->compiler_messages = "source file not found (" + su->file_name + ")";
|
||||
return;
|
||||
}
|
||||
|
||||
shell_exec("mkdir -p " + shell_escape(su->pre_path));
|
||||
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
|
||||
|
||||
printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str());
|
||||
|
||||
su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+
|
||||
shell_escape(su->src_path)+" "+
|
||||
shell_escape(su->bin_path)+" "+
|
||||
shell_escape(su->file_name)+" "+
|
||||
shell_escape(su->pre_file_name)+" "+
|
||||
shell_escape(su->bin_file_name)
|
||||
));
|
||||
|
||||
if(su->compiler_messages.length() > 0)
|
||||
{
|
||||
printf("%s \n", su->compiler_messages.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
load_shared_unit(context, su, file_name);
|
||||
printf("(i) compiled unit %s\n", file_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
SharedUnit* get_shared_unit(Request* context, String file_name)
|
||||
{
|
||||
SharedUnit* su = context->server->units[file_name];
|
||||
auto mod_time = file_mtime(file_name);
|
||||
bool do_recompile = false;
|
||||
if(su && (su->last_compiled < mod_time || mod_time == 0))
|
||||
{
|
||||
delete su;
|
||||
su = 0;
|
||||
do_recompile = true;
|
||||
}
|
||||
if(!su)
|
||||
{
|
||||
su = new SharedUnit();
|
||||
setup_unit_paths(context, su, file_name);
|
||||
if(!do_recompile)
|
||||
{
|
||||
// if we didn't decide to force recompile yet, we need to check
|
||||
// (this case should only happen if the SU cache for that entry is cold
|
||||
// but the SO itself exists _AND_ is stale)
|
||||
su->last_compiled = file_mtime(su->so_name);
|
||||
if(su->last_compiled < mod_time || mod_time == 0)
|
||||
do_recompile = true;
|
||||
}
|
||||
|
||||
int fdlock = open((su->so_name+".lock").c_str(), O_RDWR | O_CREAT, 0666 );
|
||||
int fl_excl = flock(fdlock, LOCK_EX);
|
||||
|
||||
if(do_recompile)
|
||||
{
|
||||
compile_shared_unit(context, su, file_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
load_shared_unit(context, su, file_name);
|
||||
if(!su->so_handle)
|
||||
compile_shared_unit(context, su, file_name);
|
||||
}
|
||||
|
||||
flock(fdlock, LOCK_UN);
|
||||
close(fdlock);
|
||||
remove((su->so_name+".lock").c_str());
|
||||
|
||||
context->server->units[file_name] = su;
|
||||
}
|
||||
return(su);
|
||||
}
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param)
|
||||
{
|
||||
|
||||
if(file_name[0] != '/')
|
||||
{
|
||||
file_name = expand_path(file_name);
|
||||
}
|
||||
//printf("(i) invoke %s\n", file_name.c_str());
|
||||
|
||||
//printf("(i) invoke(%s)\n", file_name.c_str());
|
||||
switch_to_system_alloc();
|
||||
auto su = get_shared_unit(context, file_name);
|
||||
switch_to_arena(context->mem);
|
||||
if(!su)
|
||||
{
|
||||
printf("Error loading unit %s\n", file_name.c_str());
|
||||
print("Error loading unit: "+file_name);
|
||||
}
|
||||
else if(!su->on_render)
|
||||
{
|
||||
context->header["Content-Type"] = "text/plain";
|
||||
print("Compiler error: "+su->compiler_messages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(su->compiler_messages.length() > 0)
|
||||
print(su->compiler_messages);
|
||||
else
|
||||
{
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
su->on_setup(context);
|
||||
su->on_render(call_param);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#define RENDER() extern "C" void render(DTree& call)
|
||||
|
||||
String process_html_literal(Request* context, SharedUnit* su, String content);
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su);
|
||||
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
|
||||
void load_shared_unit(Request* context, SharedUnit* su, String file_name);
|
||||
void compile_shared_unit(Request* context, SharedUnit* su, String file_name);
|
||||
SharedUnit* get_shared_unit(Request* context, String file_name);
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param);
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
|
||||
void DTree::each(std::function <void (DTree t, String key)> f)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('M'):
|
||||
for (auto it = _map.begin(); it != _map.end(); ++it)
|
||||
{
|
||||
f(it->second, it->first);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
f(*this, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DTree::is_array()
|
||||
{
|
||||
return(type == 'M');
|
||||
}
|
||||
|
||||
String DTree::to_string()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return(_String);
|
||||
break;
|
||||
case('F'):
|
||||
return(std::to_string(_float));
|
||||
break;
|
||||
case('B'):
|
||||
return(_bool ? "(true)" : "(false)");
|
||||
break;
|
||||
case('M'):
|
||||
return("");
|
||||
break;
|
||||
case('P'):
|
||||
return(std::to_string((u64)_ptr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String DTree::to_json()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return(json_escape(_String));
|
||||
break;
|
||||
case('F'):
|
||||
return(std::to_string(_float));
|
||||
break;
|
||||
case('B'):
|
||||
return(_bool ? "true" : "false");
|
||||
break;
|
||||
case('M'):
|
||||
return("\"(array)\"");
|
||||
break;
|
||||
case('P'):
|
||||
return("\"(pointer)\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String DTree::get_type_name()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return("String");
|
||||
break;
|
||||
case('F'):
|
||||
return("f64");
|
||||
break;
|
||||
case('B'):
|
||||
return("bool");
|
||||
break;
|
||||
case('M'):
|
||||
return("array");
|
||||
break;
|
||||
case('P'):
|
||||
return("pointer");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set_type(char t)
|
||||
{
|
||||
if(type != t)
|
||||
{
|
||||
type = t;
|
||||
switch(type)
|
||||
{
|
||||
case('M'):
|
||||
_map.clear();
|
||||
_array_index = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set(String s)
|
||||
{
|
||||
set_type('S');
|
||||
_String = s;
|
||||
}
|
||||
|
||||
void DTree::set(void* p)
|
||||
{
|
||||
set_type('P');
|
||||
_ptr = p;
|
||||
}
|
||||
|
||||
void DTree::set(f64 f)
|
||||
{
|
||||
set_type('F');
|
||||
_float = f;
|
||||
}
|
||||
|
||||
void DTree::set_bool(bool b)
|
||||
{
|
||||
set_type('B');
|
||||
_bool = b;
|
||||
}
|
||||
|
||||
void DTree::set(DTree source)
|
||||
{
|
||||
set_type(source.type);
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
_String = source._String;
|
||||
break;
|
||||
case('F'):
|
||||
_float = source._float;
|
||||
break;
|
||||
case('B'):
|
||||
_bool = source._bool;
|
||||
break;
|
||||
case('M'):
|
||||
_map = source._map;
|
||||
break;
|
||||
case('P'):
|
||||
_ptr = source._ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set(StringMap source)
|
||||
{
|
||||
set_type('M');
|
||||
for (auto it = source.begin(); it != source.end(); ++it)
|
||||
{
|
||||
_map[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
DTree* DTree::key(String s)
|
||||
{
|
||||
set_type('M');
|
||||
return(&_map[s]);
|
||||
}
|
||||
|
||||
DTree& DTree::operator [] (String s) {
|
||||
set_type('M');
|
||||
return(_map[s]);
|
||||
}
|
||||
|
||||
void DTree::operator = (String v) { set(v); }
|
||||
void DTree::operator = (f64 v) { set(v); }
|
||||
void DTree::operator = (void* v) { set(v); }
|
||||
void DTree::operator = (DTree v) { set(v); }
|
||||
void DTree::operator = (StringMap v) { set(v); }
|
||||
|
||||
void DTree::push(DTree& child)
|
||||
{
|
||||
set_type('M');
|
||||
_map[std::to_string(_array_index)] = child;
|
||||
_array_index += 1;
|
||||
}
|
||||
|
||||
DTree DTree::pop()
|
||||
{
|
||||
set_type('M');
|
||||
auto last = _map.rbegin();
|
||||
DTree result = last->second;
|
||||
_map.erase(last->first);
|
||||
return(result);
|
||||
}
|
||||
|
||||
void DTree::remove(String s)
|
||||
{
|
||||
set_type('M');
|
||||
_map.erase(s);
|
||||
}
|
||||
|
||||
void DTree::clear()
|
||||
{
|
||||
set_type('M');
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
String to_String(DTree t)
|
||||
{
|
||||
return(t.to_string());
|
||||
}
|
||||
|
||||
String var_dump(DTree map, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
if(!map.is_array())
|
||||
return(map.to_string());
|
||||
map.each([&] (DTree item, String key) {
|
||||
result += prefix + key + ": " + item.to_string() + postfix;
|
||||
if(item.is_array())
|
||||
result += var_dump(item, prefix + "\t");
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
String json_escape(String s)
|
||||
{
|
||||
//return(String("\"")+s+"\"");
|
||||
String result;
|
||||
u32 i = 0;
|
||||
result.append(1, '"');
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('\t'):
|
||||
result.append("\\t");
|
||||
break;
|
||||
case('\n'):
|
||||
result.append("\\n");
|
||||
break;
|
||||
case('"'):
|
||||
result.append("\\\"");
|
||||
break;
|
||||
case('\r'):
|
||||
result.append("\\r");
|
||||
break;
|
||||
case('\\'):
|
||||
result.append("\\\\");
|
||||
break;
|
||||
case('\b'):
|
||||
result.append("\\b");
|
||||
break;
|
||||
case('\f'):
|
||||
result.append("\\f");
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
result.append(1, '"');
|
||||
return(result);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
String json_escape(String s);
|
||||
|
||||
struct DTree {
|
||||
|
||||
char type = 'S';
|
||||
|
||||
String _String;
|
||||
f64 _float;
|
||||
s64 _array_index;
|
||||
bool _bool;
|
||||
void* _ptr;
|
||||
std::map<String, DTree> _map;
|
||||
|
||||
void each(std::function <void (DTree t, String key)> f);
|
||||
bool is_array();
|
||||
String to_string();
|
||||
String to_json();
|
||||
String get_type_name();
|
||||
void set_type(char t);
|
||||
void set(String s);
|
||||
void set(void* p);
|
||||
void set(f64 f);
|
||||
void set_bool(bool b);
|
||||
void set(DTree source);
|
||||
void set(StringMap source);
|
||||
DTree* key(String s);
|
||||
DTree& operator [] (String s);
|
||||
void operator = (String v);
|
||||
void operator = (f64 v);
|
||||
void operator = (void* v);
|
||||
void operator = (DTree v);
|
||||
void operator = (StringMap v);
|
||||
|
||||
void push(DTree& child);
|
||||
DTree pop();
|
||||
void remove(String s);
|
||||
void clear();
|
||||
};
|
||||
|
||||
String to_String(DTree t);
|
||||
String var_dump(DTree map, String prefix = "", String postfix = "\n");
|
||||
@@ -0,0 +1,412 @@
|
||||
#include "functionlib.h"
|
||||
|
||||
String var_dump(StringMap map, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
result.append(prefix + it->first + ": " + it->second + postfix);
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String var_dump(StringList slist, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
|
||||
for (auto& s : slist)
|
||||
{
|
||||
result.append(prefix + s + postfix);
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
u8 char_to_u8(char input)
|
||||
{
|
||||
if(input >= '0' && input <= '9')
|
||||
return input - '0';
|
||||
if(input >= 'A' && input <= 'F')
|
||||
return input - 'A' + 10;
|
||||
if(input >= 'a' && input <= 'f')
|
||||
return input - 'a' + 10;
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 hex_to_u8(String src)
|
||||
{
|
||||
return(char_to_u8(src[0])*16 + char_to_u8(src[1]));
|
||||
}
|
||||
|
||||
String str_to_lower(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = tolower(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String str_to_upper(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = toupper(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String trim(String raw)
|
||||
{
|
||||
u32 len = raw.length();
|
||||
u32 start_pos = 0;
|
||||
u32 end_pos = len - 1;
|
||||
if(len == 0 || (len == 1 && isspace(raw[0])))
|
||||
return("");
|
||||
while(start_pos < len && isspace(raw[start_pos]))
|
||||
start_pos++;
|
||||
while(end_pos >= 0 && isspace(raw[end_pos]))
|
||||
end_pos--;
|
||||
if(end_pos < start_pos)
|
||||
return("");
|
||||
return(raw.substr(start_pos, 1 + end_pos - start_pos));
|
||||
}
|
||||
|
||||
StringList split(String str, String delim)
|
||||
{
|
||||
StringList result;
|
||||
int start = 0;
|
||||
int end = str.find(delim);
|
||||
while (end != String::npos)
|
||||
{
|
||||
result.push_back(str.substr(start, end - start));
|
||||
start = end + delim.size();
|
||||
end = str.find(delim, start);
|
||||
}
|
||||
result.push_back(str.substr(start, end - start));
|
||||
return(result);
|
||||
}
|
||||
|
||||
String join(StringList l, String delim)
|
||||
{
|
||||
String result;
|
||||
u32 i = 0;
|
||||
for(auto& s : l)
|
||||
{
|
||||
if(i > 0)
|
||||
result.append(delim);
|
||||
result.append(s);
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String html_escape(String s)
|
||||
{
|
||||
String result;
|
||||
|
||||
for(u32 i = 0; i < s.length(); i++)
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('&'):
|
||||
result.append("&");
|
||||
break;
|
||||
case('<'):
|
||||
result.append("<");
|
||||
break;
|
||||
case('>'):
|
||||
result.append(">");
|
||||
break;
|
||||
case('"'):
|
||||
result.append(""");
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String html_escape(u64 a)
|
||||
{
|
||||
return(std::to_string(a));
|
||||
}
|
||||
|
||||
String html_escape(f64 a)
|
||||
{
|
||||
return(std::to_string(a));
|
||||
}
|
||||
|
||||
u64 int_val(String s, u32 base)
|
||||
{
|
||||
return(strtoll(s.c_str(), 0, base));
|
||||
}
|
||||
|
||||
String nibble(String& haystack, String delim)
|
||||
{
|
||||
auto idx = haystack.find(delim);
|
||||
if(idx == String::npos)
|
||||
{
|
||||
String result = haystack;
|
||||
haystack = "";
|
||||
return(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
String result = haystack.substr(0, idx);
|
||||
haystack = haystack.substr(idx+delim.length());
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
|
||||
String json_encode(DTree t)
|
||||
{
|
||||
String result = "";
|
||||
if(t.is_array())
|
||||
{
|
||||
result += "{";
|
||||
u32 count = 0;
|
||||
t.each([&] (DTree item, String key) {
|
||||
if(count > 0)
|
||||
result += ", ";
|
||||
count += 1;
|
||||
result += json_escape(key) + ": " + json_encode(item);
|
||||
});
|
||||
result += "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = t.to_json();
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
// https://i.stack.imgur.com/SHLOB.gif
|
||||
String json_decode_String(String s, u32& i, char termination_char)
|
||||
{
|
||||
String result;
|
||||
//print("json_decode_String " + s.substr(i) + "\n");
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(c == termination_char)
|
||||
{
|
||||
i += 1;
|
||||
//print("json_decode_String = " + result + "\n");
|
||||
return(result);
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
i += 1;
|
||||
c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('t'):
|
||||
result.append(1, '\t');
|
||||
break;
|
||||
case('n'):
|
||||
result.append(1, '\n');
|
||||
break;
|
||||
case('r'):
|
||||
result.append(1, '\r');
|
||||
break;
|
||||
case('\\'):
|
||||
result.append(1, '\\');
|
||||
break;
|
||||
case('b'):
|
||||
result.append(1, '\b');
|
||||
break;
|
||||
case('f'):
|
||||
result.append(1, '\f');
|
||||
break;
|
||||
case('u'):
|
||||
// todo decode
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_map(String s, u32& i);
|
||||
|
||||
void json_consume_space(String s, u32& i)
|
||||
{
|
||||
while(i < s.length() && isspace(s[i]))
|
||||
i += 1;
|
||||
}
|
||||
|
||||
String json_decode_keyword(String s, u32& i)
|
||||
{
|
||||
String result;
|
||||
json_consume_space(s, i);
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(isalnum(c))
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(result);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String json_decode_number(String s, u32& i)
|
||||
{
|
||||
String result;
|
||||
json_consume_space(s, i);
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(isdigit(c) || c == '.')
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(result);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_value(String s, u32& i)
|
||||
{
|
||||
DTree result;
|
||||
String value = "";
|
||||
json_consume_space(s, i);
|
||||
char c = s[i];
|
||||
//print("json_decode_value " + s.substr(i) + "\n");
|
||||
if(c == '"' || c == '\'') // String value
|
||||
{
|
||||
result.type = 'S';
|
||||
i += 1;
|
||||
result._String = json_decode_String(s, i, s[i-1]);
|
||||
return(result);
|
||||
}
|
||||
else if(isdigit(c))
|
||||
{
|
||||
result.type = 'S';
|
||||
result._String = json_decode_number(s, i);
|
||||
//result._float = stod(json_decode_number(s, i));
|
||||
return(result);
|
||||
}
|
||||
else if(c == '{')
|
||||
{
|
||||
i += 1;
|
||||
return(json_decode_map(s, i));
|
||||
}
|
||||
else
|
||||
{
|
||||
value = json_decode_keyword(s, i);
|
||||
if(value == "true")
|
||||
result.set_bool(true);
|
||||
else if(value == "false")
|
||||
result.set_bool(false);
|
||||
else if(value == "null")
|
||||
result.set("");
|
||||
return(result);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_map(String s, u32& i)
|
||||
{
|
||||
DTree result;
|
||||
result.type = 'M';
|
||||
String key = "";
|
||||
json_consume_space(s, i);
|
||||
//print("json_decode_map " + s.substr(i) + "\n");
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(c == '}')
|
||||
{
|
||||
i += 1;
|
||||
return(result);
|
||||
}
|
||||
else if(c == ',')
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
i += 1;
|
||||
key = json_decode_String(s, i, s[i-1]);
|
||||
json_consume_space(s, i);
|
||||
if(s[i] != ':')
|
||||
return(result); // malformed
|
||||
i += 1;
|
||||
DTree v = json_decode_value(s, i);
|
||||
//result._map[key] = json_decode_value(s, i);
|
||||
//print("KV " + key + " = " + to_String(v) + "\n");
|
||||
//printf("map add %s (%c) \n", key.c_str(), s[i]);
|
||||
result._map[key] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
// malformed
|
||||
return(result);
|
||||
}
|
||||
json_consume_space(s, i);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode(String s)
|
||||
{
|
||||
u32 i = 0;
|
||||
return(json_decode_value(s, i));
|
||||
}
|
||||
|
||||
void ob_start()
|
||||
{
|
||||
context->ob_start();
|
||||
}
|
||||
|
||||
void ob_close()
|
||||
{
|
||||
delete context->ob;
|
||||
context->ob_stack.pop_back();
|
||||
if(context->ob_stack.size() == 0)
|
||||
ob_start();
|
||||
}
|
||||
|
||||
String ob_get()
|
||||
{
|
||||
return(context->ob->str());
|
||||
}
|
||||
|
||||
String ob_get_close()
|
||||
{
|
||||
String result = context->ob->str();
|
||||
ob_close();
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
u8 char_to_u8(char input);
|
||||
u8 hex_to_u8(String src);
|
||||
u64 int_val(String s, u32 base = 10);
|
||||
String str_to_lower(String s);
|
||||
String str_to_upper(String s);
|
||||
|
||||
String trim(String raw);
|
||||
StringList split(String str, String delim = "\n");
|
||||
String join(StringList l, String delim = "\n");
|
||||
String nibble(String& haystack, String delim);
|
||||
void json_consume_space(String s, u32& i);
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> filter(std::vector<T> items, std::function<bool (T)> f)
|
||||
{
|
||||
std::vector<T> new_items;
|
||||
for(auto item : items)
|
||||
{
|
||||
if(f(item))
|
||||
new_items.push_back(item);
|
||||
}
|
||||
return(new_items);
|
||||
}
|
||||
|
||||
template <class ...Args>
|
||||
String first(Args... args)
|
||||
{
|
||||
std::vector<String> vec = {args...};
|
||||
for(auto s : vec)
|
||||
if(trim(s) != "")
|
||||
return(s);
|
||||
return("");
|
||||
}
|
||||
|
||||
String html_escape(String s);
|
||||
String html_escape(u64 a);
|
||||
String html_escape(f64 a);
|
||||
|
||||
String json_encode(DTree t);
|
||||
DTree json_decode(String s);
|
||||
|
||||
String var_dump(StringMap map, String prefix = "", String postfix = "\n");
|
||||
String var_dump(StringList slist, String prefix = "", String postfix = "\n");
|
||||
|
||||
void ob_start();
|
||||
void ob_clear();
|
||||
String ob_get_clear();
|
||||
String ob_get();
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
|
||||
/* from valgrind tests */
|
||||
|
||||
/* ================ sha1.c ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
typedef struct {
|
||||
u_int32_t state[5];
|
||||
u_int32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
|
||||
void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h> /* for u_int*_t */
|
||||
#include "hash.h"
|
||||
|
||||
#ifndef BYTE_ORDER
|
||||
#if (BSD >= 199103)
|
||||
# include <machine/endian.h>
|
||||
#else
|
||||
#if defined(linux) || defined(__linux__)
|
||||
# include <endian.h>
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
|
||||
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
|
||||
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
|
||||
|
||||
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
|
||||
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
|
||||
defined(__alpha__) || defined(__alpha)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
|
||||
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
|
||||
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
|
||||
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
|
||||
defined(__hppa) || defined(__hp9000) || \
|
||||
defined(__hp9000s300) || defined(__hp9000s700) || \
|
||||
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
#endif /* linux */
|
||||
#endif /* BSD */
|
||||
#endif /* BYTE_ORDER */
|
||||
|
||||
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
|
||||
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#else
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BYTE_ORDER) || \
|
||||
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \
|
||||
BYTE_ORDER != PDP_ENDIAN)
|
||||
/* you must determine what the correct bit order is for
|
||||
* your compiler - the next line is an intentional error
|
||||
* which will force your compiles to bomb until you fix
|
||||
* the above macros.
|
||||
*/
|
||||
#error "Undefined or invalid BYTE_ORDER"
|
||||
#endif
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#error "Endianness not defined!"
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
|
||||
{
|
||||
u_int32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
u_int32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
#ifdef SHA1HANDSOFF
|
||||
memset(block, '\0', sizeof(block));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
|
||||
{
|
||||
u_int32_t i;
|
||||
u_int32_t j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
* in finalcount. Second element first, but
|
||||
* big-endian order within element.
|
||||
* But we do it all backwards.
|
||||
*/
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
u_int32_t t = context->count[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
#endif
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
/* ================ end of sha1.c ================ */
|
||||
|
||||
String
|
||||
gen_sha1(String s, bool as_binary)
|
||||
{
|
||||
unsigned char v[20];
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, (const unsigned char *)s.data(), s.length());
|
||||
SHA1Final(v, &ctx);
|
||||
String result;
|
||||
if(as_binary)
|
||||
for(int i=0; i<20; i++)
|
||||
result.append(1, v[i]);
|
||||
else
|
||||
for(int i=0; i<20; i++)
|
||||
result += to_hex(v[i], 2);
|
||||
return(result);
|
||||
}
|
||||
|
||||
#define BIT_NOISE1 0xB5297A4D
|
||||
#define BIT_NOISE2 0x68E31DA4
|
||||
#define BIT_NOISE3 0x1B56C4E9
|
||||
|
||||
// based on Squirrel3 https://www.youtube.com/watch?v=LWFzPP8ZbdU&t=2666s
|
||||
u32 gen_noise32(u32 index, u32 seed)
|
||||
{
|
||||
u32 r = index;
|
||||
r *= BIT_NOISE1;
|
||||
r += seed;
|
||||
r ^= (r >> 8);
|
||||
r += BIT_NOISE2;
|
||||
r ^= (r << 8);
|
||||
r *= BIT_NOISE3;
|
||||
r ^= (r >> 8);
|
||||
return(r);
|
||||
}
|
||||
|
||||
#define BIT_NOISE61 0x5134811636f8cc8a
|
||||
#define BIT_NOISE62 0xb8E31DA41B56C4E9
|
||||
#define BIT_NOISE63 0x18cd227aaa1168c1
|
||||
|
||||
u64 gen_noise64(u64 index, u64 seed)
|
||||
{
|
||||
u64 r = index;
|
||||
r *= BIT_NOISE61;
|
||||
r += seed;
|
||||
r ^= (r >> 8);
|
||||
r += BIT_NOISE62;
|
||||
r ^= (r << 8);
|
||||
r *= BIT_NOISE63;
|
||||
r ^= (r >> 8);
|
||||
return(r);
|
||||
}
|
||||
|
||||
#define MAX_64 0xffffffffffffffff
|
||||
|
||||
f64 gen_noise01(u64 index, u64 seed)
|
||||
{
|
||||
return((float)gen_noise64(index, seed)/(float)MAX_64);
|
||||
}
|
||||
|
||||
u64 gen_int(u64 from, u64 to, u64 index, u64 seed)
|
||||
{
|
||||
u64 b = 1 + to - from;
|
||||
return(from + (gen_noise64(index, seed) % b));
|
||||
}
|
||||
|
||||
#include <tgmath.h>
|
||||
f64 gen_float(f64 from, f64 to, u64 index, u64 seed, f64 decimal_precision)
|
||||
{
|
||||
f64 b = to - from;
|
||||
return(from + fmod( decimal_precision*(f64)gen_noise64(index, seed), b));
|
||||
}
|
||||
|
||||
u64 draw_int(u64 from, u64 to)
|
||||
{
|
||||
return(gen_int(from, to, context->random_index++, context->random_seed));
|
||||
}
|
||||
|
||||
f64 draw_float(f64 from, f64 to, f64 decimal_precision)
|
||||
{
|
||||
return(gen_float(from, to, context->random_index++, context->random_seed, decimal_precision));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/* ================ sha1.h ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
|
||||
String gen_sha1(String s, bool as_binary = false);
|
||||
|
||||
u32 gen_noise32(u32 index, u32 seed = 0);
|
||||
u64 gen_noise64(u64 index, u64 seed = 0);
|
||||
f64 gen_noise01(u64 index, u64 seed = 0);
|
||||
|
||||
u64 gen_int(u64 from, u64 to, u64 index, u64 seed = 0);
|
||||
f64 gen_float(f64 from, f64 to, u64 index, u64 seed = 0, f64 decimal_precision = 0.000000000001);
|
||||
|
||||
u64 draw_int(u64 from, u64 to);
|
||||
f64 draw_float(f64 from, f64 to, f64 decimal_precision = 0.000000000001);
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "../3rdparty/mysql/mysql.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "mysql-connector.h"
|
||||
|
||||
bool MySQL::connect(String host, String username, String password)
|
||||
{
|
||||
switch_to_system_alloc();
|
||||
connection = mysql_init(NULL);
|
||||
if (connection == NULL)
|
||||
{
|
||||
auto e = mysql_error((MYSQL*)connection);
|
||||
fprintf(stderr, "%s\n", e);
|
||||
switch_to_arena(context->mem);
|
||||
statement_info.assign(e);
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (mysql_real_connect((MYSQL*)connection, host.c_str(), username.c_str(), password.c_str(),
|
||||
NULL, 0, NULL, 0) == NULL)
|
||||
{
|
||||
auto e = mysql_error((MYSQL*)connection);
|
||||
fprintf(stderr, "%s\n", e);
|
||||
mysql_close((MYSQL*)connection);
|
||||
switch_to_arena(context->mem);
|
||||
statement_info.assign(e);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
if (mysql_query(con, "CREATE DATABASE testdb"))
|
||||
{
|
||||
fprintf(stderr, "%s\n", mysql_error(con));
|
||||
mysql_close(con);
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
switch_to_arena(context->mem);
|
||||
statement_info = String("connected");
|
||||
context->resources.mysql_connections.push_back(connection);
|
||||
return(true);
|
||||
}
|
||||
|
||||
String MySQL::escape(String raw, char quote_char)
|
||||
{
|
||||
return(mysql_escape(raw, quote_char));
|
||||
}
|
||||
|
||||
String mysql_escape(String raw, char quote_char)
|
||||
{
|
||||
String result;
|
||||
if(quote_char > 0)
|
||||
result.append(1, quote_char);
|
||||
|
||||
for(u32 i = 0; i < raw.length(); i++)
|
||||
{
|
||||
char c = raw[i];
|
||||
switch(c)
|
||||
{
|
||||
case('\n'):
|
||||
result.append("\\n");
|
||||
break;
|
||||
case('\r'):
|
||||
result.append("\\r");
|
||||
break;
|
||||
case('\t'):
|
||||
result.append("\\t");
|
||||
break;
|
||||
case('\\'):
|
||||
case('\''):
|
||||
case('"'):
|
||||
result.append(1, '\\');
|
||||
result.append(1, c);
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(quote_char > 0)
|
||||
result.append(1, quote_char);
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree field_to_dtree_node(char* data_ptr, MySQLFieldInfo field_info, u32 len)
|
||||
{
|
||||
DTree result;
|
||||
switch(field_info.type)
|
||||
{
|
||||
case(MYSQL_TYPE_TINY):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_SHORT):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_LONG):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_INT24):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_LONGLONG):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_FLOAT):
|
||||
result.set(atof(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_DOUBLE):
|
||||
result.set(atof(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_NULL):
|
||||
break;
|
||||
default:
|
||||
String s;
|
||||
s.assign(data_ptr);
|
||||
result.set(s);
|
||||
break;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree MySQL::get_pending_result()
|
||||
{
|
||||
DTree result_data;
|
||||
// based on: https://dev.mysql.com/doc/c-api/5.7/en/mysql-field-count.html
|
||||
MYSQL_RES *result;
|
||||
result = mysql_store_result((MYSQL*)connection);
|
||||
insert_id = mysql_insert_id((MYSQL*)connection);
|
||||
//statement_info.assign(mysql_info((MYSQL*)connection));
|
||||
if (result) // there are rows
|
||||
{
|
||||
field_count = mysql_num_fields(result);
|
||||
row_count = mysql_num_rows(result);
|
||||
|
||||
field_info.clear();
|
||||
unsigned int i;
|
||||
MYSQL_FIELD *fields;
|
||||
fields = mysql_fetch_fields(result);
|
||||
for(i = 0; i < field_count; i++)
|
||||
{
|
||||
MySQLFieldInfo fi;
|
||||
if(fields[i].name) fi.name.assign(fields[i].name);
|
||||
if(fields[i].table) fi.table.assign(fields[i].table);
|
||||
if(fields[i].db) fi.db.assign(fields[i].db);
|
||||
fi.length = (fields[i].length);
|
||||
if(fields[i].def) fi.def.assign(fields[i].def);
|
||||
fi.max_length = (fields[i].max_length);
|
||||
fi.flags = (fields[i].flags);
|
||||
fi.type = (fields[i].type);
|
||||
field_info.push_back(fi);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
DTree row_data;
|
||||
auto lengths = mysql_fetch_lengths(result);
|
||||
for(i = 0; i < field_count; i++)
|
||||
{
|
||||
row_data[field_info[i].name] = field_to_dtree_node(row[i], field_info[i], lengths[i]);
|
||||
}
|
||||
result_data.push(row_data);
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else // mysql_store_result() returned nothing; should it have?
|
||||
{
|
||||
if(mysql_field_count((MYSQL*)connection) == 0)
|
||||
{
|
||||
// query does not return data
|
||||
// (it was not a SELECT)
|
||||
affected_rows = mysql_affected_rows((MYSQL*)connection);
|
||||
}
|
||||
else // mysql_store_result() should have returned data
|
||||
{
|
||||
// error
|
||||
}
|
||||
}
|
||||
return(result_data);
|
||||
}
|
||||
|
||||
DTree MySQL::query(String q)
|
||||
{
|
||||
_preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str());
|
||||
DTree result;
|
||||
if(_preload_next_error_code == 0)
|
||||
result = get_pending_result();
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree MySQL::query(String q, StringMap params)
|
||||
{
|
||||
return(query(
|
||||
parse_query_parameters(q, params).c_str()
|
||||
));
|
||||
}
|
||||
|
||||
String MySQL::parse_query_parameters(String query, StringMap map)
|
||||
{
|
||||
String result;
|
||||
query.append(1, ' ');
|
||||
|
||||
u8 mode = 0;
|
||||
char quote;
|
||||
String identifier;
|
||||
for(u32 i = 0; i < query.length(); i++)
|
||||
{
|
||||
char c = query[i];
|
||||
if(mode == 0) // normal, unquoted mode
|
||||
{
|
||||
if(c == ':')
|
||||
{
|
||||
mode = 1;
|
||||
identifier = "";
|
||||
}
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
result.append(1, c);
|
||||
mode = 2;
|
||||
quote = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(mode == 1) // identifier mode
|
||||
{
|
||||
if(isalnum(c))
|
||||
{
|
||||
identifier.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(escape(map[identifier]));
|
||||
result.append(1, c);
|
||||
mode = 0;
|
||||
}
|
||||
}
|
||||
else if(mode == 2) // quoted mode
|
||||
{
|
||||
if(c == quote)
|
||||
mode = 0;
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void MySQL::disconnect()
|
||||
{
|
||||
if(connection)
|
||||
mysql_close((MYSQL*)connection);
|
||||
connection = NULL;
|
||||
}
|
||||
|
||||
String MySQL::error()
|
||||
{
|
||||
if(_preload_next_error_code)
|
||||
{
|
||||
String p = "Unknown error";
|
||||
switch(_preload_next_error_code)
|
||||
{
|
||||
case(CR_COMMANDS_OUT_OF_SYNC):
|
||||
p = "Commands out of sync";
|
||||
break;
|
||||
case(CR_SERVER_GONE_ERROR):
|
||||
p = "Server connection error";
|
||||
break;
|
||||
case(CR_SERVER_LOST):
|
||||
p = "Server hung up";
|
||||
break;
|
||||
case(CR_OUT_OF_MEMORY):
|
||||
p = "Out of memory";
|
||||
break;
|
||||
default:
|
||||
case(CR_UNKNOWN_ERROR):
|
||||
p = "Unknown server error";
|
||||
break;
|
||||
}
|
||||
_preload_next_error_code = 0;
|
||||
return(p);
|
||||
}
|
||||
const char* res = mysql_error((MYSQL*)connection);
|
||||
if(res)
|
||||
{
|
||||
return(String(res));
|
||||
}
|
||||
else
|
||||
{
|
||||
return("");
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_mysql_connections()
|
||||
{
|
||||
switch_to_system_alloc();
|
||||
for(auto& con : context->resources.mysql_connections)
|
||||
mysql_close((MYSQL*)con);
|
||||
switch_to_arena(context->mem);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
struct MySQLFieldInfo {
|
||||
String name;
|
||||
String table;
|
||||
String db;
|
||||
u64 length;
|
||||
String def;
|
||||
u64 max_length;
|
||||
u64 flags;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
struct MySQL {
|
||||
|
||||
void* connection = 0;
|
||||
u32 _preload_next_error_code = 0;
|
||||
u32 affected_rows = 0;
|
||||
u32 field_count = 0;
|
||||
u32 row_count = 0;
|
||||
u64 insert_id = 0;
|
||||
String statement_info = ""; //
|
||||
|
||||
std::vector<MySQLFieldInfo> field_info;
|
||||
|
||||
bool connect(String host = "localhost", String username = "root", String password = "");
|
||||
void disconnect();
|
||||
String error();
|
||||
String escape(String raw, char quote_char = '\'');
|
||||
String parse_query_parameters(String query, StringMap m);
|
||||
DTree query(String q);
|
||||
DTree query(String q, StringMap params);
|
||||
DTree get_pending_result();
|
||||
|
||||
};
|
||||
|
||||
MySQL* mysql_connect(String host = "localhost", String username = "root", String password = "")
|
||||
{
|
||||
MySQL* m = new MySQL();
|
||||
m->connect(host, username, password);
|
||||
return(m);
|
||||
}
|
||||
|
||||
void mysql_disconnect(MySQL* m)
|
||||
{
|
||||
m->disconnect();
|
||||
}
|
||||
|
||||
String mysql_error(MySQL* m)
|
||||
{
|
||||
return(m->error());
|
||||
}
|
||||
|
||||
String mysql_escape(String raw, char quote_char);
|
||||
|
||||
DTree mysql_query(MySQL* m, String q)
|
||||
{
|
||||
return(m->query(q));
|
||||
}
|
||||
|
||||
DTree mysql_query(MySQL* m, String q, StringMap params)
|
||||
{
|
||||
return(m->query(q, params));
|
||||
}
|
||||
|
||||
u64 mysql_insert_id(MySQL* m)
|
||||
{
|
||||
return(m->insert_id);
|
||||
}
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <execinfo.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include "sys.h"
|
||||
|
||||
String shell_exec(String cmd)
|
||||
{
|
||||
printf("(i) shell_exec(%s)\n", cmd.c_str());
|
||||
String data;
|
||||
FILE * stream;
|
||||
const int max_buffer = 256;
|
||||
char buffer[max_buffer];
|
||||
cmd.append(" 2>&1");
|
||||
|
||||
stream = popen(cmd.c_str(), "r");
|
||||
|
||||
if (stream) {
|
||||
while (!feof(stream))
|
||||
if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
|
||||
pclose(stream);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
String shell_escape(String raw)
|
||||
{
|
||||
// FIXME
|
||||
return("\"" + raw + "\"");
|
||||
/*
|
||||
` U+0060 (Grave Accent) Backtick Command substitution
|
||||
~ U+007E Tilde Tilde expansion
|
||||
! U+0021 Exclamation mark History expansion
|
||||
# U+0023 Number sign Hash Comments
|
||||
$ U+0024 Dollar sign Parameter expansion
|
||||
& U+0026 Ampersand Background commands
|
||||
* U+002A Asterisk Filename expansion and globbing
|
||||
( U+0028 Left Parenthesis Subshells
|
||||
) U+0029 Right Parenthesis Subshells
|
||||
U+0009 Tab (⇥) Word splitting (whitespace)
|
||||
{ U+007B Left Curly Bracket Left brace Brace expansion
|
||||
[ U+005B Left Square Bracket Filename expansion and globbing
|
||||
| U+007C Vertical Line Vertical bar Pipelines
|
||||
\ U+005C Reverse Solidus Backslash Escape character
|
||||
; U+003B Semicolon Separating commands
|
||||
' U+0027 Apostrophe Single quote String quoting
|
||||
" U+0022 Quotation Mark Double quote String quoting with interpolation
|
||||
↩ U+000A Line Feed Newline Line break
|
||||
< U+003C Less than Input redirection
|
||||
> U+003E Greater than Output redirection
|
||||
? U+003F Question mark Filename expansion and globbing
|
||||
U+0020 Space Word splitting1 (whitespace)
|
||||
*/
|
||||
}
|
||||
|
||||
String basename(String fn)
|
||||
{
|
||||
String result;
|
||||
while(fn.length() > 0)
|
||||
result = nibble("/", fn);
|
||||
//printf("basename(%s) %s\n", fn.c_str(), result.c_str());
|
||||
return(result);
|
||||
}
|
||||
|
||||
String dirname(String fn)
|
||||
{
|
||||
String result;
|
||||
auto seg = split(fn, "/");
|
||||
seg.pop_back();
|
||||
result = join(seg, "/");
|
||||
//printf("dirname(%s) %s seg#%i\n", fn.c_str(), result.c_str(), seg.size());
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool mkdir(String path)
|
||||
{
|
||||
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool file_exists(String path)
|
||||
{
|
||||
std::filesystem::path fp{ path };
|
||||
return(std::filesystem::exists(fp));
|
||||
}
|
||||
|
||||
String file_get_contents(String file_name)
|
||||
{
|
||||
/*std::ifstream ifs(file_name);
|
||||
printf("stream file desc %i\n", ifs.filedesc());
|
||||
String content(
|
||||
(std::istreambuf_iterator<char>(ifs) ),
|
||||
(std::istreambuf_iterator<char>() ) );*/
|
||||
char buf[512];
|
||||
String content;
|
||||
s32 fd = open(file_name.c_str(), O_RDONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not read %s\n", file_name.c_str());
|
||||
return("");
|
||||
}
|
||||
flock(fd, LOCK_SH);
|
||||
s64 bytes_read = 0;
|
||||
//s64 size = lseek(fd, 0, SEEK_END);
|
||||
//lseek(fd, 0, SEEK_SET);
|
||||
//content.reserve(size+1);
|
||||
|
||||
while((bytes_read = read(fd, buf, 512)) > 0)
|
||||
{
|
||||
content.append(buf, bytes_read);
|
||||
}
|
||||
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(content);
|
||||
}
|
||||
|
||||
bool file_put_contents(String file_name, String content)
|
||||
{
|
||||
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not write %s\n", file_name.c_str());
|
||||
return(false);
|
||||
}
|
||||
flock(fd, LOCK_EX);
|
||||
write(fd, content.data(), content.length());
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(true);
|
||||
}
|
||||
|
||||
String get_cwd()
|
||||
{
|
||||
return(std::filesystem::current_path());
|
||||
}
|
||||
|
||||
void set_cwd(String path)
|
||||
{
|
||||
chdir(path.c_str());
|
||||
}
|
||||
|
||||
time_t file_mtime(String file_name)
|
||||
{
|
||||
struct stat info;
|
||||
if (stat(file_name.c_str(), &info) != 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(info.st_mtime);
|
||||
}
|
||||
}
|
||||
|
||||
void unlink(String file_name)
|
||||
{
|
||||
remove(file_name.c_str());
|
||||
}
|
||||
|
||||
String expand_path(String path)
|
||||
{
|
||||
String result;
|
||||
|
||||
auto base_path = split(get_cwd(), "/");
|
||||
auto rel_path = split(path, "/");
|
||||
|
||||
for(auto& s : rel_path)
|
||||
{
|
||||
if(s == "..")
|
||||
{
|
||||
base_path.pop_back();
|
||||
}
|
||||
else if(s == ".")
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
base_path.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
return(join(base_path, "/"));
|
||||
}
|
||||
|
||||
f64 microtime()
|
||||
{
|
||||
return ((f64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000000;
|
||||
}
|
||||
|
||||
u64 time()
|
||||
{
|
||||
return(std::time(0));
|
||||
}
|
||||
|
||||
String date(String format, u64 timestamp)
|
||||
{
|
||||
String ts;
|
||||
String fmt;
|
||||
if(timestamp > 0)
|
||||
ts = String("-d '@")+std::to_string(timestamp)+"'";
|
||||
if(format != "")
|
||||
fmt = String("+'"+format+"'");
|
||||
return(trim(shell_exec("date "+ts+" "+fmt)));
|
||||
}
|
||||
|
||||
String gmdate(String format, u64 timestamp)
|
||||
{
|
||||
String ts;
|
||||
String fmt;
|
||||
if(timestamp > 0)
|
||||
ts = String("-d '@")+std::to_string(timestamp)+"'";
|
||||
if(format == "RFC1123")
|
||||
format = "%a, %d %b %Y %T GMT";
|
||||
if(format != "")
|
||||
fmt = String("+'"+format+"'");
|
||||
return(trim(shell_exec("date -u "+ts+" "+fmt)));
|
||||
}
|
||||
|
||||
u64 parse_time(String time_String)
|
||||
{
|
||||
return(int_val(trim(shell_exec("date -u -d '"+time_String+"' +'%s'"))));
|
||||
}
|
||||
|
||||
u64 socket_connect(String host, short port)
|
||||
{
|
||||
|
||||
/*String addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
String sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
String addrinfo *ai_next;
|
||||
};*/
|
||||
|
||||
auto sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(sockfd < 0)
|
||||
{
|
||||
print("SOCKET ERROR (could not open socket)\n");
|
||||
perror("SOCKET ERROR ");
|
||||
return(0);
|
||||
}
|
||||
|
||||
struct sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = inet_addr(host.c_str());
|
||||
|
||||
if(connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
|
||||
{
|
||||
print("SOCKET ERROR (could not connect to address " + String(host) + ":" + std::to_string(port) + ")\n");
|
||||
perror("SOCKET ERROR ");
|
||||
return(0);
|
||||
}
|
||||
context->resources.sockets.push_back(sockfd);
|
||||
return(sockfd);
|
||||
}
|
||||
|
||||
void socket_close(u64 sockfd)
|
||||
{
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
bool socket_write(u64 sockfd, String data)
|
||||
{
|
||||
return(write(sockfd, data.c_str(), data.length()) >= 0);
|
||||
}
|
||||
|
||||
String socket_read(u64 sockfd, u32 max_length, u32 timeout)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
|
||||
char buf[max_length+1];
|
||||
auto byte_count = recv(sockfd, buf, sizeof(buf), 0);
|
||||
if(byte_count > 0)
|
||||
{
|
||||
buf[byte_count] = 0;
|
||||
String result(buf, byte_count+1);
|
||||
return(result);
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
String memcache_escape_key(String key)
|
||||
{
|
||||
String result;
|
||||
for(auto c : key)
|
||||
{
|
||||
if(isspace(c))
|
||||
c = '_';
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringList memcache_escape_keys(StringList keys)
|
||||
{
|
||||
StringList result;
|
||||
for(auto s : keys)
|
||||
{
|
||||
result.push_back(memcache_escape_key(s));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
u64 memcache_connect(String host, short port)
|
||||
{
|
||||
return(socket_connect(host, port));
|
||||
}
|
||||
|
||||
String memcache_command(u64 connection, String command)
|
||||
{
|
||||
socket_write(connection, command+"\r\n");
|
||||
return(socket_read(connection)); // FIXME: do multi-chunk until END line is received!
|
||||
}
|
||||
|
||||
bool memcache_set(u64 connection, String key, String value, u64 expires_in)
|
||||
{
|
||||
socket_write(connection,
|
||||
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
|
||||
String("set ") + memcache_escape_key(key) + " 0 " + std::to_string(expires_in) + " " + std::to_string(value.length()) + "\r\n" +
|
||||
value + "\r\n");
|
||||
return("STORED" == trim(socket_read(connection)));
|
||||
}
|
||||
|
||||
bool memcache_delete(u64 connection, String key)
|
||||
{
|
||||
socket_write(connection,
|
||||
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
|
||||
String("delete ") + memcache_escape_key(key) + "\r\n"
|
||||
);
|
||||
return("DELETED" == trim(socket_read(connection)));
|
||||
}
|
||||
|
||||
String memcache_get(u64 connection, String key, String default_value)
|
||||
{
|
||||
auto res = memcache_command(connection, String("get ")+memcache_escape_key(key));
|
||||
String t = nibble(res, " ");
|
||||
if(t == "VALUE")
|
||||
{
|
||||
String key = nibble(res, " ");
|
||||
String meta = nibble(res, " ");
|
||||
u32 length = stoi(nibble(res, "\r\n"));
|
||||
return(res.substr(0, length));
|
||||
}
|
||||
return(default_value);
|
||||
}
|
||||
|
||||
StringMap memcache_get_multiple(u64 connection, StringList keys)
|
||||
{
|
||||
StringMap result;
|
||||
// to do: escape key String
|
||||
auto res = memcache_command(connection, String("get ")+join(memcache_escape_keys(keys), " "));
|
||||
while(res.length() > 0)
|
||||
{
|
||||
String t = nibble(res, " ");
|
||||
if(t == "VALUE")
|
||||
{
|
||||
String key = nibble(res, " ");
|
||||
String meta = nibble(res, " ");
|
||||
u32 length = stoi(nibble(res, "\r\n"));
|
||||
result[key] = res.substr(0, length);
|
||||
res = res.substr(length+2);
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
void on_segfault(int sig)
|
||||
{
|
||||
void *array[10];
|
||||
size_t size;
|
||||
|
||||
// get void*'s for all entries on the stack
|
||||
size = backtrace(array, 10);
|
||||
|
||||
// print out all the frames to stderr
|
||||
fprintf(stderr, "SEG FAULT: %d:\n", sig);
|
||||
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
std::map<pid_t, Worker> workers;
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
void spawn_subprocess(std::function<void()> exec_after_spawn)
|
||||
{
|
||||
parent_pid = getpid();
|
||||
pid_t p;
|
||||
p = fork();
|
||||
if(p == 0)
|
||||
{
|
||||
my_pid = getpid();
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
exec_after_spawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
Worker w;
|
||||
w.pid = p;
|
||||
workers[w.pid] = w;
|
||||
printf("(P) child procress spawned: PID %i\n", p);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t task_pid(String key)
|
||||
{
|
||||
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
|
||||
String status_file = file_get_contents(status_file_name);
|
||||
pid_t p = 0;
|
||||
if(status_file != "")
|
||||
{
|
||||
p = int_val(status_file);
|
||||
if(kill(p, 0) == 0) // process is still running
|
||||
return(p);
|
||||
unlink(status_file_name);
|
||||
}
|
||||
return(p);
|
||||
}
|
||||
|
||||
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
{
|
||||
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
|
||||
String status_file = file_get_contents(status_file_name);
|
||||
pid_t p;
|
||||
if(status_file != "")
|
||||
{
|
||||
p = int_val(status_file);
|
||||
if(kill(p, 0) == 0) // process is still running
|
||||
{
|
||||
printf("(P) worker process '%s' already running: PID %i\n", key.c_str(), p);
|
||||
return(p);
|
||||
}
|
||||
//printf("(P) worker process '%s' had crashed: PID %i\n", key.c_str(), p);
|
||||
unlink(status_file_name);
|
||||
}
|
||||
p = fork();
|
||||
if(p == 0)
|
||||
{
|
||||
my_pid = getpid();
|
||||
file_put_contents(status_file_name, std::to_string(my_pid));
|
||||
|
||||
close(context->resources.fcgi_socket);
|
||||
context->resources.fcgi_socket = 0;
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
//prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
exec_after_spawn();
|
||||
unlink(status_file_name);
|
||||
printf("(P) worker process '%s' terminated: PID %i\n", key.c_str(), my_pid);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("(P) worker process '%s' spawned: PID %i\n", key.c_str(), p);
|
||||
return(p);
|
||||
}
|
||||
}
|
||||
|
||||
void on_child_exit(int sig)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
if ((pid = waitpid(-1, &status, WNOHANG)) != -1)
|
||||
{
|
||||
if(workers.count(pid) > 0)
|
||||
{
|
||||
workers.erase(pid);
|
||||
printf("(P) child terminated (PID:%i)\n", pid);
|
||||
//spawn_subprocess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringList ls(String dir)
|
||||
{
|
||||
return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n"));
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <signal.h>
|
||||
|
||||
String shell_exec(String cmd);
|
||||
String shell_escape(String raw);
|
||||
String basename(String fn);
|
||||
String dirname(String fn);
|
||||
bool mkdir(String path);
|
||||
bool file_exists(String path);
|
||||
String file_get_contents(String file_name);
|
||||
bool file_put_contents(String file_name, String content);
|
||||
#include <fstream>
|
||||
template <typename... Ts>
|
||||
bool file_append(String file_name, Ts... args)
|
||||
{
|
||||
std::ofstream fout;
|
||||
fout.open(file_name.c_str(), std::ios_base::app);
|
||||
((fout << args), ...);
|
||||
fout.close();
|
||||
return(true);
|
||||
}
|
||||
String get_cwd();
|
||||
void set_cwd(String path);
|
||||
time_t file_mtime(String file_name);
|
||||
void unlink(String file_name);
|
||||
String expand_path(String path);
|
||||
StringList ls(String dir);
|
||||
|
||||
f64 microtime();
|
||||
u64 time();
|
||||
String date(String format = "", u64 timestamp = 0);
|
||||
String gmdate(String format = "", u64 timestamp = 0);
|
||||
u64 parse_time(String time_String);
|
||||
|
||||
u64 socket_connect(String host, short port);
|
||||
void socket_close(u64 sockfd);
|
||||
bool socket_write(u64 sockfd, String data);
|
||||
String socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1);
|
||||
|
||||
String memcache_escape_key(String key);
|
||||
StringList memcache_escape_keys(StringList keys);
|
||||
u64 memcache_connect(String host = "127.0.0.1", short port = 11211);
|
||||
String memcache_command(u64 connection, String command);
|
||||
bool memcache_set(u64 connection, String key, String value, u64 expires_in = 60*60);
|
||||
bool memcache_delete(u64 connection, String key);
|
||||
String memcache_get(u64 connection, String key, String default_value = "");
|
||||
StringMap memcache_get_multiple(u64 connection, StringList keys);
|
||||
|
||||
pid_t parent_pid = 0;
|
||||
pid_t my_pid = 0;
|
||||
|
||||
void on_segfault(int sig);
|
||||
|
||||
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout = 60*10);
|
||||
pid_t task_pid(String key);
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
#include <ctime>
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
SharedUnit::~SharedUnit()
|
||||
{
|
||||
if(so_handle)
|
||||
{
|
||||
dlclose(so_handle);
|
||||
}
|
||||
}
|
||||
|
||||
String nibble(String div, String& haystack)
|
||||
{
|
||||
auto pos = haystack.find(div);
|
||||
if(pos == String::npos)
|
||||
{
|
||||
auto result = haystack;
|
||||
haystack.clear();
|
||||
return(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = haystack.substr(0, pos);
|
||||
haystack.erase(0, pos+div.length());
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
|
||||
void Request::invoke(String file_name)
|
||||
{
|
||||
DTree call_param;
|
||||
compiler_invoke(this, file_name, call_param);
|
||||
}
|
||||
|
||||
void Request::invoke(String file_name, DTree& call_param)
|
||||
{
|
||||
compiler_invoke(this, file_name, call_param);
|
||||
}
|
||||
|
||||
void Request::ob_start()
|
||||
{
|
||||
ob_stack.push_back(new std::ostringstream());
|
||||
ob = ob_stack.back();
|
||||
}
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
for(auto& sockfd : resources.sockets)
|
||||
close(sockfd);
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long s64;
|
||||
|
||||
typedef std::string String;
|
||||
|
||||
String operator+(String lhs, u64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, u32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, s64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, s32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, f64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, f32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
#define DEBUG_MEMORY_OFF
|
||||
#define GLOBAL_ARENA_ALLOCATOR
|
||||
|
||||
struct MemoryArena {
|
||||
|
||||
u8* data;
|
||||
u64 size = 0;
|
||||
u64 capacity = 0;
|
||||
String name = "unnamed";
|
||||
|
||||
MemoryArena(u64 cap, String _name = "unnamed")
|
||||
{
|
||||
name = _name;
|
||||
capacity = cap;
|
||||
printf("(i) memory arena '%s' created with capacity of %llu bytes\n", name.c_str(), capacity);
|
||||
data = (u8*)malloc(cap);
|
||||
}
|
||||
|
||||
~MemoryArena()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
#ifdef DEBUG_MEMORY
|
||||
printf("(i) memory arena '%s' cleared after high mark of %llu bytes\n", name.c_str(), size);
|
||||
#endif
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void* get(u64 size_needed)
|
||||
{
|
||||
u64 size_aligned = 8 + (8 * ((size_needed) / 8));
|
||||
u8* result = data + size;
|
||||
if(size_aligned + size >= capacity)
|
||||
{
|
||||
printf("(!) memory arena '%s' capacity (%llu) exceeded %llu/%llu + %llu >= %llu\n",
|
||||
name.c_str(), capacity, size_needed, size_aligned, size, capacity);
|
||||
return(0);
|
||||
}
|
||||
size += size_aligned;
|
||||
#ifdef DEBUG_MEMORY_DETAILED
|
||||
printf("(i) memory arena '%s' [+%llu]:%p alloc %llu/%llu bytes\n", name.c_str(), size, result, size_needed, size_aligned);
|
||||
#endif
|
||||
return(result);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MemoryArena* current_memory_arena = 0;
|
||||
|
||||
void switch_to_system_alloc()
|
||||
{
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
current_memory_arena = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void switch_to_arena(MemoryArena* a)
|
||||
{
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
current_memory_arena = a;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
void * operator new(decltype(sizeof(0)) n) noexcept(false)
|
||||
{
|
||||
if(current_memory_arena)
|
||||
{
|
||||
return(current_memory_arena->get(n));
|
||||
}
|
||||
else
|
||||
{
|
||||
return(malloc(n));
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void * p) throw()
|
||||
{
|
||||
if(current_memory_arena)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef std::map<String, String> StringMap;
|
||||
typedef std::vector<String> StringList;
|
||||
|
||||
struct Request;
|
||||
struct DTree;
|
||||
|
||||
typedef void (*call_handler)(DTree& call_param);
|
||||
typedef void (*request_handler)(Request* request);
|
||||
|
||||
String to_string(s64 v) { return(std::to_string(v)); }
|
||||
|
||||
struct ServerSettings {
|
||||
|
||||
String BIN_DIRECTORY = "/tmp/uce/work";
|
||||
String COMPILE_SCRIPT = "scripts/compile";
|
||||
String LIT_ESC = "3d5b5_1";
|
||||
String CONTENT_TYPE = "text/html; charset=utf-8";
|
||||
String SOCKET_PATH = "/run/uce.sock";
|
||||
String TMP_UPLOAD_PATH = "/tmp/uce/uploads";
|
||||
String SESSION_PATH = "/tmp/uce/sessions";
|
||||
String COMPILER_SYS_PATH = ".";
|
||||
u32 LISTEN_PORT = 9993;
|
||||
u64 SESSION_TIME = 60*60*24*30;
|
||||
u32 WORKER_COUNT = 4;
|
||||
u32 MAX_MEMORY = 1024*1024*16;
|
||||
|
||||
};
|
||||
|
||||
struct SharedUnit {
|
||||
|
||||
String file_name;
|
||||
String so_name;
|
||||
|
||||
String src_path;
|
||||
String bin_path;
|
||||
String pre_path;
|
||||
String src_file_name;
|
||||
String bin_file_name;
|
||||
String pre_file_name;
|
||||
|
||||
void* so_handle;
|
||||
|
||||
request_handler on_setup;
|
||||
call_handler on_render;
|
||||
|
||||
String compiler_messages;
|
||||
time_t last_compiled;
|
||||
|
||||
~SharedUnit();
|
||||
};
|
||||
|
||||
struct UploadedFile {
|
||||
String file_name;
|
||||
String tmp_name;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct ServerState {
|
||||
|
||||
std::map<String, SharedUnit*> units;
|
||||
ServerSettings config;
|
||||
u32 request_count = 0;
|
||||
|
||||
};
|
||||
|
||||
struct URI {
|
||||
|
||||
StringMap query;
|
||||
StringMap parts;
|
||||
|
||||
};
|
||||
|
||||
String nibble(String div, String& haystack);
|
||||
|
||||
template <typename ITYPE>
|
||||
String to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1)
|
||||
{
|
||||
static const char* digits = "0123456789ABCDEF";
|
||||
String rc(hex_len,'0');
|
||||
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
|
||||
rc[i] = digits[(w>>j) & 0x0f];
|
||||
return(rc);
|
||||
}
|
||||
|
||||
#include "dtree.h"
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param);
|
||||
|
||||
struct Request {
|
||||
|
||||
ServerState* server;
|
||||
|
||||
StringMap params;
|
||||
StringMap get;
|
||||
StringMap post;
|
||||
StringMap cookies;
|
||||
StringMap session;
|
||||
|
||||
DTree var;
|
||||
|
||||
String session_id = "";
|
||||
String session_name = "";
|
||||
std::vector<UploadedFile> uploaded_files;
|
||||
|
||||
StringMap header;
|
||||
StringList set_cookies;
|
||||
|
||||
u64 random_seed;
|
||||
u64 random_index;
|
||||
|
||||
MemoryArena* mem;
|
||||
|
||||
String in;
|
||||
std::vector<std::ostringstream*> ob_stack;
|
||||
std::ostringstream* ob;
|
||||
String out;
|
||||
String err;
|
||||
|
||||
bool is_finished = false;
|
||||
|
||||
struct Flags {
|
||||
bool log_request = true;
|
||||
} flags;
|
||||
|
||||
struct Stats {
|
||||
u32 bytes_written;
|
||||
f64 time_init;
|
||||
f64 time_start;
|
||||
f64 time_end;
|
||||
} stats;
|
||||
|
||||
struct Resources {
|
||||
std::vector<u64> sockets;
|
||||
std::vector<void*> mysql_connections;
|
||||
u64 fcgi_socket = 0;
|
||||
} resources;
|
||||
|
||||
void invoke(String file_name);
|
||||
void invoke(String file_name, DTree& call_param);
|
||||
|
||||
void ob_start();
|
||||
|
||||
~Request();
|
||||
|
||||
};
|
||||
|
||||
typedef Request FastCGIRequest;
|
||||
|
||||
Request* context;
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template <typename... Ts>
|
||||
void print(Ts... args)
|
||||
{
|
||||
((*context->ob << args), ...);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
#include "types.cpp"
|
||||
#include "hash.cpp"
|
||||
#include "dtree.cpp"
|
||||
#include "functionlib.cpp"
|
||||
#include "sys.cpp"
|
||||
#include "uri.cpp"
|
||||
#include "compiler.cpp"
|
||||
#include "mysql-connector.cpp"
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "hash.h"
|
||||
#include "functionlib.h"
|
||||
#include "sys.h"
|
||||
#include "uri.h"
|
||||
#include "compiler.h"
|
||||
#include "mysql-connector.h"
|
||||
|
||||
extern "C" void set_current_request(Request* _request)
|
||||
{
|
||||
context = _request;
|
||||
signal(SIGSEGV, on_segfault);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "uce_lib.h"
|
||||
|
||||
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
#include "uri.h"
|
||||
|
||||
String var_dump(URI uri, String prefix, String postfix)
|
||||
{
|
||||
return(
|
||||
prefix + "URI Parts: " + postfix +
|
||||
var_dump(uri.parts, prefix+" ", postfix)+
|
||||
prefix + " Query: " + postfix +
|
||||
var_dump(uri.query, prefix+" ", postfix)
|
||||
);
|
||||
}
|
||||
|
||||
String uri_decode(String q)
|
||||
{
|
||||
String result;
|
||||
for(u32 i = 0; i < q.length(); i++)
|
||||
{
|
||||
char c = q[i];
|
||||
if(c == '%' && q[i+1] != '%')
|
||||
{
|
||||
result.append(1, hex_to_u8(q.substr(i+1, 2)));
|
||||
i += 2;
|
||||
}
|
||||
else if(c == '+')
|
||||
{
|
||||
result.append(1, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String uri_encode(String q)
|
||||
{
|
||||
String result;
|
||||
for(u32 i = 0; i < q.length(); i++)
|
||||
{
|
||||
char c = q[i];
|
||||
if(isalnum(c) || c == '~' || c == '.' || c == '_' || c == '-')
|
||||
result.append(1, c);
|
||||
else
|
||||
{
|
||||
result.append(1, '%');
|
||||
result.append(to_hex(c));
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap parse_query(String q)
|
||||
{
|
||||
StringMap result;
|
||||
if(q.length() == 0)
|
||||
return(result);
|
||||
|
||||
bool is_key = true;
|
||||
String key = "";
|
||||
String value = "";
|
||||
for (char &c: q)
|
||||
{
|
||||
if(c == '=')
|
||||
{
|
||||
is_key = !is_key;
|
||||
}
|
||||
else if(c == '&')
|
||||
{
|
||||
result[uri_decode(key)] = uri_decode(value);
|
||||
key = "";
|
||||
value = "";
|
||||
is_key = true;
|
||||
}
|
||||
else if(is_key)
|
||||
{
|
||||
key.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.append(1, c);
|
||||
}
|
||||
}
|
||||
|
||||
result[uri_decode(key)] = uri_decode(value);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String encode_query(StringMap map)
|
||||
{
|
||||
String result;
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if(result.length() > 0)
|
||||
result.append(1, '&');
|
||||
result.append(uri_encode(it->first) + "=" + uri_encode(it->second));
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files)
|
||||
{
|
||||
StringMap result;
|
||||
|
||||
auto i = boundary.length();
|
||||
while(i < q.length())
|
||||
{
|
||||
auto end_pos = q.find(boundary, i);
|
||||
if(end_pos != String::npos)
|
||||
{
|
||||
String field = q.substr(i, end_pos - i);
|
||||
nibble(":", field);
|
||||
String ftype = trim(nibble(";", field));
|
||||
if(ftype == "form-data")
|
||||
{
|
||||
nibble("=\"", field);
|
||||
String field_name = nibble("\"", field);
|
||||
result[field_name] = field.substr(4, field.length()-6);
|
||||
}
|
||||
else if(ftype == "attachment")
|
||||
{
|
||||
nibble("=\"", field);
|
||||
UploadedFile f;
|
||||
f.tmp_name = context->server->config.TMP_UPLOAD_PATH + std::to_string(rand());
|
||||
f.file_name = nibble("\"", field);
|
||||
String bin = field.substr(4, field.length()-6);
|
||||
f.size = bin.length();
|
||||
file_put_contents(f.tmp_name, bin);
|
||||
uploaded_files.push_back(f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're done
|
||||
end_pos = q.length();
|
||||
}
|
||||
i = end_pos + boundary.length();
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
URI parse_uri(String uri_String)
|
||||
{
|
||||
URI result;
|
||||
|
||||
u8 state = 0;
|
||||
String current = "";
|
||||
char expect = 0;
|
||||
|
||||
result.parts["raw"] = uri_String;
|
||||
|
||||
String part_names[] = {
|
||||
"scheme",
|
||||
"host",
|
||||
"port",
|
||||
"path",
|
||||
"query",
|
||||
"fragment",
|
||||
};
|
||||
|
||||
if(uri_String[0] == '/')
|
||||
state = 3;
|
||||
|
||||
for (char &c: uri_String)
|
||||
{
|
||||
bool append_it = true;
|
||||
|
||||
if(expect && expect != c)
|
||||
{
|
||||
result.parts["error"] = String("\'") + c + String("\' expected");
|
||||
result.parts["error_parsing"] = current;
|
||||
result.parts["error_part"] = part_names[state];
|
||||
return(result);
|
||||
}
|
||||
expect = 0;
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case(0): // scheme
|
||||
if(c == ':')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
case(1): // host name
|
||||
if(c == '/')
|
||||
{
|
||||
if(current == "")
|
||||
{
|
||||
append_it = false;
|
||||
break;
|
||||
}
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 3;
|
||||
expect = '/';
|
||||
}
|
||||
else if(c == ':')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 2;
|
||||
}
|
||||
break;
|
||||
case(2): // port
|
||||
if(c == '/')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 3;
|
||||
expect = '/';
|
||||
}
|
||||
break;
|
||||
case(3): // path
|
||||
if(c == '/' && current == "")
|
||||
{
|
||||
append_it = false;
|
||||
break;
|
||||
}
|
||||
if(c == '?')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 4;
|
||||
}
|
||||
break;
|
||||
case(4): // query
|
||||
if(c == '#')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(append_it)
|
||||
current.append(1, c);
|
||||
}
|
||||
|
||||
result.parts[part_names[state]] = current;
|
||||
|
||||
result.query = parse_query(result.parts["query"]);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void set_cookie(
|
||||
String name, String value,
|
||||
u64 expires, String path, String domain,
|
||||
bool secure, bool http_only)
|
||||
{
|
||||
String cookie = "Set-Cookie: ";
|
||||
cookie.append(uri_encode(name) + "=" + uri_encode(value));
|
||||
if(expires > 0)
|
||||
cookie.append(String("; Expires=") + gmdate("RFC1123", expires));
|
||||
context->set_cookies.push_back(cookie);
|
||||
context->cookies[name] = value;
|
||||
}
|
||||
|
||||
StringMap parse_cookies(String cookie_String)
|
||||
{
|
||||
StringMap result;
|
||||
while(cookie_String.length() > 0)
|
||||
{
|
||||
String key = trim(nibble("=", cookie_String));
|
||||
String value = nibble(";", cookie_String);
|
||||
result[key] = value;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String make_session_id()
|
||||
{
|
||||
return(to_hex(rand())+to_hex(rand())+to_hex(rand())+to_hex(rand()));
|
||||
}
|
||||
|
||||
StringMap load_session_data(String session_id)
|
||||
{
|
||||
return(parse_query(file_get_contents(context->server->config.SESSION_PATH + "/" + session_id)));
|
||||
}
|
||||
|
||||
void save_session_data(String session_id, StringMap data)
|
||||
{
|
||||
file_put_contents(context->server->config.SESSION_PATH + "/" + session_id, encode_query(data));
|
||||
}
|
||||
|
||||
String session_start(String session_name)
|
||||
{
|
||||
if(context->cookies[session_name].length() == 0)
|
||||
{
|
||||
set_cookie(session_name, make_session_id(), time() + context->server->config.SESSION_TIME);
|
||||
}
|
||||
context->session_id = context->cookies[session_name];
|
||||
context->session_name = session_name;
|
||||
context->session = load_session_data(context->session_id);
|
||||
return(context->session_id);
|
||||
}
|
||||
|
||||
void session_destroy(String session_name)
|
||||
{
|
||||
if(context->cookies[session_name].length() > 0)
|
||||
{
|
||||
set_cookie(session_name, "", time() - context->server->config.SESSION_TIME);
|
||||
context->session.clear();
|
||||
save_session_data(context->session_id, context->session);
|
||||
context->session_id = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
String var_dump(URI uri, String prefix = "", String postfix = "\n");
|
||||
String uri_decode(String q);
|
||||
String uri_encode(String q);
|
||||
StringMap parse_query(String q);
|
||||
String encode_query(StringMap map);
|
||||
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files);
|
||||
URI parse_uri(String uri_String);
|
||||
void set_cookie(
|
||||
String name, String value = "",
|
||||
u64 expires = 0, String path = "/", String domain = "",
|
||||
bool secure = false, bool http_only = true);
|
||||
StringMap parse_cookies(String cookie_String);
|
||||
String make_session_id();
|
||||
StringMap load_session_data(String session_id);
|
||||
void save_session_data(String session_id, StringMap data);
|
||||
String session_start(String session_name = "uce-session");
|
||||
void session_destroy(String session_name = "uce-session");
|
||||
Reference in New Issue
Block a user