From 764299819016e5fb400be2fccf12d8de248e4cef Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Wed, 22 Aug 2007 10:38:44 +0000 Subject: [PATCH] httpd: replace shell-based dir indexer cgi example with C-based one. --- networking/httpd_index_cgi_example | 50 ----- networking/httpd_indexcgi.c | 331 +++++++++++++++++++++++++++++ 2 files changed, 331 insertions(+), 50 deletions(-) delete mode 100644 networking/httpd_index_cgi_example create mode 100644 networking/httpd_indexcgi.c diff --git a/networking/httpd_index_cgi_example b/networking/httpd_index_cgi_example deleted file mode 100644 index 9c8e022a6..000000000 --- a/networking/httpd_index_cgi_example +++ /dev/null @@ -1,50 +0,0 @@ -#!/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="" - -printf "%s" \ -$'HTTP/1.0 200 OK\r\n'\ -$'Content-type: text/html\r\n\r\n'\ -"Index of $QUERY_STRING"$'\r\n'\ -"

Index of $QUERY_STRING

"$'\r\n'\
-$'\r\n'\
-$'\r\n'\
-$'
NameLast modifiedSize\r\n'\ -\ -"
.."$'\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" "
$f$cdt$size"$'\r\n' - } -done -printf "

"$'\r\n' diff --git a/networking/httpd_indexcgi.c b/networking/httpd_indexcgi.c new file mode 100644 index 000000000..b7e466c4c --- /dev/null +++ b/networking/httpd_indexcgi.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +/* + * This program is a CGI application. It creates directory index page. + * Put it into cgi-bin/index.cgi and chmod 0755. + */ + +/* Build a-la +i486-linux-uclibc-gcc \ +-static -static-libgcc \ +-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ +-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ +-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ +-Wmissing-prototypes -Wmissing-declarations \ +-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ +-ffunction-sections -fdata-sections -fno-guess-branch-probability \ +-funsigned-char \ +-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ +-march=i386 -mpreferred-stack-boundary=2 \ +-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ +httpd_indexcgi.c -o index.cgi + +Size (approximate): + text data bss dec hex filename +22642 160 3052 25854 64fe index.cgi +*/ + +/* TODO: get rid of printf's: printf code is more than 50% + * of the entire executable when built against static uclibc */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Appearance of the table is controlled by style sheet *ONLY*, + * formatting code uses to apply style + * to elements. Edit stylesheet to your liking and recompile. */ + +static const char str_header[] = +"" /* Additional headers (currently none) */ +"\r\n" /* Mandatory empty line after headers */ +"Index of %s" "\n" +"" "\n" +"" "\n" +"" "\n" +"

Index of %s

