httpd: fix cgi-bin/index.cgi support, add example of it, stat: fix end-of-line if format is specified (wasn't printing it), fix %z (time) format to match coreutils 6.3
No binary (build failed)
—
(log yok)
| @@ -44,10 +44,16 @@ static char const *file_type(struct stat const *st) | |
| static char const *human_time(time_t t) | |
| { | |
| + | /* Old |
| static char *str; | |
| str = ctime(&t); | |
| str[strlen(str)-1] = '\0'; | |
| return str; | |
| + | */ |
| + | /* coreutils 6.3 compat: */ |
| + | static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")]; |
| + | strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t)); |
| + | return buf; |
| } | |
| /* Return the type of the specified file system. | |
| @@ -311,41 +317,41 @@ static void print_it(char const *masterformat, char const *filename, | |
| /* create a working copy of the format string */ | |
| char *format = xstrdup(masterformat); | |
| - | /* Add 2 to accommodate our conversion of the stat `%s' format string |
| - | * to the printf `%llu' one. */ |
| + | /* Add 2 to accomodate our conversion of the stat '%s' format string |
| + | * to the printf '%llu' one. */ |
| size_t n_alloc = strlen(format) + 2 + 1; | |
| char *dest = xmalloc(n_alloc); | |
| b = format; | |
| while (b) { | |
| + | size_t len; |
| char *p = strchr(b, '%'); | |
| - | if (p != NULL) { |
| - | size_t len; |
| - | *p++ = '\0'; |
| - | fputs(b, stdout); |
| - | |
| - | len = strspn(p, "#-+.I 0123456789"); |
| - | dest[0] = '%'; |
| - | memcpy(dest + 1, p, len); |
| - | dest[1 + len] = 0; |
| - | p += len; |
| - | |
| - | b = p + 1; |
| - | switch (*p) { |
| - | case '\0': |
| - | b = NULL; |
| - | /* fall through */ |
| - | case '%': |
| - | putchar('%'); |
| - | break; |
| - | default: |
| - | print_func(dest, n_alloc, *p, filename, data); |
| - | break; |
| - | } |
| - | |
| - | } else { |
| - | fputs(b, stdout); |
| + | if (!p) { |
| + | /* coreutils 6.3 always print <cr> at the end */ |
| + | /*fputs(b, stdout);*/ |
| + | puts(b); |
| + | break; |
| + | } |
| + | *p++ = '\0'; |
| + | fputs(b, stdout); |
| + | |
| + | len = strspn(p, "#-+.I 0123456789"); |
| + | dest[0] = '%'; |
| + | memcpy(dest + 1, p, len); |
| + | dest[1 + len] = 0; |
| + | p += len; |
| + | |
| + | b = p + 1; |
| + | switch (*p) { |
| + | case '\0': |
| b = NULL; | |
| + | /* fall through */ |
| + | case '%': |
| + | putchar('%'); |
| + | break; |
| + | default: |
| + | print_func(dest, n_alloc, *p, filename, data); |
| + | break; |
| } | |
| } | |
| @@ -372,7 +378,7 @@ static int do_statfs(char const *filename, char const *format) | |
| " ID: %-8i Namelen: %-7l Type: %T\n" | |
| "Block size: %-10s\n" | |
| "Blocks: Total: %-10b Free: %-10f Available: %a\n" | |
| - | "Inodes: Total: %-10c Free: %d\n"); |
| + | "Inodes: Total: %-10c Free: %d"); |
| print_it(format, filename, print_statfs, &statfsbuf); | |
| #else | |
| @@ -420,7 +426,7 @@ static int do_stat(char const *filename, char const *format) | |
| #ifdef CONFIG_FEATURE_STAT_FORMAT | |
| if (format == NULL) { | |
| if (flags & OPT_TERSE) { | |
| - | format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"; |
| + | format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; |
| } else { | |
| if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { | |
| format = | |
| @@ -517,7 +523,7 @@ int stat_main(int argc, char **argv) | |
| int (*statfunc)(char const *, char const *) = do_stat; | |
| flags = getopt32(argc, argv, "ftL" | |
| - | USE_FEATURE_STAT_FORMAT("c:", &format) |
| + | USE_FEATURE_STAT_FORMAT("c:", &format) |
| ); | |
| if (flags & 1) /* -f */ | |
| @@ -1103,7 +1103,7 @@ static int sendCgi(const char *url, | |
| post_readed_size = 0; | |
| post_readed_idx = 0; | |
| - | inFd = fromCgi[0]; |
| + | inFd = fromCgi[0]; |
| outFd = toCgi[1]; | |
| close(fromCgi[1]); | |
| close(toCgi[0]); | |
| @@ -1190,6 +1190,10 @@ static int sendCgi(const char *url, | |
| if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) { | |
| full_write(s, "HTTP/1.0 200 OK\r\n", 17); | |
| } | |
| + | /* Sometimes CGI is writing to pipe in small chunks |
| + | * and we don't see Content-type (because the read |
| + | * is too short) and we emit bogus "text/plain"! |
| + | * Is it a bug or CGI *has to* write it in one piece? */ |
| if (strstr(rbuf, "ontent-") == 0) { | |
| full_write(s, "Content-type: text/plain\r\n\r\n", 28); | |
| } | |
| @@ -1480,6 +1484,7 @@ static void handleIncoming(void) | |
| strcpy(url, buf); | |
| /* extract url args if present */ | |
| test = strchr(url, '?'); | |
| + | config->query = NULL; |
| if (test) { | |
| *test++ = '\0'; | |
| config->query = test; | |
| @@ -1640,20 +1645,26 @@ static void handleIncoming(void) | |
| sendHeaders(HTTP_NOT_IMPLEMENTED); | |
| break; | |
| } | |
| - | if (purl[-1] == '/') { |
| - | if (access("cgi-bin/index.cgi", X_OK) == 0) { |
| - | config->query = url; |
| - | sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type); |
| - | break; |
| - | } |
| - | } |
| #endif /* FEATURE_HTTPD_CGI */ | |
| if (purl[-1] == '/') | |
| strcpy(purl, "index.html"); | |
| if (stat(test, &sb) == 0) { | |
| + | /* It's a dir URL and there is index.html */ |
| config->ContentLength = sb.st_size; | |
| config->last_mod = sb.st_mtime; | |
| } | |
| + | #if ENABLE_FEATURE_HTTPD_CGI |
| + | else if (purl[-1] == '/') { |
| + | /* It's a dir URL and there is no index.html |
| + | * Try cgi-bin/index.cgi */ |
| + | if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { |
| + | purl[0] = '\0'; |
| + | config->query = url; |
| + | sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type); |
| + | break; |
| + | } |
| + | } |
| + | #endif /* FEATURE_HTTPD_CGI */ |
| sendFile(test); | |
| config->ContentLength = -1; | |
| } while (0); | |
| @@ -0,0 +1,55 @@ | |
| + | #!/bin/sh |
| + | # This CGI creates directory index. |
| + | # Put it into cgi-bin/index.cgi and chmod 0755. |
| + | # |
| + | # Problems: |
| + | # * Unsafe wrt weird filenames with <>"'& etc... |
| + | # * Not efficient: calls stat (program, not syscall) for each file |
| + | # * Probably requires bash |
| + | # |
| + | # If you want speed and safety, you need to code it in C |
| + | |
| + | # Must start with '/' |
| + | test "${QUERY_STRING:0:1}" = "/" || exit 1 |
| + | # /../ is not allowed |
| + | test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1 |
| + | test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1 |
| + | |
| + | # Outta cgi-bin... |
| + | cd .. 2>/dev/null || exit 1 |
| + | # Strip leading '/', go to target dir |
| + | cd "${QUERY_STRING:1}" 2>/dev/null || exit 1 |
| + | |
| + | f=`dirname "$QUERY_STRING"` |
| + | test "$f" = "/" && f="" |
| + | |
| + | # Pipe thru dd (need to write header as single write(), |
| + | # or else httpd doesn't see "Content-type: text/html" |
| + | # in first read() and decides that it is not html) |
| + | { |
| + | printf "%s" \ |
| + | $'HTTP/1.0 200 OK\r\n'\ |
| + | $'Content-type: text/html\r\n\r\n'\ |
| + | "<html><head><title>Index of $QUERY_STRING</title></head>"$'\r\n'\ |
| + | "<body><h1>Index of $QUERY_STRING</h1><pre>"$'\r\n'\ |
| + | $'<table width=100%>\r\n'\ |
| + | $'<col><col><col width=0*>\r\n'\ |
| + | $'<tr><th>Name<th align=right>Last modified<th align=right>Size\r\n'\ |
| + | \ |
| + | "<tr><td><a href='$f/'>..</a><td><td>"$'\r\n' |
| + | |
| + | IFS='#' |
| + | for f in *; do |
| + | # Guard against empty dirs... |
| + | test -e "$f" && \ |
| + | stat -c "%F#%s#%z" "$f" | { |
| + | read type size cdt junk |
| + | dir='' |
| + | test "$type" = "directory" && dir='/' |
| + | cdt="${cdt//.*}" # no fractional seconds |
| + | cdt="${cdt// / }" # prevent wrapping around space |
| + | printf "%s" "<tr><td><a href='$f$dir'>$f</a><td align=right>$cdt<td align=right>$size"$'\r\n' |
| + | } |
| + | done |
| + | printf "</table></pre><hr></body></html>"$'\r\n' |
| + | } | dd bs=4k |