2020-08-25 19:08:23 +00:00
|
|
|
/* See LICENSE file for copyright and license details. */
|
|
|
|
#include <dirent.h>
|
2020-08-29 18:36:10 +00:00
|
|
|
#include <errno.h>
|
2020-08-25 19:08:23 +00:00
|
|
|
#include <fcntl.h>
|
2020-08-30 16:07:23 +00:00
|
|
|
#include <linux/limits.h>
|
2020-08-25 19:08:23 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/sendfile.h>
|
|
|
|
#include <sys/stat.h>
|
2020-08-30 16:07:23 +00:00
|
|
|
#include <time.h>
|
2020-08-25 19:08:23 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2020-08-27 22:40:04 +00:00
|
|
|
#include "config.h"
|
2020-08-25 19:08:23 +00:00
|
|
|
#include "dirl.h"
|
|
|
|
#include "http.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2020-08-27 22:40:04 +00:00
|
|
|
static char*
|
|
|
|
suffix(int t)
|
|
|
|
{
|
2020-08-25 19:08:23 +00:00
|
|
|
switch (t) {
|
2020-08-27 22:40:04 +00:00
|
|
|
case DT_FIFO:
|
|
|
|
return "|";
|
|
|
|
case DT_DIR:
|
|
|
|
return "/";
|
|
|
|
case DT_LNK:
|
|
|
|
return "@";
|
|
|
|
case DT_SOCK:
|
|
|
|
return "=";
|
2020-08-25 19:08:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2020-08-30 16:07:23 +00:00
|
|
|
static void
|
|
|
|
html_escape(const char* src, char* dst, size_t dst_siz)
|
|
|
|
{
|
|
|
|
const struct
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
char* s;
|
|
|
|
} escape[] = {
|
|
|
|
{ '&', "&" }, { '<', "<" }, { '>', ">" },
|
|
|
|
{ '"', """ }, { '\'', "'" },
|
|
|
|
};
|
|
|
|
size_t i, j, k, esclen;
|
|
|
|
|
|
|
|
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
|
|
|
for (k = 0; k < LEN(escape); k++) {
|
|
|
|
if (src[i] == escape[k].c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (k == LEN(escape)) {
|
|
|
|
/* no escape char at src[i] */
|
|
|
|
if (j == dst_siz - 1) {
|
|
|
|
/* silent truncation */
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
dst[j++] = src[i];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* escape char at src[i] */
|
|
|
|
esclen = strlen(escape[k].s);
|
|
|
|
|
|
|
|
if (j >= dst_siz - esclen) {
|
|
|
|
/* silent truncation */
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
memcpy(&dst[j], escape[k].s, esclen);
|
|
|
|
j += esclen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dst[j] = '\0';
|
|
|
|
}
|
|
|
|
|
2020-08-29 18:36:10 +00:00
|
|
|
/* Try to find templates up until root
|
|
|
|
*
|
|
|
|
* Iterates the directory hierarchy upwards. Returns the closest path containing
|
2020-08-30 06:43:10 +00:00
|
|
|
* one of the template files or NULL if none could be found.
|
2020-08-29 18:36:10 +00:00
|
|
|
*
|
|
|
|
* Note that we are chrooted.
|
|
|
|
*/
|
|
|
|
static char*
|
|
|
|
dirl_find_templ_dir(const char* start_path)
|
2020-08-27 22:40:04 +00:00
|
|
|
{
|
2020-08-31 04:42:23 +00:00
|
|
|
char* path_buf = calloc(sizeof(char), strlen(start_path)+1);
|
2020-08-29 18:36:10 +00:00
|
|
|
strcat(path_buf, start_path);
|
|
|
|
|
2020-08-31 04:42:23 +00:00
|
|
|
if (strlen(path_buf) > 1 && path_buf[strlen(path_buf) - 1] == '/') {
|
2020-08-30 16:07:23 +00:00
|
|
|
// don't read last dir twice, except at root
|
2020-08-31 04:42:23 +00:00
|
|
|
path_buf[strlen(path_buf) - 1] = '\0';
|
2020-08-30 07:38:02 +00:00
|
|
|
}
|
|
|
|
|
2020-08-29 18:36:10 +00:00
|
|
|
while (strlen(path_buf) != 0) {
|
|
|
|
DIR* cur = opendir(path_buf);
|
|
|
|
struct dirent* de;
|
|
|
|
errno = 0;
|
|
|
|
while ((de = readdir(cur))) {
|
|
|
|
if (de->d_type == DT_REG) {
|
2020-08-31 04:42:23 +00:00
|
|
|
if (!strcmp(DIRL_HEADER, de->d_name) || !strcmp(DIRL_ENTRY, de->d_name) ||
|
|
|
|
!strcmp(DIRL_FOOTER, de->d_name)) {
|
2020-08-29 18:36:10 +00:00
|
|
|
closedir(cur);
|
|
|
|
return path_buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-30 07:38:02 +00:00
|
|
|
if (strlen(path_buf) > 1) {
|
|
|
|
char* parent = strrchr(path_buf, '/');
|
|
|
|
(*parent) = '\0'; // strip tail from path_buf
|
|
|
|
|
2020-08-31 04:42:23 +00:00
|
|
|
if (strlen(path_buf) == 0) { // we stripped root, let it loop once more
|
2020-08-30 07:38:02 +00:00
|
|
|
path_buf[0] = '/';
|
|
|
|
path_buf[1] = '\0';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
path_buf[0] = '\0'; // we checked root, now terminate loop
|
|
|
|
}
|
2020-08-25 19:08:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-29 18:36:10 +00:00
|
|
|
free(path_buf);
|
|
|
|
return NULL;
|
2020-08-25 19:08:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-30 06:43:10 +00:00
|
|
|
/* Helper function to fill template from base+name if file exists */
|
|
|
|
static void
|
|
|
|
dirl_fill_templ(char** templ, char* base, char* name, char* def)
|
|
|
|
{
|
2020-08-30 07:38:02 +00:00
|
|
|
if (!base || !name) {
|
|
|
|
*templ = def;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-30 06:43:10 +00:00
|
|
|
char* path = calloc(sizeof(char), strlen(base) + strlen(name));
|
|
|
|
strcpy(path, base);
|
|
|
|
strcat(path, name);
|
|
|
|
|
|
|
|
char* file_buf = read_file(path);
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
if (file_buf) {
|
|
|
|
*templ = file_buf;
|
|
|
|
} else {
|
|
|
|
*templ = def;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 18:36:10 +00:00
|
|
|
struct dirl_templ
|
|
|
|
dirl_read_templ(const char* path)
|
2020-08-25 19:08:23 +00:00
|
|
|
{
|
2020-08-30 06:43:10 +00:00
|
|
|
struct dirl_templ templ;
|
2020-08-29 18:36:10 +00:00
|
|
|
|
|
|
|
char* templ_dir = dirl_find_templ_dir(path);
|
|
|
|
|
2020-08-30 06:43:10 +00:00
|
|
|
dirl_fill_templ(&templ.header, templ_dir, DIRL_HEADER, DIRL_HEADER_DEFAULT);
|
|
|
|
dirl_fill_templ(&templ.entry, templ_dir, DIRL_ENTRY, DIRL_ENTRY_DEFAULT);
|
|
|
|
dirl_fill_templ(&templ.footer, templ_dir, DIRL_FOOTER, DIRL_FOOTER_DEFAULT);
|
2020-08-29 18:36:10 +00:00
|
|
|
|
|
|
|
free(templ_dir);
|
|
|
|
|
2020-08-30 06:43:10 +00:00
|
|
|
return templ;
|
2020-08-29 18:36:10 +00:00
|
|
|
}
|
2020-08-25 19:08:23 +00:00
|
|
|
|
2020-08-29 18:36:10 +00:00
|
|
|
enum status
|
|
|
|
dirl_header(int fd, const struct response* res, const struct dirl_templ* templ)
|
|
|
|
{
|
2020-08-25 19:08:23 +00:00
|
|
|
/* Replace placeholder */
|
2020-08-31 04:42:23 +00:00
|
|
|
char* nhead = calloc(sizeof(char), strlen(templ->header) + 1);
|
|
|
|
memcpy(nhead, templ->header, strlen(templ->header) + 1);
|
2020-08-30 07:38:02 +00:00
|
|
|
replace(&nhead, "{uri}", res->uri);
|
2020-08-25 19:08:23 +00:00
|
|
|
|
|
|
|
/* Write header */
|
|
|
|
write(fd, nhead, strlen(nhead));
|
|
|
|
|
|
|
|
free(nhead);
|
|
|
|
|
|
|
|
/* listing header */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum status
|
2020-08-31 04:42:23 +00:00
|
|
|
dirl_entry(int fd,
|
|
|
|
const struct dirent* entry,
|
|
|
|
const struct response* res,
|
|
|
|
const struct dirl_templ* templ)
|
2020-08-27 22:40:04 +00:00
|
|
|
{
|
2020-08-30 16:07:23 +00:00
|
|
|
struct stat stat_buf;
|
2020-08-31 04:42:23 +00:00
|
|
|
char* path_buf = calloc(sizeof(char), strlen(res->path)+strlen(entry->d_name));
|
2020-08-31 16:58:01 +00:00
|
|
|
strcat(path_buf, res->uri);
|
2020-08-31 04:42:23 +00:00
|
|
|
strcat(path_buf, entry->d_name);
|
|
|
|
lstat(path_buf, &stat_buf);
|
2020-08-30 16:07:23 +00:00
|
|
|
|
2020-08-31 04:42:23 +00:00
|
|
|
char* nentry = calloc(sizeof(char), strlen(templ->entry) + 1);
|
|
|
|
memcpy(nentry, templ->entry, strlen(templ->entry) + 1);
|
2020-08-30 16:07:23 +00:00
|
|
|
|
2020-08-27 22:40:04 +00:00
|
|
|
/* Replace placeholder */
|
2020-08-30 16:07:23 +00:00
|
|
|
char esc[PATH_MAX * 6];
|
2020-08-31 04:42:23 +00:00
|
|
|
html_escape(entry->d_name, esc, PATH_MAX * 6);
|
2020-08-30 06:43:10 +00:00
|
|
|
replace(&nentry, "{entry}", entry->d_name);
|
2020-08-30 16:07:23 +00:00
|
|
|
|
2020-08-30 06:43:10 +00:00
|
|
|
replace(&nentry, "{suffix}", suffix(entry->d_type));
|
2020-08-27 22:40:04 +00:00
|
|
|
|
2020-08-30 16:07:23 +00:00
|
|
|
char size_buf[1024];
|
2020-08-31 04:42:23 +00:00
|
|
|
if (entry->d_type == DT_REG) {
|
2020-08-30 16:07:23 +00:00
|
|
|
snprintf(size_buf, 1024, "%ld", stat_buf.st_size);
|
|
|
|
} else {
|
|
|
|
sprintf(size_buf, "-");
|
|
|
|
}
|
|
|
|
replace(&nentry, "{size}", size_buf);
|
|
|
|
|
|
|
|
char time_buf[1024];
|
|
|
|
struct tm tm;
|
|
|
|
gmtime_r(&stat_buf.st_mtim.tv_sec, &tm);
|
|
|
|
strftime(time_buf, 1024, "%F %H:%m", &tm);
|
|
|
|
replace(&nentry, "{modified}", time_buf);
|
|
|
|
|
2020-08-25 19:08:23 +00:00
|
|
|
/* Write entry */
|
2020-08-27 22:40:04 +00:00
|
|
|
write(fd, nentry, strlen(nentry));
|
|
|
|
|
|
|
|
free(nentry);
|
2020-08-25 19:08:23 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum status
|
2020-08-29 18:36:10 +00:00
|
|
|
dirl_footer(int fd, const struct dirl_templ* templ)
|
2020-08-27 22:40:04 +00:00
|
|
|
{
|
|
|
|
/* Replace placeholder */
|
2020-08-31 04:42:23 +00:00
|
|
|
char* nfoot = calloc(sizeof(char), strlen(templ->footer) + 1);
|
|
|
|
memcpy(nfoot, templ->footer, strlen(templ->footer) + 1);
|
2020-08-27 22:40:04 +00:00
|
|
|
|
2020-08-25 19:08:23 +00:00
|
|
|
/* Write footer */
|
2020-08-27 22:40:04 +00:00
|
|
|
write(fd, nfoot, strlen(nfoot));
|
2020-08-25 19:08:23 +00:00
|
|
|
|
2020-08-27 22:40:04 +00:00
|
|
|
free(nfoot);
|
|
|
|
return 0;
|
2020-08-25 19:08:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 22:40:04 +00:00
|
|
|
int
|
|
|
|
dirl_skip(const char* name)
|
|
|
|
{
|
2020-08-29 18:36:10 +00:00
|
|
|
return name[0] == '.' //
|
|
|
|
|| !strcmp(name, DIRL_HEADER) //
|
2020-08-27 22:40:04 +00:00
|
|
|
|| !strcmp(name, DIRL_ENTRY) //
|
|
|
|
|| !strcmp(name, DIRL_FOOTER) //
|
2020-08-29 18:36:10 +00:00
|
|
|
|| !strcmp(name, DIRL_STYLE); //
|
2020-08-25 19:08:23 +00:00
|
|
|
}
|