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
,