24#include <boost/algorithm/string.hpp>
30#include <unordered_map>
36 unordered_map<string, string>
const contentTypeMap = {
38 {
"arc",
"application/x-freearc"},
39 {
"avi",
"video/x-msvideo"},
40 {
"azw",
"application/vnd.amazon.ebook"},
42 {
"bz",
"application/x-bzip"},
43 {
"bz2",
"application/x-bzip2"},
44 {
"csh",
"application/x-csh"},
47 {
"deb",
"application/vnd.debian.binary-package"},
48 {
"doc",
"application/msword"},
49 {
"dot",
"application/msword"},
50 {
"docx",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
51 {
"dotx",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
52 {
"eot",
"application/vnd.ms-fontobject"},
53 {
"epub",
"application/epub+zip"},
54 {
"flv",
"video/x-flv"},
58 {
"gz",
"application/x-gzip"},
60 {
"html",
"text/html"},
61 {
"ico",
"image/vnd.microsoft.icon"},
62 {
"ics",
"text/calendar"},
63 {
"jar",
"application/java-archive"},
64 {
"java",
"text/plain"},
65 {
"jpg",
"image/jpeg"},
66 {
"jpeg",
"image/jpeg"},
67 {
"js",
"text/javascript"},
68 {
"json",
"application/json"},
69 {
"mid",
"audio/x-midi"},
70 {
"midi",
"audio/x-midi"},
71 {
"mjs",
"application/javascript"},
72 {
"mp3",
"audio/mpeg"},
73 {
"mpeg",
"video/mpeg"},
74 {
"mp4",
"application/mp4"},
77 {
"mpkg",
"application/vnd.apple.installer+xml"},
78 {
"odp",
"application/vnd.oasis.opendocument.presentation"},
79 {
"otp",
"application/vnd.oasis.opendocument.presentation"},
80 {
"ods",
"application/vnd.oasis.opendocument.spreadsheet"},
81 {
"ots",
"application/vnd.oasis.opendocument.spreadsheet"},
82 {
"odt",
"application/vnd.oasis.opendocument.text"},
83 {
"ott",
"application/vnd.oasis.opendocument.text"},
84 {
"ogg",
"application/ogg"},
85 {
"ogx",
"application/ogg"},
90 {
"pdf",
"application/pdf"},
91 {
"ppt",
"application/vnd.ms-powerpoint"},
92 {
"pptx",
"application/vnd.openxmlformats-officedocument.presentationml.presentation"},
93 {
"rar",
"application/x-rar-compressed"},
94 {
"rtf",
"application/rtf"},
95 {
"sh",
"application/x-sh"},
96 {
"svg",
"image/svg+xml"},
97 {
"swf",
"application/x-shockwave-flash"},
98 {
"tar",
"application/x-tar"},
99 {
"tif",
"image/tiff"},
100 {
"tiff",
"image/tiff"},
102 {
"txt",
"text/plain"},
103 {
"vsd",
"application/vnd.visio"},
104 {
"wav",
"audio/wav"},
105 {
"weba",
"audio/webm"},
106 {
"webm",
"video/webm"},
107 {
"webp",
"image/webp"},
108 {
"woff",
"font/woff"},
109 {
"woff2",
"font/woff2"},
110 {
"xhtml",
"application/xhtml+xml"},
111 {
"xls",
"application/vnd.ms-excel"},
112 {
"xlt",
"application/vnd.ms-excel"},
113 {
"xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
114 {
"xltx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
115 {
"xml",
"application/xml"},
116 {
"xul",
"application/vnd.mozilla.xul+xml"},
117 {
"xz",
"application/x-xz"},
118 {
"zip",
"application/zip"},
119 {
"3gp",
"video/3gpp"},
120 {
"3g2",
"video/3gpp2"},
121 {
"7z",
"application/x-7z-compressed"}};
129 inline string getDayOfWeek(
int dow) {
165 inline string getMonth(
int mon) {
214 std::function<std::string(std::vector<std::string>
const&)>
const& fmt) {
216 int marks = rgx.mark_count();
218 vector<int> submatchList;
219 for (
int i = -1; i <= marks; ++i) {
220 submatchList.push_back(i);
223 sregex_token_iterator begin(s.begin(), s.end(), rgx, submatchList), end;
228 vector<string> submatchVector;
229 for (
auto it = begin; it != end; ++it) {
230 if (submatch == -1) {
234 submatchVector.push_back(it->str());
235 if (submatch < marks) {
238 out << fmt(submatchVector);
239 submatchVector.clear();
249 rets << hex << setfill(
'0');
251 rets << setw(2) << (int) (
unsigned char) c;
257 transform(s.begin(), s.end(), s.begin(), ::tolower);
262 transform(s.begin(), s.end(), s.begin(), ::toupper);
269 switch (httpStatus) {
271 errorStr =
"Bad Request";
272 explanation =
"The server cannot process your request.";
275 errorStr =
"Unauthorized";
276 explanation =
"The necessary credentials have not been provided.";
279 errorStr =
"Forbidden";
280 explanation =
"You do not have the necessary permissions to view this page.";
283 errorStr =
"Not Found";
284 explanation =
"The requested URL was not found on this server.";
287 errorStr =
"Method Not Allowed";
288 explanation =
"The used request method is not supported for the requested resource.";
291 errorStr =
"Not Applicable";
292 explanation =
"The requested function is unable to produce a resource that satisfies your browser's Accept header.";
295 errorStr =
"Request Timeout";
296 explanation =
"A timeout occurred while waiting for your request.";
299 errorStr =
"Conflict";
300 explanation =
"The request cannot be processed due to a conflict on the underlying resource.";
304 explanation =
"The requested resource is no longer available.";
307 errorStr =
"Unsupported Media Type";
308 explanation =
"Your browser has requested a media type that cannot be provided by this resource.";
311 errorStr =
"I'm a teapot";
312 explanation =
"I cannot brew coffee for you.";
315 errorStr =
"Too Many Requests";
318 errorStr =
"Unavailable For Legal Reasons";
321 errorStr =
"Internal Server Error";
322 explanation =
"The server encountered an internal error and is unable to fulfill your request.";
325 errorStr =
"Not Implemented";
326 explanation =
"The server is not able to fulfill your request.";
329 errorStr =
"Service Unavailable";
330 explanation =
"This service is currently unavailable. Please try again later.";
333 errorStr =
"Unknown Error";
337 ep <<
"<!DOCTYPE html><html><head><title>" << httpStatus <<
' ' << errorStr <<
"</title></head><body><h1>"
338 << errorStr <<
"</h1><p>" << explanation <<
"</p></body></html>";
345 return filename.substr(filename.find_last_of(
'.') + 1);
346 }
catch (out_of_range&) {}
353 if (contentTypeMap.count(ext) == 1) {
354 return contentTypeMap.at(ext);
356 return "application/octet-stream";
360 stringstream httpTime;
362 auto retPtr = gmtime_r(&time, &gmt);
363 if (retPtr ==
nullptr) {
364 throw Exception(__PRETTY_FUNCTION__, 1,
"Interpretation of UNIX timestamp failed.", strerror(errno));
367 httpTime << getDayOfWeek(gmt.tm_wday) << put_time(&gmt,
", %d ") << getMonth(gmt.tm_mon);
368 httpTime << put_time(&gmt,
" %Y %H:%M:%S GMT");
370 return httpTime.str();
375 istringstream timeStream(httpTime);
376 timeStream.exceptions(ifstream::failbit);
378 timeStream >> get_time(&timeStruct,
"%a, %d %b %Y %H:%M:%S GMT");
379 }
catch (ios_base::failure
const& e) {
380 throw Exception(__PRETTY_FUNCTION__, 1,
"Parsing of HTTP timestamp failed.", e.what());
384 time_t unixTime = timegm(&timeStruct);
385 if (unixTime == -1) {
386 throw Exception(__PRETTY_FUNCTION__, 1,
"Conversion of parsed HTTP timestamp to a UNIX timestamp failed.", strerror(errno));
393 stringstream smtpTime;
395 auto retPtr = localtime_r(&time, <ime);
396 if (retPtr ==
nullptr) {
397 throw Exception(__PRETTY_FUNCTION__, 1,
"Interpretation of UNIX timestamp failed.", strerror(errno));
399 smtpTime << getDayOfWeek(ltime.tm_wday) << put_time(<ime,
", %e ") << getMonth(ltime.tm_mon);
400 smtpTime << put_time(<ime,
" %Y %H:%M:%S %z");
402 return smtpTime.str();
406 string smtpTimeM = smtpTime;
414 if (smtpTimeM.length() > 5 && smtpTimeM[5] ==
' ') {
417 istringstream timeStream(smtpTimeM);
418 timeStream.exceptions(ifstream::failbit);
421 timeStream >> get_time(&timeStruct,
"%a, %d %b %Y %H:%M:%S");
422 }
catch (ios_base::failure
const& e) {
423 throw Exception(__PRETTY_FUNCTION__, 1,
"Parsing of SMTP timestamp failed.", e.what());
427 time_t unixTime = timegm(&timeStruct);
428 if (unixTime == -1) {
429 throw Exception(__PRETTY_FUNCTION__, 1,
"Conversion of parsed SMTP timestamp to a UNIX timestamp failed.", strerror(errno));
433 if (smtpTimeM.length() > 30) {
435 long tzAdjust = smtpTimeM[26] ==
'-' ? 1 : -1;
436 long tzH = stol(smtpTimeM.substr(27, 2));
437 long tzM = stol(smtpTimeM.substr(29, 2));
438 unixTime += tzAdjust * (tzH * 3600 + tzM * 60);
439 }
catch (invalid_argument
const& e) {
440 throw Exception(__PRETTY_FUNCTION__, 1,
"Timezone adjustment in parsing of SMTP timestamp failed.", e.what());
450 for (
size_t pos = 0; !str.empty();) {
451 pos = str.find_first_of(delimiter);
452 auto token = str.substr(0, pos);
453 if (!ignoreEmpty || !token.empty()) {
454 ret.push_back(str.substr(0, pos));
456 if (pos < str.length()) {
457 str = str.substr(pos + 1);
469 stringstream stringPath;
470 for (
auto const& e : path) {
471 stringPath <<
'/' << e;
473 return stringPath.str();
478 string rawPath = pathString.substr(0, pathString.find(
'?'));
484 for (
const auto& c : in) {
495 ifstream f(path, ifstream::binary);
499 throw Exception(__PRETTY_FUNCTION__, 1,
"Cannot open file for reading");
503 f.seekg(0, ios::end);
508 string ret(
static_cast<unsigned long>(fs),
'\0');
515 for (
auto const& [key, val] : patterns) {
516 replace(input.begin(), input.end(), key, val);
521std::string
utils::stringReplace(std::string input, std::unordered_map<std::string, std::string>
const& patterns) {
522 for (
auto const& [key, val] : patterns) {
523 for (
size_t pos = input.find(key); pos != string::npos;) {
524 input.replace(pos, key.length(), val);
525 pos = input.find(key, pos + val.length());
533 size_t qmrkPos = queryString.find_first_of(
'?');
534 unordered_multimap<string, string> ret;
535 if (qmrkPos != string::npos && queryString.length() > qmrkPos) {
536 qs = queryString.substr(qmrkPos + 1);
537 }
else if (qmrkPos == string::npos) {
541 for (
auto const& p : pairs) {
542 size_t eqPos = p.find_first_of(
'=');
543 string k = p.substr(0, eqPos);
551 unordered_map<string, string> ret;
553 boost::erase_all(rawHeaders,
"\r");
556 for (
auto const& line : lines) {
557 auto colonPos = line.find_first_of(
':');
558 if (line.length() < colonPos + 2) {
562 auto val = line.substr(colonPos + 1);
563 boost::trim_left(val);
570 unordered_multimap<string, string> ret;
573 for (
auto c : cookies) {
577 auto eqPos = c.find_first_of(
'=');
578 if (c.length() < eqPos + 2) {
581 auto key = c.substr(0, eqPos);
582 auto val = c.substr(eqPos + 1);
583 ret.insert({key, val});
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Namespace containing functions for text encoding and decoding.
std::string urlDecode(std::string input)
std::string toLowercase(std::string s)
std::vector< std::string > splitPath(std::string const &pathString)
std::string mergePath(std::vector< std::string > const &path)
void regexReplaceCallback(std::string &s, std::regex const &rgx, std::function< std::string(std::vector< std::string > const &)> const &fmt)
std::unordered_map< std::string, std::string > parseHeaders(std::string rawHeaders)
std::string hexDump(std::string const &in)
std::string getFileExtension(std::string const &filename)
std::string convertLineEndings(std::string const &in, std::string const &ending)
std::string generateErrorPage(unsigned int httpStatus)
std::string makeHttpTime(time_t time)
time_t readSmtpTime(std::string const &smtpTime)
time_t readHttpTime(std::string const &httpTime)
std::string contentTypeByExtension(std::string extension)
std::string stringReplace(std::string input, std::unordered_map< char, char > const &patterns)
std::unordered_multimap< std::string, std::string > splitQueryString(std::string const &queryString)
std::string getFileContents(std::string const &path)
std::vector< std::string > splitString(std::string str, char delimiter, bool ignoreEmpty=false)
std::string makeSmtpTime(time_t time)
std::string toUppercase(std::string s)
std::unordered_multimap< std::string, std::string > parseCookies(std::string const &rawCookies)
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.