Annotation of /trunk/mkinitrd-magellan/busybox/networking/httpd_indexcgi.c
Parent Directory | Revision Log
Revision 991 -
(hide annotations)
(download)
Sun May 30 11:42:41 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 9896 byte(s)
Sun May 30 11:42:41 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 9896 byte(s)
-added upstream indexcgi patch
1 | niro | 816 | /* |
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 outputs 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 | |||
28 | /* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */ | ||
29 | /* Currently malloc machinery is the biggest part of libc we pull in. */ | ||
30 | /* We have only one realloc and one strdup, any idea how to do without? */ | ||
31 | niro | 984 | |
32 | /* Size (i386, static uclibc, approximate): | ||
33 | niro | 816 | * text data bss dec hex filename |
34 | * 13036 44 3052 16132 3f04 index.cgi | ||
35 | * 2576 4 2048 4628 1214 index.cgi.o | ||
36 | */ | ||
37 | |||
38 | #include <sys/types.h> | ||
39 | #include <sys/stat.h> | ||
40 | #include <errno.h> | ||
41 | #include <stdint.h> | ||
42 | #include <stdlib.h> | ||
43 | #include <string.h> | ||
44 | #include <unistd.h> | ||
45 | #include <stdio.h> | ||
46 | #include <dirent.h> | ||
47 | #include <time.h> | ||
48 | |||
49 | /* Appearance of the table is controlled by style sheet *ONLY*, | ||
50 | * formatting code uses <TAG class=CLASS> to apply style | ||
51 | * to elements. Edit stylesheet to your liking and recompile. */ | ||
52 | |||
53 | #define STYLE_STR \ | ||
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 | "white-space:nowrap;" "\n"\ | ||
77 | "}" "\n"\ | ||
78 | "tr.hdr { background-color:#eee5de; }" "\n"\ | ||
79 | "tr.o { background-color:#ffffff; }" "\n"\ | ||
80 | /* tr.e { ... } - for even rows (currently none) */ \ | ||
81 | "tr.foot { background-color:#eee5de; }" "\n"\ | ||
82 | "th.cnt { text-align:left; }" "\n"\ | ||
83 | "th.sz { text-align:right; }" "\n"\ | ||
84 | "th.dt { text-align:right; }" "\n"\ | ||
85 | "td.sz { text-align:right; }" "\n"\ | ||
86 | "td.dt { text-align:right; }" "\n"\ | ||
87 | "col.nm { width:98%; }" "\n"\ | ||
88 | "col.sz { width:1%; }" "\n"\ | ||
89 | "col.dt { width:1%; }" "\n"\ | ||
90 | "</style>" "\n"\ | ||
91 | |||
92 | typedef struct dir_list_t { | ||
93 | char *dl_name; | ||
94 | mode_t dl_mode; | ||
95 | off_t dl_size; | ||
96 | time_t dl_mtime; | ||
97 | } dir_list_t; | ||
98 | |||
99 | static int compare_dl(dir_list_t *a, dir_list_t *b) | ||
100 | { | ||
101 | /* ".." is 'less than' any other dir entry */ | ||
102 | if (strcmp(a->dl_name, "..") == 0) { | ||
103 | return -1; | ||
104 | } | ||
105 | if (strcmp(b->dl_name, "..") == 0) { | ||
106 | return 1; | ||
107 | } | ||
108 | if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { | ||
109 | /* 1 if b is a dir (and thus a is 'after' b, a > b), | ||
110 | * else -1 (a < b) */ | ||
111 | return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; | ||
112 | } | ||
113 | return strcmp(a->dl_name, b->dl_name); | ||
114 | } | ||
115 | |||
116 | static char buffer[2*1024 > sizeof(STYLE_STR) ? 2*1024 : sizeof(STYLE_STR)]; | ||
117 | static char *dst = buffer; | ||
118 | enum { | ||
119 | BUFFER_SIZE = sizeof(buffer), | ||
120 | HEADROOM = 64, | ||
121 | }; | ||
122 | |||
123 | /* After this call, you have at least size + HEADROOM bytes available | ||
124 | * ahead of dst */ | ||
125 | static void guarantee(int size) | ||
126 | { | ||
127 | if (buffer + (BUFFER_SIZE-HEADROOM) - dst >= size) | ||
128 | return; | ||
129 | write(STDOUT_FILENO, buffer, dst - buffer); | ||
130 | dst = buffer; | ||
131 | } | ||
132 | |||
133 | /* NB: formatters do not store terminating NUL! */ | ||
134 | |||
135 | /* HEADROOM bytes are available after dst after this call */ | ||
136 | static void fmt_str(/*char *dst,*/ const char *src) | ||
137 | { | ||
138 | unsigned len = strlen(src); | ||
139 | guarantee(len); | ||
140 | memcpy(dst, src, len); | ||
141 | dst += len; | ||
142 | } | ||
143 | |||
144 | /* HEADROOM bytes after dst are available after this call */ | ||
145 | static void fmt_url(/*char *dst,*/ const char *name) | ||
146 | { | ||
147 | while (*name) { | ||
148 | unsigned c = *name++; | ||
149 | guarantee(3); | ||
150 | *dst = c; | ||
151 | if ((c - '0') > 9 /* not a digit */ | ||
152 | niro | 984 | && ((c|0x20) - 'a') > ('z' - 'a') /* not A-Z or a-z */ |
153 | niro | 816 | && !strchr("._-+@", c) |
154 | ) { | ||
155 | *dst++ = '%'; | ||
156 | *dst++ = "0123456789ABCDEF"[c >> 4]; | ||
157 | *dst = "0123456789ABCDEF"[c & 0xf]; | ||
158 | } | ||
159 | dst++; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /* HEADROOM bytes are available after dst after this call */ | ||
164 | static void fmt_html(/*char *dst,*/ const char *name) | ||
165 | { | ||
166 | while (*name) { | ||
167 | char c = *name++; | ||
168 | if (c == '<') | ||
169 | fmt_str("<"); | ||
170 | else if (c == '>') | ||
171 | fmt_str(">"); | ||
172 | else if (c == '&') { | ||
173 | fmt_str("&"); | ||
174 | } else { | ||
175 | guarantee(1); | ||
176 | *dst++ = c; | ||
177 | continue; | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /* HEADROOM bytes are available after dst after this call */ | ||
183 | static void fmt_ull(/*char *dst,*/ unsigned long long n) | ||
184 | { | ||
185 | char buf[sizeof(n)*3 + 2]; | ||
186 | char *p; | ||
187 | |||
188 | p = buf + sizeof(buf) - 1; | ||
189 | *p = '\0'; | ||
190 | do { | ||
191 | *--p = (n % 10) + '0'; | ||
192 | n /= 10; | ||
193 | } while (n); | ||
194 | fmt_str(/*dst,*/ p); | ||
195 | } | ||
196 | |||
197 | /* Does not call guarantee - eats into headroom instead */ | ||
198 | static void fmt_02u(/*char *dst,*/ unsigned n) | ||
199 | { | ||
200 | /* n %= 100; - not needed, callers don't pass big n */ | ||
201 | dst[0] = (n / 10) + '0'; | ||
202 | dst[1] = (n % 10) + '0'; | ||
203 | dst += 2; | ||
204 | } | ||
205 | |||
206 | /* Does not call guarantee - eats into headroom instead */ | ||
207 | static void fmt_04u(/*char *dst,*/ unsigned n) | ||
208 | { | ||
209 | /* n %= 10000; - not needed, callers don't pass big n */ | ||
210 | fmt_02u(n / 100); | ||
211 | fmt_02u(n % 100); | ||
212 | } | ||
213 | |||
214 | niro | 984 | int main(int argc, char *argv[]) |
215 | niro | 816 | { |
216 | dir_list_t *dir_list; | ||
217 | dir_list_t *cdir; | ||
218 | unsigned dir_list_count; | ||
219 | unsigned count_dirs; | ||
220 | unsigned count_files; | ||
221 | unsigned long long size_total; | ||
222 | int odd; | ||
223 | DIR *dirp; | ||
224 | char *QUERY_STRING; | ||
225 | |||
226 | QUERY_STRING = getenv("QUERY_STRING"); | ||
227 | if (!QUERY_STRING | ||
228 | || QUERY_STRING[0] != '/' | ||
229 | niro | 984 | || strstr(QUERY_STRING, "//") |
230 | niro | 816 | || strstr(QUERY_STRING, "/../") |
231 | || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0 | ||
232 | ) { | ||
233 | return 1; | ||
234 | } | ||
235 | |||
236 | if (chdir("..") | ||
237 | || (QUERY_STRING[1] && chdir(QUERY_STRING + 1)) | ||
238 | ) { | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | dirp = opendir("."); | ||
243 | if (!dirp) | ||
244 | return 1; | ||
245 | dir_list = NULL; | ||
246 | dir_list_count = 0; | ||
247 | while (1) { | ||
248 | struct dirent *dp; | ||
249 | struct stat sb; | ||
250 | |||
251 | dp = readdir(dirp); | ||
252 | if (!dp) | ||
253 | break; | ||
254 | if (dp->d_name[0] == '.' && !dp->d_name[1]) | ||
255 | continue; | ||
256 | if (stat(dp->d_name, &sb) != 0) | ||
257 | continue; | ||
258 | dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0])); | ||
259 | dir_list[dir_list_count].dl_name = strdup(dp->d_name); | ||
260 | dir_list[dir_list_count].dl_mode = sb.st_mode; | ||
261 | dir_list[dir_list_count].dl_size = sb.st_size; | ||
262 | dir_list[dir_list_count].dl_mtime = sb.st_mtime; | ||
263 | dir_list_count++; | ||
264 | } | ||
265 | closedir(dirp); | ||
266 | |||
267 | qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); | ||
268 | |||
269 | fmt_str( | ||
270 | "" /* Additional headers (currently none) */ | ||
271 | "\r\n" /* Mandatory empty line after headers */ | ||
272 | "<html><head><title>Index of "); | ||
273 | /* Guard against directories with &, > etc */ | ||
274 | fmt_html(QUERY_STRING); | ||
275 | fmt_str( | ||
276 | "</title>\n" | ||
277 | STYLE_STR | ||
278 | "</head>" "\n" | ||
279 | "<body>" "\n" | ||
280 | "<h1>Index of "); | ||
281 | fmt_html(QUERY_STRING); | ||
282 | fmt_str( | ||
283 | "</h1>" "\n" | ||
284 | "<table>" "\n" | ||
285 | "<col class=nm><col class=sz><col class=dt>" "\n" | ||
286 | "<tr class=hdr><th class=cnt>Name<th class=sz>Size<th class=dt>Last modified" "\n"); | ||
287 | |||
288 | odd = 0; | ||
289 | count_dirs = 0; | ||
290 | count_files = 0; | ||
291 | size_total = 0; | ||
292 | cdir = dir_list; | ||
293 | while (dir_list_count--) { | ||
294 | niro | 984 | struct tm *ptm; |
295 | niro | 816 | |
296 | if (S_ISDIR(cdir->dl_mode)) { | ||
297 | count_dirs++; | ||
298 | } else if (S_ISREG(cdir->dl_mode)) { | ||
299 | count_files++; | ||
300 | size_total += cdir->dl_size; | ||
301 | } else | ||
302 | goto next; | ||
303 | |||
304 | fmt_str("<tr class="); | ||
305 | *dst++ = (odd ? 'o' : 'e'); | ||
306 | fmt_str("><td class=nm><a href='"); | ||
307 | fmt_url(cdir->dl_name); /* %20 etc */ | ||
308 | if (S_ISDIR(cdir->dl_mode)) | ||
309 | *dst++ = '/'; | ||
310 | fmt_str("'>"); | ||
311 | fmt_html(cdir->dl_name); /* < etc */ | ||
312 | if (S_ISDIR(cdir->dl_mode)) | ||
313 | *dst++ = '/'; | ||
314 | fmt_str("</a><td class=sz>"); | ||
315 | if (S_ISREG(cdir->dl_mode)) | ||
316 | fmt_ull(cdir->dl_size); | ||
317 | fmt_str("<td class=dt>"); | ||
318 | niro | 991 | ptm = gmtime(&cdir->dl_mtime); |
319 | niro | 984 | fmt_04u(1900 + ptm->tm_year); *dst++ = '-'; |
320 | fmt_02u(ptm->tm_mon + 1); *dst++ = '-'; | ||
321 | fmt_02u(ptm->tm_mday); *dst++ = ' '; | ||
322 | fmt_02u(ptm->tm_hour); *dst++ = ':'; | ||
323 | fmt_02u(ptm->tm_min); *dst++ = ':'; | ||
324 | fmt_02u(ptm->tm_sec); | ||
325 | niro | 816 | *dst++ = '\n'; |
326 | |||
327 | odd = 1 - odd; | ||
328 | next: | ||
329 | cdir++; | ||
330 | } | ||
331 | |||
332 | fmt_str("<tr class=foot><th class=cnt>Files: "); | ||
333 | fmt_ull(count_files); | ||
334 | /* count_dirs - 1: we don't want to count ".." */ | ||
335 | fmt_str(", directories: "); | ||
336 | fmt_ull(count_dirs - 1); | ||
337 | fmt_str("<th class=sz>"); | ||
338 | fmt_ull(size_total); | ||
339 | fmt_str("<th class=dt>\n"); | ||
340 | /* "</table></body></html>" - why bother? */ | ||
341 | guarantee(BUFFER_SIZE * 2); /* flush */ | ||
342 | |||
343 | return 0; | ||
344 | } |