X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/ad2d0cef574ff0143ee27cdbd058cba179368dbd..08fcf9628806af202e555bd02b3611e4e9a3d757:/test/interactive-helper/aptwebserver.cc diff --git a/test/interactive-helper/aptwebserver.cc b/test/interactive-helper/aptwebserver.cc index ff60d64a3..3e91406ab 100644 --- a/test/interactive-helper/aptwebserver.cc +++ b/test/interactive-helper/aptwebserver.cc @@ -1,124 +1,144 @@ #include -#include -#include -#include #include #include -#include - -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include -#include -#include -#include #include -#include -#include -#include +#include -char const * const httpcodeToStr(int const httpcode) { /*{{{*/ - switch (httpcode) { +#include +#include +#include +#include +#include +#include + +static std::string httpcodeToStr(int const httpcode) /*{{{*/ +{ + switch (httpcode) + { // Informational 1xx - case 100: return "100 Continue"; - case 101: return "101 Switching Protocols"; + case 100: return _config->Find("aptwebserver::httpcode::100", "100 Continue"); + case 101: return _config->Find("aptwebserver::httpcode::101", "101 Switching Protocols"); // Successful 2xx - case 200: return "200 OK"; - case 201: return "201 Created"; - case 202: return "202 Accepted"; - case 203: return "203 Non-Authoritative Information"; - case 204: return "204 No Content"; - case 205: return "205 Reset Content"; - case 206: return "206 Partial Content"; + case 200: return _config->Find("aptwebserver::httpcode::200", "200 OK"); + case 201: return _config->Find("aptwebserver::httpcode::201", "201 Created"); + case 202: return _config->Find("aptwebserver::httpcode::202", "202 Accepted"); + case 203: return _config->Find("aptwebserver::httpcode::203", "203 Non-Authoritative Information"); + case 204: return _config->Find("aptwebserver::httpcode::204", "204 No Content"); + case 205: return _config->Find("aptwebserver::httpcode::205", "205 Reset Content"); + case 206: return _config->Find("aptwebserver::httpcode::206", "206 Partial Content"); // Redirections 3xx - case 300: return "300 Multiple Choices"; - case 301: return "301 Moved Permanently"; - case 302: return "302 Found"; - case 303: return "303 See Other"; - case 304: return "304 Not Modified"; - case 305: return "304 Use Proxy"; - case 307: return "307 Temporary Redirect"; + case 300: return _config->Find("aptwebserver::httpcode::300", "300 Multiple Choices"); + case 301: return _config->Find("aptwebserver::httpcode::301", "301 Moved Permanently"); + case 302: return _config->Find("aptwebserver::httpcode::302", "302 Found"); + case 303: return _config->Find("aptwebserver::httpcode::303", "303 See Other"); + case 304: return _config->Find("aptwebserver::httpcode::304", "304 Not Modified"); + case 305: return _config->Find("aptwebserver::httpcode::305", "305 Use Proxy"); + case 307: return _config->Find("aptwebserver::httpcode::307", "307 Temporary Redirect"); // Client errors 4xx - case 400: return "400 Bad Request"; - case 401: return "401 Unauthorized"; - case 402: return "402 Payment Required"; - case 403: return "403 Forbidden"; - case 404: return "404 Not Found"; - case 405: return "405 Method Not Allowed"; - case 406: return "406 Not Acceptable"; - case 407: return "407 Proxy Authentication Required"; - case 408: return "408 Request Time-out"; - case 409: return "409 Conflict"; - case 410: return "410 Gone"; - case 411: return "411 Length Required"; - case 412: return "412 Precondition Failed"; - case 413: return "413 Request Entity Too Large"; - case 414: return "414 Request-URI Too Large"; - case 415: return "415 Unsupported Media Type"; - case 416: return "416 Requested range not satisfiable"; - case 417: return "417 Expectation Failed"; + case 400: return _config->Find("aptwebserver::httpcode::400", "400 Bad Request"); + case 401: return _config->Find("aptwebserver::httpcode::401", "401 Unauthorized"); + case 402: return _config->Find("aptwebserver::httpcode::402", "402 Payment Required"); + case 403: return _config->Find("aptwebserver::httpcode::403", "403 Forbidden"); + case 404: return _config->Find("aptwebserver::httpcode::404", "404 Not Found"); + case 405: return _config->Find("aptwebserver::httpcode::405", "405 Method Not Allowed"); + case 406: return _config->Find("aptwebserver::httpcode::406", "406 Not Acceptable"); + case 407: return _config->Find("aptwebserver::httpcode::407", "407 Proxy Authentication Required"); + case 408: return _config->Find("aptwebserver::httpcode::408", "408 Request Time-out"); + case 409: return _config->Find("aptwebserver::httpcode::409", "409 Conflict"); + case 410: return _config->Find("aptwebserver::httpcode::410", "410 Gone"); + case 411: return _config->Find("aptwebserver::httpcode::411", "411 Length Required"); + case 412: return _config->Find("aptwebserver::httpcode::412", "412 Precondition Failed"); + case 413: return _config->Find("aptwebserver::httpcode::413", "413 Request Entity Too Large"); + case 414: return _config->Find("aptwebserver::httpcode::414", "414 Request-URI Too Large"); + case 415: return _config->Find("aptwebserver::httpcode::415", "415 Unsupported Media Type"); + case 416: return _config->Find("aptwebserver::httpcode::416", "416 Requested range not satisfiable"); + case 417: return _config->Find("aptwebserver::httpcode::417", "417 Expectation Failed"); + case 418: return _config->Find("aptwebserver::httpcode::418", "418 I'm a teapot"); // Server error 5xx - case 500: return "500 Internal Server Error"; - case 501: return "501 Not Implemented"; - case 502: return "502 Bad Gateway"; - case 503: return "503 Service Unavailable"; - case 504: return "504 Gateway Time-out"; - case 505: return "505 HTTP Version not supported"; + case 500: return _config->Find("aptwebserver::httpcode::500", "500 Internal Server Error"); + case 501: return _config->Find("aptwebserver::httpcode::501", "501 Not Implemented"); + case 502: return _config->Find("aptwebserver::httpcode::502", "502 Bad Gateway"); + case 503: return _config->Find("aptwebserver::httpcode::503", "503 Service Unavailable"); + case 504: return _config->Find("aptwebserver::httpcode::504", "504 Gateway Time-out"); + case 505: return _config->Find("aptwebserver::httpcode::505", "505 HTTP Version not supported"); } - return NULL; + return ""; } /*}}}*/ -void addFileHeaders(std::list &headers, FileFd &data) { /*{{{*/ - std::ostringstream contentlength; - contentlength << "Content-Length: " << data.FileSize(); - headers.push_back(contentlength.str()); - - std::string lastmodified("Last-Modified: "); - lastmodified.append(TimeRFC1123(data.ModificationTime())); - headers.push_back(lastmodified); - - std::string const fileext = flExtension(data.Name()); - if (fileext.empty() == false && fileext != data.Name()) { - std::string confcontenttype("aptwebserver::ContentType::"); - confcontenttype.append(fileext); - std::string const contenttype = _config->Find(confcontenttype); - if (contenttype.empty() == false) { - std::string header("Content-Type: "); - header.append(contenttype); - headers.push_back(header); - } +static bool chunkedTransferEncoding(std::list const &headers) { + if (std::find(headers.begin(), headers.end(), "Transfer-Encoding: chunked") != headers.end()) + return true; + if (_config->FindB("aptwebserver::chunked-transfer-encoding", false) == true) + return true; + return false; +} +static void addFileHeaders(std::list &headers, FileFd &data)/*{{{*/ +{ + if (chunkedTransferEncoding(headers) == false) + { + std::ostringstream contentlength; + contentlength << "Content-Length: " << data.FileSize(); + headers.push_back(contentlength.str()); + } + if (_config->FindB("aptwebserver::support::last-modified", true) == true) + { + std::string lastmodified("Last-Modified: "); + lastmodified.append(TimeRFC1123(data.ModificationTime())); + headers.push_back(lastmodified); } } /*}}}*/ -void addDataHeaders(std::list &headers, std::string &data) {/*{{{*/ - std::ostringstream contentlength; - contentlength << "Content-Length: " << data.size(); - headers.push_back(contentlength.str()); +static void addDataHeaders(std::list &headers, std::string &data)/*{{{*/ +{ + if (chunkedTransferEncoding(headers) == false) + { + std::ostringstream contentlength; + contentlength << "Content-Length: " << data.size(); + headers.push_back(contentlength.str()); + } } /*}}}*/ -bool sendHead(int const client, int const httpcode, std::list &headers) { /*{{{*/ +static bool sendHead(int const client, int const httpcode, std::list &headers)/*{{{*/ +{ std::string response("HTTP/1.1 "); response.append(httpcodeToStr(httpcode)); headers.push_front(response); + _config->Set("APTWebserver::Last-Status-Code", httpcode); - headers.push_back("Server: APT webserver"); + std::stringstream buffer; + _config->Dump(buffer, "aptwebserver::response-header", "%t: %v%n", false); + std::vector addheaders = VectorizeString(buffer.str(), '\n'); + for (std::vector::const_iterator h = addheaders.begin(); h != addheaders.end(); ++h) + headers.push_back(*h); std::string date("Date: "); date.append(TimeRFC1123(time(NULL))); headers.push_back(date); - headers.push_back("Accept-Ranges: bytes"); + if (chunkedTransferEncoding(headers) == true) + headers.push_back("Transfer-Encoding: chunked"); - std::clog << ">>> RESPONSE >>>" << std::endl; + std::clog << ">>> RESPONSE to " << client << " >>>" << std::endl; bool Success = true; for (std::list::const_iterator h = headers.begin(); - Success == true && h != headers.end(); ++h) { + Success == true && h != headers.end(); ++h) + { Success &= FileFd::Write(client, h->c_str(), h->size()); if (Success == true) Success &= FileFd::Write(client, "\r\n", 2); @@ -130,66 +150,139 @@ bool sendHead(int const client, int const httpcode, std::list &head return Success; } /*}}}*/ -bool sendFile(int const client, FileFd &data) { /*{{{*/ +static bool sendFile(int const client, std::list const &headers, FileFd &data)/*{{{*/ +{ bool Success = true; + bool const chunked = chunkedTransferEncoding(headers); char buffer[500]; unsigned long long actual = 0; - while ((Success &= data.Read(buffer, sizeof(buffer), &actual)) == true) { + while ((Success &= data.Read(buffer, sizeof(buffer), &actual)) == true) + { if (actual == 0) break; - if (Success == true) + + if (chunked == true) + { + std::string size; + strprintf(size, "%llX\r\n", actual); + Success &= FileFd::Write(client, size.c_str(), size.size()); + Success &= FileFd::Write(client, buffer, actual); + Success &= FileFd::Write(client, "\r\n", strlen("\r\n")); + } + else Success &= FileFd::Write(client, buffer, actual); } - if (Success == true) - Success &= FileFd::Write(client, "\r\n", 2); + if (chunked == true) + { + char const * const finish = "0\r\n\r\n"; + Success &= FileFd::Write(client, finish, strlen(finish)); + } + if (Success == false) + std::cerr << "SENDFILE:" << (chunked ? " CHUNKED" : "") << " READ/WRITE ERROR to " << client << std::endl; return Success; } /*}}}*/ -bool sendData(int const client, std::string const &data) { /*{{{*/ - bool Success = true; - Success &= FileFd::Write(client, data.c_str(), data.size()); - if (Success == true) - Success &= FileFd::Write(client, "\r\n", 2); - return Success; +static bool sendData(int const client, std::list const &headers, std::string const &data)/*{{{*/ +{ + if (chunkedTransferEncoding(headers) == true) + { + unsigned long long const ullsize = data.length(); + std::string size; + strprintf(size, "%llX\r\n", ullsize); + char const * const finish = "\r\n0\r\n\r\n"; + if (FileFd::Write(client, size.c_str(), size.length()) == false || + FileFd::Write(client, data.c_str(), ullsize) == false || + FileFd::Write(client, finish, strlen(finish)) == false) + { + std::cerr << "SENDDATA: CHUNK WRITE ERROR to " << client << std::endl; + return false; + } + } + else if (FileFd::Write(client, data.c_str(), data.size()) == false) + { + std::cerr << "SENDDATA: WRITE ERROR to " << client << std::endl; + return false; + } + return true; } /*}}}*/ -void sendError(int const client, int const httpcode, std::string const &request, bool content, std::string const &error = "") { /*{{{*/ - std::list headers; - std::string response(""); - response.append(httpcodeToStr(httpcode)).append(""); +static void sendError(int const client, int const httpcode, std::string const &request,/*{{{*/ + bool const content, std::string const &error, std::list &headers) +{ + std::string response(""); + response.append(httpcodeToStr(httpcode)).append(""); response.append("

