Improve handling of verbosity in bindings
[src/app-framework-binder.git] / src / afb-xreq.c
1 /*
2  * Copyright (C) 2017 "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 <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <json-c/json.h>
26 #include <afb/afb-binding-v1.h>
27 #include <afb/afb-binding-v2.h>
28
29 #include "afb-context.h"
30 #include "afb-xreq.h"
31 #include "afb-evt.h"
32 #include "afb-msg-json.h"
33 #include "afb-cred.h"
34 #include "afb-hook.h"
35 #include "afb-api.h"
36 #include "afb-apiset.h"
37 #include "afb-auth.h"
38 #include "jobs.h"
39 #include "verbose.h"
40
41 /******************************************************************************/
42
43 static inline void xreq_addref(struct afb_xreq *xreq)
44 {
45         __atomic_add_fetch(&xreq->refcount, 1, __ATOMIC_RELAXED);
46 }
47
48 static inline void xreq_unref(struct afb_xreq *xreq)
49 {
50         if (!__atomic_sub_fetch(&xreq->refcount, 1, __ATOMIC_RELAXED)) {
51                 if (!xreq->replied)
52                         afb_xreq_fail(xreq, "error", "no reply");
53                 if (xreq->hookflags)
54                         afb_hook_xreq_end(xreq);
55                 xreq->queryitf->unref(xreq);
56         }
57 }
58
59 /******************************************************************************/
60
61 extern const struct afb_req_itf xreq_itf;
62 extern const struct afb_req_itf xreq_hooked_itf;
63
64 static inline struct afb_req to_req(struct afb_xreq *xreq)
65 {
66         return (struct afb_req){ .itf = xreq->hookflags ? &xreq_hooked_itf : &xreq_itf, .closure = xreq };
67 }
68
69 /******************************************************************************/
70
71 struct subcall
72 {
73         struct afb_xreq xreq;
74         struct afb_xreq *caller;
75         void (*callback)(void*, int, struct json_object*);
76         void *closure;
77         union {
78                 struct {
79                         struct jobloop *jobloop;
80                         struct json_object *result;
81                         int status;
82                 };
83                 struct {
84                         union {
85                                 void (*callback)(void*, int, struct json_object*);
86                                 void (*callback2)(void*, int, struct json_object*, struct afb_req);
87                         };
88                         void *closure;
89                 } hooked;
90         };
91 };
92
93 static int subcall_subscribe(struct afb_xreq *xreq, struct afb_event event)
94 {
95         struct subcall *subcall = CONTAINER_OF_XREQ(struct subcall, xreq);
96
97         return afb_xreq_subscribe(subcall->caller, event);
98 }
99
100 static int subcall_unsubscribe(struct afb_xreq *xreq, struct afb_event event)
101 {
102         struct subcall *subcall = CONTAINER_OF_XREQ(struct subcall, xreq);
103
104         return afb_xreq_unsubscribe(subcall->caller, event);
105 }
106
107 static void subcall_reply(struct afb_xreq *xreq, int status, struct json_object *obj)
108 {
109         struct subcall *subcall = CONTAINER_OF_XREQ(struct subcall, xreq);
110
111         if (subcall->callback)
112                 subcall->callback(subcall->closure, status, obj);
113         json_object_put(obj);
114 }
115
116 static void subcall_destroy(struct afb_xreq *xreq)
117 {
118         struct subcall *subcall = CONTAINER_OF_XREQ(struct subcall, xreq);
119
120         json_object_put(subcall->xreq.json);
121         afb_cred_unref(subcall->xreq.cred);
122         xreq_unref(subcall->caller);
123         free(subcall);
124 }
125
126 const struct afb_xreq_query_itf afb_xreq_subcall_itf = {
127         .reply = subcall_reply,
128         .unref = subcall_destroy,
129         .subscribe = subcall_subscribe,
130         .unsubscribe = subcall_unsubscribe
131 };
132
133 static struct subcall *subcall_alloc(
134                 struct afb_xreq *caller,
135                 const char *api,
136                 const char *verb,
137                 struct json_object *args
138 )
139 {
140         struct subcall *subcall;
141         size_t lenapi, lenverb;
142         char *copy;
143
144         lenapi = 1 + strlen(api);
145         lenverb = 1 + strlen(verb);
146         subcall = malloc(lenapi + lenverb + sizeof *subcall);
147         if (!subcall)
148                 ERROR("out of memory");
149         else {
150                 copy = (char*)&subcall[1];
151                 memcpy(copy, api, lenapi);
152                 api = copy;
153                 copy = &copy[lenapi];
154                 memcpy(copy, verb, lenverb);
155                 verb = copy;
156
157                 afb_xreq_init(&subcall->xreq, &afb_xreq_subcall_itf);
158                 afb_context_subinit(&subcall->xreq.context, &caller->context);
159                 subcall->xreq.cred = afb_cred_addref(caller->cred);
160                 subcall->xreq.json = args;
161                 subcall->xreq.api = api;
162                 subcall->xreq.verb = verb;
163                 subcall->caller = caller;
164                 xreq_addref(caller);
165         }
166         return subcall;
167 }
168
169 static void subcall_process(struct subcall *subcall)
170 {
171         if (subcall->caller->queryitf->subcall) {
172                 subcall->caller->queryitf->subcall(
173                         subcall->caller, subcall->xreq.api, subcall->xreq.verb,
174                         subcall->xreq.json, subcall->callback, subcall->closure);
175                 xreq_unref(&subcall->xreq);
176         } else
177                 afb_xreq_process(&subcall->xreq, subcall->caller->apiset);
178 }
179
180 static void subcall_sync_leave(struct subcall *subcall)
181 {
182         struct jobloop *jobloop = __atomic_exchange_n(&subcall->jobloop, NULL, __ATOMIC_RELAXED);
183         if (jobloop)
184                 jobs_leave(jobloop);
185 }
186
187 static void subcall_sync_reply(void *closure, int status, struct json_object *obj)
188 {
189         struct subcall *subcall = closure;
190
191         subcall->status = status;
192         subcall->result = json_object_get(obj);
193         subcall_sync_leave(subcall);
194 }
195
196 static void subcall_sync_enter(int signum, void *closure, struct jobloop *jobloop)
197 {
198         struct subcall *subcall = closure;
199
200         if (!signum) {
201                 subcall->jobloop = jobloop;
202                 subcall_process(subcall);
203         } else {
204                 subcall->status = -1;
205                 subcall_sync_leave(subcall);
206         }
207 }
208
209 /******************************************************************************/
210
211 static void vinfo(void *first, void *second, const char *fmt, va_list args, void (*fun)(void*,void*,const char*))
212 {
213         char *info;
214         if (fmt == NULL || vasprintf(&info, fmt, args) < 0)
215                 info = NULL;
216         fun(first, second, info);
217         free(info);
218 }
219
220 /******************************************************************************/
221
222 static struct json_object *xreq_json_cb(void *closure)
223 {
224         struct afb_xreq *xreq = closure;
225         if (!xreq->json && xreq->queryitf->json)
226                 xreq->json = xreq->queryitf->json(xreq);
227         return xreq->json;
228 }
229
230 static struct afb_arg xreq_get_cb(void *closure, const char *name)
231 {
232         struct afb_xreq *xreq = closure;
233         struct afb_arg arg;
234         struct json_object *object, *value;
235
236         if (xreq->queryitf->get)
237                 arg = xreq->queryitf->get(xreq, name);
238         else {
239                 object = xreq_json_cb(closure);
240                 if (json_object_object_get_ex(object, name, &value)) {
241                         arg.name = name;
242                         arg.value = json_object_get_string(value);
243                 } else {
244                         arg.name = NULL;
245                         arg.value = NULL;
246                 }
247                 arg.path = NULL;
248         }
249         return arg;
250 }
251
252 static void xreq_success_cb(void *closure, struct json_object *obj, const char *info)
253 {
254         struct afb_xreq *xreq = closure;
255
256         if (xreq->replied) {
257                 ERROR("reply called more than one time!!");
258                 json_object_put(obj);
259         } else {
260                 xreq->replied = 1;
261                 if (xreq->queryitf->success)
262                         xreq->queryitf->success(xreq, obj, info);
263                 else
264                         xreq->queryitf->reply(xreq, 0, afb_msg_json_reply_ok(info, obj, &xreq->context, NULL));
265         }
266 }
267
268 static void xreq_fail_cb(void *closure, const char *status, const char *info)
269 {
270         struct afb_xreq *xreq = closure;
271
272         if (xreq->replied) {
273                 ERROR("reply called more than one time!!");
274         } else {
275                 xreq->replied = 1;
276                 if (xreq->queryitf->fail)
277                         xreq->queryitf->fail(xreq, status, info);
278                 else
279                         xreq->queryitf->reply(xreq, -1, afb_msg_json_reply_error(status, info, &xreq->context, NULL));
280         }
281 }
282
283 static void xreq_vsuccess_cb(void *closure, struct json_object *obj, const char *fmt, va_list args)
284 {
285         vinfo(closure, obj, fmt, args, (void*)xreq_success_cb);
286 }
287
288 static void xreq_vfail_cb(void *closure, const char *status, const char *fmt, va_list args)
289 {
290         vinfo(closure, (void*)status, fmt, args, (void*)xreq_fail_cb);
291 }
292
293 static void *xreq_context_get_cb(void *closure)
294 {
295         struct afb_xreq *xreq = closure;
296         return afb_context_get(&xreq->context);
297 }
298
299 static void xreq_context_set_cb(void *closure, void *value, void (*free_value)(void*))
300 {
301         struct afb_xreq *xreq = closure;
302         afb_context_set(&xreq->context, value, free_value);
303 }
304
305 static void xreq_addref_cb(void *closure)
306 {
307         struct afb_xreq *xreq = closure;
308         xreq_addref(xreq);
309 }
310
311 static void xreq_unref_cb(void *closure)
312 {
313         struct afb_xreq *xreq = closure;
314         xreq_unref(xreq);
315 }
316
317 static void xreq_session_close_cb(void *closure)
318 {
319         struct afb_xreq *xreq = closure;
320         afb_context_close(&xreq->context);
321 }
322
323 static int xreq_session_set_LOA_cb(void *closure, unsigned level)
324 {
325         struct afb_xreq *xreq = closure;
326         return afb_context_change_loa(&xreq->context, level);
327 }
328
329 static int xreq_subscribe_cb(void *closure, struct afb_event event)
330 {
331         struct afb_xreq *xreq = closure;
332         return afb_xreq_subscribe(xreq, event);
333 }
334
335 int afb_xreq_subscribe(struct afb_xreq *xreq, struct afb_event event)
336 {
337         if (xreq->listener)
338                 return afb_evt_add_watch(xreq->listener, event);
339         if (xreq->queryitf->subscribe)
340                 return xreq->queryitf->subscribe(xreq, event);
341         ERROR("no event listener, subscription impossible");
342         errno = EINVAL;
343         return -1;
344 }
345
346 static int xreq_unsubscribe_cb(void *closure, struct afb_event event)
347 {
348         struct afb_xreq *xreq = closure;
349         return afb_xreq_unsubscribe(xreq, event);
350 }
351
352 int afb_xreq_unsubscribe(struct afb_xreq *xreq, struct afb_event event)
353 {
354         if (xreq->listener)
355                 return afb_evt_remove_watch(xreq->listener, event);
356         if (xreq->queryitf->unsubscribe)
357                 return xreq->queryitf->unsubscribe(xreq, event);
358         ERROR("no event listener, unsubscription impossible");
359         errno = EINVAL;
360         return -1;
361 }
362
363 static void xreq_subcall_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
364 {
365         struct afb_xreq *xreq = closure;
366         struct subcall *subcall;
367
368         subcall = subcall_alloc(xreq, api, verb, args);
369         if (subcall == NULL) {
370                 if (callback)
371                         callback(cb_closure, 1, afb_msg_json_internal_error());
372                 json_object_put(args);
373         } else {
374                 subcall->callback = callback;
375                 subcall->closure = cb_closure;
376                 subcall_process(subcall);
377         }
378 }
379
380 static void xreq_subcall_req_reply_cb(void *closure, int status, struct json_object *result)
381 {
382         struct subcall *subcall = closure;
383         subcall->hooked.callback2(subcall->hooked.closure, status, result, to_req(subcall->caller));
384 }
385
386 static void xreq_subcall_req_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*, struct afb_req), void *cb_closure)
387 {
388         struct afb_xreq *xreq = closure;
389         struct subcall *subcall;
390
391         subcall = subcall_alloc(xreq, api, verb, args);
392         if (subcall == NULL) {
393                 if (callback)
394                         callback(cb_closure, 1, afb_msg_json_internal_error(), to_req(xreq));
395                 json_object_put(args);
396         } else {
397                 subcall->callback = xreq_subcall_req_reply_cb;
398                 subcall->closure = subcall;
399                 subcall->hooked.callback2 = callback;
400                 subcall->hooked.closure = cb_closure;
401                 subcall_process(subcall);
402         }
403 }
404
405
406 static int xreq_subcallsync_cb(void *closure, const char *api, const char *verb, struct json_object *args, struct json_object **result)
407 {
408         int rc;
409         struct subcall *subcall;
410         struct afb_xreq *xreq = closure;
411         struct json_object *resu;
412
413         subcall = subcall_alloc(xreq, api, verb, args);
414         if (!subcall) {
415                 rc = -1;
416                 resu = afb_msg_json_internal_error();
417                 json_object_put(args);
418         } else {
419                 subcall->callback = subcall_sync_reply;
420                 subcall->closure = subcall;
421                 subcall->jobloop = NULL;
422                 subcall->result = NULL;
423                 subcall->status = 0;
424                 rc = jobs_enter(NULL, 0, subcall_sync_enter, subcall);
425                 resu = subcall->result;
426                 if (rc < 0 || subcall->status < 0) {
427                         resu = resu ?: afb_msg_json_internal_error();
428                         rc = -1;
429                 }
430         }
431         if (result)
432                 *result = resu;
433         else
434                 json_object_put(resu);
435         return rc;
436 }
437
438 static void xreq_vverbose_cb(void*closure, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
439 {
440         char *p;
441         struct afb_xreq *xreq = closure;
442
443         if (!fmt || vasprintf(&p, fmt, args) < 0)
444                 vverbose(level, file, line, func, fmt, args);
445         else {
446                 verbose(level, file, line, func, "[REQ/API %s] %s", xreq->api, p);
447                 free(p);
448         }
449 }
450
451 static struct afb_stored_req *xreq_store_cb(void *closure)
452 {
453         xreq_addref_cb(closure);
454         return closure;
455 }
456
457 static int xreq_has_permission_cb(void*closure, const char *permission)
458 {
459         struct afb_xreq *xreq = closure;
460         return afb_auth_has_permission(xreq, permission);
461 }
462
463 /******************************************************************************/
464
465 static struct json_object *xreq_hooked_json_cb(void *closure)
466 {
467         struct json_object *r = xreq_json_cb(closure);
468         struct afb_xreq *xreq = closure;
469         return afb_hook_xreq_json(xreq, r);
470 }
471
472 static struct afb_arg xreq_hooked_get_cb(void *closure, const char *name)
473 {
474         struct afb_arg r = xreq_get_cb(closure, name);
475         struct afb_xreq *xreq = closure;
476         return afb_hook_xreq_get(xreq, name, r);
477 }
478
479 static void xreq_hooked_success_cb(void *closure, struct json_object *obj, const char *info)
480 {
481         struct afb_xreq *xreq = closure;
482         afb_hook_xreq_success(xreq, obj, info);
483         xreq_success_cb(closure, obj, info);
484 }
485
486 static void xreq_hooked_fail_cb(void *closure, const char *status, const char *info)
487 {
488         struct afb_xreq *xreq = closure;
489         afb_hook_xreq_fail(xreq, status, info);
490         xreq_fail_cb(closure, status, info);
491 }
492
493 static void xreq_hooked_vsuccess_cb(void *closure, struct json_object *obj, const char *fmt, va_list args)
494 {
495         vinfo(closure, obj, fmt, args, (void*)xreq_hooked_success_cb);
496 }
497
498 static void xreq_hooked_vfail_cb(void *closure, const char *status, const char *fmt, va_list args)
499 {
500         vinfo(closure, (void*)status, fmt, args, (void*)xreq_hooked_fail_cb);
501 }
502
503 static void *xreq_hooked_context_get_cb(void *closure)
504 {
505         void *r = xreq_context_get_cb(closure);
506         struct afb_xreq *xreq = closure;
507         return afb_hook_xreq_context_get(xreq, r);
508 }
509
510 static void xreq_hooked_context_set_cb(void *closure, void *value, void (*free_value)(void*))
511 {
512         struct afb_xreq *xreq = closure;
513         afb_hook_xreq_context_set(xreq, value, free_value);
514         xreq_context_set_cb(closure, value, free_value);
515 }
516
517 static void xreq_hooked_addref_cb(void *closure)
518 {
519         struct afb_xreq *xreq = closure;
520         afb_hook_xreq_addref(xreq);
521         xreq_addref_cb(closure);
522 }
523
524 static void xreq_hooked_unref_cb(void *closure)
525 {
526         struct afb_xreq *xreq = closure;
527         afb_hook_xreq_unref(xreq);
528         xreq_unref_cb(closure);
529 }
530
531 static void xreq_hooked_session_close_cb(void *closure)
532 {
533         struct afb_xreq *xreq = closure;
534         afb_hook_xreq_session_close(xreq);
535         xreq_session_close_cb(closure);
536 }
537
538 static int xreq_hooked_session_set_LOA_cb(void *closure, unsigned level)
539 {
540         int r = xreq_session_set_LOA_cb(closure, level);
541         struct afb_xreq *xreq = closure;
542         return afb_hook_xreq_session_set_LOA(xreq, level, r);
543 }
544
545 static int xreq_hooked_subscribe_cb(void *closure, struct afb_event event)
546 {
547         int r = xreq_subscribe_cb(closure, event);
548         struct afb_xreq *xreq = closure;
549         return afb_hook_xreq_subscribe(xreq, event, r);
550 }
551
552 static int xreq_hooked_unsubscribe_cb(void *closure, struct afb_event event)
553 {
554         int r = xreq_unsubscribe_cb(closure, event);
555         struct afb_xreq *xreq = closure;
556         return afb_hook_xreq_unsubscribe(xreq, event, r);
557 }
558
559 static void xreq_hooked_subcall_reply_cb(void *closure, int status, struct json_object *result)
560 {
561         struct subcall *subcall = closure;
562
563         afb_hook_xreq_subcall_result(subcall->caller, status, result);
564         subcall->hooked.callback(subcall->hooked.closure, status, result);
565 }
566
567 static void xreq_hooked_subcall_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
568 {
569         struct afb_xreq *xreq = closure;
570         struct subcall *subcall;
571
572         afb_hook_xreq_subcall(xreq, api, verb, args);
573         subcall = subcall_alloc(xreq, api, verb, args);
574         if (subcall == NULL) {
575                 if (callback)
576                         callback(cb_closure, 1, afb_msg_json_internal_error());
577                 json_object_put(args);
578         } else {
579                 subcall->callback = xreq_hooked_subcall_reply_cb;
580                 subcall->closure = subcall;
581                 subcall->hooked.callback = callback;
582                 subcall->hooked.closure = cb_closure;
583                 subcall_process(subcall);
584         }
585 }
586
587 static void xreq_hooked_subcall_req_reply_cb(void *closure, int status, struct json_object *result)
588 {
589         struct subcall *subcall = closure;
590
591         afb_hook_xreq_subcall_req_result(subcall->caller, status, result);
592         subcall->hooked.callback2(subcall->hooked.closure, status, result, to_req(subcall->caller));
593 }
594
595 static void xreq_hooked_subcall_req_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*, struct afb_req), void *cb_closure)
596 {
597         struct afb_xreq *xreq = closure;
598         struct subcall *subcall;
599
600         afb_hook_xreq_subcall_req(xreq, api, verb, args);
601         subcall = subcall_alloc(xreq, api, verb, args);
602         if (subcall == NULL) {
603                 if (callback)
604                         callback(cb_closure, 1, afb_msg_json_internal_error(), to_req(xreq));
605                 json_object_put(args);
606         } else {
607                 subcall->callback = xreq_hooked_subcall_req_reply_cb;
608                 subcall->closure = subcall;
609                 subcall->hooked.callback2 = callback;
610                 subcall->hooked.closure = cb_closure;
611                 subcall_process(subcall);
612         }
613 }
614
615 static int xreq_hooked_subcallsync_cb(void *closure, const char *api, const char *verb, struct json_object *args, struct json_object **result)
616 {
617         int r;
618         struct afb_xreq *xreq = closure;
619         afb_hook_xreq_subcallsync(xreq, api, verb, args);
620         r = xreq_subcallsync_cb(closure, api, verb, args, result);
621         return afb_hook_xreq_subcallsync_result(xreq, r, *result);
622 }
623
624 static void xreq_hooked_vverbose_cb(void*closure, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
625 {
626         struct afb_xreq *xreq = closure;
627         va_list ap;
628         va_copy(ap, args);
629         xreq_vverbose_cb(closure, level, file, line, func, fmt, args);
630         afb_hook_xreq_vverbose(xreq, level, file, line, func, fmt, ap);
631         va_end(ap);
632 }
633
634 static struct afb_stored_req *xreq_hooked_store_cb(void *closure)
635 {
636         struct afb_xreq *xreq = closure;
637         struct afb_stored_req *r = xreq_store_cb(closure);
638         afb_hook_xreq_store(xreq, r);
639         return r;
640 }
641
642 static int xreq_hooked_has_permission_cb(void*closure, const char *permission)
643 {
644         struct afb_xreq *xreq = closure;
645         int r = xreq_has_permission_cb(closure, permission);
646         return afb_hook_xreq_has_permission(xreq, permission, r);
647 }
648
649 /******************************************************************************/
650
651 const struct afb_req_itf xreq_itf = {
652         .json = xreq_json_cb,
653         .get = xreq_get_cb,
654         .success = xreq_success_cb,
655         .fail = xreq_fail_cb,
656         .vsuccess = xreq_vsuccess_cb,
657         .vfail = xreq_vfail_cb,
658         .context_get = xreq_context_get_cb,
659         .context_set = xreq_context_set_cb,
660         .addref = xreq_addref_cb,
661         .unref = xreq_unref_cb,
662         .session_close = xreq_session_close_cb,
663         .session_set_LOA = xreq_session_set_LOA_cb,
664         .subscribe = xreq_subscribe_cb,
665         .unsubscribe = xreq_unsubscribe_cb,
666         .subcall = xreq_subcall_cb,
667         .subcallsync = xreq_subcallsync_cb,
668         .vverbose = xreq_vverbose_cb,
669         .store = xreq_store_cb,
670         .subcall_req = xreq_subcall_req_cb,
671         .has_permission = xreq_has_permission_cb
672 };
673
674 const struct afb_req_itf xreq_hooked_itf = {
675         .json = xreq_hooked_json_cb,
676         .get = xreq_hooked_get_cb,
677         .success = xreq_hooked_success_cb,
678         .fail = xreq_hooked_fail_cb,
679         .vsuccess = xreq_hooked_vsuccess_cb,
680         .vfail = xreq_hooked_vfail_cb,
681         .context_get = xreq_hooked_context_get_cb,
682         .context_set = xreq_hooked_context_set_cb,
683         .addref = xreq_hooked_addref_cb,
684         .unref = xreq_hooked_unref_cb,
685         .session_close = xreq_hooked_session_close_cb,
686         .session_set_LOA = xreq_hooked_session_set_LOA_cb,
687         .subscribe = xreq_hooked_subscribe_cb,
688         .unsubscribe = xreq_hooked_unsubscribe_cb,
689         .subcall = xreq_hooked_subcall_cb,
690         .subcallsync = xreq_hooked_subcallsync_cb,
691         .vverbose = xreq_hooked_vverbose_cb,
692         .store = xreq_hooked_store_cb,
693         .subcall_req = xreq_hooked_subcall_req_cb,
694         .has_permission = xreq_hooked_has_permission_cb
695 };
696
697 /******************************************************************************/
698
699 struct afb_req afb_xreq_unstore(struct afb_stored_req *sreq)
700 {
701         struct afb_xreq *xreq = (struct afb_xreq *)sreq;
702         if (xreq->hookflags)
703                 afb_hook_xreq_unstore(xreq);
704         return to_req(xreq);
705 }
706
707 struct json_object *afb_xreq_json(struct afb_xreq *xreq)
708 {
709         return afb_req_json(to_req(xreq));
710 }
711
712 void afb_xreq_success(struct afb_xreq *xreq, struct json_object *obj, const char *info)
713 {
714         afb_req_success(to_req(xreq), obj, info);
715 }
716
717 void afb_xreq_success_f(struct afb_xreq *xreq, struct json_object *obj, const char *info, ...)
718 {
719         char *message;
720         va_list args;
721         va_start(args, info);
722         if (info == NULL || vasprintf(&message, info, args) < 0)
723                 message = NULL;
724         va_end(args);
725         afb_xreq_success(xreq, obj, message);
726         free(message);
727 }
728
729 void afb_xreq_fail(struct afb_xreq *xreq, const char *status, const char *info)
730 {
731         afb_req_fail(to_req(xreq), status, info);
732 }
733
734 void afb_xreq_fail_f(struct afb_xreq *xreq, const char *status, const char *info, ...)
735 {
736         char *message;
737         va_list args;
738         va_start(args, info);
739         if (info == NULL || vasprintf(&message, info, args) < 0)
740                 message = NULL;
741         va_end(args);
742         afb_xreq_fail(xreq, status, message);
743         free(message);
744 }
745
746 const char *afb_xreq_raw(struct afb_xreq *xreq, size_t *size)
747 {
748         struct json_object *obj = xreq_json_cb(xreq);
749         const char *result = json_object_to_json_string(obj);
750         if (size != NULL)
751                 *size = strlen(result);
752         return result;
753 }
754
755 void afb_xreq_addref(struct afb_xreq *xreq)
756 {
757         afb_req_addref(to_req(xreq));
758 }
759
760 void afb_xreq_unref(struct afb_xreq *xreq)
761 {
762         afb_req_unref(to_req(xreq));
763 }
764
765 void afb_xreq_unhooked_subcall(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
766 {
767         xreq_subcall_cb(xreq, api, verb, args, callback, cb_closure);
768 }
769
770 void afb_xreq_subcall(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
771 {
772         afb_req_subcall(to_req(xreq), api, verb, args, callback, cb_closure);
773 }
774
775 int afb_xreq_unhooked_subcall_sync(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, struct json_object **result)
776 {
777         return xreq_subcallsync_cb(xreq, api, verb, args, result);
778 }
779
780 int afb_xreq_subcall_sync(struct afb_xreq *xreq, const char *api, const char *verb, struct json_object *args, struct json_object **result)
781 {
782         return afb_req_subcall_sync(to_req(xreq), api, verb, args, result);
783 }
784
785 static int xreq_session_check_apply_v1(struct afb_xreq *xreq, int sessionflags)
786 {
787         int loa;
788
789         if ((sessionflags & (AFB_SESSION_CLOSE_V1|AFB_SESSION_RENEW_V1|AFB_SESSION_CHECK_V1|AFB_SESSION_LOA_EQ_V1)) != 0) {
790                 if (!afb_context_check(&xreq->context)) {
791                         afb_context_close(&xreq->context);
792                         afb_xreq_fail_f(xreq, "denied", "invalid token's identity");
793                         errno = EINVAL;
794                         return -1;
795                 }
796         }
797
798         if ((sessionflags & AFB_SESSION_LOA_GE_V1) != 0) {
799                 loa = (sessionflags >> AFB_SESSION_LOA_SHIFT_V1) & AFB_SESSION_LOA_MASK_V1;
800                 if (!afb_context_check_loa(&xreq->context, loa)) {
801                         afb_xreq_fail_f(xreq, "denied", "invalid LOA");
802                         errno = EPERM;
803                         return -1;
804                 }
805         }
806
807         if ((sessionflags & AFB_SESSION_LOA_LE_V1) != 0) {
808                 loa = (sessionflags >> AFB_SESSION_LOA_SHIFT_V1) & AFB_SESSION_LOA_MASK_V1;
809                 if (afb_context_check_loa(&xreq->context, loa + 1)) {
810                         afb_xreq_fail_f(xreq, "denied", "invalid LOA");
811                         errno = EPERM;
812                         return -1;
813                 }
814         }
815
816         if ((sessionflags & AFB_SESSION_RENEW_V1) != 0) {
817                 afb_context_refresh(&xreq->context);
818         }
819         if ((sessionflags & AFB_SESSION_CLOSE_V1) != 0) {
820                 afb_context_change_loa(&xreq->context, 0);
821                 afb_context_close(&xreq->context);
822         }
823
824         return 0;
825 }
826
827 static int xreq_session_check_apply_v2(struct afb_xreq *xreq, uint32_t sessionflags, const struct afb_auth *auth)
828 {
829         int loa;
830
831         if (sessionflags != 0) {
832                 if (!afb_context_check(&xreq->context)) {
833                         afb_context_close(&xreq->context);
834                         afb_xreq_fail_f(xreq, "denied", "invalid token's identity");
835                         errno = EINVAL;
836                         return -1;
837                 }
838         }
839
840         loa = (int)(sessionflags & AFB_SESSION_LOA_MASK_V2);
841         if (loa && !afb_context_check_loa(&xreq->context, loa)) {
842                 afb_xreq_fail_f(xreq, "denied", "invalid LOA");
843                 errno = EPERM;
844                 return -1;
845         }
846
847         if (auth && !afb_auth_check(xreq, auth)) {
848                 afb_xreq_fail_f(xreq, "denied", "authorisation refused");
849                 errno = EPERM;
850                 return -1;
851         }
852
853         if ((sessionflags & AFB_SESSION_REFRESH_V2) != 0) {
854                 afb_context_refresh(&xreq->context);
855         }
856         if ((sessionflags & AFB_SESSION_CLOSE_V2) != 0) {
857                 afb_context_close(&xreq->context);
858         }
859
860         return 0;
861 }
862
863 void afb_xreq_call_verb_v1(struct afb_xreq *xreq, const struct afb_verb_desc_v1 *verb)
864 {
865         if (!verb)
866                 afb_xreq_fail_unknown_verb(xreq);
867         else
868                 if (!xreq_session_check_apply_v1(xreq, verb->session))
869                         verb->callback(to_req(xreq));
870 }
871
872 void afb_xreq_call_verb_v2(struct afb_xreq *xreq, const struct afb_verb_v2 *verb)
873 {
874         if (!verb)
875                 afb_xreq_fail_unknown_verb(xreq);
876         else
877                 if (!xreq_session_check_apply_v2(xreq, verb->session, verb->auth))
878                         verb->callback(to_req(xreq));
879 }
880
881 void afb_xreq_init(struct afb_xreq *xreq, const struct afb_xreq_query_itf *queryitf)
882 {
883         memset(xreq, 0, sizeof *xreq);
884         xreq->refcount = 1;
885         xreq->queryitf = queryitf;
886 }
887
888 void afb_xreq_fail_unknown_api(struct afb_xreq *xreq)
889 {
890         afb_xreq_fail_f(xreq, "unknown-api", "api %s not found (for verb %s)", xreq->api, xreq->verb);
891 }
892
893 void afb_xreq_fail_unknown_verb(struct afb_xreq *xreq)
894 {
895         afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, xreq->api);
896 }
897
898 static void process_sync(struct afb_xreq *xreq)
899 {
900         const struct afb_api *api;
901
902         /* init hooking */
903         afb_hook_init_xreq(xreq);
904         if (xreq->hookflags)
905                 afb_hook_xreq_begin(xreq);
906
907         /* search the api */
908         api = (const struct afb_api*)xreq->context.api_key;
909         if (api)
910                 api->itf->call(api->closure, xreq);
911         else {
912                 api = afb_apiset_lookup_started(xreq->apiset, xreq->api, 1);
913                 if (errno == ENOENT)
914                         afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
915                 else
916                         afb_xreq_fail_f(xreq, "bad-api-state", "api %s not started correctly: %m", xreq->api);
917         }
918 }
919
920 static void process_async(int signum, void *arg)
921 {
922         struct afb_xreq *xreq = arg;
923
924         if (signum != 0) {
925                 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
926         } else {
927                 process_sync(xreq);
928         }
929         xreq_unref(xreq);
930 }
931
932 void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset)
933 {
934         const struct afb_api *api;
935
936         xreq->apiset = apiset;
937         api = afb_apiset_lookup_started(apiset, xreq->api, 1);
938         xreq->context.api_key = (void*)api;
939
940         xreq_addref(xreq);
941         if (jobs_queue(api && api->noconcurrency ? (void*)api : NULL, afb_apiset_timeout_get(apiset), process_async, xreq) < 0) {
942                 /* TODO: allows or not to proccess it directly as when no threading? (see above) */
943                 ERROR("can't process job with threads: %m");
944                 afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
945                 xreq_unref(xreq);
946         }
947         xreq_unref(xreq);
948 }
949