a17668884753714e94e3782949572fa99dd5e51a
[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 but free the text */
97         free(text);
98 }
99
100 #define CALL 2
101 #define RETOK 3
102 #define RETERR 4
103
104 struct afb_wsreq
105 {
106         struct afb_ws_json *aws;
107         struct afb_wsreq *next;
108         char *text;
109         size_t size;
110         int code;
111         char *id;
112         size_t idlen;
113         char *api;
114         size_t apilen;
115         char *verb;
116         size_t verblen;
117         char *obj;
118         size_t objlen;
119         char *tok;
120         size_t toklen;
121         struct json_object *root;
122 };
123
124 static struct json_object *wsreq_json(struct afb_wsreq *wsreq);
125 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
126 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
127 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
128 static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size);
129 static void wsreq_send(struct afb_wsreq *wsreq, char *buffer, size_t size);
130 static int wsreq_session_create(struct afb_wsreq *wsreq);
131 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
132 static void wsreq_session_close(struct afb_wsreq *wsreq);
133
134
135 static const struct afb_req_itf wsreq_itf = {
136         .json = (void*)wsreq_json,
137         .get = (void*)wsreq_get,
138         .success = (void*)wsreq_success,
139         .fail = (void*)wsreq_fail,
140         .raw = (void*)wsreq_raw,
141         .send = (void*)wsreq_send,
142         .session_create = (void*)wsreq_session_create,
143         .session_check = (void*)wsreq_session_check,
144         .session_close = (void*)wsreq_session_close
145 };
146
147 static int aws_wsreq_parse(struct afb_wsreq *r, char *text, size_t size)
148 {
149         char *pos, *end, c;
150         int aux;
151
152         /* scan */
153         pos = text;
154         end = text + size;
155
156         /* scans: [ */
157         while(pos < end && *pos == ' ') pos++;
158         if (pos == end) goto bad_header;
159         if (*pos++ != '[') goto bad_header;
160
161         /* scans code: 2|3|4 */
162         while(pos < end && *pos == ' ') pos++;
163         if (pos == end) goto bad_header;
164         switch (*pos++) {
165         case '2': r->code = CALL; break;
166         case '3': r->code = RETOK; break;
167         case '4': r->code = RETERR; break;
168         default: goto bad_header;
169         }
170
171         /* scans: , */
172         while(pos < end && *pos == ' ') pos++;
173         if (pos == end) goto bad_header;
174         if (*pos++ != ',') goto bad_header;
175
176         /* scans id: "id" */
177         while(pos < end && *pos == ' ') pos++;
178         if (pos == end) goto bad_header;
179         if (*pos++ != '"') goto bad_header;
180         r->id = pos;
181         while(pos < end && *pos != '"') pos++;
182         if (pos == end) goto bad_header;
183         r->idlen = (size_t)(pos++ - r->id);
184
185         /* scans: , */
186         while(pos < end && *pos == ' ') pos++;
187         if (pos == end) goto bad_header;
188         if (*pos++ != ',') goto bad_header;
189
190         /* scans the method if needed */
191         if (r->code == CALL) {
192                 /* scans: " */
193                 while(pos < end && *pos == ' ') pos++;
194                 if (pos == end) goto bad_header;
195                 if (*pos++ != '"') goto bad_header;
196
197                 /* scans: api/ */
198                 r->api = pos;
199                 while(pos < end && *pos != '"' && *pos != '/') pos++;
200                 if (pos == end) goto bad_header;
201                 if (*pos != '/') goto bad_header;
202                 r->apilen = (size_t)(pos++ - r->api);
203                 if (r->apilen && r->api[r->apilen - 1] == '\\')
204                         r->apilen--;
205
206                 /* scans: verb" */
207                 r->verb = pos;
208                 while(pos < end && *pos != '"') pos++;
209                 if (pos == end) goto bad_header;
210                 r->verblen = (size_t)(pos++ - r->verb);
211
212                 /* scans: , */
213                 while(pos < end && *pos == ' ') pos++;
214                 if (pos == end) goto bad_header;
215                 if (*pos++ != ',') goto bad_header;
216         }
217
218         /* scan obj */
219         while(pos < end && *pos == ' ') pos++;
220         if (pos == end) goto bad_header;
221         aux = 0;
222         r->obj = pos;
223         while (pos < end && (aux != 0 || (*pos != ',' && *pos != ']'))) {
224                 if (pos == end) goto bad_header;
225                 switch(*pos) {
226                 case '{': case '[': aux++; break;
227                 case '}': case ']': if (!aux--) goto bad_header; break;
228                 case '"':
229                         do {
230                                 pos += 1 + (*pos == '\\');
231                         } while(pos < end && *pos != '"');
232                 default:
233                         break;
234                 }
235                 pos++;
236         }
237         if (pos > end) goto bad_header;
238         if (pos == end && aux != 0) goto bad_header;
239         c = *pos;
240         r->objlen = (size_t)(pos++ - r->obj);
241         while (r->objlen && r->obj[r->objlen - 1] == ' ')
242                 r->objlen--;
243
244         /* scan the token (if any) */
245         if (c == ',') {
246                 /* scans token: "token" */
247                 while(pos < end && *pos == ' ') pos++;
248                 if (pos == end) goto bad_header;
249                 if (*pos++ != '"') goto bad_header;
250                 r->tok = pos;
251                 while(pos < end && *pos != '"') pos++;
252                 if (pos == end) goto bad_header;
253                 r->toklen = (size_t)(pos++ - r->tok);
254                 while(pos < end && *pos == ' ') pos++;
255                 if (pos == end) goto bad_header;
256                 c = *pos++;
257         }
258
259         /* scan: ] */
260         if (c != ']') goto bad_header;
261         while(pos < end && *pos == ' ') pos++;
262         if (pos != end) goto bad_header;
263
264         /* done */
265         r->text = text;
266         r->size = size;
267 fprintf(stderr, "\n\nONTEXT([%d, %.*s, %.*s/%.*s, %.*s, %.*s])\n\n",
268         r->code,
269         (int)r->idlen, r->id,
270         (int)r->apilen, r->api,
271         (int)r->verblen, r->verb,
272         (int)r->objlen, r->obj,
273         (int)r->toklen, r->tok
274 );
275         return 1;
276
277 bad_header:
278         return 0;
279 }
280
281 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size)
282 {
283         struct afb_req r;
284         struct afb_wsreq *wsreq;
285
286         /* allocate */
287         wsreq = calloc(1, sizeof *wsreq);
288         if (wsreq == NULL)
289                 goto alloc_error;
290
291         /* init */
292         if (!aws_wsreq_parse(wsreq, text, size))
293                 goto bad_header;
294
295         /* fill and record the request */
296         wsreq->aws = ws;
297         wsreq->next = ws->requests;
298         ws->requests = wsreq;
299
300         r.data = wsreq;
301         r.itf = &wsreq_itf;
302         afb_apis_call(r, ws->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen);
303         return;
304
305 bad_header:
306         free(wsreq);
307 alloc_error:
308         free(text);
309         afb_ws_close(ws->ws, 1008);
310         return;
311 }
312
313 static struct json_object *wsreq_json(struct afb_wsreq *wsreq)
314 {
315         struct json_object *root = wsreq->root;
316         if (root == NULL) {
317                 json_tokener_reset(wsreq->aws->tokener);
318                 root = json_tokener_parse_ex(wsreq->aws->tokener, wsreq->obj, (int)wsreq->objlen);
319                 if (root == NULL) {
320                         /* lazy discovering !!!! not good TODO improve*/
321                         root = json_object_new_object();
322                 }
323                 wsreq->root = root;
324         }
325         return root;
326 }
327
328 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
329 {
330         struct afb_arg arg;
331         struct json_object *value, *root;
332
333         root = wsreq_json(wsreq);
334         if (json_object_object_get_ex(root, name, &value)) {
335                 arg.name = name;
336                 arg.value = json_object_get_string(value);
337         } else {
338                 arg.name = NULL;
339                 arg.value = NULL;
340         }
341         arg.path = NULL;
342         return arg;
343 }
344
345 static int wsreq_session_create(struct afb_wsreq *wsreq)
346 {
347         struct AFB_clientCtx *context = wsreq->aws->context;
348         if (context->created)
349                 return 0;
350         return wsreq_session_check(wsreq, 1);
351 }
352
353 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
354 {
355         struct AFB_clientCtx *context = wsreq->aws->context;
356
357         if (wsreq->tok == NULL)
358                 return 0;
359
360         if (!ctxTokenCheckLen (context, wsreq->tok, wsreq->toklen))
361                 return 0;
362
363         if (refresh) {
364                 ctxTokenNew (context);
365         }
366
367         return 1;
368 }
369
370 static void wsreq_session_close(struct afb_wsreq *wsreq)
371 {
372         struct AFB_clientCtx *context = wsreq->aws->context;
373         ctxClientClose(context);
374 }
375
376
377 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
378 {
379         json_object *root, *request, *reply;
380         const char *message;
381
382         /* builds the answering structure */
383         root = json_object_new_object();
384         json_object_object_add(root, "jtype", json_object_new_string("afb-reply"));
385         request = json_object_new_object();
386         json_object_object_add(root, "request", request);
387         json_object_object_add(request, "status", json_object_new_string(status));
388         if (info)
389                 json_object_object_add(request, "info", json_object_new_string(info));
390         if (resp)
391                 json_object_object_add(root, "response", resp);
392
393         /* make the reply */
394         reply = json_object_new_array();
395         json_object_array_add(reply, json_object_new_int(retcode));
396         json_object_array_add(reply, json_object_new_string_len(wsreq->id, (int)wsreq->idlen));
397         json_object_array_add(reply, root);
398         json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token));
399
400         /* emits the reply */
401         message = json_object_to_json_string(reply);
402         afb_ws_text(wsreq->aws->ws, message, strlen(message));
403         json_object_put(reply);
404
405         /* TODO eliminates the wsreq */
406 }
407
408 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
409 {
410         wsreq_reply(wsreq, RETERR, status, info, NULL);
411 }
412
413 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
414 {
415         wsreq_reply(wsreq, RETOK, "success", info, obj);
416 }
417
418 static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size)
419 {
420         *size = wsreq->objlen;
421         return wsreq->obj;
422 }
423
424 static void wsreq_send(struct afb_wsreq *wsreq, char *buffer, size_t size)
425 {
426         afb_ws_text(wsreq->aws->ws, buffer, size);
427 }
428