X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/bf3daa15e9e744d9481d9e6d1e250b77d1f7b256..14c84d021d82335255275f1eabf3a856bde4df07:/test/interactive-helper/aptwebserver.cc?ds=sidebyside diff --git a/test/interactive-helper/aptwebserver.cc b/test/interactive-helper/aptwebserver.cc index d25f50624..4dae342dd 100644 --- a/test/interactive-helper/aptwebserver.cc +++ b/test/interactive-helper/aptwebserver.cc @@ -100,8 +100,13 @@ bool sendHead(int const client, int const httpcode, std::list &head 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))); @@ -156,14 +161,28 @@ void sendError(int const client, int const httpcode, std::string const &request, std::string response(""); response.append(httpcodeToStr(httpcode)).append(""); response.append("

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

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

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

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

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

"); + response.append("This error is a result of the request:
");
+   }
+   else
+   {
+      if (error.empty() == false)
+	 response.append("

Success: ").append(error).append("

"); + response.append("The successfully executed operation was requested by:
");
+   }
    response.append(request).append("
"); addDataHeaders(headers, response); sendHead(client, httpcode, headers); if (content == true) sendData(client, response); +} +void sendSuccess(int const client, std::string const &request, + bool content, std::string const &error = "") +{ + sendError(client, 200, request, content, error); } /*}}}*/ void sendRedirect(int const client, int const httpcode, std::string const &uri,/*{{{*/ @@ -319,6 +338,33 @@ bool parseFirstLine(int const client, std::string const &request, /*{{{*/ sendError(client, 500, request, sendContent, "Filename contains an unencoded space"); return false; } + + std::string host = LookupTag(request, "Host", ""); + if (host.empty() == true) + { + // RFC 2616 §14.23 requires Host + sendError(client, 400, request, sendContent, "Host header is required"); + return false; + } + host = "http://" + host; + + // Proxies require absolute uris, so this is a simple proxy-fake option + std::string const absolute = _config->Find("aptwebserver::request::absolute", "uri,path"); + if (strncmp(host.c_str(), filename.c_str(), host.length()) == 0) + { + if (absolute.find("uri") == std::string::npos) + { + sendError(client, 400, request, sendContent, "Request is absoluteURI, but configured to not accept that"); + return false; + } + // strip the host from the request to make it an absolute path + filename.erase(0, host.length()); + } + else if (absolute.find("path") == std::string::npos) + { + sendError(client, 400, request, sendContent, "Request is absolutePath, but configured to not accept that"); + return false; + } filename = DeQuoteString(filename); // this is not a secure server, but at least prevent the obvious … @@ -338,10 +384,54 @@ bool parseFirstLine(int const client, std::string const &request, /*{{{*/ return true; } /*}}}*/ +bool handleOnTheFlyReconfiguration(int const client, std::string const &request, std::vector const &parts)/*{{{*/ +{ + size_t const pcount = parts.size(); + if (pcount == 4 && parts[1] == "set") + { + _config->Set(parts[2], parts[3]); + sendSuccess(client, request, true, "Option '" + parts[2] + "' was set to '" + parts[3] + "'!"); + return true; + } + else if (pcount == 4 && parts[1] == "find") + { + std::list headers; + std::string response = _config->Find(parts[2], parts[3]); + addDataHeaders(headers, response); + sendHead(client, 200, headers); + sendData(client, response); + return true; + } + else if (pcount == 3 && parts[1] == "find") + { + std::list headers; + if (_config->Exists(parts[2]) == true) + { + std::string response = _config->Find(parts[2]); + addDataHeaders(headers, response); + sendHead(client, 200, headers); + sendData(client, response); + return true; + } + sendError(client, 404, request, "Requested Configuration option doesn't exist."); + return false; + } + else if (pcount == 3 && parts[1] == "clear") + { + _config->Clear(parts[2]); + sendSuccess(client, request, true, "Option '" + parts[2] + "' was cleared."); + return true; + } + + sendError(client, 400, request, true, "Unknown on-the-fly configuration request"); + return false; +} + /*}}}*/ int main(int const argc, const char * argv[]) { CommandLine::Args Args[] = { {0, "port", "aptwebserver::port", CommandLine::HasArg}, + {0, "request-absolute", "aptwebserver::request::absolute", CommandLine::HasArg}, {'c',"config-file",0,CommandLine::ConfigFile}, {'o',"option",0,CommandLine::ArbItem}, {0,0,0,0} @@ -387,11 +477,49 @@ int main(int const argc, const char * argv[]) return 2; } + FileFd pidfile; + if (_config->FindB("aptwebserver::fork", false) == true) + { + std::string const pidfilename = _config->Find("aptwebserver::pidfile", "aptwebserver.pid"); + int const pidfilefd = GetLock(pidfilename); + if (pidfilefd < 0 || pidfile.OpenDescriptor(pidfilefd, FileFd::WriteOnly) == false) + { + _error->Errno("aptwebserver", "Couldn't acquire lock on pidfile '%s'", pidfilename.c_str()); + _error->DumpErrors(std::cerr); + return 3; + } + + pid_t child = fork(); + if (child < 0) + { + _error->Errno("aptwebserver", "Forking failed"); + _error->DumpErrors(std::cerr); + return 4; + } + else if (child != 0) + { + // successfully forked: ready to serve! + std::string pidcontent; + strprintf(pidcontent, "%d", child); + pidfile.Write(pidcontent.c_str(), pidcontent.size()); + if (_error->PendingError() == true) + { + _error->DumpErrors(std::cerr); + return 5; + } + std::cout << "Successfully forked as " << child << std::endl; + return 0; + } + } + std::clog << "Serving ANY file on port: " << port << std::endl; listen(sock, 1); /*}}}*/ + _config->CndSet("aptwebserver::response-header::Server", "APT webserver"); + _config->CndSet("aptwebserver::response-header::Accept-Ranges", "bytes"); + std::vector messages; int client; while ((client = accept(sock, NULL, NULL)) != -1) @@ -412,14 +540,59 @@ int main(int const argc, const char * argv[]) if (parseFirstLine(client, *m, filename, sendContent, closeConnection) == false) continue; - std::string host = LookupTag(*m, "Host", ""); - if (host.empty() == true) + // special webserver command request + if (filename.length() > 1 && filename[0] == '_') { - // RFC 2616 §14.23 requires Host - sendError(client, 400, *m, sendContent, "Host header is required"); - continue; + std::vector parts = VectorizeString(filename, '/'); + if (parts[0] == "_config") + { + handleOnTheFlyReconfiguration(client, *m, parts); + continue; + } + } + + // string replacements in the requested filename + ::Configuration::Item const *Replaces = _config->Tree("aptwebserver::redirect::replace"); + if (Replaces != NULL) + { + std::string redirect = "/" + filename; + for (::Configuration::Item *I = Replaces->Child; I != NULL; I = I->Next) + redirect = SubstVar(redirect, I->Tag, I->Value); + redirect.erase(0,1); + if (redirect != filename) + { + sendRedirect(client, 301, redirect, *m, sendContent); + continue; + } } + ::Configuration::Item const *Overwrite = _config->Tree("aptwebserver::overwrite"); + if (Overwrite != NULL) + { + for (::Configuration::Item *I = Overwrite->Child; I != NULL; I = I->Next) + { + regex_t *pattern = new regex_t; + int const res = regcomp(pattern, I->Tag.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB); + if (res != 0) + { + char error[300]; + regerror(res, pattern, error, sizeof(error)); + sendError(client, 500, *m, sendContent, error); + continue; + } + if (regexec(pattern, filename.c_str(), 0, 0, 0) == 0) + { + filename = _config->Find("aptwebserver::overwrite::" + I->Tag + "::filename", filename); + if (filename[0] == '/') + filename.erase(0,1); + regfree(pattern); + break; + } + regfree(pattern); + } + } + + // deal with the request if (RealFileExists(filename) == true) { FileFd data(filename, FileFd::ReadOnly); @@ -435,6 +608,60 @@ int main(int const argc, const char * argv[]) } } + if (_config->FindB("aptwebserver::support::range", true) == true) + condition = LookupTag(*m, "Range", ""); + else + condition.clear(); + if (condition.empty() == false && strncmp(condition.c_str(), "bytes=", 6) == 0) + { + time_t cache; + std::string ifrange; + if (_config->FindB("aptwebserver::support::if-range", true) == true) + ifrange = LookupTag(*m, "If-Range", ""); + bool validrange = (ifrange.empty() == true || + (RFC1123StrToTime(ifrange.c_str(), cache) == true && + cache <= data.ModificationTime())); + + // FIXME: support multiple byte-ranges (APT clients do not do this) + if (condition.find(',') == std::string::npos) + { + size_t start = 6; + unsigned long long filestart = strtoull(condition.c_str() + start, NULL, 10); + // FIXME: no support for last-byte-pos being not the end of the file (APT clients do not do this) + size_t dash = condition.find('-') + 1; + unsigned long long fileend = strtoull(condition.c_str() + dash, NULL, 10); + unsigned long long filesize = data.FileSize(); + if ((fileend == 0 || (fileend == filesize && fileend >= filestart)) && + validrange == true) + { + if (filesize > filestart) + { + data.Skip(filestart); + std::ostringstream contentlength; + contentlength << "Content-Length: " << (filesize - filestart); + headers.push_back(contentlength.str()); + std::ostringstream contentrange; + contentrange << "Content-Range: bytes " << filestart << "-" + << filesize - 1 << "/" << filesize; + headers.push_back(contentrange.str()); + sendHead(client, 206, headers); + if (sendContent == true) + sendFile(client, data); + continue; + } + else + { + headers.push_back("Content-Length: 0"); + std::ostringstream contentrange; + contentrange << "Content-Range: bytes */" << filesize; + headers.push_back(contentrange.str()); + sendHead(client, 416, headers); + continue; + } + } + } + } + addFileHeaders(headers, data); sendHead(client, 200, headers); if (sendContent == true) @@ -460,5 +687,7 @@ int main(int const argc, const char * argv[]) << " on socket " << sock << std::endl; close(client); } + pidfile.Close(); + return 0; }