file.c 21 KB

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