2 * uhttpd - Tiny single-threaded httpd
4 * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
21 #include <libubox/blobmsg.h>
22 #include <libubox/blobmsg_json.h>
23 #include <libubox/avl.h>
24 #include <libubox/avl-cmp.h>
30 #include "ubus-session.h"
32 static const struct uhttpd_ops *ops;
33 static struct config *_conf;
36 static struct ubus_context *ctx;
37 static struct blob_buf buf;
39 #define UH_UBUS_MAX_POST_SIZE 4096
41 static char *split_str(char *str)
44 str = strchr(str, '/');
46 while (str && *str == '/') {
54 uh_ubus_request_parse_url(struct client *cl, char *url, char **sid, char **obj, char **fun)
56 url += strlen(conf.ubus_prefix);
57 while (url && *url == '/')
68 return *sid && *obj && *fun;
72 uh_ubus_request_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
74 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
75 struct client *cl = container_of(du, struct client, dispatch.ubus);
78 if (!du->header_sent) {
79 ops->http_header(cl, 200, "OK");
80 ustream_printf(cl->us, "Content-Type: application/json\r\n\r\n");
81 du->header_sent = true;
84 str = blobmsg_format_json_indent(msg, true, 0);
85 ops->chunk_write(cl, str, strlen(str));
90 uh_ubus_request_cb(struct ubus_request *req, int ret)
92 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
93 struct client *cl = container_of(du, struct client, dispatch.ubus);
96 return ops->client_error(cl, 204, "No content", "Function did not return data");
98 ops->request_done(cl);
101 static void uh_ubus_close_fds(struct client *cl)
103 if (ctx->sock.fd < 0)
110 static void uh_ubus_request_free(struct client *cl)
112 struct dispatch_ubus *du = &cl->dispatch.ubus;
115 json_object_put(du->jsobj);
118 json_tokener_free(du->jstok);
121 ubus_abort_request(ctx, &du->req);
124 static void uh_ubus_json_error(struct client *cl)
126 ops->client_error(cl, 400, "Bad Request", "Invalid JSON data");
129 static void uh_ubus_send_request(struct client *cl, json_object *obj)
131 struct dispatch *d = &cl->dispatch;
132 struct dispatch_ubus *du = &d->ubus;
135 blob_buf_init(&buf, 0);
137 if (obj && !blobmsg_add_object(&buf, obj))
138 return uh_ubus_json_error(cl);
140 ret = ubus_invoke_async(ctx, du->obj, du->func, buf.head, &du->req);
142 return ops->client_error(cl, 500, "Internal Error",
143 "Error sending ubus request: %s", ubus_strerror(ret));
145 du->req.data_cb = uh_ubus_request_data_cb;
146 du->req.complete_cb = uh_ubus_request_cb;
147 ubus_complete_request_async(ctx, &du->req);
149 du->req_pending = true;
152 static void uh_ubus_data_done(struct client *cl)
154 struct dispatch_ubus *du = &cl->dispatch.ubus;
155 struct json_object *obj = du->jsobj;
157 if (!obj || json_object_get_type(obj) != json_type_object)
158 return uh_ubus_json_error(cl);
160 uh_ubus_send_request(cl, obj);
163 static int uh_ubus_data_send(struct client *cl, const char *data, int len)
165 struct dispatch_ubus *du = &cl->dispatch.ubus;
168 uh_ubus_json_error(cl);
173 if (du->post_len > UH_UBUS_MAX_POST_SIZE) {
174 ops->client_error(cl, 413, "Too Large", "Message too big");
178 du->jsobj = json_tokener_parse_ex(du->jstok, data, len);
182 static void uh_ubus_defer_post(struct client *cl)
184 struct dispatch *d = &cl->dispatch;
186 d->ubus.jstok = json_tokener_new();
188 return ops->client_error(cl, 500, "Internal Error", "Internal Error");
190 d->data_send = uh_ubus_data_send;
191 d->data_done = uh_ubus_data_done;
194 static void uh_ubus_handle_request(struct client *cl, char *url, struct path_info *pi)
196 struct uh_ubus_session *ses;
197 struct dispatch *d = &cl->dispatch;
198 char *sid, *obj, *fun;
200 blob_buf_init(&buf, 0);
202 if (!uh_ubus_request_parse_url(cl, url, &sid, &obj, &fun))
203 return ops->client_error(cl, 400, "Bad Request", "Invalid Request");
205 ses = uh_ubus_session_get(sid);
207 return ops->client_error(cl, 404, "Not Found", "No such session %s", sid);
209 if (!uh_ubus_session_acl_allowed(ses, obj, fun))
210 return ops->client_error(cl, 403, "Denied", "Access to object denied");
212 if (ubus_lookup_id(ctx, obj, &d->ubus.obj))
213 return ops->client_error(cl, 500, "Not Found", "No such object");
215 d->close_fds = uh_ubus_close_fds;
216 d->free = uh_ubus_request_free;
219 if (cl->request.method == UH_HTTP_MSG_POST)
220 uh_ubus_defer_post(cl);
222 uh_ubus_send_request(cl, NULL);
226 uh_ubus_check_url(const char *url)
228 return ops->path_match(conf.ubus_prefix, url);
234 static struct dispatch_handler ubus_dispatch = {
235 .check_url = uh_ubus_check_url,
236 .handle_request = uh_ubus_handle_request,
239 ctx = ubus_connect(conf.ubus_socket);
241 fprintf(stderr, "Unable to connect to ubus socket\n");
245 ops->dispatch_add(&ubus_dispatch);
246 if (ubus_session_api_init(ctx)) {
247 fprintf(stderr, "Unable to initialize ubus session API\n");
256 static int uh_ubus_plugin_init(const struct uhttpd_ops *o, struct config *c)
260 return uh_ubus_init();
263 static void uh_ubus_post_init(void)
268 const struct uhttpd_plugin uhttpd_plugin = {
269 .init = uh_ubus_plugin_init,
270 .post_init = uh_ubus_post_init,