#include #include #include #include #include #include #include "src/tesseswebserverhttputils.hpp" void byte_array_f(std::string& s,FILE* f) { s.push_back('{'); uint8_t buffer[1024]; size_t read = 0; bool first=true; do { read=fread(buffer,1,1024,f); for(size_t i = 0;i> 4) & 0x0F); uint8_t low = (uint8_t)(buffer[i] & 0x0F); s.push_back(HttpUtils::nibble_to_hex_char(high)); s.push_back(HttpUtils::nibble_to_hex_char(low)); first=false; } } while(read > 0); s.push_back('}'); } void byte_array_a(std::string& s,uint8_t* buffer,size_t read) { s.push_back('{'); bool first=true; for(size_t i = 0;i> 4) & 0x0F); uint8_t low = (uint8_t)(buffer[i] & 0x0F); s.push_back(HttpUtils::nibble_to_hex_char(high)); s.push_back(HttpUtils::nibble_to_hex_char(low)); first=false; } s.push_back('}'); } class Resource { public: std::string mime; std::string data; std::string path; bool twss; }; void follow_symlink(std::filesystem::path& p) { while(std::filesystem::is_symlink(p)) { p = std::filesystem::read_symlink(p); } } void get_files(std::vector& resources, std::vector& indexes, bool enableListing, std::filesystem::path srcDir, std::string httpDir) { std::filesystem::path index; bool useIndexFile=false; for(auto item : indexes) { std::filesystem::path myIndex = srcDir / item; follow_symlink(myIndex); if(std::filesystem::is_regular_file(myIndex)) { index = myIndex; useIndexFile=true; break; } } bool mustList=false; std::vector> paths; if(useIndexFile) { Resource res; res.path = httpDir; res.twss = index.extension() == ".twss"; FILE* f = fopen(index.c_str(),"rb"); byte_array_f(res.data,f); fclose(f); res.mime = HttpUtils::MimeType(index); resources.push_back(res); if(!httpDir.ends_with('/')) { res.path = httpDir + "/"; resources.push_back(res); } } else if(enableListing) { mustList=true; } for(auto item : std::filesystem::directory_iterator(srcDir)) { auto ent = item.path(); follow_symlink(ent); if(std::filesystem::is_directory(ent)) { get_files(resources, indexes, enableListing,ent,httpDir.ends_with('/') ? (httpDir + item.path().filename().string()) : (httpDir + "/" + item.path().filename().string())); if(mustList) paths.push_back(std::pair(item.path().filename().string(),true)); } if(std::filesystem::is_regular_file(ent)) { Resource res; res.path = httpDir.ends_with('/') ? (httpDir + item.path().filename().string()) : (httpDir + "/" + item.path().filename().string()); res.twss = item.path().extension() == ".twss"; FILE* f = fopen(ent.c_str(),"rb"); byte_array_f(res.data,f); fclose(f); res.mime = HttpUtils::MimeType(ent); resources.push_back(res); if(mustList) paths.push_back(std::pair(item.path().filename().string(),false)); } } if(mustList) { std::string html = "Index of "; html.append(HttpUtils::HtmlEncode(httpDir.ends_with('/') ? httpDir : (httpDir + "/"))); html.append("

Index of "); html.append(HttpUtils::HtmlEncode(httpDir.ends_with('/') ? httpDir : (httpDir + "/"))); html.append("


