From 329dedc33bca35a19dc5ee4a0754e3f3f920c1b5 Mon Sep 17 00:00:00 2001 From: Armin Friedl Date: Tue, 25 Aug 2020 21:08:23 +0200 Subject: [PATCH] Read dirlist from templates Reads header, footer and entry templates from files defined in config.h. Defaults to original output if not found. --- Makefile | 5 +- config.def.h | 4 ++ dirl.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++ dirl.h | 29 ++++++++ http.c | 14 +--- resp.c | 50 ++++++-------- util.c | 36 ++++++++++ util.h | 1 + 8 files changed, 280 insertions(+), 44 deletions(-) create mode 100644 dirl.c create mode 100644 dirl.h diff --git a/Makefile b/Makefile index cf57f13..f5208ac 100644 --- a/Makefile +++ b/Makefile @@ -4,14 +4,15 @@ include config.mk -COMPONENTS = util sock http resp +COMPONENTS = util sock http resp dirl all: quark util.o: util.c util.h config.mk sock.o: sock.c sock.h util.h config.mk http.o: http.c http.h util.h http.h resp.h config.h config.mk -resp.o: resp.c resp.h util.h http.h config.mk +resp.o: resp.c resp.h util.h http.h dirl.h config.mk +dirl.o: dirl.c dirl.h util.h http.h config.mk main.o: main.c util.h sock.h http.h arg.h config.h config.mk quark: $(COMPONENTS:=.o) $(COMPONENTS:=.h) main.o config.mk diff --git a/config.def.h b/config.def.h index 6d7f690..7197ca8 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,9 @@ #define HEADER_MAX 4096 #define FIELD_MAX 200 +#define DIRL_HEADER "header.qhtml" +#define DIRL_ENTRY "entry.qhtml" +#define DIRL_FOOTER "footer.qhtml" +#define DIRL_STYLE "dirlist.css" /* mime-types */ static const struct { diff --git a/dirl.c b/dirl.c new file mode 100644 index 0000000..d047b4e --- /dev/null +++ b/dirl.c @@ -0,0 +1,185 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirl.h" +#include "http.h" +#include "util.h" +#include "config.h" + +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 +dirl_header_default(int fd, const struct response *res) { + char esc[PATH_MAX]; + html_escape(res->uri, esc, sizeof(esc)); + if (dprintf(fd, + "\n\n\t" + "Index of %s\n" + "\t\n" + "\t\t

Index of %s