").append(httpcodeToStr(httpcode)).append("

"); + if (httpcode != 200) + response.append("

Error: "); + else + response.append("

Success: "); if (error.empty() == false) - response.append("

Error: ").append(error).append("

"); - response.append("This error is a result of the request:
");
+      response.append(error);
+   else
+      response.append(httpcodeToStr(httpcode));
+   if (httpcode != 200)
+      response.append("

This error is a result of the request:
");
+   else
+      response.append("The successfully executed operation was requested by: 
");
    response.append(request).append("
"); + if (httpcode != 200) + { + if (_config->FindB("aptwebserver::closeOnError", false) == true) + headers.push_back("Connection: close"); + } addDataHeaders(headers, response); sendHead(client, httpcode, headers); if (content == true) - sendData(client, response); + sendData(client, headers, response); +} +static void sendSuccess(int const client, std::string const &request, + bool const content, std::string const &error, std::list &headers) +{ + sendError(client, 200, request, content, error, headers); } /*}}}*/ -void sendRedirect(int const client, int const httpcode, std::string const &uri, std::string const &request, bool content) { /*{{{*/ +static void sendRedirect(int const client, int const httpcode, std::string const &uri,/*{{{*/ + std::string const &request, bool content) +{ std::list headers; - std::string response(""); - response.append(httpcodeToStr(httpcode)).append(""); + std::string response(""); + response.append(httpcodeToStr(httpcode)).append(""); response.append("

").append(httpcodeToStr(httpcode)).append("You should be redirected to ").append(uri).append("

"); response.append("This page is a result of the request:
");
    response.append(request).append("
"); addDataHeaders(headers, response); std::string location("Location: "); - if (strncmp(uri.c_str(), "http://", 7) != 0) - location.append("http://").append(LookupTag(request, "Host")).append("/").append(uri); + if (strncmp(uri.c_str(), "http://", 7) != 0 && strncmp(uri.c_str(), "https://", 8) != 0) + { + std::string const host = LookupTag(request, "Host"); + unsigned int const httpsport = _config->FindI("aptwebserver::port::https", 4433); + std::string hosthttpsport; + strprintf(hosthttpsport, ":%u", httpsport); + if (host.find(hosthttpsport) != std::string::npos) + location.append("https://"); + else + location.append("http://"); + location.append(host).append("/"); + if (strncmp("/home/", uri.c_str(), strlen("/home/")) == 0 && uri.find("/public_html/") != std::string::npos) + { + std::string homeuri = SubstVar(uri, "/home/", "~"); + homeuri = SubstVar(homeuri, "/public_html/", "/"); + location.append(homeuri); + } + else + location.append(uri); + } else location.append(uri); headers.push_back(location); sendHead(client, httpcode, headers); if (content == true) - sendData(client, response); + sendData(client, headers, response); } /*}}}*/ -// sendDirectoryLisiting /*{{{*/ -int filter_hidden_files(const struct dirent *a) { +static int filter_hidden_files(const struct dirent *a) /*{{{*/ +{ if (a->d_name[0] == '.') return 0; #ifdef _DIRENT_HAVE_D_TYPE @@ -202,7 +295,7 @@ int filter_hidden_files(const struct dirent *a) { #endif return 1; } -int grouped_alpha_case_sort(const struct dirent **a, const struct dirent **b) { +static int grouped_alpha_case_sort(const struct dirent **a, const struct dirent **b) { #ifdef _DIRENT_HAVE_D_TYPE if ((*a)->d_type == DT_DIR && (*b)->d_type == DT_DIR); else if ((*a)->d_type == DT_DIR && (*b)->d_type == DT_REG) @@ -225,18 +318,21 @@ int grouped_alpha_case_sort(const struct dirent **a, const struct dirent **b) { } return strcasecmp((*a)->d_name, (*b)->d_name); } -void sendDirectoryListing(int const client, std::string const &dir, std::string const &request, bool content) { - std::list headers; + /*}}}*/ +static void sendDirectoryListing(int const client, std::string const &dir,/*{{{*/ + std::string const &request, bool content, std::list &headers) +{ std::ostringstream listing; struct dirent **namelist; int const counter = scandir(dir.c_str(), &namelist, filter_hidden_files, grouped_alpha_case_sort); - if (counter == -1) { - sendError(client, 500, request, content); + if (counter == -1) + { + sendError(client, 500, request, content, "scandir failed", headers); return; } - listing << "Index of " << dir << "" + listing << "Index of " << dir << "" << "