i2pdtunnelwizard/http/httpserver.cpp

289 lines
10 KiB
C++

#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<const char*>(constructor.first.errorString().c_str()), language, query(req)));
res.end();
return;
}
const auto text = configPage( reinterpret_cast<const char*>(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<const char*>(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<crow::CookieParser>(req);
ctx.set_cookie("lang", lang).path("/").max_age(60*60*24).httponly();
return Notepad::stringToLang(lang);
}
auto& ctxCookie = app_.get_context<crow::CookieParser>(req);
const std::string langFromCookie = ctxCookie.get_cookie("lang");
if (langFromCookie.empty())
{
auto& ctxFromHeader = app_.get_context<LanguageHandler>(req);
return ctxFromHeader.lang;
}
return langFromCookie == "en" ? Notepad::Lang::en : Notepad::Lang::ru;
}
std::pair<TunnelConstructor, bool> 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<const char*>(Notepad::WebUi::tagline(lang));
ctx["payload"] = generated;
ctx["go_back"] = reinterpret_cast<const char*>(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 = "<span class=\"code-block-comment\">" + buffer + "</span>";
}
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<const char*>(Notepad::WebUi::tagline(lang));
ctx["text"] = text;
ctx["go_back"] = reinterpret_cast<const char*>(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<Notepad::Lang, std::string> 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<const char*>(Notepad::WebUi::tagline(lang));
ctx["th_option"] = reinterpret_cast<const char*>(Notepad::WebUi::mainThOption(lang));
ctx["th_value"] = reinterpret_cast<const char*>(Notepad::WebUi::mainThInput(lang));
ctx["tunnel_name"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelName(lang));
ctx["tunnel_name_p"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelName(lang));
ctx["tunnel_type"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelType(lang));
ctx["client_tcp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownClientTCP(lang));
ctx["client_udp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownClientUDP(lang));
ctx["server_tcp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerTCP(lang));
ctx["server_udp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerUDP(lang));
ctx["server_http"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerHTTP(lang));
ctx["server_irc"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerIRC(lang));
ctx["socks_proxy"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownSOCKSProxy(lang));
ctx["http_proxy"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownHTTPProxy(lang));
ctx["inbound"] = reinterpret_cast<const char*>(Notepad::WebUi::mainInbound(lang));
ctx["outbound"] = reinterpret_cast<const char*>(Notepad::WebUi::mainOutbound(lang));
ctx["length"] = reinterpret_cast<const char*>(Notepad::WebUi::mainLength(lang));
ctx["quantity"] = reinterpret_cast<const char*>(Notepad::WebUi::mainQuantity(lang));
ctx["variance"] = reinterpret_cast<const char*>(Notepad::WebUi::mainVariance(lang));
ctx["b33"] = reinterpret_cast<const char*>(Notepad::WebUi::mainB33(lang));
ctx["transient"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTransient(lang));
ctx["keepalive"] = reinterpret_cast<const char*>(Notepad::WebUi::mainKeepalive(lang));
ctx["comments"] = reinterpret_cast<const char*>(Notepad::WebUi::mainComments(lang));
ctx["generate"] = reinterpret_cast<const char*>(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];
}