\n" + "\t\t\t..", + esc, esc) < 0) { + return S_REQUEST_TIMEOUT; + } + + return 0; +} + +enum status +dirl_header(int fd, const struct response *res) +{ + + /* No header file defined, default */ +#ifndef DIRL_HEADER + return dirl_header_default(fd, res); +#endif + + /* Try find header in root (note that we are chroot'ed) */ + int header_fd; + if ( (header_fd = open(("/" DIRL_HEADER), O_RDONLY)) < 0 ) { + return dirl_header_default(fd, res); + } + + /* Get size of header */ + struct stat header_stat; + if (fstat(header_fd, &header_stat) < 0) { + return dirl_header_default(fd, res); + } + + /* Allocate space for file */ + char *header = (char *) malloc(sizeof(char) * header_stat.st_size); + if (header == NULL) { + return dirl_header_default(fd, res); + } + + /* Read header into string for template replacement */ + if ( (read(header_fd, header, header_stat.st_size)) < 0) { + free(header); + return dirl_header_default(fd, res); + } + + /* Replace placeholder */ + char *nhead = replace(header, "{idx}", "something"); + + /* Write header */ + write(fd, nhead, strlen(nhead)); + + free(header); + free(nhead); + + /* listing header */ + return 0; +} + +static enum status +dirl_entry_default(int fd, const struct dirent* entry) { + char esc[PATH_MAX]; + + html_escape(entry->d_name, esc, sizeof(esc)); + if (dprintf(fd, "
\n\t\t%s%s", esc, + (entry->d_type == DT_DIR) ? "/" : "", esc, + suffix(entry->d_type)) < 0) { + return S_REQUEST_TIMEOUT; + } + + return 0; +} + +enum status +dirl_entry(int fd, const struct dirent* entry) { + /* Try find entry in root (note that we are chroot'ed) */ + int entry_fd; + if ((entry_fd = open(("/" DIRL_ENTRY), O_RDONLY)) < 0) { + return dirl_entry_default(fd, entry); + } + + /* Get size of entry*/ + struct stat entry_stat; + if (fstat(entry_fd, &entry_stat) < 0) { + return dirl_entry_default(fd, entry); + } + + /* Write entry */ + if (sendfile(fd, entry_fd, NULL, entry_stat.st_size) < 0) { + return dirl_entry_default(fd, entry); + } + + return 0; +} + +static enum status +dirl_footer_default(int fd) { + if (dprintf(fd, "\n\t\n\n") < 0) { + return S_REQUEST_TIMEOUT; + } + + return 0; +} + +enum status +dirl_footer(int fd) { + /* Try find footer in root (note that we are chroot'ed) */ + int footer_fd; + if ((footer_fd = open(("/" DIRL_FOOTER), O_RDONLY)) < 0) { + return dirl_footer_default(fd); + } + + /* Get size of footer */ + struct stat footer_stat; + if (fstat(footer_fd, &footer_stat) < 0) { + return dirl_footer_default(fd); + } + + /* Write footer */ + if (sendfile(fd, footer_fd, NULL, footer_stat.st_size) < 0) { + return dirl_footer_default(fd); + } + + return 0; +} + +int dirl_skip(const char *name) { + (void) name; +#ifdef DIRL_HEADER + if(!strcmp(name, DIRL_HEADER)) { + return 1; + } +#endif +#ifdef DIRL_ENTRY + if (!strcmp(name, DIRL_ENTRY)) { + return 1; + } +#endif +#ifdef DIRL_FOOTER + if (!strcmp(name, DIRL_FOOTER)) { + return 1; + } +#endif +#ifdef DIRL_STYLE + if (!strcmp(name, DIRL_STYLE)) { + return 1; + } +#endif + + return 0; +} diff --git a/dirl.h b/dirl.h new file mode 100644 index 0000000..d8df880 --- /dev/null +++ b/dirl.h @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef DIRL_H +#define DIRL_H + +#include +#include +#include + +#include "http.h" + +struct dirl_env_entry { + char *name; + char *value; +}; + +struct dirl_env { + struct dirl_env_entry *entries; +}; + +/* + * Determine if an dirlist entry should be skipped because it has special + * meaning. Skips header-, entry-, footer templates and dirlist style css. + */ +int dirl_skip(const char*); +enum status dirl_header(int, const struct response*); +enum status dirl_entry(int, const struct dirent*); +enum status dirl_footer(int); + +#endif /* DIRL_H */ diff --git a/http.c b/http.c index b6b1ab7..cfd439c 100644 --- a/http.c +++ b/http.c @@ -61,7 +61,7 @@ const char *res_field_str[] = { enum status http_send_header(int fd, const struct response *res) { - char t[FIELD_MAX], esc[PATH_MAX]; + char t[FIELD_MAX]; size_t i; if (timestamp(t, sizeof(t), time(NULL))) { @@ -89,18 +89,6 @@ http_send_header(int fd, const struct response *res) return S_REQUEST_TIMEOUT; } - /* listing header */ - if (res->type == RESTYPE_DIRLISTING) { - html_escape(res->uri, esc, sizeof(esc)); - if (dprintf(fd, - "\n\n\t" - "Index of %s\n" - "\t\n\t\t..", - esc) < 0) { - return S_REQUEST_TIMEOUT; - } - } - return res->status; } diff --git a/resp.c b/resp.c index b7441dc..2d9b948 100644 --- a/resp.c +++ b/resp.c @@ -10,6 +10,7 @@ #include "http.h" #include "resp.h" #include "util.h" +#include "dirl.h" static int compareent(const struct dirent **d1, const struct dirent **d2) @@ -25,19 +26,6 @@ compareent(const struct dirent **d1, const struct dirent **d2) 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) { @@ -52,30 +40,34 @@ resp_dir(int fd, const struct response *res) return S_FORBIDDEN; } - /* listing */ + /* listing header */ + if ((ret = dirl_header(fd, res))) { + return ret; + } + + /* entries */ 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, "
\n\t\t%s%s", - esc, - (e[i]->d_type == DT_DIR) ? "/" : "", - esc, - suffix(e[i]->d_type)) < 0) { - ret = S_REQUEST_TIMEOUT; - goto cleanup; - } + /* skip dirl special files */ + if(dirl_skip(e[i]->d_name)) { + continue; + } + + /* entry line */ + if ((ret = dirl_entry(fd, e[i]))) { + goto cleanup; + } + } - /* listing footer */ - if (dprintf(fd, "\n\t\n\n") < 0) { - ret = S_REQUEST_TIMEOUT; - goto cleanup; - } + /* listing footer */ + if ((ret = dirl_footer(fd))) { + goto cleanup; + } cleanup: while (dirlen--) { diff --git a/util.c b/util.c index 2b54df1..487b94a 100644 --- a/util.c +++ b/util.c @@ -168,6 +168,42 @@ html_escape(const char *src, char *dst, size_t dst_siz) dst[j] = '\0'; } +char* +replace(const char *src, const char *old, const char* new) { + int old_len = strlen(old); + int new_len = strlen(new); + int src_len = strlen(src); + + /* Count needed replacements */ + const char* tmp = src; + int replc=0; + while ((tmp=strstr(tmp, old))) { + replc++; + tmp += old_len; + } + + /* Allocate enough space for the new string */ + char *buf = malloc( (sizeof(char)*src_len) + (sizeof(char)*replc*(new_len-old_len)) + 1); + + /* Now start replacing */ + const char *srcidx = src; + const char *srcidx_old = src; + char *bufidx = buf; + + while (replc--) { + srcidx_old = strstr(srcidx, old); + + bufidx = strncpy(bufidx, srcidx, srcidx_old - srcidx) + (srcidx_old-srcidx); + bufidx = strcpy(bufidx, new) + new_len; + + srcidx = srcidx_old+old_len; + } + + strcpy(bufidx, srcidx); // copy tail + + return buf; +} + #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 diff --git a/util.h b/util.h index 6b6d17d..70179ac 100644 --- a/util.h +++ b/util.h @@ -53,6 +53,7 @@ int timestamp(char *, size_t, time_t); int esnprintf(char *, size_t, const char *, ...); int prepend(char *, size_t, const char *); void html_escape(const char *, char *, size_t); +char *replace(const char *, const char *, const char *); void *reallocarray(void *, size_t, size_t); long long strtonum(const char *, long long, long long, const char **);