" "\n" +"" "\n" +"" "\n" +"" "\n" +"
NameSizeLast modified" "\n" +; + +static const char str_footer[] = +"
Files: %u, directories: %u%llu " "\n" +/* "
" - why bother? */ +; + +static int bad_url_char(unsigned c) +{ + return (c - '0') > 9 /* not a digit */ + && ((c|0x20) - 'a') > 26 /* not A-Z or a-z */ + && !strchr("._-+@", c); +} + +static char *url_encode(const char *name) +{ + int i; + int size = 0; + int len = strlen(name); + char *p, *result; + + i = 0; + while (name[i]) { + if (bad_url_char((unsigned)name[i])) + size++; + i++; + } + + /* No %xx needed! */ + if (!size) + return (char*)name; + + /* Each %xx requires 2 additional chars */ + size = size * 2 + len + 1; + p = result = malloc(size); + + i = 0; + while (name[i]) { + *p = name[i]; + if (bad_url_char((unsigned)name[i])) { + *p++ = '%'; + *p++ = "0123456789ABCDEF"[(uint8_t)(name[i]) >> 4]; + *p = "0123456789ABCDEF"[(uint8_t)(name[i]) & 0xf]; + } + p++; + i++; + } + *p = 0; + return result; +} + +static char *html_encode(const char *name) +{ + int i; + int size = 0; + int len = strlen(name); + char *p, *result; + + i = 0; + while (name[i]) { + if (name[i] == '<' + || name[i] == '>' + || name[i] == '&' + ) { + size++; + } + i++; + } + + /* No < etc needed! */ + if (!size) + return (char*)name; + + /* & requires 4 additional chars */ + size = size * 4 + len + 1; + p = result = malloc(size); + + i = 0; + while (name[i]) { + char c; + *p = c = name[i++]; + if (c == '<') + strcpy(p, "<"); + else if (c == '>') + strcpy(p, ">"); + else if (c == '&') + strcpy(++p, "amp;"); + else { + p++; + continue; + } + p += 4; + } + *p = 0; + return result; +} + +typedef struct dir_list_t { + char *dl_name; + mode_t dl_mode; + off_t dl_size; + time_t dl_mtime; +} dir_list_t; + +static int compare_dl(dir_list_t *a, dir_list_t *b) +{ + if (strcmp(a->dl_name, "..") == 0) { + /* ".." is 'less than' any other dir entry */ + return -1; + } + if (strcmp(b->dl_name, "..") == 0) { + return 1; + } + if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { + /* 1 if b is a dir (and thus a is 'after' b, a > b), + * else -1 (a < b)*/ + return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; + } + return strcmp(a->dl_name, b->dl_name); +} + +int main(void) +{ + dir_list_t *dir_list; + dir_list_t *cdir; + unsigned dir_list_count; + unsigned count_dirs; + unsigned count_files; + unsigned long long size_total; + int odd; + DIR *dirp; + char *QUERY_STRING; + + QUERY_STRING = getenv("QUERY_STRING"); + if (!QUERY_STRING + || QUERY_STRING[0] != '/' + || strstr(QUERY_STRING, "/../") + || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0 + ) { + return 1; + } + + if (chdir("..") + || (QUERY_STRING[1] && chdir(QUERY_STRING + 1)) + ) { + return 1; + } + + dirp = opendir("."); + if (!dirp) + return 1; + + dir_list = NULL; + dir_list_count = 0; + while (1) { + struct dirent *dp; + struct stat sb; + + dp = readdir(dirp); + if (!dp) + break; + if (dp->d_name[0] == '.' && !dp->d_name[1]) + continue; + if (stat(dp->d_name, &sb) != 0) + continue; + dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0])); + dir_list[dir_list_count].dl_name = strdup(dp->d_name); + dir_list[dir_list_count].dl_mode = sb.st_mode; + dir_list[dir_list_count].dl_size = sb.st_size; + dir_list[dir_list_count].dl_mtime = sb.st_mtime; + dir_list_count++; + } + + qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); + + /* Guard against directories wit &, > etc */ + QUERY_STRING = html_encode(QUERY_STRING); + printf(str_header, QUERY_STRING, QUERY_STRING); + + odd = 0; + count_dirs = 0; + count_files = 0; + size_total = 0; + + cdir = dir_list; + while (dir_list_count--) { + char size_str[sizeof(long long) * 3]; + const char *slash_if_dir; + struct tm *tm; + char *href; + char *filename; + char datetime_str[sizeof("2000-02-02 02:02:02")]; + + slash_if_dir = "/"; + if (S_ISDIR(cdir->dl_mode)) { + count_dirs++; + size_str[0] = '\0'; + } else if (S_ISREG(cdir->dl_mode)) { + count_files++; + size_total += cdir->dl_size; + slash_if_dir++; /* points to "" now */ + sprintf(size_str, "%llu", (unsigned long long)(cdir->dl_size)); + } else + goto next; + href = url_encode(cdir->dl_name); /* %20 etc */ + filename = html_encode(cdir->dl_name); /* < etc */ + tm = gmtime(&cdir->dl_mtime); + sprintf(datetime_str, "%04u-%02u-%02u %02u:%02u:%02u", + (unsigned)(1900 + tm->tm_year), + (unsigned)(tm->tm_mon + 1), + (unsigned)(tm->tm_mday), + (unsigned)(tm->tm_hour), + (unsigned)(tm->tm_min), + (unsigned)(tm->tm_sec) + ); + printf("%s%s%s%s\n", + odd ? 'o' : 'e', + href, slash_if_dir, + filename, slash_if_dir, + size_str, + datetime_str + ); + if (cdir->dl_name != href) + free(href); + if (cdir->dl_name != filename) + free(filename); + odd = 1 - odd; + next: + cdir++; + } + + /* count_dirs - 1: we don't want to count ".." */ + printf(str_footer, count_files, count_dirs - 1, size_total); + return 0; +}