../\r\n"); for(auto item : paths) { html.append(""); else html.append("\">"); html.append(HttpUtils::HtmlEncode(item.first)); if(item.second) html.append("/\r\n"); else html.append("\r\n"); } html.append("

"); Resource res; res.path = httpDir; res.twss = false; byte_array_a(res.data,(uint8_t*)&html[0],html.size()); res.mime = "text/html"; resources.push_back(res); if(!httpDir.ends_with('/')) { res.path = httpDir + "/"; resources.push_back(res); } } } bool file_exists(const char* dir, const char* path) { std::filesystem::path p0(dir); std::filesystem::path p1(path); std::filesystem::path p2 = p0 / p1; follow_symlink(p2); return std::filesystem::is_regular_file(p2); } std::string escape_string(std::string path) { std::string s={}; s.push_back('\"'); for(auto item : path) { switch(item) { case '\\': case '\"': case '\'': s.push_back('\\'); s.push_back(item); break; case '\0': s.push_back('\\'); s.push_back('0'); break; case '\t': s.push_back('\\'); s.push_back('t'); break; case '\n': s.push_back('\\'); s.push_back('n'); break; case '\r': s.push_back('\\'); s.push_back('r'); break; case '\v': s.push_back('\\'); s.push_back('v'); break; case '\f': s.push_back('\\'); s.push_back('f'); break; case '\b': s.push_back('\\'); s.push_back('b'); break; case '\a': s.push_back('\\'); s.push_back('a'); break; case '\?': s.push_back('\\'); s.push_back('?'); break; default: { if(item >= ' ' && item <= '~') s.push_back(item); else { s.push_back('\\'); s.push_back('x'); s.push_back(HttpUtils::nibble_to_hex_char((uint8_t)((item >> 4) & 0x0F))); s.push_back(HttpUtils::nibble_to_hex_char((uint8_t)(item & 0x0F))); } } break; } } s.push_back('\"'); return s; } int main(int argc,char** argv) { const char* error = NULL; const char* output = "WWWGen.hpp"; const char* source = "wwwroot"; bool enableScripts=true; bool enableListing=true; bool enableDefaultIndexes=true; bool help=false; std::vector indexes; for(int i = 1;i resources; get_files(resources, indexes, enableListing,source,"/"); std::string cls = "#pragma once\n/* Generated via wwwgen */\n#include \n#include \n#include \n#include \n#include \"tesseswebserver.hpp\"\nclass " + name + "Resource {public: std::vector data; std::string mime; std::string path; bool isScript; " + name + "Resource(std::string path, std::string mime,bool isScript, std::initializer_list data) {this->path = path; this->mime = mime; this->isScript=isScript; this->data = data;} };\nclass " + name + " : public Tesses::WebServer::IServer {\n"; if(enableScripts) { cls.append("#if defined(USE_SCRIPT_ENGINE)\nTesses::WebServer::ScriptEngine::RootEnvironment* env;\nTesses::WebServer::ScriptEngine::BytecodeFile* initFile;\n#endif\n"); cls.append("\nstd::vector* get_script_file(std::filesystem::path p){ int i = get_file(p); if(i > -1) return &(resources[i].data); return nullptr; }\n"); } cls.append("int get_file(std::filesystem::path p){ int index = 0; for(auto item : resources) { if(p.string() == item.path) return index; index++; } return -1; }"); cls.append("std::filesystem::path sanitise(std::string name){std::filesystem::path p=\"/\"; auto path_parts = Tesses::WebServer::HttpUtils::SplitString(name.substr(1),\"/\");for(auto item : path_parts){auto decoded = Tesses::WebServer::HttpUtils::Sanitise(Tesses::WebServer::HttpUtils::UrlPathDecode(item));if(decoded == \"..\" || decoded == \".\") continue; p /= decoded;} return p;}\n"); cls.append("std::vector<"); cls.append(name); cls.append("Resource> resources;\n"); cls.append("public:\n"); cls.append(name); cls.append("(){\n"); for(auto item : resources) { cls.append("resources.push_back("); cls.append(name); cls.append("Resource("); cls.append(escape_string(item.path)); cls.push_back(','); cls.append(escape_string(item.mime)); cls.append(item.twss ? ",true," : ",false,"); cls.append(item.data); cls.append("));\n"); } if(enableScripts) { cls.append("\n#if defined(USE_SCRIPT_ENGINE)\n"); cls.append("env = new Tesses::WebServer::ScriptEngine::RootEnvironment();"); cls.append("env->print = [](Tesses::WebServer::ScriptEngine::ScriptType t)->void {"); cls.append("std::cout << \"[Script]: \" << Tesses::WebServer::ScriptEngine::ConvertToString(t) << std::endl;"); cls.append("};"); if(file_exists(source,"init.twss")) { cls.append("Tesses::WebServer::ScriptEngine::ConstBufferReadFile readFile([this](std::filesystem::path p) -> std::vector* { return get_script_file(p);});"); cls.append("Tesses::WebServer::ScriptEngine::BytecodeCompiler bcc(Tesses::WebServer::ScriptEngine::ScriptParser::Parse(&readFile,\"/init.twss\"));"); cls.append("bcc.Compile();"); cls.append("this->initFile = bcc.file;auto rf = bcc.file->rootFunction;rf->env = env;rf->isRoot=true;for(auto f : bcc.file->functions){auto rf2 = f.second;rf2->env = env;rf2->isRoot=false;env->SetValue(f.first,Tesses::WebServer::ScriptEngine::ObjectType(f.second));}rf->Execute(env,{});"); } cls.append("\n#endif\n"); } cls.append("}"); if(enableScripts) { cls.append("\n#if defined(USE_SCRIPT_ENGINE)\nTesses::WebServer::ScriptEngine::RootEnvironment* GetRootEnvironment(){return this->env;}\n#endif\n"); } cls.append("bool Handle(Tesses::WebServer::ServerContext* ctx){auto path=sanitise(ctx->path); int index = get_file(path); if(index == -1) return false;"); if(enableScripts) { cls.append("\n#if defined(USE_SCRIPT_ENGINE)\n"); cls.append("if(resources[index].isScript) {"); cls.append("auto myEnv = new Tesses::WebServer::ScriptEngine::RootEnvironment();"); cls.append("myEnv->global = this->env;"); cls.append("std::string str={};"); cls.append("myEnv->print = [&str](Tesses::WebServer::ScriptEngine::ScriptType typ) -> void {"); cls.append("str.append(Tesses::WebServer::ScriptEngine::ConvertToString(typ));"); cls.append("};"); cls.append("Tesses::WebServer::ScriptEngine::ConstBufferReadFile readFile([this](std::filesystem::path p) -> std::vector* { return get_script_file(p);});"); cls.append("Tesses::WebServer::ScriptEngine::BytecodeCompiler bcc(Tesses::WebServer::ScriptEngine::ScriptParser::Parse(&readFile,resources[index].path));"); cls.append("bcc.Compile();"); cls.append("auto rf = bcc.file->rootFunction;"); cls.append("rf->env = myEnv;"); cls.append("rf->isRoot=true;"); cls.append("for(auto f : bcc.file->functions)"); cls.append("{"); cls.append("auto rf2 = f.second;"); cls.append("rf2->env = myEnv;"); cls.append("rf2->isRoot=false;"); cls.append("env->SetValue(f.first,Tesses::WebServer::ScriptEngine::ObjectType(f.second));"); cls.append("}"); cls.append("myEnv->RegisterHttpFuncs(ctx);"); cls.append("rf->Execute(myEnv,{});"); cls.append("delete bcc.file;"); cls.append("delete myEnv;"); cls.append("ctx->SetContentType(\"text/html\")->SendText(str);"); cls.append("}else{\n#endif\n"); } cls.append("ctx->SetContentType(resources[index].mime)->SendBytes(resources[index].data.data(),resources[index].data.size());"); if(enableScripts) { cls.append("\n#if defined(USE_SCRIPT_ENGINE)\n}\n#endif\n"); } cls.append("return true;}"); if(enableScripts){ cls.append("~"); cls.append(name); cls.append("(){\n#if defined(USE_SCRIPT_ENGINE)\n"); if(file_exists(source,"deinit.twss")) { cls.append("Tesses::WebServer::ScriptEngine::ConstBufferReadFile readFile([this](std::filesystem::path p) -> std::vector* { return get_script_file(p);});"); cls.append("Tesses::WebServer::ScriptEngine::BytecodeCompiler bcc(Tesses::WebServer::ScriptEngine::ScriptParser::Parse(&readFile,\"/deinit.twss\"));"); cls.append("bcc.Compile();"); cls.append("auto rf = bcc.file->rootFunction;rf->env = env;rf->isRoot=true;for(auto f : bcc.file->functions){auto rf2 = f.second;rf2->env = env;rf2->isRoot=false;env->SetValue(f.first,Tesses::WebServer::ScriptEngine::ObjectType(f.second));}rf->Execute(env,{});"); cls.append("delete bcc.file;"); } cls.append("delete initFile;"); cls.append("delete env;"); cls.append("\n#endif\n}"); } cls.append("};"); std::ofstream strm(output,std::ofstream::binary); strm << cls << "\n"; } return 0; }