summaryrefslogtreecommitdiff
path: root/usr.sbin/httpd
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2024-01-04 18:17:48 +0000
committerMarc Espie <espie@cvs.openbsd.org>2024-01-04 18:17:48 +0000
commite039a1a895b3ab4875a974d8c177129af76afc9a (patch)
tree60235628bd936ff04f4365475dea638dc08a3324 /usr.sbin/httpd
parentc4f461bfe11cbda1a4713204ee04e1b8e2ab41a0 (diff)
make auto-index better
- make it an actual table - use "human readable sizes" for the file sizes - add some decoration and javascript to be able to sort it per-column (client side) (this means some extra column attribute) - add glue to facilitate embedding js + css directly in the program - add some graphical indication for directories - should still validate as proper html everywhere (custom properties need to be called data-* for this!) Work with claudio@ and tb@, many thanks to claudio@ for some of the finer points of css handling, and tb@ for some fine spaces fixes. I've tried it with lynx as well, shows up correctly. One big plus is that the size of columns work as utf-8, so you can expose filenames without any problems (I've tried it with non-js text navigators as well as firefox, chromium and friends) And it looks slightly less yahoo ca. 1995. It's still "one size fits all". If people object to the current look, adding httpd.conf(5) properties to override the default css should be easy. okay claudio@, tb@
Diffstat (limited to 'usr.sbin/httpd')
-rw-r--r--usr.sbin/httpd/Makefile13
-rw-r--r--usr.sbin/httpd/css.h.in34
-rw-r--r--usr.sbin/httpd/js.h.in18
-rw-r--r--usr.sbin/httpd/server_file.c53
-rw-r--r--usr.sbin/httpd/toheader.sed10
5 files changed, 102 insertions, 26 deletions
diff --git a/usr.sbin/httpd/Makefile b/usr.sbin/httpd/Makefile
index 376667567d5..4ddf61521c0 100644
--- a/usr.sbin/httpd/Makefile
+++ b/usr.sbin/httpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.30 2017/07/03 22:21:47 espie Exp $
+# $OpenBSD: Makefile,v 1.31 2024/01/04 18:17:47 espie Exp $
PROG= httpd
SRCS= parse.y
@@ -12,11 +12,20 @@ MAN+= patterns.7
LDADD= -levent -ltls -lssl -lcrypto -lutil
DPADD= ${LIBEVENT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
#DEBUG= -g -DDEBUG=3 -O0
-CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wall -I${.CURDIR} -I${.OBJDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith
CFLAGS+= -Wsign-compare -Wcast-qual
YFLAGS=
+.for h in css.h js.h
+$h: $h.in
+ sed -f ${.CURDIR}/toheader.sed <${.CURDIR}/$h.in >$@.tmp && mv $@.tmp $@
+.endfor
+
+server_file.o: css.h js.h
+
+CLEANFILES += css.h js.h
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/httpd/css.h.in b/usr.sbin/httpd/css.h.in
new file mode 100644
index 00000000000..96ec89f2fee
--- /dev/null
+++ b/usr.sbin/httpd/css.h.in
@@ -0,0 +1,34 @@
+static const char *css =
+body {
+ background-color: white;
+ color: black;
+ font-family: sans-serif;
+}
+table {
+ border-collapse: collapse;
+ border: 1px solid;
+}
+tr.sort th {
+ border-bottom: 1px solid;
+ font-weight: normal;
+ text-decoration: underline;
+ cursor: pointer;
+}
+tr.sort th.sorted { font-weight: bold; }
+tr.sort th::after { content: "\a0\2195"; }
+tr.dir td:nth-child(2n+1) {
+ font-weight: bold;
+ font-style: italic;
+}
+td, th { padding: 2pt 2em; }
+td:first-child, th:first-child { padding-left: 5pt; }
+td:last-child, th:last-child { padding-right: 5pt; }
+td:nth-child(n+2) { text-align: end; }
+thead { text-align: left; }
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #1E1F21;
+ color: #EEEFF1;
+ }
+ a { color: #BAD7FF; }
+}
diff --git a/usr.sbin/httpd/js.h.in b/usr.sbin/httpd/js.h.in
new file mode 100644
index 00000000000..8d0ea22af36
--- /dev/null
+++ b/usr.sbin/httpd/js.h.in
@@ -0,0 +1,18 @@
+static const char *js =
+const rowValue = (tr, idx) => tr.children[idx].getAttribute('data-o') || tr.children[idx].innerText || tr.children[idx].textContent;
+
+const compare = (idx, asc) => (a, b) => ((v1, v2) =>
+ v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2)
+ )(rowValue(asc ? a : b, idx), rowValue(asc ? b : a, idx));
+
+// set up the listener
+document.querySelectorAll('tr.sort th').forEach(th => th.addEventListener('click', (() => {
+ const table = th.closest('table');
+ // make the sorted column bold
+ table.querySelectorAll('tr.sort th').forEach(th2 =>
+ th2.className = th2 == th ? 'sorted' : 'unsorted');
+ const body = table.querySelector('tbody');
+ Array.from(body.querySelectorAll('tr'))
+ .sort(compare(Array.from(th.parentNode.children).indexOf(th), this.asc = !this.asc))
+ .forEach(tr => body.appendChild(tr) );
+})))
diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c
index 582ff9c8ed5..61d9da73709 100644
--- a/usr.sbin/httpd/server_file.c
+++ b/usr.sbin/httpd/server_file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_file.c,v 1.76 2023/12/28 18:05:32 espie Exp $ */
+/* $OpenBSD: server_file.c,v 1.77 2024/01/04 18:17:47 espie Exp $ */
/*
* Copyright (c) 2006 - 2017 Reyk Floeter <reyk@openbsd.org>
@@ -30,9 +30,12 @@
#include <dirent.h>
#include <time.h>
#include <event.h>
+#include <util.h>
#include "httpd.h"
#include "http.h"
+#include "css.h"
+#include "js.h"
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
@@ -486,15 +489,16 @@ server_file_index(struct httpd *env, struct client *clt)
struct http_descriptor *desc = clt->clt_descreq;
struct server_config *srv_conf = clt->clt_srv_conf;
struct dirent **namelist, *dp;
- int namesize, i, ret, fd = -1, namewidth, skip;
+ int namesize, i, ret, fd = -1, skip;
int code = 500;
struct evbuffer *evb = NULL;
struct media_type *media;
- const char *stripped, *style;
+ const char *stripped;
char *escapeduri, *escapedhtml, *escapedpath;
struct tm tm;
struct stat st;
time_t t, dir_mtime;
+ char human_size[FMT_SCALED_STRSIZE];
if ((ret = server_file_method(clt)) != 0) {
code = ret;
@@ -522,26 +526,22 @@ server_file_index(struct httpd *env, struct client *clt)
if ((escapedpath = escape_html(desc->http_path)) == NULL)
goto abort;
- /* A CSS stylesheet allows minimal customization by the user */
- style = "body { background-color: white; color: black; font-family: "
- "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n"
- "@media (prefers-color-scheme: dark) {\n"
- "body { background-color: #1E1F21; color: #EEEFF1; }\n"
- "a { color: #BAD7FF; }\n}";
-
/* Generate simple HTML index document */
if (evbuffer_add_printf(evb,
"<!DOCTYPE html>\n"
- "<html>\n"
+ "<html lang=\"en\">\n"
"<head>\n"
"<meta charset=\"utf-8\">\n"
"<title>Index of %s</title>\n"
- "<style type=\"text/css\"><!--\n%s\n--></style>\n"
+ "<style><!--\n%s--></style>\n"
"</head>\n"
"<body>\n"
"<h1>Index of %s</h1>\n"
- "<hr>\n<pre>\n",
- escapedpath, style, escapedpath) == -1) {
+ "<table><thead>\n"
+ "<tr class=\"sort\"><th class=\"sorted\">Name</th>\n"
+ " <th>Date</th><th>Size</th></tr>\n"
+ "</thead><tbody>\n",
+ escapedpath, css, escapedpath) == -1) {
free(escapedpath);
goto abort;
}
@@ -569,7 +569,6 @@ server_file_index(struct httpd *env, struct client *clt)
t = subst.st_mtime;
localtime_r(&t, &tm);
strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
- namewidth = 51 - strlen(dp->d_name);
if ((escapeduri = url_encode(dp->d_name)) == NULL) {
skip = 1;
@@ -584,20 +583,24 @@ server_file_index(struct httpd *env, struct client *clt)
}
if (S_ISDIR(subst.st_mode)) {
- namewidth -= 1; /* trailing slash */
if (evbuffer_add_printf(evb,
- "<a href=\"%s%s/\">%s/</a>%*s%s%20s\n",
+ "<tr class=\"dir\">"
+ "<td><a href=\"%s%s/\">%s/</a></td>\n"
+ " <td data-o=\"%lld\">%s</td><td>%s</td></tr>\n",
strchr(escapeduri, ':') != NULL ? "./" : "",
- escapeduri, escapedhtml,
- MAXIMUM(namewidth, 0), " ", tmstr, "-") == -1)
+ escapeduri, escapedhtml,
+ (long long)t, tmstr, "-") == -1)
skip = 1;
} else if (S_ISREG(subst.st_mode)) {
- if (evbuffer_add_printf(evb,
- "<a href=\"%s%s\">%s</a>%*s%s%20llu\n",
+ if ((fmt_scaled(subst.st_size, human_size) != 0) ||
+ (evbuffer_add_printf(evb,
+ "<tr><td><a href=\"%s%s\">%s</a></td>\n"
+ " <td data-o=\"%lld\">%s</td>"
+ "<td data-o=\"%llu\">%s</td></tr>\n",
strchr(escapeduri, ':') != NULL ? "./" : "",
escapeduri, escapedhtml,
- MAXIMUM(namewidth, 0), " ",
- tmstr, subst.st_size) == -1)
+ (long long)t, tmstr,
+ subst.st_size, human_size) == -1))
skip = 1;
}
free(escapeduri);
@@ -608,7 +611,9 @@ server_file_index(struct httpd *env, struct client *clt)
if (skip ||
evbuffer_add_printf(evb,
- "</pre>\n<hr>\n</body>\n</html>\n") == -1)
+ "</tbody></table>\n<script>\n"
+ "%s\n"
+ "</script>\n</body>\n</html>\n", js) == -1)
goto abort;
close(fd);
diff --git a/usr.sbin/httpd/toheader.sed b/usr.sbin/httpd/toheader.sed
new file mode 100644
index 00000000000..37b1ec0e786
--- /dev/null
+++ b/usr.sbin/httpd/toheader.sed
@@ -0,0 +1,10 @@
+# first line of input is the variable declaration, don't touch that
+2,$ {
+# XXX beware of the order ! we have to quote \ and " before inserting \n"
+ s/\\/\\\\/g
+ s/"/\\"/g
+ s/^/ "/
+ s/$/\\n"/
+}
+# and append a ; at the end !
+$s/$/;/