diff options
Diffstat (limited to 'handler.c')
-rw-r--r-- | handler.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/handler.c b/handler.c new file mode 100644 index 0000000..56720fb --- /dev/null +++ b/handler.c @@ -0,0 +1,219 @@ +/* + * uhttpd - Tiny single-threaded httpd + * + * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org> + * + * 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. + * + * 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. + */ + +#include <libubox/blobmsg.h> +#include <libubox/blobmsg_json.h> +#include <libubox/json_script.h> + +#include "uhttpd.h" + +struct handler { + struct list_head list; + + struct json_script_file *request; + struct json_script_file *fallback; +}; + +static LIST_HEAD(handlers); +static struct json_script_ctx handler_ctx; +static struct env_var *cur_vars; +static struct blob_buf b; +static int handler_ret; +static struct client *cur_client; +static char **cur_url; + +static void +handle_redirect(struct json_script_ctx *ctx, struct blob_attr *data) +{ + struct client *cl = cur_client; + static struct blobmsg_policy policy = { + .type = BLOBMSG_TYPE_STRING, + }; + struct blob_attr *tb; + + blobmsg_parse_array(&policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data)); + if (!tb) + return; + + uh_http_header(cl, 302, "Found"); + ustream_printf(cl->us, "Content-Length: 0\r\n"); + ustream_printf(cl->us, "Location: %s\r\n\r\n", + blobmsg_get_string(tb)); + uh_request_done(cl); + *cur_url = NULL; + + handler_ret = 1; + json_script_abort(ctx); +} + +static void +handle_set_uri(struct json_script_ctx *ctx, struct blob_attr *data) +{ + struct client *cl = cur_client; + static struct blobmsg_policy policy = { + .type = BLOBMSG_TYPE_STRING, + }; + struct blob_attr *tb; + struct blob_attr *old_url = blob_data(cl->hdr.head); + + blobmsg_parse_array(&policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data)); + if (!tb) + return; + + blob_buf_init(&b, 0); + blob_put_raw(&b, blob_next(old_url), blob_len(cl->hdr.head) - blob_pad_len(old_url)); + + /* replace URL in client header cache */ + blob_buf_init(&cl->hdr, 0); + blobmsg_add_string(&cl->hdr, "URL", blobmsg_get_string(tb)); + blob_put_raw(&cl->hdr, blob_data(b.head), blob_len(b.head)); + *cur_url = blobmsg_data(blob_data(cl->hdr.head)); + cur_vars = NULL; + + blob_buf_init(&b, 0); + + handler_ret = 1; + json_script_abort(ctx); +} + +static void +handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *data, struct blob_attr *vars) +{ + static const struct { + const char *name; + void (*func)(struct json_script_ctx *ctx, struct blob_attr *data); + } cmds[] = { + { "redirect", handle_redirect }, + { "set_uri", handle_set_uri } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(cmds); i++) { + if (!strcmp(cmds[i].name, name)) { + cmds[i].func(ctx, data); + return; + } + } +} + +static const char * +handle_var(struct json_script_ctx *ctx, const char *name, + struct blob_attr *vars) +{ + struct client *cl = cur_client; + struct env_var *cur; + static struct path_info empty_path; + + if (!cur_vars) { + struct path_info *p = uh_path_lookup(cl, *cur_url); + + if (!p) + p = &empty_path; + + cur_vars = uh_get_process_vars(cl, p); + } + + for (cur = cur_vars; cur->name; cur++) { + if (!strcmp(cur->name, name)) + return cur->value; + } + return NULL; +} + +static void +handler_init(void) +{ + if (handler_ctx.handle_command) + return; + + json_script_init(&handler_ctx); + handler_ctx.handle_command = handle_command; + handler_ctx.handle_var = handle_var; +} + +static bool set_handler(struct json_script_file **dest, struct blob_attr *data) +{ + if (!data) + return true; + + *dest = json_script_file_from_blobmsg(NULL, blobmsg_data(data), blobmsg_data_len(data)); + return *dest; +} + +int uh_handler_add(const char *file) +{ + enum { + H_REQUEST, + H_FALLBACK, + __H_MAX, + }; + struct blobmsg_policy policy[__H_MAX] = { + [H_REQUEST] = { "request", BLOBMSG_TYPE_ARRAY }, + [H_FALLBACK] = { "fallback", BLOBMSG_TYPE_ARRAY }, + }; + struct blob_attr *tb[__H_MAX]; + struct handler *h; + + handler_init(); + blob_buf_init(&b, 0); + + if (!blobmsg_add_json_from_file(&b, file)) + return -1; + + blobmsg_parse(policy, __H_MAX, tb, blob_data(b.head), blob_len(b.head)); + if (!tb[H_REQUEST] && !tb[H_FALLBACK]) + return -1; + + h = calloc(1, sizeof(*h)); + if (!set_handler(&h->request, tb[H_REQUEST]) || + !set_handler(&h->fallback, tb[H_FALLBACK])) { + free(h->request); + free(h->fallback); + free(h); + return -1; + } + + list_add_tail(&h->list, &handlers); + return 0; +} + +int uh_handler_run(struct client *cl, char **url, bool fallback) +{ + struct json_script_file *f; + struct handler *h; + + cur_client = cl; + cur_url = url; + cur_vars = NULL; + + handler_ret = 0; + + list_for_each_entry(h, &handlers, list) { + f = fallback ? h->fallback : h->request; + if (!f) + continue; + + blob_buf_init(&b, 0); + json_script_run_file(&handler_ctx, f, b.head); + if (handler_ctx.abort) + break; + } + + return handler_ret; +} |