aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--networking/httpd_indexcgi.c381
1 files changed, 196 insertions, 185 deletions
diff --git a/networking/httpd_indexcgi.c b/networking/httpd_indexcgi.c
index def863224..fd64af38f 100644
--- a/networking/httpd_indexcgi.c
+++ b/networking/httpd_indexcgi.c
@@ -5,7 +5,7 @@
5 */ 5 */
6 6
7/* 7/*
8 * This program is a CGI application. It creates directory index page. 8 * This program is a CGI application. It outputs directory index page.
9 * Put it into cgi-bin/index.cgi and chmod 0755. 9 * Put it into cgi-bin/index.cgi and chmod 0755.
10 */ 10 */
11 11
@@ -23,14 +23,16 @@ i486-linux-uclibc-gcc \
23-march=i386 -mpreferred-stack-boundary=2 \ 23-march=i386 -mpreferred-stack-boundary=2 \
24-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ 24-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
25httpd_indexcgi.c -o index.cgi 25httpd_indexcgi.c -o index.cgi
26
27Size (approximate):
28 text data bss dec hex filename
2922642 160 3052 25854 64fe index.cgi
30*/ 26*/
31 27
32/* TODO: get rid of printf's: printf code is more than 50% 28/* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */
33 * of the entire executable when built against static uclibc */ 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/* Size (i386, approximate):
32 * text data bss dec hex filename
33 * 13036 44 3052 16132 3f04 index.cgi
34 * 2576 4 2048 4628 1214 index.cgi.o
35 */
34 36
35#include <sys/types.h> 37#include <sys/types.h>
36#include <sys/stat.h> 38#include <sys/stat.h>
@@ -47,172 +49,165 @@ Size (approximate):
47 * formatting code uses <TAG class=CLASS> to apply style 49 * formatting code uses <TAG class=CLASS> to apply style
48 * to elements. Edit stylesheet to your liking and recompile. */ 50 * to elements. Edit stylesheet to your liking and recompile. */
49 51
50static const char str_header[] = 52#define STYLE_STR \
51"" /* Additional headers (currently none) */ 53"<style>" "\n"\
52"\r\n" /* Mandatory empty line after headers */ 54"table {" "\n"\
53"<html><head><title>Index of %s</title>" "\n" 55 "width:100%;" "\n"\
54"<style>" "\n" 56 "background-color:#fff5ee;" "\n"\
55"table {" "\n" 57 "border-width:1px;" /* 1px 1px 1px 1px; */ "\n"\
56" width: 100%%;" "\n" 58 "border-spacing:2px;" "\n"\
57" background-color: #fff5ee;" "\n" 59 "border-style:solid;" /* solid solid solid solid; */ "\n"\
58" border-width: 1px;" /* 1px 1px 1px 1px; */ "\n" 60 "border-color:black;" /* black black black black; */ "\n"\
59" border-spacing: 2px;" "\n" 61 "border-collapse:collapse;" "\n"\
60" border-style: solid;" /* solid solid solid solid; */ "\n" 62"}" "\n"\
61" border-color: black;" /* black black black black; */ "\n" 63"th {" "\n"\
62" border-collapse: collapse;" "\n" 64 "border-width:1px;" /* 1px 1px 1px 1px; */ "\n"\
63"}" "\n" 65 "padding:1px;" /* 1px 1px 1px 1px; */ "\n"\
64"th {" "\n" 66 "border-style:solid;" /* solid solid solid solid; */ "\n"\
65" border-width: 1px;" /* 1px 1px 1px 1px; */ "\n" 67 "border-color:black;" /* black black black black; */ "\n"\
66" padding: 1px;" /* 1px 1px 1px 1px; */ "\n" 68"}" "\n"\
67" border-style: solid;" /* solid solid solid solid; */ "\n" 69"td {" "\n"\
68" border-color: black;" /* black black black black; */ "\n" 70 /* top right bottom left */ \
69"}" "\n" 71 "border-width:0px 1px 0px 1px;" "\n"\
70"td {" "\n" 72 "padding:1px;" /* 1px 1px 1px 1px; */ "\n"\
71 /* top right bottom left */ 73 "border-style:solid;" /* solid solid solid solid; */ "\n"\
72" border-width: 0px 1px 0px 1px;" "\n" 74 "border-color:black;" /* black black black black; */ "\n"\
73" padding: 1px;" /* 1px 1px 1px 1px; */ "\n" 75 "white-space:nowrap;" "\n"\
74" border-style: solid;" /* solid solid solid solid; */ "\n" 76"}" "\n"\
75" border-color: black;" /* black black black black; */ "\n" 77"tr.hdr { background-color:#eee5de; }" "\n"\
76"}" "\n" 78"tr.o { background-color:#ffffff; }" "\n"\
77"tr.hdr { background-color:#eee5de; }" "\n" 79/* tr.e { ... } - for even rows (currently none) */ \
78"tr.o { background-color:#ffffff; }" "\n" 80"tr.foot { background-color:#eee5de; }" "\n"\
79/* tr.e { ... } - for even rows (currently none) */ 81"th.cnt { text-align:left; }" "\n"\
80"tr.foot { background-color:#eee5de; }" "\n" 82"th.sz { text-align:right; }" "\n"\
81"th.cnt { text-align:left; }" "\n" 83"th.dt { text-align:right; }" "\n"\
82"th.sz { text-align:right; }" "\n" 84"td.sz { text-align:right; }" "\n"\
83"th.dt { text-align:right; }" "\n" 85"td.dt { text-align:right; }" "\n"\
84"td.sz { text-align:right; }" "\n" 86"col.nm { width:98%; }" "\n"\
85"td.dt { text-align:right; }" "\n" 87"col.sz { width:1%; }" "\n"\
86"col.nm { width: 98%%; }" "\n" 88"col.dt { width:1%; }" "\n"\
87"col.sz { width: 1%%; }" "\n" 89"</style>" "\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 90
99static const char str_footer[] = 91typedef struct dir_list_t {
100"<tr class=foot><th class=cnt>Files: %u, directories: %u<th class=sz>%llu<th class=dt>&nbsp;" "\n" 92 char *dl_name;
101/* "</table></body></html>" - why bother? */ 93 mode_t dl_mode;
102; 94 off_t dl_size;
95 time_t dl_mtime;
96} dir_list_t;
103 97
104static int bad_url_char(unsigned c) 98static int compare_dl(dir_list_t *a, dir_list_t *b)
105{ 99{
106 return (c - '0') > 9 /* not a digit */ 100 /* ".." is 'less than' any other dir entry */
107 && ((c|0x20) - 'a') > 26 /* not A-Z or a-z */ 101 if (strcmp(a->dl_name, "..") == 0) {
108 && !strchr("._-+@", c); 102 return -1;
103 }
104 if (strcmp(b->dl_name, "..") == 0) {
105 return 1;
106 }
107 if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) {
108 /* 1 if b is a dir (and thus a is 'after' b, a > b),
109 * else -1 (a < b) */
110 return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1;
111 }
112 return strcmp(a->dl_name, b->dl_name);
109} 113}
110 114
111static char *url_encode(const char *name) 115static char buffer[2*1024 > sizeof(STYLE_STR) ? 2*1024 : sizeof(STYLE_STR)];
112{ 116static char *dst = buffer;
113 int i; 117enum {
114 int size = 0; 118 BUFFER_SIZE = sizeof(buffer),
115 int len = strlen(name); 119 HEADROOM = 64,
116 char *p, *result; 120};
117
118 i = 0;
119 while (name[i]) {
120 if (bad_url_char((unsigned)name[i]))
121 size++;
122 i++;
123 }
124 121
125 /* No %xx needed! */ 122/* After this call, you have at least size + HEADROOM bytes available
126 if (!size) 123 * ahead of dst */
127 return (char*)name; 124static void guarantee(int size)
125{
126 if (buffer + (BUFFER_SIZE-HEADROOM) - dst >= size)
127 return;
128 write(1, buffer, dst - buffer);
129 dst = buffer;
130}
128 131
129 /* Each %xx requires 2 additional chars */ 132/* NB: formatters do not store terminating NUL! */
130 size = size * 2 + len + 1;
131 p = result = malloc(size);
132 133
133 i = 0; 134/* HEADROOM bytes are available after dst after this call */
134 while (name[i]) { 135static void fmt_str(/*char *dst,*/ const char *src)
135 *p = name[i]; 136{
136 if (bad_url_char((unsigned)name[i])) { 137 unsigned len = strlen(src);
137 *p++ = '%'; 138 guarantee(len);
138 *p++ = "0123456789ABCDEF"[(uint8_t)(name[i]) >> 4]; 139 memcpy(dst, src, len);
139 *p = "0123456789ABCDEF"[(uint8_t)(name[i]) & 0xf]; 140 dst += len;
140 }
141 p++;
142 i++;
143 }
144 *p = 0;
145 return result;
146} 141}
147 142
148static char *html_encode(const char *name) 143/* HEADROOM bytes after dst are available after this call */
144static void fmt_url(/*char *dst,*/ const char *name)
149{ 145{
150 int i; 146 while (*name) {
151 int size = 0; 147 unsigned c = *name++;
152 int len = strlen(name); 148 guarantee(3);
153 char *p, *result; 149 *dst = c;
154 150 if ((c - '0') > 9 /* not a digit */
155 i = 0; 151 && ((c|0x20) - 'a') > 26 /* not A-Z or a-z */
156 while (name[i]) { 152 && !strchr("._-+@", c)
157 if (name[i] == '<'
158 || name[i] == '>'
159 || name[i] == '&'
160 ) { 153 ) {
161 size++; 154 *dst++ = '%';
155 *dst++ = "0123456789ABCDEF"[c >> 4];
156 *dst = "0123456789ABCDEF"[c & 0xf];
162 } 157 }
163 i++; 158 dst++;
164 } 159 }
160}
165 161
166 /* No &lt; etc needed! */ 162/* HEADROOM bytes are available after dst after this call */
167 if (!size) 163static void fmt_html(/*char *dst,*/ const char *name)
168 return (char*)name; 164{
169 165 while (*name) {
170 /* &amp; requires 4 additional chars */ 166 char c = *name++;
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 == '<') 167 if (c == '<')
179 strcpy(p, "&lt;"); 168 fmt_str("&lt;");
180 else if (c == '>') 169 else if (c == '>')
181 strcpy(p, "&gt;"); 170 fmt_str("&gt;");
182 else if (c == '&') 171 else if (c == '&') {
183 strcpy(++p, "amp;"); 172 fmt_str("&amp;");
184 else { 173 } else {
185 p++; 174 guarantee(1);
175 *dst++ = c;
186 continue; 176 continue;
187 } 177 }
188 p += 4;
189 } 178 }
190 *p = 0;
191 return result;
192} 179}
193 180
194typedef struct dir_list_t { 181/* HEADROOM bytes are available after dst after this call */
195 char *dl_name; 182static void fmt_ull(/*char *dst,*/ unsigned long long n)
196 mode_t dl_mode; 183{
197 off_t dl_size; 184 char buf[sizeof(n)*3 + 2];
198 time_t dl_mtime; 185 char *p;
199} dir_list_t; 186
187 p = buf + sizeof(buf) - 1;
188 *p = '\0';
189 do {
190 *--p = (n % 10) + '0';
191 n /= 10;
192 } while (n);
193 fmt_str(/*dst,*/ p);
194}
200 195
201static int compare_dl(dir_list_t *a, dir_list_t *b) 196/* Does not call guarantee - eats into headroom instead */
197static void fmt_02u(/*char *dst,*/ unsigned n)
202{ 198{
203 if (strcmp(a->dl_name, "..") == 0) { 199 /* n %= 100; - not needed, callers don't pass big n */
204 /* ".." is 'less than' any other dir entry */ 200 dst[0] = (n / 10) + '0';
205 return -1; 201 dst[1] = (n % 10) + '0';
206 } 202 dst += 2;
207 if (strcmp(b->dl_name, "..") == 0) { 203}
208 return 1; 204
209 } 205/* Does not call guarantee - eats into headroom instead */
210 if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { 206static void fmt_04u(/*char *dst,*/ unsigned n)
211 /* 1 if b is a dir (and thus a is 'after' b, a > b), 207{
212 * else -1 (a < b)*/ 208 /* n %= 10000; - not needed, callers don't pass big n */
213 return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; 209 fmt_02u(n / 100);
214 } 210 fmt_02u(n % 100);
215 return strcmp(a->dl_name, b->dl_name);
216} 211}
217 212
218int main(void) 213int main(void)
@@ -245,7 +240,6 @@ int main(void)
245 dirp = opendir("."); 240 dirp = opendir(".");
246 if (!dirp) 241 if (!dirp)
247 return 1; 242 return 1;
248
249 dir_list = NULL; 243 dir_list = NULL;
250 dir_list_count = 0; 244 dir_list_count = 0;
251 while (1) { 245 while (1) {
@@ -266,66 +260,83 @@ int main(void)
266 dir_list[dir_list_count].dl_mtime = sb.st_mtime; 260 dir_list[dir_list_count].dl_mtime = sb.st_mtime;
267 dir_list_count++; 261 dir_list_count++;
268 } 262 }
263 closedir(dirp);
269 264
270 qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); 265 qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl);
271 266
272 /* Guard against directories wit &, > etc */ 267 fmt_str(
273 QUERY_STRING = html_encode(QUERY_STRING); 268 "" /* Additional headers (currently none) */
274 printf(str_header, QUERY_STRING, QUERY_STRING); 269 "\r\n" /* Mandatory empty line after headers */
270 "<html><head><title>Index of ");
271 /* Guard against directories with &, > etc */
272 fmt_html(QUERY_STRING);
273 fmt_str(
274 "</title>\n"
275 STYLE_STR
276 "</head>" "\n"
277 "<body>" "\n"
278 "<h1>Index of ");
279 fmt_html(QUERY_STRING);
280 fmt_str(
281 "</h1>" "\n"
282 "<table>" "\n"
283 "<col class=nm><col class=sz><col class=dt>" "\n"
284 "<tr class=hdr><th class=cnt>Name<th class=sz>Size<th class=dt>Last modified" "\n");
275 285
276 odd = 0; 286 odd = 0;
277 count_dirs = 0; 287 count_dirs = 0;
278 count_files = 0; 288 count_files = 0;
279 size_total = 0; 289 size_total = 0;
280
281 cdir = dir_list; 290 cdir = dir_list;
282 while (dir_list_count--) { 291 while (dir_list_count--) {
283 char size_str[sizeof(long long) * 3];
284 const char *slash_if_dir;
285 struct tm *tm; 292 struct tm *tm;
286 char *href;
287 char *filename;
288 char datetime_str[sizeof("2000-02-02&nbsp;02:02:02")];
289 293
290 slash_if_dir = "/";
291 if (S_ISDIR(cdir->dl_mode)) { 294 if (S_ISDIR(cdir->dl_mode)) {
292 count_dirs++; 295 count_dirs++;
293 size_str[0] = '\0';
294 } else if (S_ISREG(cdir->dl_mode)) { 296 } else if (S_ISREG(cdir->dl_mode)) {
295 count_files++; 297 count_files++;
296 size_total += cdir->dl_size; 298 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 299 } else
300 goto next; 300 goto next;
301 href = url_encode(cdir->dl_name); /* %20 etc */ 301
302 filename = html_encode(cdir->dl_name); /* &lt; etc */ 302 fmt_str("<tr class=");
303 *dst++ = (odd ? 'o' : 'e');
304 fmt_str("><td class=nm><a href='");
305 fmt_url(cdir->dl_name); /* %20 etc */
306 if (S_ISDIR(cdir->dl_mode))
307 *dst++ = '/';
308 fmt_str("'>");
309 fmt_html(cdir->dl_name); /* &lt; etc */
310 if (S_ISDIR(cdir->dl_mode))
311 *dst++ = '/';
312 fmt_str("</a><td class=sz>");
313 if (S_ISREG(cdir->dl_mode))
314 fmt_ull(cdir->dl_size);
315 fmt_str("<td class=dt>");
303 tm = gmtime(&cdir->dl_mtime); 316 tm = gmtime(&cdir->dl_mtime);
304 sprintf(datetime_str, "%04u-%02u-%02u&nbsp;%02u:%02u:%02u", 317 fmt_04u(1900 + tm->tm_year); *dst++ = '-';
305 (unsigned)(1900 + tm->tm_year), 318 fmt_02u(tm->tm_mon + 1); *dst++ = '-';
306 (unsigned)(tm->tm_mon + 1), 319 fmt_02u(tm->tm_mday); *dst++ = ' ';
307 (unsigned)(tm->tm_mday), 320 fmt_02u(tm->tm_hour); *dst++ = ':';
308 (unsigned)(tm->tm_hour), 321 fmt_02u(tm->tm_min); *dst++ = ':';
309 (unsigned)(tm->tm_min), 322 fmt_02u(tm->tm_sec);
310 (unsigned)(tm->tm_sec) 323 *dst++ = '\n';
311 ); 324
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; 325 odd = 1 - odd;
324 next: 326 next:
325 cdir++; 327 cdir++;
326 } 328 }
327 329
330 fmt_str("<tr class=foot><th class=cnt>Files: ");
331 fmt_ull(count_files);
328 /* count_dirs - 1: we don't want to count ".." */ 332 /* count_dirs - 1: we don't want to count ".." */
329 printf(str_footer, count_files, count_dirs - 1, size_total); 333 fmt_str(", directories: ");
334 fmt_ull(count_dirs - 1);
335 fmt_str("<th class=sz>");
336 fmt_ull(size_total);
337 fmt_str("<th class=dt>\n");
338 /* "</table></body></html>" - why bother? */
339 guarantee(BUFFER_SIZE * 2); /* flush */
340
330 return 0; 341 return 0;
331} 342}