Read dirlist from templates
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
|
|
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
|
||||
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,
|
||||
"<!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;
|
||||
}
|
||||
|
||||
|
|
38
resp.c
38
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,28 +40,32 @@ 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;
|
||||
}
|
||||
|
||||
/* skip dirl special files */
|
||||
if(dirl_skip(e[i]->d_name)) {
|
||||
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;
|
||||
if ((ret = dirl_entry(fd, e[i]))) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* listing footer */
|
||||
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
|
||||
ret = S_REQUEST_TIMEOUT;
|
||||
if ((ret = dirl_footer(fd))) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
36
util.c
36
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
|
||||
|
|
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 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 **);
|
||||
|
|
Loading…
Reference in a new issue