diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-22 10:38:44 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-22 10:38:44 +0000 |
| commit | 764299819016e5fb400be2fccf12d8de248e4cef (patch) | |
| tree | a031b6c86205944508794b9ef2dc0dca1e629f3d | |
| parent | 72b6a65b2fab7325767ce72fc71b9cf45514764a (diff) | |
| download | busybox-w32-764299819016e5fb400be2fccf12d8de248e4cef.tar.gz busybox-w32-764299819016e5fb400be2fccf12d8de248e4cef.tar.bz2 busybox-w32-764299819016e5fb400be2fccf12d8de248e4cef.zip | |
httpd: replace shell-based dir indexer cgi example with C-based one.
| -rw-r--r-- | networking/httpd_index_cgi_example | 50 | ||||
| -rw-r--r-- | networking/httpd_indexcgi.c | 331 |
2 files changed, 331 insertions, 50 deletions
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 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # This CGI creates directory index. | ||
| 3 | # Put it into cgi-bin/index.cgi and chmod 0755. | ||
| 4 | # | ||
| 5 | # Problems: | ||
| 6 | # * Unsafe wrt weird filenames with <>"'& etc... | ||
| 7 | # * Not efficient: calls stat (program, not syscall) for each file | ||
| 8 | # * Probably requires bash | ||
| 9 | # | ||
| 10 | # If you want speed and safety, you need to code it in C | ||
| 11 | |||
| 12 | # Must start with '/' | ||
| 13 | test "${QUERY_STRING:0:1}" = "/" || exit 1 | ||
| 14 | # /../ is not allowed | ||
| 15 | test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1 | ||
| 16 | test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1 | ||
| 17 | |||
| 18 | # Outta cgi-bin... | ||
| 19 | cd .. 2>/dev/null || exit 1 | ||
| 20 | # Strip leading '/', go to target dir | ||
| 21 | cd "${QUERY_STRING:1}" 2>/dev/null || exit 1 | ||
| 22 | |||
| 23 | f=`dirname "$QUERY_STRING"` | ||
| 24 | test "$f" = "/" && f="" | ||
| 25 | |||
| 26 | printf "%s" \ | ||
| 27 | $'HTTP/1.0 200 OK\r\n'\ | ||
| 28 | $'Content-type: text/html\r\n\r\n'\ | ||
| 29 | "<html><head><title>Index of $QUERY_STRING</title></head>"$'\r\n'\ | ||
| 30 | "<body><h1>Index of $QUERY_STRING</h1><pre>"$'\r\n'\ | ||
| 31 | $'<table width=100%>\r\n'\ | ||
| 32 | $'<col><col><col width=0*>\r\n'\ | ||
| 33 | $'<tr><th>Name<th align=right>Last modified<th align=right>Size\r\n'\ | ||
| 34 | \ | ||
| 35 | "<tr><td><a href='$f/'>..</a><td><td>"$'\r\n' | ||
| 36 | |||
| 37 | IFS='#' | ||
| 38 | for f in *; do | ||
| 39 | # Guard against empty dirs... | ||
| 40 | test -e "$f" && \ | ||
| 41 | stat -c "%F#%s#%z" "$f" | { | ||
| 42 | read type size cdt junk | ||
| 43 | dir='' | ||
| 44 | test "$type" = "directory" && dir='/' | ||
| 45 | cdt="${cdt//.*}" # no fractional seconds | ||
| 46 | cdt="${cdt// / }" # prevent wrapping around space | ||
| 47 | printf "%s" "<tr><td><a href='$f$dir'>$f</a><td align=right>$cdt<td align=right>$size"$'\r\n' | ||
| 48 | } | ||
| 49 | done | ||
| 50 | printf "</table></pre><hr></body></html>"$'\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 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com> | ||
| 3 | * | ||
| 4 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
| 5 | */ | ||
| 6 | |||
| 7 | /* | ||
| 8 | * This program is a CGI application. It creates directory index page. | ||
| 9 | * Put it into cgi-bin/index.cgi and chmod 0755. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* Build a-la | ||
| 13 | i486-linux-uclibc-gcc \ | ||
| 14 | -static -static-libgcc \ | ||
| 15 | -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ | ||
| 16 | -Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ | ||
| 17 | -Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ | ||
| 18 | -Wmissing-prototypes -Wmissing-declarations \ | ||
| 19 | -Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ | ||
| 20 | -ffunction-sections -fdata-sections -fno-guess-branch-probability \ | ||
| 21 | -funsigned-char \ | ||
| 22 | -falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ | ||
| 23 | -march=i386 -mpreferred-stack-boundary=2 \ | ||
| 24 | -Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ | ||
| 25 | httpd_indexcgi.c -o index.cgi | ||
| 26 | |||
| 27 | Size (approximate): | ||
| 28 | text data bss dec hex filename | ||
| 29 | 22642 160 3052 25854 64fe index.cgi | ||
| 30 | */ | ||
| 31 | |||
| 32 | /* TODO: get rid of printf's: printf code is more than 50% | ||
| 33 | * of the entire executable when built against static uclibc */ | ||
| 34 | |||
| 35 | #include <sys/types.h> | ||
| 36 | #include <sys/stat.h> | ||
| 37 | #include <errno.h> | ||
| 38 | #include <stdint.h> | ||
| 39 | #include <stdlib.h> | ||
| 40 | #include <string.h> | ||
| 41 | #include <unistd.h> | ||
| 42 | #include <stdio.h> | ||
| 43 | #include <dirent.h> | ||
| 44 | #include <time.h> | ||
| 45 | |||
| 46 | /* Appearance of the table is controlled by style sheet *ONLY*, | ||
| 47 | * formatting code uses <TAG class=CLASS> to apply style | ||
| 48 | * to elements. Edit stylesheet to your liking and recompile. */ | ||
| 49 | |||
| 50 | static const char str_header[] = | ||
| 51 | "" /* Additional headers (currently none) */ | ||
| 52 | "\r\n" /* Mandatory empty line after headers */ | ||
| 53 | "<html><head><title>Index of %s</title>" "\n" | ||
| 54 | "<style>" "\n" | ||
| 55 | "table {" "\n" | ||
| 56 | " width: 100%%;" "\n" | ||
| 57 | " background-color: #fff5ee;" "\n" | ||
| 58 | " border-width: 1px;" /* 1px 1px 1px 1px; */ "\n" | ||
| 59 | " border-spacing: 2px;" "\n" | ||
| 60 | " border-style: solid;" /* solid solid solid solid; */ "\n" | ||
| 61 | " border-color: black;" /* black black black black; */ "\n" | ||
| 62 | " border-collapse: collapse;" "\n" | ||
| 63 | "}" "\n" | ||
| 64 | "th {" "\n" | ||
| 65 | " border-width: 1px;" /* 1px 1px 1px 1px; */ "\n" | ||
| 66 | " padding: 1px;" /* 1px 1px 1px 1px; */ "\n" | ||
| 67 | " border-style: solid;" /* solid solid solid solid; */ "\n" | ||
| 68 | " border-color: black;" /* black black black black; */ "\n" | ||
| 69 | "}" "\n" | ||
| 70 | "td {" "\n" | ||
| 71 | /* top right bottom left */ | ||
| 72 | " border-width: 0px 1px 0px 1px;" "\n" | ||
| 73 | " padding: 1px;" /* 1px 1px 1px 1px; */ "\n" | ||
| 74 | " border-style: solid;" /* solid solid solid solid; */ "\n" | ||
| 75 | " border-color: black;" /* black black black black; */ "\n" | ||
| 76 | "}" "\n" | ||
| 77 | "tr.hdr { background-color:#eee5de; }" "\n" | ||
| 78 | "tr.o { background-color:#ffffff; }" "\n" | ||
| 79 | /* tr.e { ... } - for even rows (currently none) */ | ||
| 80 | "tr.foot { background-color:#eee5de; }" "\n" | ||
| 81 | "th.cnt { text-align:left; }" "\n" | ||
| 82 | "th.sz { text-align:right; }" "\n" | ||
| 83 | "th.dt { text-align:right; }" "\n" | ||
| 84 | "td.sz { text-align:right; }" "\n" | ||
| 85 | "td.dt { text-align:right; }" "\n" | ||
| 86 | "col.nm { width: 98%%; }" "\n" | ||
| 87 | "col.sz { width: 1%%; }" "\n" | ||
| 88 | "col.dt { width: 1%%; }" "\n" | ||
| 89 | "</style>" "\n" | ||
| 90 | "</head>" "\n" | ||
| 91 | "<body>" "\n" | ||
| 92 | "<h1>Index of %s</h1>" "\n" | ||
| 93 | "" "\n" | ||
| 94 | "<table>" "\n" | ||
| 95 | "<col class=nm><col class=sz><col class=dt>" "\n" | ||
| 96 | "<tr class=hdr><th class=cnt>Name<th class=sz>Size<th class=dt>Last modified" "\n" | ||
| 97 | ; | ||
| 98 | |||
| 99 | static const char str_footer[] = | ||
| 100 | "<tr class=foot><th class=cnt>Files: %u, directories: %u<th class=sz>%llu<th class=dt> " "\n" | ||
| 101 | /* "</table></body></html>" - why bother? */ | ||
| 102 | ; | ||
| 103 | |||
| 104 | static int bad_url_char(unsigned c) | ||
| 105 | { | ||
| 106 | return (c - '0') > 9 /* not a digit */ | ||
| 107 | && ((c|0x20) - 'a') > 26 /* not A-Z or a-z */ | ||
| 108 | && !strchr("._-+@", c); | ||
| 109 | } | ||
| 110 | |||
| 111 | static char *url_encode(const char *name) | ||
| 112 | { | ||
| 113 | int i; | ||
| 114 | int size = 0; | ||
| 115 | int len = strlen(name); | ||
| 116 | char *p, *result; | ||
| 117 | |||
| 118 | i = 0; | ||
| 119 | while (name[i]) { | ||
| 120 | if (bad_url_char((unsigned)name[i])) | ||
| 121 | size++; | ||
| 122 | i++; | ||
| 123 | } | ||
| 124 | |||
| 125 | /* No %xx needed! */ | ||
| 126 | if (!size) | ||
| 127 | return (char*)name; | ||
| 128 | |||
| 129 | /* Each %xx requires 2 additional chars */ | ||
| 130 | size = size * 2 + len + 1; | ||
| 131 | p = result = malloc(size); | ||
| 132 | |||
| 133 | i = 0; | ||
| 134 | while (name[i]) { | ||
| 135 | *p = name[i]; | ||
| 136 | if (bad_url_char((unsigned)name[i])) { | ||
| 137 | *p++ = '%'; | ||
| 138 | *p++ = "0123456789ABCDEF"[(uint8_t)(name[i]) >> 4]; | ||
| 139 | *p = "0123456789ABCDEF"[(uint8_t)(name[i]) & 0xf]; | ||
| 140 | } | ||
| 141 | p++; | ||
| 142 | i++; | ||
| 143 | } | ||
| 144 | *p = 0; | ||
| 145 | return result; | ||
| 146 | } | ||
| 147 | |||
| 148 | static char *html_encode(const char *name) | ||
| 149 | { | ||
| 150 | int i; | ||
| 151 | int size = 0; | ||
| 152 | int len = strlen(name); | ||
| 153 | char *p, *result; | ||
| 154 | |||
| 155 | i = 0; | ||
| 156 | while (name[i]) { | ||
| 157 | if (name[i] == '<' | ||
| 158 | || name[i] == '>' | ||
| 159 | || name[i] == '&' | ||
| 160 | ) { | ||
| 161 | size++; | ||
| 162 | } | ||
| 163 | i++; | ||
| 164 | } | ||
| 165 | |||
| 166 | /* No < etc needed! */ | ||
| 167 | if (!size) | ||
| 168 | return (char*)name; | ||
| 169 | |||
| 170 | /* & requires 4 additional chars */ | ||
| 171 | size = size * 4 + len + 1; | ||
| 172 | p = result = malloc(size); | ||
| 173 | |||
| 174 | i = 0; | ||
| 175 | while (name[i]) { | ||
| 176 | char c; | ||
| 177 | *p = c = name[i++]; | ||
| 178 | if (c == '<') | ||
| 179 | strcpy(p, "<"); | ||
| 180 | else if (c == '>') | ||
| 181 | strcpy(p, ">"); | ||
| 182 | else if (c == '&') | ||
| 183 | strcpy(++p, "amp;"); | ||
| 184 | else { | ||
| 185 | p++; | ||
| 186 | continue; | ||
| 187 | } | ||
| 188 | p += 4; | ||
| 189 | } | ||
| 190 | *p = 0; | ||
| 191 | return result; | ||
| 192 | } | ||
| 193 | |||
| 194 | typedef struct dir_list_t { | ||
| 195 | char *dl_name; | ||
| 196 | mode_t dl_mode; | ||
| 197 | off_t dl_size; | ||
| 198 | time_t dl_mtime; | ||
| 199 | } dir_list_t; | ||
| 200 | |||
| 201 | static int compare_dl(dir_list_t *a, dir_list_t *b) | ||
| 202 | { | ||
| 203 | if (strcmp(a->dl_name, "..") == 0) { | ||
| 204 | /* ".." is 'less than' any other dir entry */ | ||
| 205 | return -1; | ||
| 206 | } | ||
| 207 | if (strcmp(b->dl_name, "..") == 0) { | ||
| 208 | return 1; | ||
| 209 | } | ||
| 210 | if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { | ||
| 211 | /* 1 if b is a dir (and thus a is 'after' b, a > b), | ||
| 212 | * else -1 (a < b)*/ | ||
| 213 | return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; | ||
| 214 | } | ||
| 215 | return strcmp(a->dl_name, b->dl_name); | ||
| 216 | } | ||
| 217 | |||
| 218 | int main(void) | ||
| 219 | { | ||
| 220 | dir_list_t *dir_list; | ||
| 221 | dir_list_t *cdir; | ||
| 222 | unsigned dir_list_count; | ||
| 223 | unsigned count_dirs; | ||
| 224 | unsigned count_files; | ||
| 225 | unsigned long long size_total; | ||
| 226 | int odd; | ||
| 227 | DIR *dirp; | ||
| 228 | char *QUERY_STRING; | ||
| 229 | |||
| 230 | QUERY_STRING = getenv("QUERY_STRING"); | ||
| 231 | if (!QUERY_STRING | ||
| 232 | || QUERY_STRING[0] != '/' | ||
| 233 | || strstr(QUERY_STRING, "/../") | ||
| 234 | || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0 | ||
| 235 | ) { | ||
| 236 | return 1; | ||
| 237 | } | ||
| 238 | |||
| 239 | if (chdir("..") | ||
| 240 | || (QUERY_STRING[1] && chdir(QUERY_STRING + 1)) | ||
| 241 | ) { | ||
| 242 | return 1; | ||
| 243 | } | ||
| 244 | |||
| 245 | dirp = opendir("."); | ||
| 246 | if (!dirp) | ||
| 247 | return 1; | ||
| 248 | |||
| 249 | dir_list = NULL; | ||
| 250 | dir_list_count = 0; | ||
| 251 | while (1) { | ||
| 252 | struct dirent *dp; | ||
| 253 | struct stat sb; | ||
| 254 | |||
| 255 | dp = readdir(dirp); | ||
| 256 | if (!dp) | ||
| 257 | break; | ||
| 258 | if (dp->d_name[0] == '.' && !dp->d_name[1]) | ||
| 259 | continue; | ||
| 260 | if (stat(dp->d_name, &sb) != 0) | ||
| 261 | continue; | ||
| 262 | dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0])); | ||
| 263 | dir_list[dir_list_count].dl_name = strdup(dp->d_name); | ||
| 264 | dir_list[dir_list_count].dl_mode = sb.st_mode; | ||
| 265 | dir_list[dir_list_count].dl_size = sb.st_size; | ||
| 266 | dir_list[dir_list_count].dl_mtime = sb.st_mtime; | ||
| 267 | dir_list_count++; | ||
| 268 | } | ||
| 269 | |||
| 270 | qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); | ||
| 271 | |||
| 272 | /* Guard against directories wit &, > etc */ | ||
| 273 | QUERY_STRING = html_encode(QUERY_STRING); | ||
| 274 | printf(str_header, QUERY_STRING, QUERY_STRING); | ||
| 275 | |||
| 276 | odd = 0; | ||
| 277 | count_dirs = 0; | ||
| 278 | count_files = 0; | ||
| 279 | size_total = 0; | ||
| 280 | |||
| 281 | cdir = dir_list; | ||
| 282 | while (dir_list_count--) { | ||
| 283 | char size_str[sizeof(long long) * 3]; | ||
| 284 | const char *slash_if_dir; | ||
| 285 | struct tm *tm; | ||
| 286 | char *href; | ||
| 287 | char *filename; | ||
| 288 | char datetime_str[sizeof("2000-02-02 02:02:02")]; | ||
| 289 | |||
| 290 | slash_if_dir = "/"; | ||
| 291 | if (S_ISDIR(cdir->dl_mode)) { | ||
| 292 | count_dirs++; | ||
| 293 | size_str[0] = '\0'; | ||
| 294 | } else if (S_ISREG(cdir->dl_mode)) { | ||
| 295 | count_files++; | ||
| 296 | size_total += cdir->dl_size; | ||
| 297 | slash_if_dir++; /* points to "" now */ | ||
| 298 | sprintf(size_str, "%llu", (unsigned long long)(cdir->dl_size)); | ||
| 299 | } else | ||
| 300 | goto next; | ||
| 301 | href = url_encode(cdir->dl_name); /* %20 etc */ | ||
| 302 | filename = html_encode(cdir->dl_name); /* < etc */ | ||
| 303 | tm = gmtime(&cdir->dl_mtime); | ||
| 304 | sprintf(datetime_str, "%04u-%02u-%02u %02u:%02u:%02u", | ||
| 305 | (unsigned)(1900 + tm->tm_year), | ||
| 306 | (unsigned)(tm->tm_mon + 1), | ||
| 307 | (unsigned)(tm->tm_mday), | ||
| 308 | (unsigned)(tm->tm_hour), | ||
| 309 | (unsigned)(tm->tm_min), | ||
| 310 | (unsigned)(tm->tm_sec) | ||
| 311 | ); | ||
| 312 | printf("<tr class=%c><td class=nm><a href='%s%s'>%s%s</a><td class=sz>%s<td class=dt>%s\n", | ||
| 313 | odd ? 'o' : 'e', | ||
| 314 | href, slash_if_dir, | ||
| 315 | filename, slash_if_dir, | ||
| 316 | size_str, | ||
| 317 | datetime_str | ||
| 318 | ); | ||
| 319 | if (cdir->dl_name != href) | ||
| 320 | free(href); | ||
| 321 | if (cdir->dl_name != filename) | ||
| 322 | free(filename); | ||
| 323 | odd = 1 - odd; | ||
| 324 | next: | ||
| 325 | cdir++; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* count_dirs - 1: we don't want to count ".." */ | ||
| 329 | printf(str_footer, count_files, count_dirs - 1, size_total); | ||
| 330 | return 0; | ||
| 331 | } | ||
