From 8dc592d3fc51eb3ab7381843b51e1f699e3fb9ad Mon Sep 17 00:00:00 2001 From: espie Date: Thu, 4 Jan 2024 18:17:47 +0000 Subject: [PATCH] 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@ --- usr.sbin/httpd/Makefile | 13 +++++++-- usr.sbin/httpd/css.h.in | 34 +++++++++++++++++++++++ usr.sbin/httpd/js.h.in | 18 ++++++++++++ usr.sbin/httpd/server_file.c | 53 ++++++++++++++++++++---------------- usr.sbin/httpd/toheader.sed | 10 +++++++ 5 files changed, 102 insertions(+), 26 deletions(-) create mode 100644 usr.sbin/httpd/css.h.in create mode 100644 usr.sbin/httpd/js.h.in create mode 100644 usr.sbin/httpd/toheader.sed 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 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 @@ -30,9 +30,12 @@ #include #include #include +#include #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, "\n" - "\n" + "\n" "\n" "\n" "Index of %s\n" - "\n" + "\n" "\n" "\n" "

Index of %s

\n" - "
\n
\n",
-	    escapedpath, style, escapedpath) == -1) {
+	    "\n"
+	    "\n"
+	    "    \n"
+	    "\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,
-			    "%s/%*s%s%20s\n",
+			    ""
+			    "\n"
+			    "    \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,
-			    "%s%*s%s%20llu\n",
+			if ((fmt_scaled(subst.st_size, human_size) != 0) ||
+			   (evbuffer_add_printf(evb,
+			    "\n"
+			    "    "
+			    "\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,
-	    "\n
\n\n\n") == -1) + "
NameDateSize
%s/%s%s
%s%s%s
\n\n\n\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/$/;/ -- 2.20.1