43c70a4fd7703593473bd13212bfee64870dd68a
[src/app-framework-binder.git] / src / afb-ws-json.c
1 /*
2  * Copyright 2016 IoT.bzh
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <string.h>
25
26 #include <json.h>
27
28 #include "afb-ws.h"
29 #include "afb-ws-json.h"
30 #include "session.h"
31 #include "afb-req-itf.h"
32 #include "afb-apis.h"
33
34 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size);
35 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size);
36
37 static struct afb_ws_itf aws_itf = {
38         .on_close = (void*)aws_on_close,
39         .on_text = (void*)aws_on_text,
40         .on_binary = NULL,
41 };
42
43 struct afb_wsreq;
44
45 struct afb_ws_json
46 {
47         void (*cleanup)(void*);
48         void *cleanup_closure;
49         struct afb_wsreq *requests;
50         struct AFB_clientCtx *context;
51         struct json_tokener *tokener;
52         struct afb_ws *ws;
53 };
54
55 struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *closure)
56 {
57         struct afb_ws_json *result;
58
59         assert(fd >= 0);
60         assert(context != NULL);
61
62         result = malloc(sizeof * result);
63         if (result == NULL)
64                 goto error;
65
66         result->cleanup = cleanup;
67         result->cleanup_closure = closure;
68         result->requests = NULL;
69         result->context = ctxClientGet(context);
70         if (result->context == NULL)
71                 goto error2;
72
73         result->tokener = json_tokener_new();
74         if (result->tokener == NULL)
75                 goto error3;
76
77         result->ws = afb_ws_create(fd, &aws_itf, result);
78         if (result->ws == NULL)
79                 goto error4;
80
81         return result;
82
83 error4:
84         json_tokener_free(result->tokener);
85 error3:
86         ctxClientPut(result->context);
87 error2:
88         free(result);
89 error:
90         close(fd);
91         return NULL;
92 }
93
94 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size)
95 {
96         /* do nothing */
97         free(text);
98 }
99
100
101 struct afb_wsreq
102 {
103         struct afb_ws_json *aws;
104         struct afb_wsreq *next;
105         struct json_object *id;
106         struct json_object *name;
107         struct json_object *token;
108         struct json_object *request;
109 };
110 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
111 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure);
112 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
113 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
114 static int wsreq_session_create(struct afb_wsreq *wsreq);
115 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
116 static void wsreq_session_close(struct afb_wsreq *wsreq);
117
118 static const struct afb_req_itf wsreq_itf = {
119         .get = (void*)wsreq_get,
120         .iterate = (void*)wsreq_iterate,
121         .fail = (void*)wsreq_fail,
122         .success = (void*)wsreq_success,
123         .session_create = (void*)wsreq_session_create,
124         .session_check = (void*)wsreq_session_check,
125         .session_close = (void*)wsreq_session_close
126 };
127
128 static int aws_handle_json(struct afb_ws_json *aws, struct json_object *obj)
129 {
130         struct afb_req r;
131         int count, num;
132         struct json_object *type, *id, *name, *req, *token;
133         struct afb_wsreq *wsreq;
134         const char *api, *verb;
135         size_t lenapi, lenverb;
136
137         /* protocol inspired by http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml */
138
139         /* the object must be an array of 4 or 5 elements */
140         if (!json_object_is_type(obj, json_type_array))
141                 goto error;
142         count = json_object_array_length(obj);
143         if (count < 4 || count > 5)
144                 goto error;
145
146         /* get the 5 elements: type id name request token */
147         type = json_object_array_get_idx(obj, 0);
148         id = json_object_array_get_idx(obj, 1);
149         name = json_object_array_get_idx(obj, 2);
150         req = json_object_array_get_idx(obj, 3);
151         token = json_object_array_get_idx(obj, 4);
152
153         /* check the types: int string string object string */
154         if (!json_object_is_type(type, json_type_int))
155                 goto error;
156         if (!json_object_is_type(id, json_type_string))
157                 goto error;
158         if (!json_object_is_type(name, json_type_string))
159                 goto error;
160         if (!json_object_is_type(req, json_type_object))
161                 goto error;
162         if (token != NULL && !json_object_is_type(token, json_type_string))
163                 goto error;
164
165         /* the type is only 2 */
166         num = json_object_get_int(type);
167         if (num != 2)
168                 goto error;
169
170         /* checks the api/verb structure of name */
171         api = json_object_get_string(name);
172         for (lenapi = 0 ; api[lenapi] && api[lenapi] != '/' ; lenapi++);
173         if (!lenapi || !api[lenapi])
174                 goto error;
175         verb = &api[lenapi+1];
176         for (lenverb = 0 ; verb[lenverb] && verb[lenverb] != '/' ; lenverb++);
177         if (!lenverb || verb[lenverb])
178                 goto error;
179
180         /* allocates the request data */
181         wsreq = malloc(sizeof *wsreq);
182         if (wsreq == NULL)
183                 goto error;
184
185         /* fill and record the request */
186         wsreq->aws = aws;
187         wsreq->id = json_object_get(id);
188         wsreq->name = json_object_get(name);
189         wsreq->token = json_object_get(token);
190         wsreq->request = json_object_get(req);
191         wsreq->next = aws->requests;
192         aws->requests = wsreq;
193         json_object_put(obj);
194
195         r.data = wsreq;
196         r.itf = &wsreq_itf;
197         afb_apis_call(r, aws->context, api, lenapi, verb, lenverb);
198         return 1;
199
200 error:
201         json_object_put(obj);
202         return 0;
203 }
204
205 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size)
206 {
207         struct json_object *obj;
208         json_tokener_reset(ws->tokener);
209         obj = json_tokener_parse_ex(ws->tokener, text, (int)size);
210         if (obj == NULL) {
211                 afb_ws_close(ws->ws, 1008);
212         } else if (!aws_handle_json(ws, obj)) {
213                 afb_ws_close(ws->ws, 1008);
214         }
215 }
216
217 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
218 {
219         struct afb_arg arg;
220         struct json_object *value;
221
222         if (json_object_object_get_ex(wsreq->request, name, &value)) {
223                 arg.name = name;
224                 arg.value = json_object_get_string(value);
225                 arg.size = strlen(arg.value);
226         } else {
227                 arg.name = NULL;
228                 arg.value = NULL;
229                 arg.size = 0;
230         }
231         arg.path = NULL;
232         return arg;
233 }
234
235 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure)
236 {
237         struct afb_arg arg;
238         struct json_object_iterator it = json_object_iter_begin(wsreq->request);
239         struct json_object_iterator end = json_object_iter_end(wsreq->request);
240
241         arg.size = 0;
242         arg.path = NULL;
243         while(!json_object_iter_equal(&it, &end)) {
244                 arg.name = json_object_iter_peek_name(&it);
245                 arg.value = json_object_get_string(json_object_iter_peek_value(&it));
246                 if (!iterator(closure, arg))
247                         break;
248                 json_object_iter_next(&it);
249         }
250 }
251
252 static int wsreq_session_create(struct afb_wsreq *wsreq)
253 {
254         struct AFB_clientCtx *context = wsreq->aws->context;
255         if (context->created)
256                 return 0;
257         return wsreq_session_check(wsreq, 1);
258 }
259
260 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
261 {
262         const char *token;
263         struct AFB_clientCtx *context = wsreq->aws->context;
264
265         if (wsreq->token == NULL)
266                 return 0;
267
268         token = json_object_get_string(wsreq->token);
269         if (token == NULL)
270                 return 0;
271
272         if (!ctxTokenCheck (context, token))
273                 return 0;
274
275         if (refresh) {
276                 ctxTokenNew (context);
277         }
278
279         return 1;
280 }
281
282 static void wsreq_session_close(struct afb_wsreq *wsreq)
283 {
284         struct AFB_clientCtx *context = wsreq->aws->context;
285         ctxClientClose(context);
286 }
287
288
289 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
290 {
291         json_object *root, *request, *reply;
292         const char *message;
293
294         /* builds the answering structure */
295         root = json_object_new_object();
296         json_object_object_add(root, "jtype", json_object_new_string("afb-reply"));
297         request = json_object_new_object();
298         json_object_object_add(root, "request", request);
299         json_object_object_add(request, "status", json_object_new_string(status));
300         if (info)
301                 json_object_object_add(request, "info", json_object_new_string(info));
302         if (resp)
303                 json_object_object_add(root, "response", resp);
304
305         /* make the reply */
306         reply = json_object_new_array();
307         json_object_array_add(reply, json_object_new_int(retcode));
308         json_object_array_add(reply, wsreq->id);
309         json_object_array_add(reply, root);
310         json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token));
311
312         /* emits the reply */
313         message = json_object_to_json_string(reply);
314         afb_ws_text(wsreq->aws->ws, message, strlen(message));
315         json_object_put(reply);
316
317         /* TODO eliminates the wsreq */
318 }
319
320 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
321 {
322         wsreq_reply(wsreq, 4, status, info, NULL);
323 }
324
325 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
326 {
327         wsreq_reply(wsreq, 3, "success", info, obj);
328 }
329