httpserver.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include "httpserver.h"
  2. #include "../tunnelconstructor.h"
  3. #include "../memorymanagment.h"
  4. #include "crow/mustache.h"
  5. #include "../htmldata.h"
  6. #include "../tunnelconstructor.h"
  7. #include "../versionnumber.h"
  8. HttpServer::HttpServer(const std::string &address, uint16_t port) : address_(address), port_(port)
  9. {
  10. app_.loglevel(crow::LogLevel::Critical);
  11. setup();
  12. }
  13. void HttpServer::run()
  14. {
  15. app_.bindaddr(address_).port(port_).multithreaded().run();
  16. }
  17. void HttpServer::setup()
  18. {
  19. CROW_CATCHALL_ROUTE(app_)
  20. ([](crow::response& res) {
  21. if (res.code == 404)
  22. {
  23. res.body = "The URL does not seem to be correct.";
  24. }
  25. else if (res.code == 405)
  26. {
  27. res.body = "The HTTP method does not seem to be correct.";
  28. }
  29. res.end();
  30. });
  31. CROW_ROUTE(app_, "/generate/html")([&](const crow::request& req, crow::response& res){
  32. res.set_header("Content-Type", "text/html");
  33. const auto language = lang(req);
  34. auto constructor = initTunnelConstructorViaUrlQuery(req, language);
  35. if (constructor.second == false)
  36. {
  37. res.code = 400;
  38. res.write( errorPage(reinterpret_cast<const char*>(constructor.first.errorString().c_str()), language, query(req)));
  39. res.end();
  40. return;
  41. }
  42. const auto text = configPage( reinterpret_cast<const char*>(constructor.first.generate().c_str()) , language, constructor.first.isCommentsEnabled(), query(req));
  43. res.write(text);
  44. res.end();
  45. });
  46. CROW_ROUTE(app_, "/favicon.png")([&](const crow::request& req, crow::response& res){
  47. res.set_header("Content-Type", "image/png");
  48. res.add_header("Content-Length", std::to_string(sizeof(faviconB64)));
  49. res.write(std::string(reinterpret_cast<const char*>(faviconB64), sizeof(faviconB64)));
  50. res.end();
  51. });
  52. CROW_ROUTE(app_, "/")([&](const crow::request& req, crow::response& res){
  53. res.set_header("Content-Type", "text/html");
  54. const auto language = lang(req);
  55. const auto text = mainPage(language);
  56. res.write(text);
  57. res.end();
  58. });
  59. }
  60. Notepad::Lang HttpServer::lang(const crow::request &req)
  61. {
  62. const auto lang = req.url_params.get("lang");
  63. if (lang)
  64. {
  65. auto& ctx = app_.get_context<crow::CookieParser>(req);
  66. ctx.set_cookie("lang", lang).path("/").max_age(60*60*24).httponly();
  67. return Notepad::stringToLang(lang);
  68. }
  69. auto& ctxCookie = app_.get_context<crow::CookieParser>(req);
  70. const std::string langFromCookie = ctxCookie.get_cookie("lang");
  71. if (langFromCookie.empty())
  72. {
  73. auto& ctxFromHeader = app_.get_context<LanguageHandler>(req);
  74. return ctxFromHeader.lang;
  75. }
  76. return langFromCookie == "en" ? Notepad::Lang::en : Notepad::Lang::ru;
  77. }
  78. std::pair<TunnelConstructor, bool> HttpServer::initTunnelConstructorViaUrlQuery(const crow::request &req, Notepad::Lang lang)
  79. {
  80. TunnelConstructor constructor;
  81. constructor.setLang(lang);
  82. bool comments = false;
  83. int8_t inbound_length = 3;
  84. int8_t outbound_length = 3;
  85. int8_t inbound_quantity = 5;
  86. int8_t outbound_quantity = 5;
  87. int8_t inbound_lengthVariance = 0;
  88. int8_t outbound_lengthVariance = 0;
  89. int8_t keepalive = 0;
  90. for (const auto& key: req.url_params.keys())
  91. {
  92. if (key == "name" and not constructor.setName(req.url_params.get("name")))
  93. {
  94. return { constructor, false };
  95. }
  96. else if (key == "type" and not constructor.setTunnelType( TunnelConstructor::stringToTunnelType(req.url_params.get("type")) ))
  97. {
  98. return { constructor, false };
  99. }
  100. else if (key == "inbound.length")
  101. {
  102. try { inbound_length = std::stoi(req.url_params.get("inbound.length")); } catch (...) { }
  103. }
  104. else if (key == "outbound.length")
  105. {
  106. try { outbound_length = std::stoi(req.url_params.get("outbound.length")); } catch (...) { }
  107. }
  108. else if (key == "inbound.quntity")
  109. {
  110. try { inbound_quantity = std::stoi(req.url_params.get("inbound.quntity")); } catch (...) { }
  111. }
  112. else if (key == "outbound.quntity")
  113. {
  114. try { outbound_quantity = std::stoi(req.url_params.get("outbound.quntity")); } catch (...) { }
  115. }
  116. else if (key == "inbound.lengthVariance")
  117. {
  118. try { inbound_lengthVariance = std::stoi(req.url_params.get("inbound.lengthVariance")); } catch (...) { }
  119. }
  120. else if (key == "outbound.lengthVariance")
  121. {
  122. try { outbound_lengthVariance = std::stoi(req.url_params.get("outbound.lengthVariance")); } catch (...) { }
  123. }
  124. else if (key == "blinded" and not constructor.setBlindedLeaseSet(std::string(req.url_params.get("blinded")) == "true"))
  125. {
  126. return { constructor, false };
  127. }
  128. else if (key == "transient" and not constructor.setTransient(std::string(req.url_params.get("transient")) == "true"))
  129. {
  130. return { constructor, false };
  131. }
  132. else if (key == "comments")
  133. {
  134. comments = std::string(req.url_params.get("comments")) == "true";
  135. }
  136. else if (key == "keepalive")
  137. {
  138. try { keepalive = std::stoi(req.url_params.get("keepalive")); } catch (...) { }
  139. }
  140. }
  141. if (not constructor.setComments(comments))
  142. {
  143. return { constructor, false };
  144. }
  145. if (not constructor.setLength(inbound_length, outbound_length))
  146. {
  147. return { constructor, false };
  148. }
  149. if (not constructor.setQuantity(inbound_quantity, outbound_quantity))
  150. {
  151. return { constructor, false };
  152. }
  153. if (not constructor.setLengthVariance(inbound_lengthVariance, outbound_lengthVariance))
  154. {
  155. return { constructor, false };
  156. }
  157. if (not constructor.setKeepAlive(keepalive))
  158. {
  159. return { constructor, false };
  160. }
  161. return { constructor, true };
  162. }
  163. std::string HttpServer::query(const crow::request &req)
  164. {
  165. std::string q;
  166. for (const auto& key: req.url_params.keys())
  167. {
  168. if (key == "lang") continue;
  169. if (q.empty())
  170. {
  171. q = "?";
  172. }
  173. else
  174. {
  175. q += "&";
  176. }
  177. q += key + "=" + req.url_params.get(key);
  178. }
  179. return q;
  180. }
  181. std::string HttpServer::configPage(const std::string& generated, Notepad::Lang lang, bool comments, const std::string& query)
  182. {
  183. crow::mustache::context ctx;
  184. ctx["lang_code"] = Notepad::langToCode(lang);
  185. ctx["tagline"] = reinterpret_cast<const char*>(Notepad::WebUi::tagline(lang));
  186. ctx["payload"] = generated;
  187. ctx["go_back"] = reinterpret_cast<const char*>(Notepad::WebUi::configGoBack(lang));
  188. ctx["url"] = query;
  189. ctx["version"] = VERSION;
  190. static const auto templ = crow::mustache::compile(HTML_CONFIG_PAGE);
  191. std::string pageWithoutCommentsHighlight = templ.render_string(ctx);
  192. if (! comments) return pageWithoutCommentsHighlight;
  193. std::istringstream stream(pageWithoutCommentsHighlight);
  194. std::string buffer;
  195. std::string result;
  196. while (std::getline(stream, buffer))
  197. {
  198. if (!buffer.empty() && buffer[0] == '#')
  199. {
  200. buffer = "<span class=\"code-block-comment\">" + buffer + "</span>";
  201. }
  202. result += buffer + "\n";
  203. }
  204. return result;
  205. }
  206. std::string HttpServer::errorPage(const std::string &text, Notepad::Lang lang, const std::string& query)
  207. {
  208. crow::mustache::context ctx;
  209. ctx["lang_code"] = Notepad::langToCode(lang);
  210. ctx["tagline"] = reinterpret_cast<const char*>(Notepad::WebUi::tagline(lang));
  211. ctx["text"] = text;
  212. ctx["go_back"] = reinterpret_cast<const char*>(Notepad::WebUi::configGoBack(lang));
  213. ctx["url"] = query;
  214. ctx["version"] = VERSION;
  215. static const auto templ = crow::mustache::compile(HTML_ERROR_PAGE);
  216. return templ.render_string(ctx);
  217. }
  218. std::string HttpServer::mainPage(Notepad::Lang lang)
  219. {
  220. static std::map<Notepad::Lang, std::string> map;
  221. auto iter = map.find(lang);
  222. if (iter != map.end())
  223. {
  224. return iter->second;
  225. }
  226. crow::mustache::context ctx;
  227. ctx["lang_code"] = Notepad::langToCode(lang);
  228. ctx["tagline"] = reinterpret_cast<const char*>(Notepad::WebUi::tagline(lang));
  229. ctx["th_option"] = reinterpret_cast<const char*>(Notepad::WebUi::mainThOption(lang));
  230. ctx["th_value"] = reinterpret_cast<const char*>(Notepad::WebUi::mainThInput(lang));
  231. ctx["tunnel_name"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelName(lang));
  232. ctx["tunnel_name_p"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelName(lang));
  233. ctx["tunnel_type"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTunnelType(lang));
  234. ctx["client_tcp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownClientTCP(lang));
  235. ctx["client_udp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownClientUDP(lang));
  236. ctx["server_tcp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerTCP(lang));
  237. ctx["server_udp"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerUDP(lang));
  238. ctx["server_http"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerHTTP(lang));
  239. ctx["server_irc"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownServerIRC(lang));
  240. ctx["socks_proxy"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownSOCKSProxy(lang));
  241. ctx["http_proxy"] = reinterpret_cast<const char*>(Notepad::WebUi::mainDropdownHTTPProxy(lang));
  242. ctx["inbound"] = reinterpret_cast<const char*>(Notepad::WebUi::mainInbound(lang));
  243. ctx["outbound"] = reinterpret_cast<const char*>(Notepad::WebUi::mainOutbound(lang));
  244. ctx["length"] = reinterpret_cast<const char*>(Notepad::WebUi::mainLength(lang));
  245. ctx["quantity"] = reinterpret_cast<const char*>(Notepad::WebUi::mainQuantity(lang));
  246. ctx["variance"] = reinterpret_cast<const char*>(Notepad::WebUi::mainVariance(lang));
  247. ctx["b33"] = reinterpret_cast<const char*>(Notepad::WebUi::mainB33(lang));
  248. ctx["transient"] = reinterpret_cast<const char*>(Notepad::WebUi::mainTransient(lang));
  249. ctx["keepalive"] = reinterpret_cast<const char*>(Notepad::WebUi::mainKeepalive(lang));
  250. ctx["comments"] = reinterpret_cast<const char*>(Notepad::WebUi::mainComments(lang));
  251. ctx["generate"] = reinterpret_cast<const char*>(Notepad::WebUi::mainGenerate(lang));
  252. ctx["version"] = VERSION;
  253. const auto templ = crow::mustache::compile(HTML_MAIN_PAGE);
  254. map[lang] = templ.render_string(ctx);
  255. return map[lang];
  256. }