58d0f44e03
The function http_send_response() did too much. It not only took the request fields and built them together into a response, it delegated too little and many functions were "hacked" into it, for instance shady directory-changes for vhosts and hand-construction of response structs. The preparations for a rework were already made in previous commits, including a tighter focus on the response-struct itself. Instead of doing everything locally in the http_send_response() function, the new http_prepare_response() only really takes the request-struct and builds a response-struct. The response-struct is expanded such that it's possible to do the data-sending simply with the response-struct itself and not any other magic parameters that just drop out of the function. Another matter are the http_send_status()-calls. Because the aforementioned function is so central, this refactoring has included many areas. Instead of calling http_send_status() in every error-case, which makes little sense now given we first delegate everything through a response struct, errors are just sent as a return value and caught centrally (in serve() in main.c), which centralizes the error handling a bit. It might look a bit strange now and it might not be clear in which direction this is going, but subsequent commits will hopefully give clarity in this regard. Signed-off-by: Laslo Hunhold <dev@frign.de>
137 lines
2.5 KiB
C
137 lines
2.5 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "http.h"
|
|
#include "resp.h"
|
|
#include "util.h"
|
|
|
|
static int
|
|
compareent(const struct dirent **d1, const struct dirent **d2)
|
|
{
|
|
int v;
|
|
|
|
v = ((*d2)->d_type == DT_DIR ? 1 : -1) -
|
|
((*d1)->d_type == DT_DIR ? 1 : -1);
|
|
if (v) {
|
|
return v;
|
|
}
|
|
|
|
return strcmp((*d1)->d_name, (*d2)->d_name);
|
|
}
|
|
|
|
static char *
|
|
suffix(int t)
|
|
{
|
|
switch (t) {
|
|
case DT_FIFO: return "|";
|
|
case DT_DIR: return "/";
|
|
case DT_LNK: return "@";
|
|
case DT_SOCK: return "=";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
enum status
|
|
resp_dir(int fd, const struct response *res)
|
|
{
|
|
enum status ret;
|
|
struct dirent **e;
|
|
size_t i;
|
|
int dirlen;
|
|
char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
|
|
|
|
/* read directory */
|
|
if ((dirlen = scandir(res->path, &e, NULL, compareent)) < 0) {
|
|
return S_FORBIDDEN;
|
|
}
|
|
|
|
/* listing */
|
|
for (i = 0; i < (size_t)dirlen; i++) {
|
|
/* skip hidden files, "." and ".." */
|
|
if (e[i]->d_name[0] == '.') {
|
|
continue;
|
|
}
|
|
|
|
/* entry line */
|
|
html_escape(e[i]->d_name, esc, sizeof(esc));
|
|
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
|
|
esc,
|
|
(e[i]->d_type == DT_DIR) ? "/" : "",
|
|
esc,
|
|
suffix(e[i]->d_type)) < 0) {
|
|
ret = S_REQUEST_TIMEOUT;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* listing footer */
|
|
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
|
|
ret = S_REQUEST_TIMEOUT;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
while (dirlen--) {
|
|
free(e[dirlen]);
|
|
}
|
|
free(e);
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum status
|
|
resp_file(int fd, const struct response *res)
|
|
{
|
|
FILE *fp;
|
|
enum status ret = 0;
|
|
ssize_t bread, bwritten;
|
|
size_t remaining;
|
|
static char buf[BUFSIZ], *p;
|
|
|
|
/* open file */
|
|
if (!(fp = fopen(res->path, "r"))) {
|
|
ret = S_FORBIDDEN;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* seek to lower bound */
|
|
if (fseek(fp, res->file.lower, SEEK_SET)) {
|
|
ret = S_INTERNAL_SERVER_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* write data until upper bound is hit */
|
|
remaining = res->file.upper - res->file.lower + 1;
|
|
|
|
while ((bread = fread(buf, 1, MIN(sizeof(buf),
|
|
remaining), fp))) {
|
|
if (bread < 0) {
|
|
ret = S_INTERNAL_SERVER_ERROR;
|
|
goto cleanup;
|
|
}
|
|
remaining -= bread;
|
|
p = buf;
|
|
while (bread > 0) {
|
|
bwritten = write(fd, p, bread);
|
|
if (bwritten <= 0) {
|
|
ret = S_REQUEST_TIMEOUT;
|
|
goto cleanup;
|
|
}
|
|
bread -= bwritten;
|
|
p += bwritten;
|
|
}
|
|
}
|
|
cleanup:
|
|
if (fp) {
|
|
fclose(fp);
|
|
}
|
|
|
|
return ret;
|
|
}
|