• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uhttpd/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 }
972 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt