suckless-quark/resp.c
Laslo Hunhold 1879e14e79 Be extra pedantic again and remove all warnings
Since now config.def.h has been reduced we don't have any more unused
variables and thus the manual fiddling with error-levels is no longer
necessary.
To get a completely clean result though we have to still cast some
variables here and there.
2018-03-05 00:30:53 +01:00

191 lines
4 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, char *name, struct request *r)
{
struct dirent **e;
size_t i;
int dirlen, s;
static char t[TIMESTAMP_LEN];
/* read directory */
if ((dirlen = scandir(name, &e, NULL, compareent)) < 0) {
return http_send_status(fd, S_FORBIDDEN);
}
/* send header as late as possible */
if (dprintf(fd,
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n",
S_OK, status_str[S_OK], timestamp(time(NULL), t)) < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
if (r->method == M_GET) {
/* listing header */
if (dprintf(fd,
"<!DOCTYPE html>\n<html>\n\t<head>"
"<title>Index of %s</title></head>\n"
"\t<body>\n\t\t<a href=\"..\">..</a>",
name) < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
/* listing */
for (i = 0; i < (size_t)dirlen; i++) {
/* skip hidden files, "." and ".." */
if (e[i]->d_name[0] == '.') {
continue;
}
/* entry line */
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
e[i]->d_name,
(e[i]->d_type == DT_DIR) ? "/" : "",
e[i]->d_name,
suffix(e[i]->d_type)) < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
}
/* listing footer */
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
}
s = S_OK;
cleanup:
while (dirlen--) {
free(e[dirlen]);
}
free(e);
return s;
}
enum status
resp_file(int fd, char *name, struct request *r, struct stat *st, char *mime,
off_t lower, off_t upper)
{
FILE *fp;
enum status s;
ssize_t bread, bwritten;
off_t remaining;
int range;
static char buf[BUFSIZ], *p, t1[TIMESTAMP_LEN], t2[TIMESTAMP_LEN];
/* open file */
if (!(fp = fopen(name, "r"))) {
s = http_send_status(fd, S_FORBIDDEN);
goto cleanup;
}
/* seek to lower bound */
if (fseek(fp, lower, SEEK_SET)) {
s = http_send_status(fd, S_INTERNAL_SERVER_ERROR);
goto cleanup;
}
/* send header as late as possible */
range = r->field[REQ_RANGE][0];
s = range ? S_PARTIAL_CONTENT : S_OK;
if (dprintf(fd,
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Connection: close\r\n"
"Last-Modified: %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %zu\r\n",
s, status_str[s], timestamp(time(NULL), t1),
timestamp(st->st_mtim.tv_sec, t2), mime,
upper - lower + 1) < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
if (range) {
if (dprintf(fd, "Content-Range: bytes %zd-%zd/%zu\r\n",
lower, upper + (upper < 0), st->st_size) < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
}
if (dprintf(fd, "\r\n") < 0) {
s = S_REQUEST_TIMEOUT;
goto cleanup;
}
if (r->method == M_GET) {
/* write data until upper bound is hit */
remaining = upper - lower + 1;
while ((bread = fread(buf, 1, MIN(sizeof(buf),
(size_t)remaining), fp))) {
if (bread < 0) {
return S_INTERNAL_SERVER_ERROR;
}
remaining -= bread;
p = buf;
while (bread > 0) {
bwritten = write(fd, p, bread);
if (bwritten <= 0) {
return S_REQUEST_TIMEOUT;
}
bread -= bwritten;
p += bwritten;
}
}
}
cleanup:
if (fp) {
fclose(fp);
}
return s;
}