diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2024-03-06 12:29:36 +0000 |
---|---|---|
committer | Laurent Bercot <ska@appnovation.com> | 2024-03-06 12:29:36 +0000 |
commit | 4a2abe7bf4c05db91093e4a2d29d27430316bc05 (patch) | |
tree | f2ef6b5b77d4ac0dfef4f317b0a520bba1d4e2e1 /src | |
parent | 628b64629d17fef775838736a17ff43bcef8e471 (diff) | |
download | tipidee-4a2abe7bf4c05db91093e4a2d29d27430316bc05.tar.xz |
Add ls.cgi, prepare for 0.0.4.0
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/tipidee/util.h | 2 | ||||
-rw-r--r-- | src/libtipidee/deps-lib/tipidee | 1 | ||||
-rw-r--r-- | src/libtipidee/tipidee_util_htmlescape.c | 38 | ||||
-rw-r--r-- | src/misc/deps-exe/ls.cgi | 2 | ||||
-rw-r--r-- | src/misc/ls.cgi.c | 154 |
5 files changed, 197 insertions, 0 deletions
diff --git a/src/include/tipidee/util.h b/src/include/tipidee/util.h index 18eb2ce..b3d1151 100644 --- a/src/include/tipidee/util.h +++ b/src/include/tipidee/util.h @@ -34,4 +34,6 @@ extern int tipidee_util_httpdate (char const *, tain *) ; extern int tipidee_util_defaulttext (unsigned int, tipidee_defaulttext *) ; extern int tipidee_util_parse_range (char const *, off_t, uint64_t *, uint64_t *) ; +extern char const *tipidee_util_htmlescape (char const *) ; + #endif diff --git a/src/libtipidee/deps-lib/tipidee b/src/libtipidee/deps-lib/tipidee index 33f9f2f..b0b2c08 100644 --- a/src/libtipidee/deps-lib/tipidee +++ b/src/libtipidee/deps-lib/tipidee @@ -42,6 +42,7 @@ tipidee_rql_read.o tipidee_uri_parse.o tipidee_util_chunked_read.o tipidee_util_defaulttext.o +tipidee_util_htmlescape.o tipidee_util_httpdate.o tipidee_util_parse_range.o -lskarnet diff --git a/src/libtipidee/tipidee_util_htmlescape.c b/src/libtipidee/tipidee_util_htmlescape.c new file mode 100644 index 0000000..1dfd552 --- /dev/null +++ b/src/libtipidee/tipidee_util_htmlescape.c @@ -0,0 +1,38 @@ +/* ISC license. */ + +#include <stdlib.h> + +#include <tipidee/util.h> + +struct htmlescape_s +{ + char c ; + char const *code ; +} ; + +static struct htmlescape_s const table[] = +{ + { .c = '\"', .code = """ }, + { .c = '&', .code = "&" }, + { .c = '\'', .code = "'" }, + { .c = '<', .code = "<" }, + { .c = '>', .code = ">" }, +} ; + +static int htmlescape_cmp (void const *a, void const *b) +{ + char aa = *((char const *)a) ; + char bb = ((struct htmlescape_s const *)b)->c ; + return aa < bb ? -1 : aa > bb ; +} + +char const *tipidee_util_htmlescape (char const *s) +{ + struct htmlescape_s const *p = bsearch( + s, + table, + sizeof(table) / sizeof(struct htmlescape_s), + sizeof(struct htmlescape_s), + &htmlescape_cmp) ; + return p ? p->code : s ; +} diff --git a/src/misc/deps-exe/ls.cgi b/src/misc/deps-exe/ls.cgi new file mode 100644 index 0000000..8ecd888 --- /dev/null +++ b/src/misc/deps-exe/ls.cgi @@ -0,0 +1,2 @@ +libtipidee.a.xyzzy +-lskarnet diff --git a/src/misc/ls.cgi.c b/src/misc/ls.cgi.c new file mode 100644 index 0000000..d72ae10 --- /dev/null +++ b/src/misc/ls.cgi.c @@ -0,0 +1,154 @@ +/* ISC license. */ + +#include <skalibs/bsdsnowflake.h> + +#include <errno.h> +#include <stdlib.h> + +#include <skalibs/gccattributes.h> +#include <skalibs/buffer.h> +#include <skalibs/stat.h> +#include <skalibs/direntry.h> +#include <skalibs/strerr.h> +#include <skalibs/tai.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/unix-transactional.h> + +#include <tipidee/response.h> +#include <tipidee/util.h> + +#define USAGE "ls.cgi is meant to be used as a CGI script invoked by tipideed" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") +#define dieout() strerr_diefu1sys(111, "write to stdout") + +#define HEADER1 "Status: 200\nContent-Type: text/html\n\n\ +<html>\n\ +<head>\n\ + <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\ + <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n\ + <meta http-equiv=\"Content-Language\" content=\"en\" />\n\ + <title>index of directory at " + +#define HEADER2 "</title>\n\ + <meta name=\"Description\" content=\"directory entries list, generated by index.cgi\" />\n\ + <meta name=\"Keywords\" content=\"tipideed index index.cgi CGI directory web server skarnet.org skarnet software\" />\n\ + </head>\n\ +<body>\n\ +<h1> Directory entries for " + +#define HEADER3 " </h1>\n<ul>\n" + +#define FOOTER "</ul>\n</body>\n</html>\n" + +static void print_one_entry (int dfd, char const *path, char const *name) +{ + struct stat st ; + if (stat_at(dfd, name, &st) == -1) strerr_diefu4sys(111, "stat ", path, "/", name) ; + if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return ; + if (buffer_puts(buffer_1, " <li> <a href=\"") == -1 + || buffer_puts(buffer_1, name) == -1 + || (S_ISDIR(st.st_mode) && buffer_put(buffer_1, "/", 1) == -1) + || buffer_puts(buffer_1, "\"> ") == -1 + || buffer_puts(buffer_1, name) == -1 + || (S_ISDIR(st.st_mode) && buffer_put(buffer_1, "/", 1) == -1) + || buffer_puts(buffer_1, " </a>") == -1) dieout() ; + if (!S_ISDIR(st.st_mode)) + { + int iskB = st.st_size >= 1024 ; + char lmdate[128] ; + char fmt[UINT64_FMT] ; + tain t ; + size_t l ; + if (!tain_from_timespec(&t, &st.st_mtim)) + strerr_diefu4sys(111, "convert st_mtim for ", path, "/", name) ; + l = tipidee_response_header_date_fmt(lmdate, 128, &t) ; + if (!l) strerr_diefu4sys(111, "convert st_mtim for ", path, "/", name) ; + lmdate[l++] = 0 ; + fmt[uint64_fmt(fmt, iskB ? st.st_size / 1024 : st.st_size)] = 0 ; + if (buffer_put(buffer_1, " (", 2) == -1 + || buffer_puts(buffer_1, fmt) == -1 + || buffer_put(buffer_1, " ", 1) == -1 + || buffer_puts(buffer_1, iskB ? "kB" : "bytes") == -1 + || buffer_puts(buffer_1, ", last modified ") == -1 + || buffer_put(buffer_1, lmdate, l) == -1 + || buffer_put(buffer_1, ")", 1) == -1) dieout() ; + } + if (buffer_puts(buffer_1, " </li>\n") == -1) dieout() ; +} + +static void html_escape (stralloc *sa, char const *s) +{ + size_t len = strlen(s) ; + char cc[2] = "\0" ; + for (size_t i = 0 ; i < len ; i++) + { + cc[0] = s[i] ; + if (!stralloc_cats(sa, tipidee_util_htmlescape(cc))) dienomem() ; + } + if (!stralloc_0(sa)) dienomem() ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + stralloc sa = STRALLOC_ZERO ; + stralloc tmp = STRALLOC_ZERO ; + size_t dirlen ; + char const *x ; + DIR *dir ; + int dfd ; + PROG = "ls.cgi" ; + + x = getenv("SERVER_PROTOCOL") ; + if (!x) strerr_dienotset(100, "SERVER_PROTOCOL") ; + if (strncmp(x, "HTTP/1.", 7)) + strerr_diefu1x(100, "SERVER_PROTOCOL isn't HTTP") ; + x = getenv("REQUEST_METHOD") ; + if (!x) strerr_dienotset(100, "REQUEST_METHOD") ; + if (strcmp(x, "GET") && strcmp(x, "HEAD")) + strerr_diefu1x(100, "ls.cgi can only be used with the GET or HEAD methods") ; + + x = getenv("SERVER_NAME") ; + if (!x) strerr_dienotset(100, "SERVER_NAME") ; + if (!stralloc_cats(&sa, x)) dienomem() ; + if (!stralloc_catb(&sa, ":", 1)) dienomem() ; + x = getenv("SERVER_PORT") ; + if (!x) strerr_dienotset(100, "SERVER_PORT") ; + if (!stralloc_cats(&sa, x)) dienomem() ; + x = getenv("SCRIPT_NAME") ; + if (!x) strerr_dienotset(100, "SCRIPT_NAME") ; + if (*x != '/') strerr_diefu2x(100, "SCRIPT_NAME", " must be absolute") ; + if (!stralloc_cats(&sa, x)) dienomem() ; + while (sa.s[sa.len - 1] != '/') sa.len-- ; + sa.s[--sa.len] = 0 ; + html_escape(&tmp, sa.s) ; + dirlen = tmp.len - 1 ; + + dir = opendir(sa.s) ; + if (!dir) strerr_diefu2sys(111, "opendir ", sa.s) ; + dfd = dir_fd(dir) ; + + if (buffer_puts(buffer_1, HEADER1) == -1 + || buffer_put(buffer_1, tmp.s, dirlen) == -1 + || buffer_puts(buffer_1, HEADER2) == -1 + || buffer_put(buffer_1, tmp.s, dirlen) == -1 + || buffer_puts(buffer_1, HEADER3) == -1) dieout() ; + + for (;;) + { + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + html_escape(&tmp, d->d_name) ; + print_one_entry(dfd, tmp.s, tmp.s + dirlen + 1) ; + tmp.len = dirlen + 1 ; + } + if (errno) strerr_diefu2sys(111, "readdir ", sa.s) ; + dir_close(dir) ; + + if (buffer_putsflush(buffer_1, FOOTER) == -1) dieout() ; + return 0 ; +} |