X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=file.c;h=6b3bb823b981a6f3a0ba26d6f2446b1e3a429762;hp=9268577f833342bba95c00472e56b8f22eca7d83;hb=fd8e5e379c23c5fbcec3e76894b839233df09067;hpb=0bf7e921683e2a6ef4b24d44f11436a6b9f20484 diff --git a/file.c b/file.c index 9268577..6b3bb82 100644 --- a/file.c +++ b/file.c @@ -1,29 +1,31 @@ /* * 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 @@ -32,6 +34,16 @@ 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; @@ -174,6 +186,13 @@ uh_path_lookup(struct client *cl, const char *url) 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; } @@ -184,10 +203,6 @@ uh_path_lookup(struct client *cl, const char *url) path_phys[docroot_len] != '/')) return NULL; - /* test current path */ - if (stat(path_phys, &p.stat)) - return NULL; - /* is a regular file */ if (p.stat.st_mode & S_IFREG) { p.root = docroot; @@ -217,6 +232,7 @@ uh_path_lookup(struct client *cl, const char *url) url with trailing slash appended */ if (!slash) { uh_http_header(cl, 302, "Found"); + 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 ? "?" : "", @@ -233,8 +249,10 @@ uh_path_lookup(struct client *cl, const char *url) continue; strcpy(pathptr, idx->name); - if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) + if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) { + memcpy(&p.stat, &s, sizeof(p.stat)); break; + } *pathptr = 0; } @@ -248,7 +266,7 @@ uh_path_lookup(struct client *cl, const char *url) 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) { @@ -267,9 +285,9 @@ 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\"", + snprintf(buf, len, "\"%x-%x-%x\"", (unsigned int) s->st_ino, (unsigned int) s->st_size, (unsigned int) s->st_mtime); @@ -311,7 +329,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))); } @@ -340,7 +358,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; @@ -381,7 +399,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; @@ -432,21 +450,69 @@ 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) + continue; + + sprintf(file, "%s", name); + if (stat(local_path, &s)) + continue; + + if (!dir) { + suffix = ""; + mode = S_IROTH; + type = uh_file_mime_lookup(local_path); + } + + if (!(s.st_mode & mode)) + continue; + + 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; + free(files[i]); + } } static void uh_file_dirlist(struct client *cl, struct path_info *pi) { - int i; - int count = 0; - char *filename = uh_buf; - char *pathptr; struct dirent **files = NULL; - struct stat s; - char buf[128]; + int count = 0; uh_file_response_200(cl, NULL); ustream_printf(cl->us, "Content-Type: text/html\r\n\r\n"); @@ -456,66 +522,15 @@ 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) - { - int len; - - strcpy(filename, pi->phys); - len = strlen(filename); - pathptr = filename + len; - len = PATH_MAX - len; - - /* list subdirs */ - for (i = 0; i < count; i++) { - snprintf(pathptr, len, "%s", 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, buf, sizeof(buf)), - s.st_size / 1024.0); - - *pathptr = 0; - } - - /* list files */ - for (i = 0; i < count; i++) { - snprintf(pathptr, len, "%s", 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, buf, sizeof(buf)), - 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) @@ -552,6 +567,7 @@ static void uh_file_data(struct client *cl, struct path_info *pi, int fd) !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; @@ -581,10 +597,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; @@ -609,6 +629,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); @@ -643,7 +673,107 @@ dispatch_find(const char *url, struct path_info *pi) return NULL; } -static bool __handle_file_request(struct client *cl, const char *url) +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); + + dr->called = true; + uh_invoke_script(dr->cl, dr->d, dr->path ? &dr->pi : NULL); + } +} + + +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) \ + _field(auth) + +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, *_auth; + + 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; + 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 }, @@ -673,7 +803,7 @@ static bool __handle_file_request(struct client *cl, const char *url) 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); @@ -682,18 +812,26 @@ static bool __handle_file_request(struct client *cl, const char *url) void uh_handle_request(struct client *cl) { + struct http_request *req = &cl->request; struct dispatch_handler *d; - const char *url = cl->request.url; + char *url = blobmsg_data(blob_data(cl->hdr.head)); + char *error_handler; + 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) || - __handle_file_request(cl, conf.error_handler)) + if (__handle_file_request(cl, url)) return; - uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.", cl->request.url); + 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); }