uhttpd/file: fix string out of buffer range on uh_defer_script
[project/uhttpd.git] / file.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #ifndef _DEFAULT_SOURCE
21 # define _DEFAULT_SOURCE
22 #endif
23
24 #define _BSD_SOURCE
25 #define _DARWIN_C_SOURCE
26 #define _XOPEN_SOURCE 700
27
28 #include <sys/types.h>
29 #include <sys/dir.h>
30 #include <time.h>
31 #include <strings.h>
32 #include <dirent.h>
33 #include <inttypes.h>
34
35 #include <libubox/blobmsg.h>
36
37 #include "uhttpd.h"
38 #include "mimetypes.h"
39
40 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
41
42 static LIST_HEAD(index_files);
43 static LIST_HEAD(dispatch_handlers);
44 static LIST_HEAD(pending_requests);
45 static int n_requests;
46
47 struct deferred_request {
48 struct list_head list;
49 struct dispatch_handler *d;
50 struct client *cl;
51 struct path_info pi;
52 char *url;
53 bool called, path;
54 };
55
56 struct index_file {
57 struct list_head list;
58 const char *name;
59 };
60
61 enum file_hdr {
62 HDR_AUTHORIZATION,
63 HDR_IF_MODIFIED_SINCE,
64 HDR_IF_UNMODIFIED_SINCE,
65 HDR_IF_MATCH,
66 HDR_IF_NONE_MATCH,
67 HDR_IF_RANGE,
68 __HDR_MAX
69 };
70
71 void uh_index_add(const char *filename)
72 {
73 struct index_file *idx;
74
75 idx = calloc(1, sizeof(*idx));
76 idx->name = filename;
77 list_add_tail(&idx->list, &index_files);
78 }
79
80 static char * canonpath(const char *path, char *path_resolved)
81 {
82 const char *path_cpy = path;
83 char *path_res = path_resolved;
84
85 if (conf.no_symlinks)
86 return realpath(path, path_resolved);
87
88 /* normalize */
89 while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) {
90 if (*path_cpy != '/')
91 goto next;
92
93 /* skip repeating / */
94 if (path_cpy[1] == '/') {
95 path_cpy++;
96 continue;
97 }
98
99 /* /./ or /../ */
100 if (path_cpy[1] == '.') {
101 /* skip /./ */
102 if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) {
103 path_cpy += 2;
104 continue;
105 }
106
107 /* collapse /x/../ */
108 if ((path_cpy[2] == '.') &&
109 ((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) {
110 while ((path_res > path_resolved) && (*--path_res != '/'));
111
112 path_cpy += 3;
113 continue;
114 }
115 }
116
117 next:
118 *path_res++ = *path_cpy++;
119 }
120
121 /* remove trailing slash if not root / */
122 if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
123 path_res--;
124 else if (path_res == path_resolved)
125 *path_res++ = '/';
126
127 *path_res = '\0';
128
129 return path_resolved;
130 }
131
132 /* Returns NULL on error.
133 ** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning
134 ** NULL here causes 404 [Not Found], but that's not too unreasonable. */
135 struct path_info *
136 uh_path_lookup(struct client *cl, const char *url)
137 {
138 static char path_phys[PATH_MAX];
139 static char path_info[PATH_MAX];
140 static char path_query[PATH_MAX];
141 static struct path_info p;
142
143 const char *docroot = conf.docroot;
144 int docroot_len = strlen(docroot);
145 char *pathptr = NULL;
146 bool slash;
147
148 int i = 0;
149 int len;
150 struct stat s;
151 struct index_file *idx;
152
153 /* back out early if url is undefined */
154 if (url == NULL)
155 return NULL;
156
157 memset(&p, 0, sizeof(p));
158 path_phys[0] = 0;
159 path_info[0] = 0;
160
161 strcpy(uh_buf, docroot);
162
163 /* separate query string from url */
164 if ((pathptr = strchr(url, '?')) != NULL) {
165 if (pathptr[1]) {
166 p.query = path_query;
167 snprintf(path_query, sizeof(path_query), "%s",
168 pathptr + 1);
169 }
170
171 /* urldecode component w/o query */
172 if (pathptr > url) {
173 if (uh_urldecode(&uh_buf[docroot_len],
174 sizeof(uh_buf) - docroot_len - 1,
175 url, pathptr - url ) < 0)
176 return NULL;
177 }
178 }
179
180 /* no query string, decode all of url */
181 else if (uh_urldecode(&uh_buf[docroot_len],
182 sizeof(uh_buf) - docroot_len - 1,
183 url, strlen(url) ) < 0)
184 return NULL;
185
186 /* create canon path */
187 len = strlen(uh_buf);
188 slash = len && uh_buf[len - 1] == '/';
189 len = min(len, sizeof(path_phys) - 1);
190
191 for (i = len; i >= 0; i--) {
192 char ch = uh_buf[i];
193 bool exists;
194
195 if (ch != 0 && ch != '/')
196 continue;
197
198 uh_buf[i] = 0;
199 exists = !!canonpath(uh_buf, path_phys);
200 uh_buf[i] = ch;
201
202 if (!exists)
203 continue;
204
205 /* test current path */
206 if (stat(path_phys, &p.stat))
207 continue;
208
209 snprintf(path_info, sizeof(path_info), "%s", uh_buf + i);
210 break;
211 }
212
213 /* check whether found path is within docroot */
214 if (strncmp(path_phys, docroot, docroot_len) != 0 ||
215 (path_phys[docroot_len] != 0 &&
216 path_phys[docroot_len] != '/'))
217 return NULL;
218
219 /* is a regular file */
220 if (p.stat.st_mode & S_IFREG) {
221 p.root = docroot;
222 p.phys = path_phys;
223 p.name = &path_phys[docroot_len];
224 p.info = path_info[0] ? path_info : NULL;
225 return &p;
226 }
227
228 if (!(p.stat.st_mode & S_IFDIR))
229 return NULL;
230
231 if (path_info[0])
232 return NULL;
233
234 pathptr = path_phys + strlen(path_phys);
235
236 /* ensure trailing slash */
237 if (pathptr[-1] != '/') {
238 pathptr[0] = '/';
239 pathptr[1] = 0;
240 pathptr++;
241 }
242
243 /* if requested url resolves to a directory and a trailing slash
244 is missing in the request url, redirect the client to the same
245 url with trailing slash appended */
246 if (!slash) {
247 uh_http_header(cl, 302, "Found");
248 if (!uh_use_chunked(cl))
249 ustream_printf(cl->us, "Content-Length: 0\r\n");
250 ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n",
251 &path_phys[docroot_len],
252 p.query ? "?" : "",
253 p.query ? p.query : "");
254 uh_request_done(cl);
255 p.redirected = 1;
256 return &p;
257 }
258
259 /* try to locate index file */
260 len = path_phys + sizeof(path_phys) - pathptr - 1;
261 list_for_each_entry(idx, &index_files, list) {
262 if (strlen(idx->name) > len)
263 continue;
264
265 strcpy(pathptr, idx->name);
266 if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) {
267 memcpy(&p.stat, &s, sizeof(p.stat));
268 break;
269 }
270
271 *pathptr = 0;
272 }
273
274 p.root = docroot;
275 p.phys = path_phys;
276 p.name = &path_phys[docroot_len];
277
278 return p.phys ? &p : NULL;
279 }
280
281 static const char * uh_file_mime_lookup(const char *path)
282 {
283 const struct mimetype *m = &uh_mime_types[0];
284 const char *e;
285
286 while (m->extn) {
287 e = &path[strlen(path)-1];
288
289 while (e >= path) {
290 if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
291 return m->mime;
292
293 e--;
294 }
295
296 m++;
297 }
298
299 return "application/octet-stream";
300 }
301
302 static const char * uh_file_mktag(struct stat *s, char *buf, int len)
303 {
304 snprintf(buf, len, "\"%" PRIx64 "-%" PRIx64 "-%" PRIx64 "\"",
305 s->st_ino, s->st_size, (uint64_t)s->st_mtime);
306
307 return buf;
308 }
309
310 static time_t uh_file_date2unix(const char *date)
311 {
312 struct tm t;
313
314 memset(&t, 0, sizeof(t));
315
316 if (strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) != NULL)
317 return timegm(&t);
318
319 return 0;
320 }
321
322 static char * uh_file_unix2date(time_t ts, char *buf, int len)
323 {
324 struct tm *t = gmtime(&ts);
325
326 strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t);
327
328 return buf;
329 }
330
331 static char *uh_file_header(struct client *cl, int idx)
332 {
333 if (!cl->dispatch.file.hdr[idx])
334 return NULL;
335
336 return (char *) blobmsg_data(cl->dispatch.file.hdr[idx]);
337 }
338
339 static void uh_file_response_ok_hdrs(struct client *cl, struct stat *s)
340 {
341 char buf[128];
342
343 if (s) {
344 ustream_printf(cl->us, "ETag: %s\r\n", uh_file_mktag(s, buf, sizeof(buf)));
345 ustream_printf(cl->us, "Last-Modified: %s\r\n",
346 uh_file_unix2date(s->st_mtime, buf, sizeof(buf)));
347 }
348 ustream_printf(cl->us, "Date: %s\r\n",
349 uh_file_unix2date(time(NULL), buf, sizeof(buf)));
350 }
351
352 static void uh_file_response_200(struct client *cl, struct stat *s)
353 {
354 uh_http_header(cl, 200, "OK");
355 return uh_file_response_ok_hdrs(cl, s);
356 }
357
358 static void uh_file_response_304(struct client *cl, struct stat *s)
359 {
360 uh_http_header(cl, 304, "Not Modified");
361
362 return uh_file_response_ok_hdrs(cl, s);
363 }
364
365 static void uh_file_response_405(struct client *cl)
366 {
367 uh_http_header(cl, 405, "Method Not Allowed");
368 }
369
370 static void uh_file_response_412(struct client *cl)
371 {
372 uh_http_header(cl, 412, "Precondition Failed");
373 }
374
375 static bool uh_file_if_match(struct client *cl, struct stat *s)
376 {
377 char buf[128];
378 const char *tag = uh_file_mktag(s, buf, sizeof(buf));
379 char *hdr = uh_file_header(cl, HDR_IF_MATCH);
380 char *p;
381 int i;
382
383 if (!hdr)
384 return true;
385
386 p = &hdr[0];
387 for (i = 0; i < strlen(hdr); i++)
388 {
389 if ((hdr[i] == ' ') || (hdr[i] == ',')) {
390 hdr[i++] = 0;
391 p = &hdr[i];
392 } else if (!strcmp(p, "*") || !strcmp(p, tag)) {
393 return true;
394 }
395 }
396
397 uh_file_response_412(cl);
398 return false;
399 }
400
401 static int uh_file_if_modified_since(struct client *cl, struct stat *s)
402 {
403 char *hdr = uh_file_header(cl, HDR_IF_MODIFIED_SINCE);
404
405 if (!hdr)
406 return true;
407
408 if (uh_file_date2unix(hdr) >= s->st_mtime) {
409 uh_file_response_304(cl, s);
410 return false;
411 }
412
413 return true;
414 }
415
416 static int uh_file_if_none_match(struct client *cl, struct stat *s)
417 {
418 char buf[128];
419 const char *tag = uh_file_mktag(s, buf, sizeof(buf));
420 char *hdr = uh_file_header(cl, HDR_IF_NONE_MATCH);
421 char *p;
422 int i;
423
424 if (!hdr)
425 return true;
426
427 p = &hdr[0];
428 for (i = 0; i < strlen(hdr); i++) {
429 if ((hdr[i] == ' ') || (hdr[i] == ',')) {
430 hdr[i++] = 0;
431 p = &hdr[i];
432 } else if (!strcmp(p, "*") || !strcmp(p, tag)) {
433 if ((cl->request.method == UH_HTTP_MSG_GET) ||
434 (cl->request.method == UH_HTTP_MSG_HEAD))
435 uh_file_response_304(cl, s);
436 else
437 uh_file_response_412(cl);
438
439 return false;
440 }
441 }
442
443 return true;
444 }
445
446 static int uh_file_if_range(struct client *cl, struct stat *s)
447 {
448 char *hdr = uh_file_header(cl, HDR_IF_RANGE);
449
450 if (hdr) {
451 uh_file_response_412(cl);
452 return false;
453 }
454
455 return true;
456 }
457
458 static int uh_file_if_unmodified_since(struct client *cl, struct stat *s)
459 {
460 char *hdr = uh_file_header(cl, HDR_IF_UNMODIFIED_SINCE);
461
462 if (hdr && uh_file_date2unix(hdr) <= s->st_mtime) {
463 uh_file_response_412(cl);
464 return false;
465 }
466
467 return true;
468 }
469
470 static int dirent_cmp(const struct dirent **a, const struct dirent **b)
471 {
472 bool dir_a = !!((*a)->d_type & DT_DIR);
473 bool dir_b = !!((*b)->d_type & DT_DIR);
474
475 /* directories first */
476 if (dir_a != dir_b)
477 return dir_b - dir_a;
478
479 return alphasort(a, b);
480 }
481
482 static void list_entries(struct client *cl, struct dirent **files, int count,
483 const char *path, char *local_path)
484 {
485 const char *suffix = "/";
486 const char *type = "directory";
487 unsigned int mode = S_IXOTH;
488 struct stat s;
489 char *escaped;
490 char *file;
491 char buf[128];
492 int i;
493
494 file = local_path + strlen(local_path);
495 for (i = 0; i < count; i++) {
496 const char *name = files[i]->d_name;
497 bool dir = !!(files[i]->d_type & DT_DIR);
498
499 if (name[0] == '.' && name[1] == 0)
500 goto next;
501
502 sprintf(file, "%s", name);
503 if (stat(local_path, &s))
504 goto next;
505
506 if (!dir) {
507 suffix = "";
508 mode = S_IROTH;
509 type = uh_file_mime_lookup(local_path);
510 }
511
512 if (!(s.st_mode & mode))
513 goto next;
514
515 escaped = uh_htmlescape(name);
516
517 if (!escaped)
518 goto next;
519
520 uh_chunk_printf(cl,
521 "<li><strong><a href='%s%s%s'>%s</a>%s"
522 "</strong><br /><small>modified: %s"
523 "<br />%s - %.02f kbyte<br />"
524 "<br /></small></li>",
525 path, escaped, suffix,
526 escaped, suffix,
527 uh_file_unix2date(s.st_mtime, buf, sizeof(buf)),
528 type, s.st_size / 1024.0);
529
530 free(escaped);
531 *file = 0;
532 next:
533 free(files[i]);
534 }
535 }
536
537 static void uh_file_dirlist(struct client *cl, struct path_info *pi)
538 {
539 struct dirent **files = NULL;
540 char *escaped_path = uh_htmlescape(pi->name);
541 int count = 0;
542
543 if (!escaped_path)
544 {
545 uh_client_error(cl, 500, "Internal Server Error", "Out of memory");
546 return;
547 }
548
549 uh_file_response_200(cl, NULL);
550 ustream_printf(cl->us, "Content-Type: text/html; charset=%s\r\n\r\n",
551 conf.dirlist_charset ? conf.dirlist_charset : "UTF-8");
552
553 uh_chunk_printf(cl,
554 "<html><head><title>Index of %s</title></head>"
555 "<body><h1>Index of %s</h1><hr /><ol>",
556 escaped_path, escaped_path);
557
558 count = scandir(pi->phys, &files, NULL, dirent_cmp);
559 if (count > 0) {
560 strcpy(uh_buf, pi->phys);
561 list_entries(cl, files, count, escaped_path, uh_buf);
562 }
563 free(escaped_path);
564 free(files);
565
566 uh_chunk_printf(cl, "</ol><hr /></body></html>");
567 uh_request_done(cl);
568 }
569
570 static void file_write_cb(struct client *cl)
571 {
572 int fd = cl->dispatch.file.fd;
573 int r;
574
575 while (cl->us->w.data_bytes < 256) {
576 r = read(fd, uh_buf, sizeof(uh_buf));
577 if (r < 0) {
578 if (errno == EINTR)
579 continue;
580 }
581
582 if (!r) {
583 uh_request_done(cl);
584 return;
585 }
586
587 uh_chunk_write(cl, uh_buf, r);
588 }
589 }
590
591 static void uh_file_free(struct client *cl)
592 {
593 close(cl->dispatch.file.fd);
594 }
595
596 static void uh_file_data(struct client *cl, struct path_info *pi, int fd)
597 {
598 /* test preconditions */
599 if (!cl->dispatch.no_cache &&
600 (!uh_file_if_modified_since(cl, &pi->stat) ||
601 !uh_file_if_match(cl, &pi->stat) ||
602 !uh_file_if_range(cl, &pi->stat) ||
603 !uh_file_if_unmodified_since(cl, &pi->stat) ||
604 !uh_file_if_none_match(cl, &pi->stat))) {
605 ustream_printf(cl->us, "\r\n");
606 uh_request_done(cl);
607 close(fd);
608 return;
609 }
610
611 /* write status */
612 uh_file_response_200(cl, &pi->stat);
613
614 ustream_printf(cl->us, "Content-Type: %s\r\n",
615 uh_file_mime_lookup(pi->name));
616
617 ustream_printf(cl->us, "Content-Length: %" PRIu64 "\r\n\r\n",
618 pi->stat.st_size);
619
620
621 /* send body */
622 if (cl->request.method == UH_HTTP_MSG_HEAD) {
623 uh_request_done(cl);
624 close(fd);
625 return;
626 }
627
628 cl->dispatch.file.fd = fd;
629 cl->dispatch.write_cb = file_write_cb;
630 cl->dispatch.free = uh_file_free;
631 cl->dispatch.close_fds = uh_file_free;
632 file_write_cb(cl);
633 }
634
635 static bool __handle_file_request(struct client *cl, char *url, bool is_error_handler);
636
637 static void uh_file_request(struct client *cl, const char *url,
638 struct path_info *pi, struct blob_attr **tb)
639 {
640 int fd;
641 struct http_request *req = &cl->request;
642 char *error_handler, *escaped_url;
643
644 switch (cl->request.method) {
645 case UH_HTTP_MSG_GET:
646 case UH_HTTP_MSG_POST:
647 case UH_HTTP_MSG_HEAD:
648 case UH_HTTP_MSG_OPTIONS:
649 break;
650
651 default:
652 uh_file_response_405(cl);
653 ustream_printf(cl->us, "\r\n");
654 uh_request_done(cl);
655 return;
656 }
657
658 if (!(pi->stat.st_mode & S_IROTH))
659 goto error;
660
661 if (pi->stat.st_mode & S_IFREG) {
662 fd = open(pi->phys, O_RDONLY);
663 if (fd < 0)
664 goto error;
665
666 req->disable_chunked = true;
667 cl->dispatch.file.hdr = tb;
668 uh_file_data(cl, pi, fd);
669 cl->dispatch.file.hdr = NULL;
670 return;
671 }
672
673 if ((pi->stat.st_mode & S_IFDIR)) {
674 if (conf.no_dirlists)
675 goto error;
676
677 uh_file_dirlist(cl, pi);
678 return;
679 }
680
681 error:
682 /* check for a previously set 403 redirect status to prevent infinite
683 recursion when the error page itself lacks sufficient permissions */
684 if (conf.error_handler && req->redirect_status != 403) {
685 req->redirect_status = 403;
686 error_handler = alloca(strlen(conf.error_handler) + 1);
687 strcpy(error_handler, conf.error_handler);
688 if (__handle_file_request(cl, error_handler, true))
689 return;
690 }
691
692 escaped_url = uh_htmlescape(url);
693
694 uh_client_error(cl, 403, "Forbidden",
695 "You don't have permission to access %s on this server.",
696 escaped_url ? escaped_url : "the url");
697
698 if (escaped_url)
699 free(escaped_url);
700 }
701
702 void uh_dispatch_add(struct dispatch_handler *d)
703 {
704 list_add_tail(&d->list, &dispatch_handlers);
705 }
706
707 static struct dispatch_handler *
708 dispatch_find(const char *url, struct path_info *pi)
709 {
710 struct dispatch_handler *d;
711
712 list_for_each_entry(d, &dispatch_handlers, list) {
713 if (pi) {
714 if (d->check_url)
715 continue;
716
717 if (d->check_path(pi, url))
718 return d;
719 } else {
720 if (d->check_path)
721 continue;
722
723 if (d->check_url(url))
724 return d;
725 }
726 }
727
728 return NULL;
729 }
730
731 static void
732 uh_invoke_script(struct client *cl, struct dispatch_handler *d, char *url, struct path_info *pi)
733 {
734 n_requests++;
735 d->handle_request(cl, url, pi);
736 }
737
738 static void uh_complete_request(struct client *cl)
739 {
740 struct deferred_request *dr;
741
742 n_requests--;
743
744 while (!list_empty(&pending_requests)) {
745 if (n_requests >= conf.max_script_requests)
746 return;
747
748 dr = list_first_entry(&pending_requests, struct deferred_request, list);
749 list_del(&dr->list);
750
751 cl = dr->cl;
752 dr->called = true;
753 cl->dispatch.data_blocked = false;
754 uh_invoke_script(cl, dr->d, dr->url, dr->path ? &dr->pi : NULL);
755 client_poll_post_data(cl);
756 ustream_poll(cl->us);
757 }
758 }
759
760
761 static void
762 uh_free_pending_request(struct client *cl)
763 {
764 struct deferred_request *dr = cl->dispatch.req_data;
765
766 if (dr->called)
767 uh_complete_request(cl);
768 else
769 list_del(&dr->list);
770 free(dr);
771 }
772
773 static int field_len(const char *ptr)
774 {
775 if (!ptr)
776 return 0;
777
778 return strlen(ptr) + 1;
779 }
780
781 #define path_info_fields \
782 _field(root) \
783 _field(phys) \
784 _field(name) \
785 _field(info) \
786 _field(query)
787
788 static void
789 uh_defer_script(struct client *cl, struct dispatch_handler *d, char *url, struct path_info *pi)
790 {
791 struct deferred_request *dr;
792 char *_url, *_root, *_phys, *_name, *_info, *_query;
793
794 cl->dispatch.req_free = uh_free_pending_request;
795
796 if (pi) {
797 /* allocate enough memory to duplicate all path_info strings in one block */
798 #undef _field
799 #define _field(_name) &_##_name, field_len(pi->_name),
800 dr = calloc_a(sizeof(*dr), &_url, strlen(url) + 1, path_info_fields NULL);
801
802 memcpy(&dr->pi, pi, sizeof(*pi));
803 dr->path = true;
804
805 /* copy all path_info strings */
806 #undef _field
807 #define _field(_name) if (pi->_name) dr->pi._name = strcpy(_##_name, pi->_name);
808 path_info_fields
809 } else {
810 dr = calloc_a(sizeof(*dr), &_url, strlen(url) + 1, NULL);
811 }
812
813 cl->dispatch.req_data = dr;
814 cl->dispatch.data_blocked = true;
815 dr->url = strcpy(_url, url);
816 dr->cl = cl;
817 dr->d = d;
818 list_add(&dr->list, &pending_requests);
819 }
820
821 static void
822 uh_invoke_handler(struct client *cl, struct dispatch_handler *d, char *url, struct path_info *pi)
823 {
824 if (!d->script)
825 return d->handle_request(cl, url, pi);
826
827 if (n_requests >= conf.max_script_requests)
828 return uh_defer_script(cl, d, url, pi);
829
830 cl->dispatch.req_free = uh_complete_request;
831 uh_invoke_script(cl, d, url, pi);
832 }
833
834 static bool __handle_file_request(struct client *cl, char *url, bool is_error_handler)
835 {
836 static const struct blobmsg_policy hdr_policy[__HDR_MAX] = {
837 [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING },
838 [HDR_IF_MODIFIED_SINCE] = { "if-modified-since", BLOBMSG_TYPE_STRING },
839 [HDR_IF_UNMODIFIED_SINCE] = { "if-unmodified-since", BLOBMSG_TYPE_STRING },
840 [HDR_IF_MATCH] = { "if-match", BLOBMSG_TYPE_STRING },
841 [HDR_IF_NONE_MATCH] = { "if-none-match", BLOBMSG_TYPE_STRING },
842 [HDR_IF_RANGE] = { "if-range", BLOBMSG_TYPE_STRING },
843 };
844 struct dispatch_handler *d;
845 struct blob_attr *tb[__HDR_MAX];
846 struct path_info *pi;
847 char *user, *pass, *auth;
848
849 if (is_error_handler) {
850 d = dispatch_find(url, NULL);
851
852 if (d) {
853 uh_invoke_handler(cl, d, url, NULL);
854
855 return true;
856 }
857 }
858
859 pi = uh_path_lookup(cl, url);
860 if (!pi)
861 return false;
862
863 if (pi->redirected)
864 return true;
865
866 blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head));
867
868 auth = tb[HDR_AUTHORIZATION] ? blobmsg_data(tb[HDR_AUTHORIZATION]) : NULL;
869
870 if (!uh_auth_check(cl, pi->name, auth, &user, &pass))
871 return true;
872
873 if (user && pass) {
874 blobmsg_add_string(&cl->hdr, "http-auth-user", user);
875 blobmsg_add_string(&cl->hdr, "http-auth-pass", pass);
876 }
877
878 d = dispatch_find(url, pi);
879 if (d)
880 uh_invoke_handler(cl, d, url, pi);
881 else
882 uh_file_request(cl, url, pi, tb);
883
884 return true;
885 }
886
887 static char *uh_handle_alias(char *old_url)
888 {
889 struct alias *alias;
890 static char *new_url;
891 static int url_len;
892
893 if (!list_empty(&conf.cgi_alias)) list_for_each_entry(alias, &conf.cgi_alias, list) {
894 int old_len;
895 int new_len;
896 int path_len = 0;
897
898 if (!uh_path_match(alias->alias, old_url))
899 continue;
900
901 if (alias->path)
902 path_len = strlen(alias->path);
903
904 old_len = strlen(old_url) + 1;
905 new_len = old_len + MAX(conf.cgi_prefix_len, path_len);
906
907 if (new_len > url_len) {
908 new_url = realloc(new_url, new_len);
909 url_len = new_len;
910 }
911
912 *new_url = '\0';
913
914 if (alias->path)
915 strcpy(new_url, alias->path);
916 else if (conf.cgi_prefix)
917 strcpy(new_url, conf.cgi_prefix);
918 strcat(new_url, old_url);
919
920 return new_url;
921 }
922 return old_url;
923 }
924
925 void uh_handle_request(struct client *cl)
926 {
927 struct http_request *req = &cl->request;
928 struct dispatch_handler *d;
929 char *url = blobmsg_data(blob_data(cl->hdr.head));
930 char *error_handler, *escaped_url;
931
932 blob_buf_init(&cl->hdr_response, 0);
933 url = uh_handle_alias(url);
934
935 uh_handler_run(cl, &url, false);
936 if (!url)
937 return;
938
939 req->redirect_status = 200;
940 d = dispatch_find(url, NULL);
941 if (d)
942 return uh_invoke_handler(cl, d, url, NULL);
943
944 if (__handle_file_request(cl, url, false))
945 return;
946
947 if (uh_handler_run(cl, &url, true)) {
948 if (!url)
949 return;
950
951 uh_handler_run(cl, &url, false);
952 if (__handle_file_request(cl, url, false))
953 return;
954 }
955
956 req->redirect_status = 404;
957 if (conf.error_handler) {
958 error_handler = alloca(strlen(conf.error_handler) + 1);
959 strcpy(error_handler, conf.error_handler);
960 if (__handle_file_request(cl, error_handler, true))
961 return;
962 }
963
964 escaped_url = uh_htmlescape(url);
965
966 uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.",
967 escaped_url ? escaped_url : "");
968
969 if (escaped_url)
970 free(escaped_url);
971 }