X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=file.c;h=a4d9b1d4b2402e7dbec8dec893db224ef99b2f83;hp=22bdfb83f29ab017092757107774c6d28611d453;hb=99957f6c6ff429f17d6d6002fef4d4ef7de8844a;hpb=857bf0231d89d3a2bbccfef27af53ce3baaa2403 diff --git a/file.c b/file.c index 22bdfb8..a4d9b1d 100644 --- a/file.c +++ b/file.c @@ -1,20 +1,20 @@ /* * 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 @@ -26,14 +26,27 @@ #include #include #include +#include #include #include "uhttpd.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 deferred_request { + struct list_head list; + struct dispatch_handler *d; + struct client *cl; + struct path_info pi; + bool called, path; +}; struct index_file { struct list_head list; @@ -114,7 +127,7 @@ next: /* 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. */ -static struct path_info * +struct path_info * uh_path_lookup(struct client *cl, const char *url) { static char path_phys[PATH_MAX]; @@ -222,6 +235,8 @@ uh_path_lookup(struct client *cl, const char *url) 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 ? "?" : "", @@ -274,12 +289,10 @@ static const char * uh_file_mime_lookup(const char *path) return "application/octet-stream"; } -static const char * uh_file_mktag(struct stat *s, char *buf) +static const char * uh_file_mktag(struct stat *s, char *buf, int len) { - snprintf(buf, sizeof(buf), "\"%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 buf; } @@ -318,7 +331,7 @@ 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, buf)); + 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, buf, sizeof(buf))); } @@ -347,7 +360,7 @@ static void uh_file_response_412(struct client *cl) static bool uh_file_if_match(struct client *cl, struct stat *s) { char buf[128]; - const char *tag = uh_file_mktag(s, buf); + const char *tag = uh_file_mktag(s, buf, sizeof(buf)); char *hdr = uh_file_header(cl, HDR_IF_MATCH); char *p; int i; @@ -388,7 +401,7 @@ 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) { char buf[128]; - const char *tag = uh_file_mktag(s, buf); + const char *tag = uh_file_mktag(s, buf, sizeof(buf)); char *hdr = uh_file_header(cl, HDR_IF_NONE_MATCH); char *p; int i; @@ -468,11 +481,11 @@ static void list_entries(struct client *cl, struct dirent **files, int count, bool dir = !!(files[i]->d_type & DT_DIR); if (name[0] == '.' && name[1] == 0) - continue; + goto next; sprintf(file, "%s", name); if (stat(local_path, &s)) - continue; + goto next; if (!dir) { suffix = ""; @@ -481,7 +494,7 @@ static void list_entries(struct client *cl, struct dirent **files, int count, } if (!(s.st_mode & mode)) - continue; + goto next; uh_chunk_printf(cl, "
  • %s%s" @@ -494,6 +507,7 @@ static void list_entries(struct client *cl, struct dirent **files, int count, type, s.st_size / 1024.0); *file = 0; +next: free(files[i]); } } @@ -551,11 +565,13 @@ static void uh_file_free(struct client *cl) 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; @@ -567,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); @@ -585,10 +601,14 @@ static void uh_file_data(struct client *cl, struct path_info *pi, int fd) file_write_cb(cl); } +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; @@ -598,6 +618,7 @@ static void uh_file_request(struct client *cl, const char *url, 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; @@ -613,6 +634,16 @@ static void uh_file_request(struct client *cl, const char *url, } 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); @@ -647,6 +678,109 @@ dispatch_find(const char *url, struct path_info *pi) 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] = { @@ -660,6 +794,7 @@ static bool __handle_file_request(struct client *cl, char *url) struct dispatch_handler *d; struct blob_attr *tb[__HDR_MAX]; struct path_info *pi; + char *user, *pass; pi = uh_path_lookup(cl, url); if (!pi) @@ -669,38 +804,94 @@ static bool __handle_file_request(struct client *cl, char *url) return true; blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head)); - if (tb[HDR_AUTHORIZATION]) - pi->auth = blobmsg_data(tb[HDR_AUTHORIZATION]); + if (tb[HDR_AUTHORIZATION]) { + if (!uh_auth_check(cl, pi->name, blobmsg_data(tb[HDR_AUTHORIZATION]), &user, &pass)) + return true; - if (!uh_auth_check(cl, pi)) - return true; + 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) - d->handle_request(cl, url, pi); + uh_invoke_handler(cl, d, url, pi); else uh_file_request(cl, url, pi, tb); return true; } +static char *uh_handle_alias(char *old_url) +{ + struct alias *alias; + static char *new_url; + static int url_len; + + 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; +} + 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 *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; + req->redirect_status = 200; d = dispatch_find(url, NULL); - if (d) { - d->handle_request(cl, url, NULL); - return; - } + 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);