file.c 21 KB

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