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<std::string> addheaders = VectorizeString(buffer.str(), '\n');
+ for (std::vector<std::string>::const_iterator h = addheaders.begin(); h != addheaders.end(); ++h)
+ headers.push_back(*h);
std::string date("Date: ");
date.append(TimeRFC1123(time(NULL)));
{
if (actual == 0)
break;
- if (Success == true)
- Success &= FileFd::Write(client, buffer, actual);
+ Success &= FileFd::Write(client, buffer, actual);
}
- if (Success == true)
- Success &= FileFd::Write(client, "\r\n", 2);
+ if (Success == false)
+ std::cerr << "SENDFILE: 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;
+ 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,/*{{{*/
std::string response("<html><head><title>");
response.append(httpcodeToStr(httpcode)).append("</title></head>");
response.append("<body><h1>").append(httpcodeToStr(httpcode)).append("</h1>");
- if (error.empty() == false)
- response.append("<p><em>Error</em>: ").append(error).append("</p>");
- response.append("This error is a result of the request: <pre>");
+ if (httpcode != 200)
+ {
+ if (error.empty() == false)
+ response.append("<p><em>Error</em>: ").append(error).append("</p>");
+ response.append("This error is a result of the request: <pre>");
+ }
+ else
+ {
+ if (error.empty() == false)
+ response.append("<p><em>Success</em>: ").append(error).append("</p>");
+ response.append("The successfully executed operation was requested by: <pre>");
+ }
response.append(request).append("</pre></body></html>");
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,/*{{{*/
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);
+ {
+ location.append("http://").append(LookupTag(request, "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);
<< "</head>" << std::endl
<< "<body><h1>Index of " << dir << "</h1>" << std::endl
<< "<table><tr><th>#</th><th>Name</th><th>Size</th><th>Last-Modified</th></tr>" << std::endl;
- if (dir != ".")
+ if (dir != "./")
listing << "<tr><td>d</td><td><a href=\"..\">Parent Directory</a></td><td>-</td><td>-</td></tr>";
for (int i = 0; i < counter; ++i) {
struct stat fs;
}
/*}}}*/
bool parseFirstLine(int const client, std::string const &request, /*{{{*/
- std::string &filename, bool &sendContent,
+ std::string &filename, std::string ¶ms, bool &sendContent,
bool &closeConnection)
{
if (strncmp(request.c_str(), "HEAD ", 5) == 0)
sendError(client, 400, request, sendContent, "Request is absolutePath, but configured to not accept that");
return false;
}
+
+ size_t paramspos = filename.find('?');
+ if (paramspos != std::string::npos)
+ {
+ params = filename.substr(paramspos + 1);
+ filename.erase(paramspos);
+ }
+
filename = DeQuoteString(filename);
// this is not a secure server, but at least prevent the obvious …
// nuke the first character which is a / as we assured above
filename.erase(0, 1);
if (filename.empty() == true)
- filename = ".";
+ filename = "./";
+ // support ~user/ uris to refer to /home/user/public_html/ as a kind-of special directory
+ else if (filename[0] == '~')
+ {
+ // /home/user is actually not entirely correct, but good enough for now
+ size_t dashpos = filename.find('/');
+ if (dashpos != std::string::npos)
+ {
+ std::string home = filename.substr(1, filename.find('/') - 1);
+ std::string pubhtml = filename.substr(filename.find('/') + 1);
+ filename = "/home/" + home + "/public_html/" + pubhtml;
+ }
+ else
+ filename = "/home/" + filename.substr(1) + "/public_html/";
+ }
+
+ // if no filename is given, but a valid directory see if we can use an index or
+ // have to resort to a autogenerated directory listing later on
+ if (DirectoryExists(filename) == true)
+ {
+ std::string const directoryIndex = _config->Find("aptwebserver::directoryindex");
+ if (directoryIndex.empty() == false && directoryIndex == flNotDir(directoryIndex) &&
+ RealFileExists(filename + directoryIndex) == true)
+ filename += directoryIndex;
+ }
+
return true;
}
/*}}}*/
+bool handleOnTheFlyReconfiguration(int const client, std::string const &request, std::vector<std::string> 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<std::string> 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<std::string> 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[] = {
listen(sock, 1);
/*}}}*/
+ _config->CndSet("aptwebserver::response-header::Server", "APT webserver");
+ _config->CndSet("aptwebserver::response-header::Accept-Ranges", "bytes");
+ _config->CndSet("aptwebserver::directoryindex", "index.html");
+
std::vector<std::string> messages;
int client;
while ((client = accept(sock, NULL, NULL)) != -1)
if (parseFirstLine(client, *m, filename, sendContent, closeConnection) == false)
continue;
+ // special webserver command request
+ if (filename.length() > 1 && filename[0] == '_')
+ {
+ std::vector<std::string> 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)
}
}
+ 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)
}
else if (DirectoryExists(filename) == true)
{
- if (filename == "." || filename[filename.length()-1] == '/')
+ if (filename[filename.length()-1] == '/')
sendDirectoryListing(client, filename, *m, sendContent);
else
sendRedirect(client, 301, filename.append("/"), *m, sendContent);