X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=file.c;h=a4d9b1d4b2402e7dbec8dec893db224ef99b2f83;hp=f1f5d6d9eb210f349b8f164b4c5e27e6e1b8da1f;hb=99957f6c6ff429f17d6d6002fef4d4ef7de8844a;hpb=30fff08a44094df9b775f2e4bf9f5abceb847415 diff --git a/file.c b/file.c index f1f5d6d..a4d9b1d 100644 --- a/file.c +++ b/file.c @@ -1,48 +1,60 @@ /* * uhttpd - Tiny single-threaded httpd * - * Copyright (C) 2010-2012 Jo-Philipp Wich - * Copyright (C) 2012 Felix Fietkau + * Copyright (C) 2010-2013 Jo-Philipp Wich + * Copyright (C) 2013 Felix Fietkau * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define _BSD_SOURCE +#define _DARWIN_C_SOURCE +#define _XOPEN_SOURCE 700 + #include #include +#include +#include +#include +#include #include #include "uhttpd.h" -#include "uhttpd-mimetypes.h" +#include "mimetypes.h" + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) static LIST_HEAD(index_files); +static LIST_HEAD(dispatch_handlers); +static LIST_HEAD(pending_requests); +static int n_requests; -struct index_file { +struct deferred_request { struct list_head list; - const char *name; + struct dispatch_handler *d; + struct client *cl; + struct path_info pi; + bool called, path; }; -struct path_info { - char *root; - char *phys; - char *name; - char *info; - char *query; - int redirected; - struct stat stat; +struct index_file { + struct list_head list; + const char *name; }; enum file_hdr { + HDR_AUTHORIZATION, HDR_IF_MODIFIED_SINCE, HDR_IF_UNMODIFIED_SINCE, HDR_IF_MATCH, @@ -62,22 +74,14 @@ void uh_index_add(const char *filename) static char * canonpath(const char *path, char *path_resolved) { - char path_copy[PATH_MAX]; - char *path_cpy = path_copy; + const char *path_cpy = path; char *path_res = path_resolved; - struct stat s; - /* relative -> absolute */ - if (*path != '/') { - getcwd(path_copy, PATH_MAX); - strncat(path_copy, "/", PATH_MAX - strlen(path_copy)); - strncat(path_copy, path, PATH_MAX - strlen(path_copy)); - } else { - strncpy(path_copy, path, PATH_MAX); - } + if (conf.no_symlinks) + return realpath(path, path_resolved); /* normalize */ - while ((*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2))) { + while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) { if (*path_cpy != '/') goto next; @@ -117,29 +121,26 @@ next: *path_res = '\0'; - /* test access */ - if (!stat(path_resolved, &s) && (s.st_mode & S_IROTH)) - return path_resolved; - - return NULL; + return path_resolved; } /* Returns NULL on error. ** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning ** NULL here causes 404 [Not Found], but that's not too unreasonable. */ -struct path_info * uh_path_lookup(struct client *cl, const char *url) +struct path_info * +uh_path_lookup(struct client *cl, const char *url) { static char path_phys[PATH_MAX]; static char path_info[PATH_MAX]; static struct path_info p; - char buffer[UH_LIMIT_MSGHEAD]; - char *docroot = conf.docroot; + const char *docroot = conf.docroot; + int docroot_len = strlen(docroot); char *pathptr = NULL; + bool slash; - int slash = 0; - int no_sym = conf.no_symlinks; int i = 0; + int len; struct stat s; struct index_file *idx; @@ -147,14 +148,11 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) if (url == NULL) return NULL; - memset(path_phys, 0, sizeof(path_phys)); - memset(path_info, 0, sizeof(path_info)); - memset(buffer, 0, sizeof(buffer)); memset(&p, 0, sizeof(p)); + path_phys[0] = 0; + path_info[0] = 0; - /* copy docroot */ - memcpy(buffer, docroot, - min(strlen(docroot), sizeof(buffer) - 1)); + strcpy(uh_buf, docroot); /* separate query string from url */ if ((pathptr = strchr(url, '?')) != NULL) { @@ -162,104 +160,117 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) /* urldecode component w/o query */ if (pathptr > url) { - if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, - url, pathptr - url ) < 0) - return NULL; /* bad URL */ + if (uh_urldecode(&uh_buf[docroot_len], + sizeof(uh_buf) - docroot_len - 1, + url, pathptr - url ) < 0) + return NULL; } } /* no query string, decode all of url */ - else if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, + else if (uh_urldecode(&uh_buf[docroot_len], + sizeof(uh_buf) - docroot_len - 1, url, strlen(url) ) < 0) - return NULL; /* bad URL */ + return NULL; /* create canon path */ - for (i = strlen(buffer), slash = (buffer[max(0, i-1)] == '/'); i >= 0; i--) { - if ((buffer[i] == 0) || (buffer[i] == '/')) { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1)); - - if (no_sym ? realpath(path_info, path_phys) - : canonpath(path_info, path_phys)) { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, &buffer[i], - min(strlen(buffer) - i, sizeof(path_info) - 1)); - - break; - } - } + len = strlen(uh_buf); + slash = len && uh_buf[len - 1] == '/'; + len = min(len, sizeof(path_phys) - 1); + + for (i = len; i >= 0; i--) { + char ch = uh_buf[i]; + bool exists; + + if (ch != 0 && ch != '/') + continue; + + uh_buf[i] = 0; + exists = !!canonpath(uh_buf, path_phys); + uh_buf[i] = ch; + + if (!exists) + continue; + + /* test current path */ + if (stat(path_phys, &p.stat)) + continue; + + snprintf(path_info, sizeof(path_info), "%s", uh_buf + i); + break; } /* check whether found path is within docroot */ - if (strncmp(path_phys, docroot, strlen(docroot)) || - ((path_phys[strlen(docroot)] != 0) && - (path_phys[strlen(docroot)] != '/'))) + if (strncmp(path_phys, docroot, docroot_len) != 0 || + (path_phys[docroot_len] != 0 && + path_phys[docroot_len] != '/')) return NULL; - /* test current path */ - if (!stat(path_phys, &p.stat)) { - /* is a regular file */ - if (p.stat.st_mode & S_IFREG) { - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; - p.info = path_info[0] ? path_info : NULL; - } + /* is a regular file */ + if (p.stat.st_mode & S_IFREG) { + p.root = docroot; + p.phys = path_phys; + p.name = &path_phys[docroot_len]; + p.info = path_info[0] ? path_info : NULL; + return &p; + } - /* is a directory */ - else if ((p.stat.st_mode & S_IFDIR) && !strlen(path_info)) { - /* ensure trailing slash */ - if (path_phys[strlen(path_phys)-1] != '/') - path_phys[strlen(path_phys)] = '/'; - - /* try to locate index file */ - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, path_phys, sizeof(buffer)); - pathptr = &buffer[strlen(buffer)]; - - /* if requested url resolves to a directory and a trailing slash - is missing in the request url, redirect the client to the same - url with trailing slash appended */ - if (!slash) { - uh_http_header(cl, 302, "Found"); - ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n", - &path_phys[strlen(docroot)], - p.query ? "?" : "", - p.query ? p.query : ""); - uh_request_done(cl); - p.redirected = 1; - } else { - list_for_each_entry(idx, &index_files, list) { - strncat(buffer, idx->name, sizeof(buffer)); - - if (!stat(buffer, &s) && (s.st_mode & S_IFREG)) { - memcpy(path_phys, buffer, sizeof(path_phys)); - memcpy(&p.stat, &s, sizeof(p.stat)); - break; - } - - *pathptr = 0; - } - } + if (!(p.stat.st_mode & S_IFDIR)) + return NULL; + + if (path_info[0]) + return NULL; + + pathptr = path_phys + strlen(path_phys); + + /* ensure trailing slash */ + if (pathptr[-1] != '/') { + pathptr[0] = '/'; + pathptr[1] = 0; + pathptr++; + } + + /* if requested url resolves to a directory and a trailing slash + is missing in the request url, redirect the client to the same + url with trailing slash appended */ + if (!slash) { + uh_http_header(cl, 302, "Found"); + if (!uh_use_chunked(cl)) + ustream_printf(cl->us, "Content-Length: 0\r\n"); + ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n", + &path_phys[docroot_len], + p.query ? "?" : "", + p.query ? p.query : ""); + uh_request_done(cl); + p.redirected = 1; + return &p; + } - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; + /* try to locate index file */ + len = path_phys + sizeof(path_phys) - pathptr - 1; + list_for_each_entry(idx, &index_files, list) { + if (strlen(idx->name) > len) + continue; + + strcpy(pathptr, idx->name); + if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) { + memcpy(&p.stat, &s, sizeof(p.stat)); + break; } + + *pathptr = 0; } + p.root = docroot; + p.phys = path_phys; + p.name = &path_phys[docroot_len]; + return p.phys ? &p : NULL; } -#ifdef __APPLE__ -time_t timegm (struct tm *tm); -#endif - static const char * uh_file_mime_lookup(const char *path) { - struct mimetype *m = &uh_mime_types[0]; + const struct mimetype *m = &uh_mime_types[0]; const char *e; while (m->extn) { @@ -278,16 +289,12 @@ static const char * uh_file_mime_lookup(const char *path) return "application/octet-stream"; } -static const char * uh_file_mktag(struct stat *s) +static const char * uh_file_mktag(struct stat *s, char *buf, int len) { - static char tag[128]; - - snprintf(tag, sizeof(tag), "\"%x-%x-%x\"", - (unsigned int) s->st_ino, - (unsigned int) s->st_size, - (unsigned int) s->st_mtime); + snprintf(buf, len, "\"%" PRIx64 "-%" PRIx64 "-%" PRIx64 "\"", + s->st_ino, s->st_size, (uint64_t)s->st_mtime); - return tag; + return buf; } static time_t uh_file_date2unix(const char *date) @@ -302,32 +309,34 @@ static time_t uh_file_date2unix(const char *date) return 0; } -static char * uh_file_unix2date(time_t ts) +static char * uh_file_unix2date(time_t ts, char *buf, int len) { - static char str[128]; struct tm *t = gmtime(&ts); - strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S GMT", t); + strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t); - return str; + return buf; } static char *uh_file_header(struct client *cl, int idx) { - if (!cl->data.file.hdr[idx]) + if (!cl->dispatch.file.hdr[idx]) return NULL; - return (char *) blobmsg_data(cl->data.file.hdr[idx]); + return (char *) blobmsg_data(cl->dispatch.file.hdr[idx]); } static void uh_file_response_ok_hdrs(struct client *cl, struct stat *s) { + char buf[128]; + if (s) { - ustream_printf(cl->us, "ETag: %s\r\n", uh_file_mktag(s)); + ustream_printf(cl->us, "ETag: %s\r\n", uh_file_mktag(s, buf, sizeof(buf))); ustream_printf(cl->us, "Last-Modified: %s\r\n", - uh_file_unix2date(s->st_mtime)); + uh_file_unix2date(s->st_mtime, buf, sizeof(buf))); } - ustream_printf(cl->us, "Date: %s\r\n", uh_file_unix2date(time(NULL))); + ustream_printf(cl->us, "Date: %s\r\n", + uh_file_unix2date(time(NULL), buf, sizeof(buf))); } static void uh_file_response_200(struct client *cl, struct stat *s) @@ -350,7 +359,8 @@ static void uh_file_response_412(struct client *cl) static bool uh_file_if_match(struct client *cl, struct stat *s) { - const char *tag = uh_file_mktag(s); + char buf[128]; + const char *tag = uh_file_mktag(s, buf, sizeof(buf)); char *hdr = uh_file_header(cl, HDR_IF_MATCH); char *p; int i; @@ -390,7 +400,8 @@ static int uh_file_if_modified_since(struct client *cl, struct stat *s) static int uh_file_if_none_match(struct client *cl, struct stat *s) { - const char *tag = uh_file_mktag(s); + char buf[128]; + const char *tag = uh_file_mktag(s, buf, sizeof(buf)); char *hdr = uh_file_header(cl, HDR_IF_NONE_MATCH); char *p; int i; @@ -441,20 +452,70 @@ static int uh_file_if_unmodified_since(struct client *cl, struct stat *s) return true; } +static int dirent_cmp(const struct dirent **a, const struct dirent **b) +{ + bool dir_a = !!((*a)->d_type & DT_DIR); + bool dir_b = !!((*b)->d_type & DT_DIR); + + /* directories first */ + if (dir_a != dir_b) + return dir_b - dir_a; -static int uh_file_scandir_filter_dir(const struct dirent *e) + return alphasort(a, b); +} + +static void list_entries(struct client *cl, struct dirent **files, int count, + const char *path, char *local_path) { - return strcmp(e->d_name, ".") ? 1 : 0; + const char *suffix = "/"; + const char *type = "directory"; + unsigned int mode = S_IXOTH; + struct stat s; + char *file; + char buf[128]; + int i; + + file = local_path + strlen(local_path); + for (i = 0; i < count; i++) { + const char *name = files[i]->d_name; + bool dir = !!(files[i]->d_type & DT_DIR); + + if (name[0] == '.' && name[1] == 0) + goto next; + + sprintf(file, "%s", name); + if (stat(local_path, &s)) + goto next; + + if (!dir) { + suffix = ""; + mode = S_IROTH; + type = uh_file_mime_lookup(local_path); + } + + if (!(s.st_mode & mode)) + goto next; + + uh_chunk_printf(cl, + "
  • %s%s" + "
    modified: %s" + "
    %s - %.02f kbyte
    " + "
  • ", + path, name, suffix, + name, suffix, + uh_file_unix2date(s.st_mtime, buf, sizeof(buf)), + type, s.st_size / 1024.0); + + *file = 0; +next: + free(files[i]); + } } static void uh_file_dirlist(struct client *cl, struct path_info *pi) { - int i; - int count = 0; - char filename[PATH_MAX]; - char *pathptr; struct dirent **files = NULL; - struct stat s; + int count = 0; uh_file_response_200(cl, NULL); ustream_printf(cl->us, "Content-Type: text/html\r\n\r\n"); @@ -464,75 +525,24 @@ static void uh_file_dirlist(struct client *cl, struct path_info *pi) "

    Index of %s


      ", pi->name, pi->name); - if ((count = scandir(pi->phys, &files, uh_file_scandir_filter_dir, - alphasort)) > 0) - { - memset(filename, 0, sizeof(filename)); - memcpy(filename, pi->phys, sizeof(filename)); - pathptr = &filename[strlen(filename)]; - - /* list subdirs */ - for (i = 0; i < count; i++) { - strncat(filename, files[i]->d_name, - sizeof(filename) - strlen(files[i]->d_name)); - - if (!stat(filename, &s) && - (s.st_mode & S_IFDIR) && (s.st_mode & S_IXOTH)) - uh_chunk_printf(cl, - "
    1. %s/" - "
      modified: %s" - "
      directory - %.02f kbyte
      " - "
    2. ", - pi->name, files[i]->d_name, - files[i]->d_name, - uh_file_unix2date(s.st_mtime), - s.st_size / 1024.0); - - *pathptr = 0; - } - - /* list files */ - for (i = 0; i < count; i++) { - strncat(filename, files[i]->d_name, - sizeof(filename) - strlen(files[i]->d_name)); - - if (!stat(filename, &s) && - !(s.st_mode & S_IFDIR) && (s.st_mode & S_IROTH)) - uh_chunk_printf(cl, - "
    3. %s" - "
      modified: %s" - "
      %s - %.02f kbyte
      " - "
    4. ", - pi->name, files[i]->d_name, - files[i]->d_name, - uh_file_unix2date(s.st_mtime), - uh_file_mime_lookup(filename), - s.st_size / 1024.0); - - *pathptr = 0; - } + count = scandir(pi->phys, &files, NULL, dirent_cmp); + if (count > 0) { + strcpy(uh_buf, pi->phys); + list_entries(cl, files, count, pi->name, uh_buf); } + free(files); uh_chunk_printf(cl, "

    "); uh_request_done(cl); - - if (files) - { - for (i = 0; i < count; i++) - free(files[i]); - - free(files); - } } static void file_write_cb(struct client *cl) { - char buf[512]; - int fd = cl->data.file.fd; + int fd = cl->dispatch.file.fd; int r; while (cl->us->w.data_bytes < 256) { - r = read(fd, buf, sizeof(buf)); + r = read(fd, uh_buf, sizeof(uh_buf)); if (r < 0) { if (errno == EINTR) continue; @@ -543,23 +553,25 @@ static void file_write_cb(struct client *cl) return; } - uh_chunk_write(cl, buf, r); + uh_chunk_write(cl, uh_buf, r); } } static void uh_file_free(struct client *cl) { - close(cl->data.file.fd); + close(cl->dispatch.file.fd); } static void uh_file_data(struct client *cl, struct path_info *pi, int fd) { /* test preconditions */ - if (!uh_file_if_modified_since(cl, &pi->stat) || - !uh_file_if_match(cl, &pi->stat) || - !uh_file_if_range(cl, &pi->stat) || - !uh_file_if_unmodified_since(cl, &pi->stat) || - !uh_file_if_none_match(cl, &pi->stat)) { + if (!cl->dispatch.no_cache && + (!uh_file_if_modified_since(cl, &pi->stat) || + !uh_file_if_match(cl, &pi->stat) || + !uh_file_if_range(cl, &pi->stat) || + !uh_file_if_unmodified_since(cl, &pi->stat) || + !uh_file_if_none_match(cl, &pi->stat))) { + ustream_printf(cl->us, "\r\n"); uh_request_done(cl); close(fd); return; @@ -571,7 +583,7 @@ static void uh_file_data(struct client *cl, struct path_info *pi, int fd) ustream_printf(cl->us, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name)); - ustream_printf(cl->us, "Content-Length: %i\r\n\r\n", + ustream_printf(cl->us, "Content-Length: %" PRIu64 "\r\n\r\n", pi->stat.st_size); @@ -582,49 +594,311 @@ static void uh_file_data(struct client *cl, struct path_info *pi, int fd) return; } - cl->data.file.fd = fd; - cl->dispatch_write_cb = file_write_cb; - cl->dispatch_free = uh_file_free; + cl->dispatch.file.fd = fd; + cl->dispatch.write_cb = file_write_cb; + cl->dispatch.free = uh_file_free; + cl->dispatch.close_fds = uh_file_free; file_write_cb(cl); } -static void uh_file_request(struct client *cl, struct path_info *pi) +static bool __handle_file_request(struct client *cl, char *url); + +static void uh_file_request(struct client *cl, const char *url, + struct path_info *pi, struct blob_attr **tb) +{ + int fd; + struct http_request *req = &cl->request; + char *error_handler; + + if (!(pi->stat.st_mode & S_IROTH)) + goto error; + + if (pi->stat.st_mode & S_IFREG) { + fd = open(pi->phys, O_RDONLY); + if (fd < 0) + goto error; + + req->disable_chunked = true; + cl->dispatch.file.hdr = tb; + uh_file_data(cl, pi, fd); + cl->dispatch.file.hdr = NULL; + return; + } + + if ((pi->stat.st_mode & S_IFDIR)) { + if (conf.no_dirlists) + goto error; + + uh_file_dirlist(cl, pi); + return; + } + +error: + /* check for a previously set 403 redirect status to prevent infinite + recursion when the error page itself lacks sufficient permissions */ + if (conf.error_handler && req->redirect_status != 403) { + req->redirect_status = 403; + error_handler = alloca(strlen(conf.error_handler) + 1); + strcpy(error_handler, conf.error_handler); + if (__handle_file_request(cl, error_handler)) + return; + } + + uh_client_error(cl, 403, "Forbidden", + "You don't have permission to access %s on this server.", + url); +} + +void uh_dispatch_add(struct dispatch_handler *d) +{ + list_add_tail(&d->list, &dispatch_handlers); +} + +static struct dispatch_handler * +dispatch_find(const char *url, struct path_info *pi) +{ + struct dispatch_handler *d; + + list_for_each_entry(d, &dispatch_handlers, list) { + if (pi) { + if (d->check_url) + continue; + + if (d->check_path(pi, url)) + return d; + } else { + if (d->check_path) + continue; + + if (d->check_url(url)) + return d; + } + } + + return NULL; +} + +static void +uh_invoke_script(struct client *cl, struct dispatch_handler *d, struct path_info *pi) +{ + char *url = blobmsg_data(blob_data(cl->hdr.head)); + + n_requests++; + d->handle_request(cl, url, pi); +} + +static void uh_complete_request(struct client *cl) +{ + struct deferred_request *dr; + + n_requests--; + + while (!list_empty(&pending_requests)) { + if (n_requests >= conf.max_script_requests) + return; + + dr = list_first_entry(&pending_requests, struct deferred_request, list); + list_del(&dr->list); + + cl = dr->cl; + dr->called = true; + cl->dispatch.data_blocked = false; + uh_invoke_script(cl, dr->d, dr->path ? &dr->pi : NULL); + client_poll_post_data(cl); + } +} + + +static void +uh_free_pending_request(struct client *cl) +{ + struct deferred_request *dr = cl->dispatch.req_data; + + if (dr->called) + uh_complete_request(cl); + else + list_del(&dr->list); + free(dr); +} + +static int field_len(const char *ptr) +{ + if (!ptr) + return 0; + + return strlen(ptr) + 1; +} + +#define path_info_fields \ + _field(root) \ + _field(phys) \ + _field(name) \ + _field(info) \ + _field(query) + +static void +uh_defer_script(struct client *cl, struct dispatch_handler *d, struct path_info *pi) +{ + struct deferred_request *dr; + char *_root, *_phys, *_name, *_info, *_query; + + cl->dispatch.req_free = uh_free_pending_request; + + if (pi) { + /* allocate enough memory to duplicate all path_info strings in one block */ +#undef _field +#define _field(_name) &_##_name, field_len(pi->_name), + dr = calloc_a(sizeof(*dr), path_info_fields NULL); + + memcpy(&dr->pi, pi, sizeof(*pi)); + dr->path = true; + + /* copy all path_info strings */ +#undef _field +#define _field(_name) if (pi->_name) dr->pi._name = strcpy(_##_name, pi->_name); + path_info_fields + } else { + dr = calloc(1, sizeof(*dr)); + } + + cl->dispatch.req_data = dr; + cl->dispatch.data_blocked = true; + dr->cl = cl; + dr->d = d; + list_add(&dr->list, &pending_requests); +} + +static void +uh_invoke_handler(struct client *cl, struct dispatch_handler *d, char *url, struct path_info *pi) +{ + if (!d->script) + return d->handle_request(cl, url, pi); + + if (n_requests >= conf.max_script_requests) + return uh_defer_script(cl, d, pi); + + cl->dispatch.req_free = uh_complete_request; + uh_invoke_script(cl, d, pi); +} + +static bool __handle_file_request(struct client *cl, char *url) { static const struct blobmsg_policy hdr_policy[__HDR_MAX] = { + [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING }, [HDR_IF_MODIFIED_SINCE] = { "if-modified-since", BLOBMSG_TYPE_STRING }, [HDR_IF_UNMODIFIED_SINCE] = { "if-unmodified-since", BLOBMSG_TYPE_STRING }, [HDR_IF_MATCH] = { "if-match", BLOBMSG_TYPE_STRING }, [HDR_IF_NONE_MATCH] = { "if-none-match", BLOBMSG_TYPE_STRING }, [HDR_IF_RANGE] = { "if-range", BLOBMSG_TYPE_STRING }, }; + struct dispatch_handler *d; struct blob_attr *tb[__HDR_MAX]; - int fd; + struct path_info *pi; + char *user, *pass; + + pi = uh_path_lookup(cl, url); + if (!pi) + return false; + + if (pi->redirected) + return true; blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head)); + if (tb[HDR_AUTHORIZATION]) { + if (!uh_auth_check(cl, pi->name, blobmsg_data(tb[HDR_AUTHORIZATION]), &user, &pass)) + return true; - cl->data.file.hdr = tb; - if ((pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0)) - uh_file_data(cl, pi, fd); - else if ((pi->stat.st_mode & S_IFDIR) && !conf.no_dirlists) - uh_file_dirlist(cl, pi); + if (user && pass) { + blobmsg_add_string(&cl->hdr, "http-auth-user", user); + blobmsg_add_string(&cl->hdr, "http-auth-pass", pass); + } + } + + d = dispatch_find(url, pi); + if (d) + uh_invoke_handler(cl, d, url, pi); else - uh_client_error(cl, 403, "Forbidden", - "Access to this resource is forbidden"); - cl->data.file.hdr = NULL; + uh_file_request(cl, url, pi, tb); + + return true; } -void uh_handle_file_request(struct client *cl) +static char *uh_handle_alias(char *old_url) { - struct path_info *pi; + struct alias *alias; + static char *new_url; + static int url_len; - pi = uh_path_lookup(cl, cl->request.url); - if (!pi) { - uh_request_done(cl); - return; + if (!list_empty(&conf.cgi_alias)) list_for_each_entry(alias, &conf.cgi_alias, list) { + int old_len; + int new_len; + int path_len = 0; + + if (!uh_path_match(alias->alias, old_url)) + continue; + + if (alias->path) + path_len = strlen(alias->path); + + old_len = strlen(old_url) + 1; + new_len = old_len + MAX(conf.cgi_prefix_len, path_len); + + if (new_len > url_len) { + new_url = realloc(new_url, new_len); + url_len = new_len; + } + + *new_url = '\0'; + + if (alias->path) + strcpy(new_url, alias->path); + else if (conf.cgi_prefix) + strcpy(new_url, conf.cgi_prefix); + strcat(new_url, old_url); + + return new_url; } + return old_url; +} - if (pi->redirected) +void uh_handle_request(struct client *cl) +{ + struct http_request *req = &cl->request; + struct dispatch_handler *d; + char *url = blobmsg_data(blob_data(cl->hdr.head)); + char *error_handler; + + blob_buf_init(&cl->hdr_response, 0); + url = uh_handle_alias(url); + + uh_handler_run(cl, &url, false); + if (!url) return; - uh_file_request(cl, pi); + req->redirect_status = 200; + d = dispatch_find(url, NULL); + if (d) + return uh_invoke_handler(cl, d, url, NULL); + + if (__handle_file_request(cl, url)) + return; + + if (uh_handler_run(cl, &url, true)) { + if (!url) + return; + + uh_handler_run(cl, &url, false); + if (__handle_file_request(cl, url)) + return; + } + + req->redirect_status = 404; + if (conf.error_handler) { + error_handler = alloca(strlen(conf.error_handler) + 1); + strcpy(error_handler, conf.error_handler); + if (__handle_file_request(cl, error_handler)) + return; + } + + uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.", url); }