Reads header, footer and entry templates from files defined in config.h. Defaults to original output if not found.
This commit is contained in:
parent
670d2ed65c
commit
329dedc33b
8 changed files with 280 additions and 44 deletions
5
Makefile
5
Makefile
|
@ -4,14 +4,15 @@
|
||||||
|
|
||||||
include config.mk
|
include config.mk
|
||||||
|
|
||||||
COMPONENTS = util sock http resp
|
COMPONENTS = util sock http resp dirl
|
||||||
|
|
||||||
all: quark
|
all: quark
|
||||||
|
|
||||||
util.o: util.c util.h config.mk
|
util.o: util.c util.h config.mk
|
||||||
sock.o: sock.c sock.h 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
|
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
|
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
|
quark: $(COMPONENTS:=.o) $(COMPONENTS:=.h) main.o config.mk
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#define HEADER_MAX 4096
|
#define HEADER_MAX 4096
|
||||||
#define FIELD_MAX 200
|
#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 */
|
/* mime-types */
|
||||||
static const struct {
|
static const struct {
|
||||||
|
|
185
dirl.c
Normal file
185
dirl.c
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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,
|
||||||
|
"<!DOCTYPE html>\n<html>\n\t<head>"
|
||||||
|
"<title>Index of %s</title></head>\n"
|
||||||
|
"\t<body>\n"
|
||||||
|
"\t\t<h1>Index of %s</h1>\n"
|
||||||
|
"\t\t\t<a href=\"..\">..</a>",
|
||||||
|
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, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>", 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</body>\n</html>\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;
|
||||||
|
}
|
29
dirl.h
Normal file
29
dirl.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#ifndef DIRL_H
|
||||||
|
#define DIRL_H
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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 */
|
14
http.c
14
http.c
|
@ -61,7 +61,7 @@ const char *res_field_str[] = {
|
||||||
enum status
|
enum status
|
||||||
http_send_header(int fd, const struct response *res)
|
http_send_header(int fd, const struct response *res)
|
||||||
{
|
{
|
||||||
char t[FIELD_MAX], esc[PATH_MAX];
|
char t[FIELD_MAX];
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (timestamp(t, sizeof(t), time(NULL))) {
|
if (timestamp(t, sizeof(t), time(NULL))) {
|
||||||
|
@ -89,18 +89,6 @@ http_send_header(int fd, const struct response *res)
|
||||||
return S_REQUEST_TIMEOUT;
|
return S_REQUEST_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* listing header */
|
|
||||||
if (res->type == RESTYPE_DIRLISTING) {
|
|
||||||
html_escape(res->uri, esc, sizeof(esc));
|
|
||||||
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>",
|
|
||||||
esc) < 0) {
|
|
||||||
return S_REQUEST_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res->status;
|
return res->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
resp.c
50
resp.c
|
@ -10,6 +10,7 @@
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "resp.h"
|
#include "resp.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "dirl.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compareent(const struct dirent **d1, const struct dirent **d2)
|
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);
|
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
|
enum status
|
||||||
resp_dir(int fd, const struct response *res)
|
resp_dir(int fd, const struct response *res)
|
||||||
{
|
{
|
||||||
|
@ -52,30 +40,34 @@ resp_dir(int fd, const struct response *res)
|
||||||
return S_FORBIDDEN;
|
return S_FORBIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* listing */
|
/* listing header */
|
||||||
|
if ((ret = dirl_header(fd, res))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* entries */
|
||||||
for (i = 0; i < (size_t)dirlen; i++) {
|
for (i = 0; i < (size_t)dirlen; i++) {
|
||||||
/* skip hidden files, "." and ".." */
|
/* skip hidden files, "." and ".." */
|
||||||
if (e[i]->d_name[0] == '.') {
|
if (e[i]->d_name[0] == '.') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* entry line */
|
/* skip dirl special files */
|
||||||
html_escape(e[i]->d_name, esc, sizeof(esc));
|
if(dirl_skip(e[i]->d_name)) {
|
||||||
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
|
continue;
|
||||||
esc,
|
}
|
||||||
(e[i]->d_type == DT_DIR) ? "/" : "",
|
|
||||||
esc,
|
/* entry line */
|
||||||
suffix(e[i]->d_type)) < 0) {
|
if ((ret = dirl_entry(fd, e[i]))) {
|
||||||
ret = S_REQUEST_TIMEOUT;
|
goto cleanup;
|
||||||
goto cleanup;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* listing footer */
|
/* listing footer */
|
||||||
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
|
if ((ret = dirl_footer(fd))) {
|
||||||
ret = S_REQUEST_TIMEOUT;
|
goto cleanup;
|
||||||
goto cleanup;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
while (dirlen--) {
|
while (dirlen--) {
|
||||||
|
|
36
util.c
36
util.c
|
@ -168,6 +168,42 @@ html_escape(const char *src, char *dst, size_t dst_siz)
|
||||||
dst[j] = '\0';
|
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 INVALID 1
|
||||||
#define TOOSMALL 2
|
#define TOOSMALL 2
|
||||||
#define TOOLARGE 3
|
#define TOOLARGE 3
|
||||||
|
|
1
util.h
1
util.h
|
@ -53,6 +53,7 @@ int timestamp(char *, size_t, time_t);
|
||||||
int esnprintf(char *, size_t, const char *, ...);
|
int esnprintf(char *, size_t, const char *, ...);
|
||||||
int prepend(char *, size_t, const char *);
|
int prepend(char *, size_t, const char *);
|
||||||
void html_escape(const char *, char *, size_t);
|
void html_escape(const char *, char *, size_t);
|
||||||
|
char *replace(const char *, const char *, const char *);
|
||||||
|
|
||||||
void *reallocarray(void *, size_t, size_t);
|
void *reallocarray(void *, size_t, size_t);
|
||||||
long long strtonum(const char *, long long, long long, const char **);
|
long long strtonum(const char *, long long, long long, const char **);
|
||||||
|
|
Loading…
Reference in a new issue