* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE
+#endif
+
#define _BSD_SOURCE
#define _DARWIN_C_SOURCE
#define _XOPEN_SOURCE 700
#include <time.h>
#include <strings.h>
#include <dirent.h>
+#include <inttypes.h>
#include <libubox/blobmsg.h>
#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);
/* 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];
static char path_info[PATH_MAX];
+ static char path_query[PATH_MAX];
static struct path_info p;
const char *docroot = conf.docroot;
/* separate query string from url */
if ((pathptr = strchr(url, '?')) != NULL) {
- p.query = pathptr[1] ? pathptr + 1 : NULL;
+ if (pathptr[1]) {
+ p.query = path_query;
+ snprintf(path_query, sizeof(path_query), "%s",
+ pathptr + 1);
+ }
/* urldecode component w/o query */
if (pathptr > url) {
url with trailing slash appended */
if (!slash) {
uh_http_header(cl, 302, "Found");
- ustream_printf(cl->us, "Content-Length: 0\r\n");
+ 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 ? "?" : "",
static const char * uh_file_mktag(struct stat *s, char *buf, int len)
{
- snprintf(buf, len, "\"%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;
}
return uh_file_response_ok_hdrs(cl, s);
}
+static void uh_file_response_405(struct client *cl)
+{
+ uh_http_header(cl, 405, "Method Not Allowed");
+}
+
static void uh_file_response_412(struct client *cl)
{
uh_http_header(cl, 412, "Precondition Failed");
const char *type = "directory";
unsigned int mode = S_IXOTH;
struct stat s;
+ char *escaped;
char *file;
char buf[128];
int i;
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 = "";
}
if (!(s.st_mode & mode))
- continue;
+ goto next;
+
+ escaped = uh_htmlescape(name);
+
+ if (!escaped)
+ goto next;
uh_chunk_printf(cl,
"<li><strong><a href='%s%s%s'>%s</a>%s"
"</strong><br /><small>modified: %s"
"<br />%s - %.02f kbyte<br />"
"<br /></small></li>",
- path, name, suffix,
- name, suffix,
+ path, escaped, suffix,
+ escaped, suffix,
uh_file_unix2date(s.st_mtime, buf, sizeof(buf)),
type, s.st_size / 1024.0);
+ free(escaped);
*file = 0;
+next:
free(files[i]);
}
}
static void uh_file_dirlist(struct client *cl, struct path_info *pi)
{
struct dirent **files = NULL;
+ char *escaped_path = uh_htmlescape(pi->name);
int count = 0;
+ if (!escaped_path)
+ {
+ uh_client_error(cl, 500, "Internal Server Error", "Out of memory");
+ return;
+ }
+
uh_file_response_200(cl, NULL);
ustream_printf(cl->us, "Content-Type: text/html\r\n\r\n");
uh_chunk_printf(cl,
"<html><head><title>Index of %s</title></head>"
"<body><h1>Index of %s</h1><hr /><ol>",
- pi->name, pi->name);
+ escaped_path, escaped_path);
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);
+ list_entries(cl, files, count, escaped_path, uh_buf);
}
+ free(escaped_path);
free(files);
uh_chunk_printf(cl, "</ol><hr /></body></html>");
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);
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);
{
int fd;
struct http_request *req = &cl->request;
- char *error_handler;
+ char *error_handler, *escaped_url;
+
+ switch (cl->request.method) {
+ case UH_HTTP_MSG_GET:
+ case UH_HTTP_MSG_POST:
+ case UH_HTTP_MSG_HEAD:
+ case UH_HTTP_MSG_OPTIONS:
+ break;
+
+ default:
+ uh_file_response_405(cl);
+ ustream_printf(cl->us, "\r\n");
+ uh_request_done(cl);
+ return;
+ }
if (!(pi->stat.st_mode & S_IROTH))
goto error;
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;
}
+ escaped_url = uh_htmlescape(url);
+
uh_client_error(cl, 403, "Forbidden",
"You don't have permission to access %s on this server.",
- url);
+ escaped_url ? escaped_url : "the url");
+
+ if (escaped_url)
+ free(escaped_url);
}
void uh_dispatch_add(struct dispatch_handler *d)
dr = list_first_entry(&pending_requests, struct deferred_request, list);
list_del(&dr->list);
+ cl = dr->cl;
dr->called = true;
- uh_invoke_script(dr->cl, dr->d, dr->path ? &dr->pi : NULL);
+ cl->dispatch.data_blocked = false;
+ uh_invoke_script(cl, dr->d, dr->path ? &dr->pi : NULL);
+ client_poll_post_data(cl);
}
}
_field(phys) \
_field(name) \
_field(info) \
- _field(query) \
- _field(auth)
+ _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, *_auth;
+ char *_root, *_phys, *_name, *_info, *_query;
cl->dispatch.req_free = uh_free_pending_request;
}
cl->dispatch.req_data = dr;
+ cl->dispatch.data_blocked = true;
dr->cl = cl;
dr->d = d;
list_add(&dr->list, &pending_requests);
struct dispatch_handler *d;
struct blob_attr *tb[__HDR_MAX];
struct path_info *pi;
+ char *user, *pass, *auth;
pi = uh_path_lookup(cl, url);
if (!pi)
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 (!uh_auth_check(cl, pi))
+ auth = tb[HDR_AUTHORIZATION] ? blobmsg_data(tb[HDR_AUTHORIZATION]) : NULL;
+
+ if (!uh_auth_check(cl, pi->name, auth, &user, &pass))
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)
uh_invoke_handler(cl, d, url, pi);
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 *error_handler;
+ char *error_handler, *escaped_url;
+
+ 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 (__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);
return;
}
- uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.", url);
+ escaped_url = uh_htmlescape(url);
+
+ uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.",
+ escaped_url ? escaped_url : "");
+
+ if (escaped_url)
+ free(escaped_url);
}