coverage and test: Add tests
[src/app-framework-binder.git] / bindings / samples / hello3.c
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author "Fulup Ar Foll"
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 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <string.h>
20 #include <pthread.h>
21 #include <fcntl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <json-c/json.h>
27
28 #define AFB_BINDING_VERSION 3
29 #include <afb/afb-binding.h>
30
31 #if !defined(APINAME)
32 #define APINAME "hello3"
33 #endif
34
35 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
36
37 /**************************************************************************/
38
39 struct event
40 {
41         struct event *next;
42         afb_event_t event;
43         char tag[1];
44 };
45
46 static struct event *events = 0;
47
48 /* searchs the event of tag */
49 static struct event *event_get(const char *tag)
50 {
51         struct event *e = events;
52         while(e && strcmp(e->tag, tag))
53                 e = e->next;
54         return e;
55 }
56
57 /* deletes the event of tag */
58 static int event_del(const char *tag)
59 {
60         struct event *e, **p;
61
62         /* check exists */
63         e = event_get(tag);
64         if (!e) return -1;
65
66         /* unlink */
67         p = &events;
68         while(*p != e) p = &(*p)->next;
69         *p = e->next;
70
71         /* destroys */
72         afb_event_unref(e->event);
73         free(e);
74         return 0;
75 }
76
77 /* creates the event of tag */
78 static int event_add(const char *tag, const char *name)
79 {
80         struct event *e;
81
82         /* check valid tag */
83         e = event_get(tag);
84         if (e) return -1;
85
86         /* creation */
87         e = malloc(strlen(tag) + sizeof *e);
88         if (!e) return -1;
89         strcpy(e->tag, tag);
90
91         /* make the event */
92         e->event = afb_daemon_make_event(name);
93         if (!e->event) { free(e); return -1; }
94
95         /* link */
96         e->next = events;
97         events = e;
98         return 0;
99 }
100
101 static int event_subscribe(afb_req_t request, const char *tag)
102 {
103         struct event *e;
104         e = event_get(tag);
105         return e ? afb_req_subscribe(request, e->event) : -1;
106 }
107
108 static int event_unsubscribe(afb_req_t request, const char *tag)
109 {
110         struct event *e;
111         e = event_get(tag);
112         return e ? afb_req_unsubscribe(request, e->event) : -1;
113 }
114
115 static int event_push(struct json_object *args, const char *tag)
116 {
117         struct event *e;
118         e = event_get(tag);
119         return e ? afb_event_push(e->event, json_object_get(args)) : -1;
120 }
121
122 static int event_broadcast(struct json_object *args, const char *tag)
123 {
124         struct event *e;
125         e = event_get(tag);
126         return e ? afb_event_broadcast(e->event, json_object_get(args)) : -1;
127 }
128
129 /**************************************************************************/
130
131 struct api
132 {
133         struct api *next;
134         afb_api_t api;
135         char name[1];
136 };
137
138 static struct api *apis = 0;
139
140 /* search the api of name */
141 static struct api *searchapi(const char *name, struct api ***previous)
142 {
143         struct api *a, **p = &apis;
144         while((a = *p) && strcmp(a->name, name))
145                 p = &a->next;
146         if (previous)
147                 *previous = p;
148         return a;
149 }
150
151 /**************************************************************************/
152
153 // Sample Generic Ping Debug API
154 static void ping(afb_req_t request, json_object *jresp, const char *tag)
155 {
156         static int pingcount = 0;
157         json_object *query = afb_req_json(request);
158         afb_req_success_f(request, jresp, "Ping Binder Daemon tag=%s count=%d query=%s", tag, ++pingcount, json_object_to_json_string(query));
159 }
160
161 static void pingSample (afb_req_t request)
162 {
163         ping(request, json_object_new_string ("Some String"), "pingSample");
164 }
165
166 static void pingFail (afb_req_t request)
167 {
168         afb_req_fail(request, "failed", "Ping Binder Daemon fails");
169 }
170
171 static void pingNull (afb_req_t request)
172 {
173         ping(request, NULL, "pingNull");
174 }
175
176 static void pingBug (afb_req_t request)
177 {
178         ping(NULL, NULL, "pingBug");
179 }
180
181 static void pingEvent(afb_req_t request)
182 {
183         json_object *query = afb_req_json(request);
184         afb_daemon_broadcast_event("event", json_object_get(query));
185         ping(request, json_object_get(query), "event");
186 }
187
188
189 // For samples https://linuxprograms.wordpress.com/2010/05/20/json-c-libjson-tutorial/
190 static void pingJson (afb_req_t request) {
191     json_object *jresp, *embed;
192
193     jresp = json_object_new_object();
194     json_object_object_add(jresp, "myString", json_object_new_string ("Some String"));
195     json_object_object_add(jresp, "myInt", json_object_new_int (1234));
196
197     embed  = json_object_new_object();
198     json_object_object_add(embed, "subObjString", json_object_new_string ("Some String"));
199     json_object_object_add(embed, "subObjInt", json_object_new_int (5678));
200
201     json_object_object_add(jresp,"eobj", embed);
202
203     ping(request, jresp, "pingJson");
204 }
205
206 static void subcallcb (void *prequest, int status, json_object *object, afb_req_t request)
207 {
208         if (status < 0)
209                 afb_req_fail(request, "failed", json_object_to_json_string(object));
210         else
211                 afb_req_success(request, json_object_get(object), NULL);
212 }
213
214 static void subcall (afb_req_t request)
215 {
216         const char *api = afb_req_value(request, "api");
217         const char *verb = afb_req_value(request, "verb");
218         const char *args = afb_req_value(request, "args");
219         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
220
221         if (object == NULL)
222                 afb_req_fail(request, "failed", "bad arguments");
223         else
224                 afb_req_subcall_legacy(request, api, verb, object, subcallcb, NULL);
225 }
226
227 static void subcallreqcb (void *prequest, int status, json_object *object, afb_req_t request)
228 {
229         if (status < 0)
230                 afb_req_fail(request, "failed", json_object_to_json_string(object));
231         else
232                 afb_req_success(request, json_object_get(object), NULL);
233 }
234
235 static void subcallreq (afb_req_t request)
236 {
237         const char *api = afb_req_value(request, "api");
238         const char *verb = afb_req_value(request, "verb");
239         const char *args = afb_req_value(request, "args");
240         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
241
242         if (object == NULL)
243                 afb_req_fail(request, "failed", "bad arguments");
244         else
245                 afb_req_subcall_legacy(request, api, verb, object, subcallreqcb, NULL);
246 }
247
248 static void subcallsync (afb_req_t request)
249 {
250         int rc;
251         const char *api = afb_req_value(request, "api");
252         const char *verb = afb_req_value(request, "verb");
253         const char *args = afb_req_value(request, "args");
254         json_object *result, *object = api && verb && args ? json_tokener_parse(args) : NULL;
255
256         if (object == NULL)
257                 afb_req_fail(request, "failed", "bad arguments");
258         else {
259                 rc = afb_req_subcall_sync_legacy(request, api, verb, object, &result);
260                 if (rc >= 0)
261                         afb_req_success(request, result, NULL);
262                 else {
263                         afb_req_fail(request, "failed", json_object_to_json_string(result));
264                         json_object_put(result);
265                 }
266         }
267 }
268
269 static void eventadd (afb_req_t request)
270 {
271         const char *tag = afb_req_value(request, "tag");
272         const char *name = afb_req_value(request, "name");
273
274         pthread_mutex_lock(&mutex);
275         if (tag == NULL || name == NULL)
276                 afb_req_fail(request, "failed", "bad arguments");
277         else if (0 != event_add(tag, name))
278                 afb_req_fail(request, "failed", "creation error");
279         else
280                 afb_req_success(request, NULL, NULL);
281         pthread_mutex_unlock(&mutex);
282 }
283
284 static void eventdel (afb_req_t request)
285 {
286         const char *tag = afb_req_value(request, "tag");
287
288         pthread_mutex_lock(&mutex);
289         if (tag == NULL)
290                 afb_req_fail(request, "failed", "bad arguments");
291         else if (0 != event_del(tag))
292                 afb_req_fail(request, "failed", "deletion error");
293         else
294                 afb_req_success(request, NULL, NULL);
295         pthread_mutex_unlock(&mutex);
296 }
297
298 static void eventsub (afb_req_t request)
299 {
300         const char *tag = afb_req_value(request, "tag");
301
302         pthread_mutex_lock(&mutex);
303         if (tag == NULL)
304                 afb_req_fail(request, "failed", "bad arguments");
305         else if (0 != event_subscribe(request, tag))
306                 afb_req_fail(request, "failed", "subscription error");
307         else
308                 afb_req_success(request, NULL, NULL);
309         pthread_mutex_unlock(&mutex);
310 }
311
312 static void eventunsub (afb_req_t request)
313 {
314         const char *tag = afb_req_value(request, "tag");
315
316         pthread_mutex_lock(&mutex);
317         if (tag == NULL)
318                 afb_req_fail(request, "failed", "bad arguments");
319         else if (0 != event_unsubscribe(request, tag))
320                 afb_req_fail(request, "failed", "unsubscription error");
321         else
322                 afb_req_success(request, NULL, NULL);
323         pthread_mutex_unlock(&mutex);
324 }
325
326 static void eventpush (afb_req_t request)
327 {
328         const char *tag = afb_req_value(request, "tag");
329         const char *data = afb_req_value(request, "data");
330         json_object *object = data ? json_tokener_parse(data) : NULL;
331
332         pthread_mutex_lock(&mutex);
333         if (tag == NULL)
334                 afb_req_fail(request, "failed", "bad arguments");
335         else if (0 > event_push(object, tag))
336                 afb_req_fail(request, "failed", "push error");
337         else
338                 afb_req_success(request, NULL, NULL);
339         pthread_mutex_unlock(&mutex);
340         json_object_put(object);
341 }
342
343 static void callcb (void *prequest, json_object *object, const char *error, const char *info, afb_api_t api)
344 {
345         afb_req_t request = prequest;
346         afb_req_reply(request, json_object_get(object), error, info);
347         afb_req_unref(request);
348 }
349
350 static void call (afb_req_t request)
351 {
352         const char *api = afb_req_value(request, "api");
353         const char *verb = afb_req_value(request, "verb");
354         const char *args = afb_req_value(request, "args");
355         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
356
357         afb_service_call(api, verb, object, callcb, afb_req_addref(request));
358 }
359
360 static void callsync (afb_req_t request)
361 {
362         const char *api = afb_req_value(request, "api");
363         const char *verb = afb_req_value(request, "verb");
364         const char *args = afb_req_value(request, "args");
365         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
366         json_object *result;
367         char *error, *info;
368
369         afb_service_call_sync(api, verb, object, &result, &error, &info);
370         afb_req_reply(request, result, error, info);
371         free(error);
372         free(info);
373 }
374
375 static void verbose (afb_req_t request)
376 {
377         int level = 5;
378         json_object *query = afb_req_json(request), *l;
379
380         if (json_object_is_type(query,json_type_int))
381                 level = json_object_get_int(query);
382         else if (json_object_object_get_ex(query, "level", &l) && json_object_is_type(l, json_type_int))
383                 level = json_object_get_int(l);
384
385         if (!json_object_object_get_ex(query,"message",&l))
386                 l = query;
387
388         AFB_REQ_VERBOSE(request, level, "verbose called for %s", json_object_get_string(l));
389         afb_req_success(request, NULL, NULL);
390 }
391
392 static void exitnow (afb_req_t request)
393 {
394         int code = 0;
395         json_object *query = afb_req_json(request), *l;
396
397         if (json_object_is_type(query,json_type_int))
398                 code = json_object_get_int(query);
399         else if (json_object_object_get_ex(query, "code", &l) && json_object_is_type(l, json_type_int))
400                 code = json_object_get_int(l);
401
402         if (!json_object_object_get_ex(query,"reason",&l))
403                 l = NULL;
404
405         AFB_REQ_NOTICE(request, "in phase of exiting with code %d, reason: %s", code, l ? json_object_get_string(l) : "unknown");
406         afb_req_success(request, NULL, NULL);
407         exit(code);
408 }
409
410 static void broadcast(afb_req_t request)
411 {
412         const char *tag = afb_req_value(request, "tag");
413         const char *name = afb_req_value(request, "name");
414         const char *data = afb_req_value(request, "data");
415         json_object *object = data ? json_tokener_parse(data) : NULL;
416
417         if (tag != NULL) {
418                 pthread_mutex_lock(&mutex);
419                 if (0 > event_broadcast(object, tag))
420                         afb_req_fail(request, "failed", "broadcast error");
421                 else
422                         afb_req_success(request, NULL, NULL);
423                 pthread_mutex_unlock(&mutex);
424         } else if (name != NULL) {
425                 if (0 > afb_daemon_broadcast_event(name, object))
426                         afb_req_fail(request, "failed", "broadcast error");
427                 else
428                         afb_req_success(request, NULL, NULL);
429         } else {
430                 afb_req_fail(request, "failed", "bad arguments");
431         }
432         json_object_put(object);
433 }
434
435 static void hasperm (afb_req_t request)
436 {
437         const char *perm = afb_req_value(request, "perm");
438         if (afb_req_has_permission(request, perm))
439                 afb_req_success_f(request, NULL, "permission %s granted", perm?:"(null)");
440         else
441                 afb_req_fail_f(request, "not-granted", "permission %s NOT granted", perm?:"(null)");
442 }
443
444 static void appid (afb_req_t request)
445 {
446         char *aid = afb_req_get_application_id(request);
447         afb_req_success_f(request, aid ? json_object_new_string(aid) : NULL, "application is %s", aid?:"?");
448         free(aid);
449 }
450
451 static void uid (afb_req_t request)
452 {
453         int uid = afb_req_get_uid(request);
454         afb_req_success_f(request, json_object_new_int(uid), "uid is %d", uid);
455 }
456
457 static void closess (afb_req_t request)
458 {
459         afb_req_session_close(request);
460         afb_req_reply(request, NULL, NULL, "session closed");
461 }
462
463 static void setloa (afb_req_t request)
464 {
465         int loa = json_object_get_int(afb_req_json(request));
466         afb_req_session_set_LOA(request, loa);
467         afb_req_reply_f(request, NULL, NULL, "LOA set to %d", loa);
468 }
469
470 static void setctx (afb_req_t request)
471 {
472         struct json_object *x = afb_req_json(request);
473         afb_req_context(request, (int)(intptr_t)afb_req_get_vcbdata(request), (void*)json_object_get, (void*)json_object_put, x);
474         afb_req_reply(request, json_object_get(x), NULL, "context set");
475 }
476
477 static void getctx (afb_req_t request)
478 {
479         struct json_object *x = afb_req_context(request, 0, 0, 0, 0);
480         afb_req_reply(request, json_object_get(x), NULL, "returning the context");
481 }
482
483 static void info (afb_req_t request)
484 {
485         afb_req_reply(request, afb_req_get_client_info(request), NULL, NULL);
486 }
487
488 static void eventloop (afb_req_t request)
489 {
490         afb_api_t api = afb_req_get_api(request);
491         struct sd_event *ev = afb_api_get_event_loop(api);
492         afb_req_reply(request, NULL, ev ? NULL : "no-event-loop", NULL);
493 }
494
495 static void dbus (afb_req_t request)
496 {
497         afb_api_t api = afb_req_get_api(request);
498         json_object *json = afb_req_json(request);
499         struct sd_bus *bus = (json_object_get_boolean(json) ? afb_api_get_system_bus : afb_api_get_user_bus)(api);
500         afb_req_reply(request, NULL, bus ? NULL : "no-bus", NULL);
501 }
502
503 static void replycount (afb_req_t request)
504 {
505         json_object *json = afb_req_json(request);
506         int count = json_object_get_int(json);
507         while (count-- > 0)
508                 afb_req_reply(request, NULL, NULL, NULL);
509 }
510
511 static void get(afb_req_t request)
512 {
513         struct afb_arg arg = afb_req_get(request, "name");
514         const char *name, *value, *path;
515
516         if (!arg.name || !arg.value)
517                 afb_req_reply(request, NULL, "invalid", "the parameter 'name' is missing");
518         else {
519                 name = arg.name;
520                 value = afb_req_value(request, name);
521                 path = afb_req_path(request, name);
522                 afb_req_reply_f(request, NULL, NULL, "found for '%s': %s", name, value ?: path ?: "NULL");
523         }
524 }
525
526 static void ref(afb_req_t request)
527 {
528         afb_req_addref(request);
529         afb_req_reply(request, NULL, NULL, NULL);
530         afb_req_unref(request);
531 }
532
533 static void rootdir (afb_req_t request)
534 {
535         ssize_t s;
536         afb_api_t api = afb_req_get_api(request);
537         int fd = afb_api_rootdir_get_fd(api);
538         char buffer[150], root[1025];
539         sprintf(buffer, "/proc/self/fd/%d", fd);
540         s = readlink(buffer, root, sizeof root - 1);
541         if (s < 0)
542                 afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
543         else {
544                 root[s] = 0;
545                 afb_req_reply(request, json_object_new_string(root), NULL, NULL);
546         }
547 }
548
549 static void locale (afb_req_t request)
550 {
551         char buffer[150], root[1025];
552         const char *lang, *file;
553         ssize_t s;
554         json_object *json = afb_req_json(request), *x;
555         afb_api_t api = afb_req_get_api(request);
556         int fd;
557
558         lang = NULL;
559         if (json_object_is_type(json, json_type_string))
560                 file = json_object_get_string(json);
561         else {
562                 if (!json_object_object_get_ex(json, "file", &x)) {
563                         afb_req_reply(request, NULL, "invalid", "no file");
564                         return;
565                 }
566                 file = json_object_get_string(x);
567                 if (json_object_object_get_ex(json, "lang", &x))
568                         lang = json_object_get_string(x);
569         }
570
571         fd = afb_api_rootdir_open_locale(api, file, O_RDONLY, lang);
572         if (fd < 0)
573                 afb_req_reply_f(request, NULL, "error", "can't open %s [%s]: %m", file?:"NULL", lang?:"NULL");
574         else {
575                 sprintf(buffer, "/proc/self/fd/%d", fd);
576                 s = readlink(buffer, root, sizeof root - 1);
577                 if (s < 0)
578                         afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
579                 else {
580                         root[s] = 0;
581                         afb_req_reply(request, json_object_new_string(root), NULL, NULL);
582                 }
583                 close(fd);
584         }
585 }
586
587 static void api (afb_req_t request);
588
589 // NOTE: this sample does not use session to keep test a basic as possible
590 //       in real application most APIs should be protected with AFB_SESSION_CHECK
591 static const struct afb_verb_v3 verbs[]= {
592   { .verb="ping",        .callback=pingSample },
593   { .verb="pingfail",    .callback=pingFail },
594   { .verb="pingnull",    .callback=pingNull },
595   { .verb="pingbug",     .callback=pingBug },
596   { .verb="pingJson",    .callback=pingJson },
597   { .verb="pingevent",   .callback=pingEvent },
598   { .verb="subcall",     .callback=subcall },
599   { .verb="subcallreq",  .callback=subcallreq },
600   { .verb="subcallsync", .callback=subcallsync },
601   { .verb="eventadd",    .callback=eventadd },
602   { .verb="eventdel",    .callback=eventdel },
603   { .verb="eventsub",    .callback=eventsub },
604   { .verb="eventunsub",  .callback=eventunsub },
605   { .verb="eventpush",   .callback=eventpush },
606   { .verb="call",        .callback=call },
607   { .verb="callsync",    .callback=callsync },
608   { .verb="verbose",     .callback=verbose },
609   { .verb="broadcast",   .callback=broadcast },
610   { .verb="hasperm",     .callback=hasperm },
611   { .verb="appid",       .callback=appid },
612   { .verb="uid",         .callback=uid },
613   { .verb="exit",        .callback=exitnow },
614   { .verb="close",       .callback=closess },
615   { .verb="set-loa",     .callback=setloa },
616   { .verb="setctx",      .callback=setctx, .vcbdata = (void*)(intptr_t)1 },
617   { .verb="setctxif",    .callback=setctx, .vcbdata = (void*)(intptr_t)0 },
618   { .verb="getctx",      .callback=getctx },
619   { .verb="info",        .callback=info },
620   { .verb="eventloop",   .callback=eventloop },
621   { .verb="dbus",        .callback=dbus },
622   { .verb="reply-count", .callback=replycount },
623   { .verb="get",         .callback=get},
624   { .verb="ref",         .callback=ref},
625   { .verb="rootdir",     .callback=rootdir},
626   { .verb="locale",      .callback=locale},
627   { .verb="api",         .callback=api},
628   { .verb=NULL}
629 };
630
631 static void pingSample2 (struct afb_req_x1 req)
632 {
633         pingSample(req.closure);
634 }
635
636 static const struct afb_verb_v2 apiverbs2[]= {
637   { .verb="ping",        .callback=pingSample2 },
638   { .verb="ping2",        .callback=pingSample2 },
639   { .verb=NULL }
640 };
641
642 static int apipreinit(void *closure, afb_api_t api)
643 {
644         afb_api_set_verbs_v2(api, apiverbs2);
645         afb_api_set_verbs_v3(api, verbs);
646         return 0;
647 }
648
649 static void apiverb (afb_req_t request)
650 {
651         afb_req_reply_f(request, json_object_get(afb_req_json(request)), NULL, "api: %s, verb: %s",
652                 afb_req_get_called_api(request), afb_req_get_called_verb(request));
653 }
654
655 static void apievhndl(void *closure, const char *event, struct json_object *args, afb_api_t api)
656 {
657         struct json_object *obj = closure;
658         afb_api_verbose(api, 0, NULL, 0, NULL, "the handler of closure(%s) received the event %s(%s)",
659                 json_object_get_string(obj), event, json_object_get_string(args));
660 }
661
662 static void api (afb_req_t request)
663 {
664         struct api *sapi, **psapi;
665         const char *action, *apiname, *verbname, *pattern;
666         json_object *json = afb_req_json(request), *x, *closure;
667         afb_api_t api = afb_req_get_api(request), oapi;
668
669         /* get the action */
670         if (!json_object_object_get_ex(json, "action", &x)) {
671                 afb_req_reply(request, NULL, "invalid", "no action");
672                 goto end;
673         }
674         action = json_object_get_string(x);
675
676         /* get the verb */
677         verbname = json_object_object_get_ex(json, "verb", &x) ?
678                 json_object_get_string(x) : NULL;
679
680         /* get the pattern */
681         pattern = json_object_object_get_ex(json, "pattern", &x) ?
682                 json_object_get_string(x) : NULL;
683
684         /* get the closure */
685         closure = NULL;
686         json_object_object_get_ex(json, "closure", &closure);
687
688         /* get the api */
689         if (json_object_object_get_ex(json, "api", &x)) {
690                 apiname = json_object_get_string(x);
691                 sapi = searchapi(apiname, &psapi);
692                 oapi = sapi ? sapi->api : NULL;
693         } else {
694                 oapi = api;
695                 apiname = afb_api_name(api);
696                 sapi = searchapi(apiname, &psapi);
697         }
698
699         /* search the sapi */
700         if (!strcasecmp(action, "create")) {
701                 if (!apiname) {
702                         afb_req_reply(request, NULL, "invalid", "no api");
703                         goto end;
704                 }
705                 if (sapi) {
706                         afb_req_reply(request, NULL, "already-exist", NULL);
707                         goto end;
708                 }
709                 sapi = malloc (sizeof * sapi + strlen(apiname));
710                 if (!sapi) {
711                         afb_req_reply(request, NULL, "out-of-memory", NULL);
712                         goto end;
713                 }
714                 sapi->api = afb_api_new_api(api, apiname, NULL, 1, apipreinit, NULL);
715                 if (!sapi->api) {
716                         afb_req_reply_f(request, NULL, "cant-create", "%m");
717                         goto end;
718                 }
719                 strcpy(sapi->name, apiname);
720                 sapi->next = NULL;
721                 *psapi = sapi;
722         } else {
723                 if (!oapi) {
724                         afb_req_reply(request, NULL, "cant-find-api", NULL);
725                         goto end;
726                 }
727                 if (!strcasecmp(action, "destroy")) {
728                         if (!sapi) {
729                                 afb_req_reply(request, NULL, "cant-destroy", NULL);
730                                 goto end;
731                         }
732                         afb_api_delete_api(oapi);
733                         *psapi = sapi->next;
734                         free(sapi);
735                 } else if (!strcasecmp(action, "addverb")) {
736                         if (!verbname){
737                                 afb_req_reply(request, NULL, "invalid", "no verb");
738                                 goto end;
739                         }
740                         afb_api_add_verb(oapi, verbname, NULL, apiverb, NULL, NULL, 0, !!strchr(verbname, '*'));
741                 } else if (!strcasecmp(action, "delverb")) {
742                         if (!verbname){
743                                 afb_req_reply(request, NULL, "invalid", "no verb");
744                                 goto end;
745                         }
746                         afb_api_del_verb(oapi, verbname, NULL);
747                 } else if (!strcasecmp(action, "addhandler")) {
748                         if (!pattern){
749                                 afb_req_reply(request, NULL, "invalid", "no pattern");
750                                 goto end;
751                         }
752                         afb_api_event_handler_add(oapi, pattern, apievhndl, json_object_get(closure));
753                 } else if (!strcasecmp(action, "delhandler")) {
754                         if (!pattern){
755                                 afb_req_reply(request, NULL, "invalid", "no pattern");
756                                 goto end;
757                         }
758                         closure = NULL;
759                         afb_api_event_handler_del(oapi, pattern, (void**)&closure);
760                         json_object_put(closure);
761                 } else if (!strcasecmp(action, "seal")) {
762                         afb_api_seal(oapi);
763                 } else {
764                         afb_req_reply_f(request, NULL, "invalid", "unknown action %s", action ?: "NULL");
765                         goto end;
766                 }
767         }
768         afb_req_reply(request, NULL, NULL, NULL);
769 end:    return;
770 }
771
772 /*************************************************************/
773
774 static int preinit(afb_api_t api)
775 {
776         AFB_NOTICE("hello binding comes to live");
777 #if defined(PREINIT_PROVIDE_CLASS)
778         afb_api_provide_class(api, PREINIT_PROVIDE_CLASS);
779 #endif
780 #if defined(PREINIT_REQUIRE_CLASS)
781         afb_api_require_class(api, PREINIT_REQUIRE_CLASS);
782 #endif
783         return 0;
784 }
785
786 static int init(afb_api_t api)
787 {
788         AFB_NOTICE("hello binding starting");
789 #if defined(INIT_REQUIRE_API)
790         afb_api_require_api(api, INIT_REQUIRE_API, 1);
791 #endif
792         afb_api_add_alias(api, api->apiname, "fakename");
793         return 0;
794 }
795
796 static void onevent(afb_api_t api, const char *event, struct json_object *object)
797 {
798         AFB_NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
799 }
800
801 const struct afb_binding_v3 afbBindingV3 = {
802         .api = APINAME,
803         .specification = NULL,
804         .verbs = verbs,
805         .preinit = preinit,
806         .init = init,
807         .onevent = onevent
808 };
809