#include "httpserver.h" #include "../tunnelconstructor.h" #include "crow/mustache.h" #include "../htmldata.h" #include "../tunnelconstructor.h" #include "../versionnumber.h" HttpServer::HttpServer(const std::string &address, uint16_t port) : address_(address), port_(port) { app_.loglevel(crow::LogLevel::Critical); setup(); } void HttpServer::run() { app_.bindaddr(address_).port(port_).multithreaded().run(); } void HttpServer::setup() { CROW_CATCHALL_ROUTE(app_) ([](crow::response& res) { res.code = 303; res.set_header("Location", "/"); res.end(); }); CROW_ROUTE(app_, "/generate/html")([&](const crow::request& req, crow::response& res){ res.set_header("Content-Type", "text/html"); const auto language = lang(req); auto constructor = initTunnelConstructorViaUrlQuery(req, language); if (constructor.second == false) { res.code = 400; res.write( errorPage(reinterpret_cast(constructor.first.errorString().c_str()), language, query(req))); res.end(); return; } const auto text = configPage( reinterpret_cast(constructor.first.generate().c_str()) , language, constructor.first.isCommentsEnabled(), query(req)); res.write(text); res.end(); }); CROW_ROUTE(app_, "/favicon.png")([&](const crow::request& req, crow::response& res){ res.set_header("Content-Type", "image/png"); res.add_header("Content-Length", std::to_string(sizeof(faviconB64))); res.write(std::string(reinterpret_cast(faviconB64), sizeof(faviconB64))); res.end(); }); CROW_ROUTE(app_, "/")([&](const crow::request& req, crow::response& res){ res.set_header("Content-Type", "text/html"); const auto language = lang(req); const auto text = mainPage(language); res.write(text); res.end(); }); } Notepad::Lang HttpServer::lang(const crow::request &req) { const auto lang = req.url_params.get("lang"); if (lang) { auto& ctx = app_.get_context(req); ctx.set_cookie("lang", lang).path("/").max_age(60*60*24).httponly(); return Notepad::stringToLang(lang); } auto& ctxCookie = app_.get_context(req); const std::string langFromCookie = ctxCookie.get_cookie("lang"); if (langFromCookie.empty()) { auto& ctxFromHeader = app_.get_context(req); return ctxFromHeader.lang; } return langFromCookie == "en" ? Notepad::Lang::en : Notepad::Lang::ru; } std::pair HttpServer::initTunnelConstructorViaUrlQuery(const crow::request &req, Notepad::Lang lang) { TunnelConstructor constructor; constructor.setLang(lang); bool comments = false; int8_t inbound_length = 3; int8_t outbound_length = 3; int8_t inbound_quantity = 5; int8_t outbound_quantity = 5; int8_t inbound_lengthVariance = 0; int8_t outbound_lengthVariance = 0; int8_t keepalive = 0; for (const auto& key: req.url_params.keys()) { if (key == "name" and not constructor.setName(req.url_params.get("name"))) { return { constructor, false }; } else if (key == "type" and not constructor.setTunnelType( TunnelConstructor::stringToTunnelType(req.url_params.get("type")) )) { return { constructor, false }; } else if (key == "inbound.length") { try { inbound_length = std::stoi(req.url_params.get("inbound.length")); } catch (...) { } } else if (key == "outbound.length") { try { outbound_length = std::stoi(req.url_params.get("outbound.length")); } catch (...) { } } else if (key == "inbound.quntity") { try { inbound_quantity = std::stoi(req.url_params.get("inbound.quntity")); } catch (...) { } } else if (key == "outbound.quntity") { try { outbound_quantity = std::stoi(req.url_params.get("outbound.quntity")); } catch (...) { } } else if (key == "inbound.lengthVariance") { try { inbound_lengthVariance = std::stoi(req.url_params.get("inbound.lengthVariance")); } catch (...) { } } else if (key == "outbound.lengthVariance") { try { outbound_lengthVariance = std::stoi(req.url_params.get("outbound.lengthVariance")); } catch (...) { } } else if (key == "blinded" and not constructor.setBlindedLeaseSet(std::string(req.url_params.get("blinded")) == "true")) { return { constructor, false }; } else if (key == "transient" and not constructor.setTransient(std::string(req.url_params.get("transient")) == "true")) { return { constructor, false }; } else if (key == "comments") { comments = std::string(req.url_params.get("comments")) == "true"; } else if (key == "keepalive") { try { keepalive = std::stoi(req.url_params.get("keepalive")); } catch (...) { } } } if (not constructor.setComments(comments)) { return { constructor, false }; } if (not constructor.setLength(inbound_length, outbound_length)) { return { constructor, false }; } if (not constructor.setQuantity(inbound_quantity, outbound_quantity)) { return { constructor, false }; } if (not constructor.setLengthVariance(inbound_lengthVariance, outbound_lengthVariance)) { return { constructor, false }; } if (not constructor.setKeepAlive(keepalive)) { return { constructor, false }; } return { constructor, true }; } std::string HttpServer::query(const crow::request &req) { std::string q; for (const auto& key: req.url_params.keys()) { if (key == "lang") continue; if (q.empty()) { q = "?"; } else { q += "&"; } q += key + "=" + req.url_params.get(key); } return q; } std::string HttpServer::configPage(const std::string& generated, Notepad::Lang lang, bool comments, const std::string& query) { crow::mustache::context ctx; ctx["lang_code"] = Notepad::langToCode(lang); ctx["tagline"] = reinterpret_cast(Notepad::WebUi::tagline(lang)); ctx["payload"] = generated; ctx["go_back"] = reinterpret_cast(Notepad::WebUi::configGoBack(lang)); ctx["url"] = query; ctx["version"] = VERSION; static const auto templ = crow::mustache::compile(HTML_CONFIG_PAGE); std::string pageWithoutCommentsHighlight = templ.render_string(ctx); if (! comments) return pageWithoutCommentsHighlight; std::istringstream stream(pageWithoutCommentsHighlight); std::string buffer; std::string result; while (std::getline(stream, buffer)) { if (!buffer.empty() && buffer[0] == '#') { buffer = "" + buffer + ""; } result += buffer + "\n"; } return result; } std::string HttpServer::errorPage(const std::string &text, Notepad::Lang lang, const std::string& query) { crow::mustache::context ctx; ctx["lang_code"] = Notepad::langToCode(lang); ctx["tagline"] = reinterpret_cast(Notepad::WebUi::tagline(lang)); ctx["text"] = text; ctx["go_back"] = reinterpret_cast(Notepad::WebUi::configGoBack(lang)); ctx["url"] = query; ctx["version"] = VERSION; static const auto templ = crow::mustache::compile(HTML_ERROR_PAGE); return templ.render_string(ctx); } std::string HttpServer::mainPage(Notepad::Lang lang) { static std::map map; auto iter = map.find(lang); if (iter != map.end()) { return iter->second; } crow::mustache::context ctx; ctx["lang_code"] = Notepad::langToCode(lang); ctx["tagline"] = reinterpret_cast(Notepad::WebUi::tagline(lang)); ctx["th_option"] = reinterpret_cast(Notepad::WebUi::mainThOption(lang)); ctx["th_value"] = reinterpret_cast(Notepad::WebUi::mainThInput(lang)); ctx["tunnel_name"] = reinterpret_cast(Notepad::WebUi::mainTunnelName(lang)); ctx["tunnel_name_p"] = reinterpret_cast(Notepad::WebUi::mainTunnelName(lang)); ctx["tunnel_type"] = reinterpret_cast(Notepad::WebUi::mainTunnelType(lang)); ctx["client_tcp"] = reinterpret_cast(Notepad::WebUi::mainDropdownClientTCP(lang)); ctx["client_udp"] = reinterpret_cast(Notepad::WebUi::mainDropdownClientUDP(lang)); ctx["server_tcp"] = reinterpret_cast(Notepad::WebUi::mainDropdownServerTCP(lang)); ctx["server_udp"] = reinterpret_cast(Notepad::WebUi::mainDropdownServerUDP(lang)); ctx["server_http"] = reinterpret_cast(Notepad::WebUi::mainDropdownServerHTTP(lang)); ctx["server_irc"] = reinterpret_cast(Notepad::WebUi::mainDropdownServerIRC(lang)); ctx["socks_proxy"] = reinterpret_cast(Notepad::WebUi::mainDropdownSOCKSProxy(lang)); ctx["http_proxy"] = reinterpret_cast(Notepad::WebUi::mainDropdownHTTPProxy(lang)); ctx["inbound"] = reinterpret_cast(Notepad::WebUi::mainInbound(lang)); ctx["outbound"] = reinterpret_cast(Notepad::WebUi::mainOutbound(lang)); ctx["length"] = reinterpret_cast(Notepad::WebUi::mainLength(lang)); ctx["quantity"] = reinterpret_cast(Notepad::WebUi::mainQuantity(lang)); ctx["variance"] = reinterpret_cast(Notepad::WebUi::mainVariance(lang)); ctx["b33"] = reinterpret_cast(Notepad::WebUi::mainB33(lang)); ctx["transient"] = reinterpret_cast(Notepad::WebUi::mainTransient(lang)); ctx["keepalive"] = reinterpret_cast(Notepad::WebUi::mainKeepalive(lang)); ctx["comments"] = reinterpret_cast(Notepad::WebUi::mainComments(lang)); ctx["generate"] = reinterpret_cast(Notepad::WebUi::mainGenerate(lang)); ctx["version"] = VERSION; const auto templ = crow::mustache::compile(HTML_MAIN_PAGE); map[lang] = templ.render_string(ctx); return map[lang]; }