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 } 972
This page was automatically generated by LXR 0.3.1. • OpenWrt