Update copyright dates
[src/app-framework-binder.git] / bindings / samples / hello3.c
index 0136d22..3a6c9ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2018 "IoT.bzh"
+ * Copyright (C) 2015-2020 "IoT.bzh"
  * Author "Fulup Ar Foll"
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
 #include <stdio.h>
 #include <string.h>
 #include <pthread.h>
+#include <fcntl.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <json-c/json.h>
 
 #define AFB_BINDING_VERSION 3
 #include <afb/afb-binding.h>
 
+#if !defined(APINAME)
+#define APINAME "hello"
+#endif
+
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
+/**************************************************************************/
+
 struct event
 {
        struct event *next;
@@ -116,6 +128,30 @@ static int event_broadcast(struct json_object *args, const char *tag)
        return e ? afb_event_broadcast(e->event, json_object_get(args)) : -1;
 }
 
+/**************************************************************************/
+
+struct api
+{
+       struct api *next;
+       afb_api_t api;
+       char name[1];
+};
+
+static struct api *apis = 0;
+
+/* search the api of name */
+static struct api *searchapi(const char *name, struct api ***previous)
+{
+       struct api *a, **p = &apis;
+       while((a = *p) && strcmp(a->name, name))
+               p = &a->next;
+       if (previous)
+               *previous = p;
+       return a;
+}
+
+/**************************************************************************/
+
 // Sample Generic Ping Debug API
 static void ping(afb_req_t request, json_object *jresp, const char *tag)
 {
@@ -175,7 +211,6 @@ static void subcallcb (void *prequest, int status, json_object *object, afb_req_
                afb_req_fail(request, "failed", json_object_to_json_string(object));
        else
                afb_req_success(request, json_object_get(object), NULL);
-       afb_req_unref(request);
 }
 
 static void subcall (afb_req_t request)
@@ -389,7 +424,7 @@ static void broadcast(afb_req_t request)
                        afb_req_success(request, NULL, NULL);
                pthread_mutex_unlock(&mutex);
        } else if (name != NULL) {
-               if (0 > afb_daemon_broadcast_event(name, object))
+               if (0 > afb_daemon_broadcast_event(name, json_object_get(object)))
                        afb_req_fail(request, "failed", "broadcast error");
                else
                        afb_req_success(request, NULL, NULL);
@@ -421,23 +456,326 @@ static void uid (afb_req_t request)
        afb_req_success_f(request, json_object_new_int(uid), "uid is %d", uid);
 }
 
-static int preinit(afb_api_t api)
+static void closess (afb_req_t request)
 {
-       AFB_NOTICE("hello binding comes to live");
-       return 0;
+       afb_req_session_close(request);
+       afb_req_reply(request, NULL, NULL, "session closed");
 }
 
-static int init(afb_api_t api)
+static void setloa (afb_req_t request)
 {
-       AFB_NOTICE("hello binding starting");
-       return 0;
+       int loa = json_object_get_int(afb_req_json(request));
+       afb_req_session_set_LOA(request, loa);
+       afb_req_reply_f(request, NULL, NULL, "LOA set to %d", loa);
 }
 
-static void onevent(afb_api_t api, const char *event, struct json_object *object)
+static void ok (afb_req_t request)
 {
-       AFB_NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
+       afb_req_reply_f(request, NULL, NULL, NULL);
+}
+
+static void setctx (afb_req_t request)
+{
+       struct json_object *x = afb_req_json(request);
+       afb_req_context(request, (int)(intptr_t)afb_req_get_vcbdata(request), (void*)json_object_get, (void*)json_object_put, x);
+       afb_req_reply(request, json_object_get(x), NULL, "context set");
+}
+
+static void getctx (afb_req_t request)
+{
+       struct json_object *x = afb_req_context(request, 0, 0, 0, 0);
+       afb_req_reply(request, json_object_get(x), NULL, "returning the context");
+}
+
+static void info (afb_req_t request)
+{
+       afb_req_reply(request, afb_req_get_client_info(request), NULL, NULL);
+}
+
+static void eventloop (afb_req_t request)
+{
+       afb_api_t api = afb_req_get_api(request);
+       struct sd_event *ev = afb_api_get_event_loop(api);
+       afb_req_reply(request, NULL, ev ? NULL : "no-event-loop", NULL);
+}
+
+static void dbus (afb_req_t request)
+{
+       afb_api_t api = afb_req_get_api(request);
+       json_object *json = afb_req_json(request);
+       struct sd_bus *bus = (json_object_get_boolean(json) ? afb_api_get_system_bus : afb_api_get_user_bus)(api);
+       afb_req_reply(request, NULL, bus ? NULL : "no-bus", NULL);
+}
+
+static void replycount (afb_req_t request)
+{
+       json_object *json = afb_req_json(request);
+       int count = json_object_get_int(json);
+       while (count-- > 0)
+               afb_req_reply(request, NULL, NULL, NULL);
+}
+
+static void get(afb_req_t request)
+{
+       struct afb_arg arg = afb_req_get(request, "name");
+       const char *name, *value, *path;
+
+       if (!arg.name || !arg.value)
+               afb_req_reply(request, NULL, "invalid", "the parameter 'name' is missing");
+       else {
+               name = arg.name;
+               value = afb_req_value(request, name);
+               path = afb_req_path(request, name);
+               afb_req_reply_f(request, NULL, NULL, "found for '%s': %s", name, value ?: path ?: "NULL");
+       }
 }
 
+static void ref(afb_req_t request)
+{
+       afb_req_addref(request);
+       afb_req_reply(request, NULL, NULL, NULL);
+       afb_req_unref(request);
+}
+
+static void mute(afb_req_t request)
+{
+}
+
+static void mutebug(afb_req_t request)
+{
+       afb_req_addref(request);
+}
+
+void queue_cb(int signum, void *arg)
+{
+       afb_req_t request = arg;
+       afb_req_reply(request, NULL, NULL, NULL);
+       afb_req_unref(request);
+}
+
+static void queue(afb_req_t request)
+{
+       afb_req_addref(request);
+       afb_api_queue_job(afb_req_get_api(request), queue_cb, request, NULL, 0);
+}
+
+static void settings(afb_req_t request)
+{
+       afb_api_t api = afb_req_get_api(request);
+       struct json_object *object = afb_api_settings(api);
+       afb_req_reply(request, json_object_get(object), NULL, NULL);
+}
+
+static void rootdir (afb_req_t request)
+{
+       ssize_t s;
+       afb_api_t api = afb_req_get_api(request);
+       int fd = afb_api_rootdir_get_fd(api);
+       char buffer[150], root[1025];
+       sprintf(buffer, "/proc/self/fd/%d", fd);
+       s = readlink(buffer, root, sizeof root - 1);
+       if (s < 0)
+               afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
+       else {
+               root[s] = 0;
+               afb_req_reply(request, json_object_new_string(root), NULL, NULL);
+       }
+}
+
+static void locale (afb_req_t request)
+{
+       char buffer[150], root[1025];
+       const char *lang, *file;
+       ssize_t s;
+       json_object *json = afb_req_json(request), *x;
+       afb_api_t api = afb_req_get_api(request);
+       int fd;
+
+       lang = NULL;
+       if (json_object_is_type(json, json_type_string))
+               file = json_object_get_string(json);
+       else {
+               if (!json_object_object_get_ex(json, "file", &x)) {
+                       afb_req_reply(request, NULL, "invalid", "no file");
+                       return;
+               }
+               file = json_object_get_string(x);
+               if (json_object_object_get_ex(json, "lang", &x))
+                       lang = json_object_get_string(x);
+       }
+
+       fd = afb_api_rootdir_open_locale(api, file, O_RDONLY, lang);
+       if (fd < 0)
+               afb_req_reply_f(request, NULL, "error", "can't open %s [%s]: %m", file?:"NULL", lang?:"NULL");
+       else {
+               sprintf(buffer, "/proc/self/fd/%d", fd);
+               s = readlink(buffer, root, sizeof root - 1);
+               if (s < 0)
+                       afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
+               else {
+                       root[s] = 0;
+                       afb_req_reply(request, json_object_new_string(root), NULL, NULL);
+               }
+               close(fd);
+       }
+}
+
+static void in_after (afb_req_t request)
+{
+       int rc;
+       const char *ts, *ty;
+       char *te;
+       double td;
+       struct timespec t;
+
+       /* get the type */
+       ty = afb_req_value(request, "type") ?: "call";
+       if (strcmp(ty, "call") && strcmp(ty, "callsync")
+        && strcmp(ty, "subcall") && strcmp(ty, "subcallsync"))
+               return afb_req_reply(request, NULL, "invalid", "bad type");
+
+       /* get the delay */
+       ts = afb_req_value(request, "delay");
+       if (!ts)
+               return afb_req_reply(request, NULL, "invalid", "no delay");
+       td = strtod(ts, &te);
+       if (*te || td < 0 || td > 3e6) /* a month is the biggest accepted */
+               return afb_req_reply(request, NULL, "invalid", "bad delay");
+
+       /* wait for that time */
+       if (td > 0) {
+               t.tv_nsec = (long)(1e6 * modf(td, &td));
+               t.tv_sec = (time_t)td;
+               do {
+                       rc = nanosleep(&t, &t);
+               } while (rc != 0 && errno == EINTR);
+
+               if (rc)
+                       return afb_req_reply(request, NULL, "error", "sleep failed");
+       }
+
+       /* do the call */
+       if (!strcmp(ty, "subcallsync"))
+               subcall(request);
+       else if (!strcmp(ty, "subcall"))
+               subcallsync(request);
+       else if (!strcmp(ty, "callsync"))
+               callsync(request);
+       else
+               call(request);
+}
+
+static void *thread_after (void *closure)
+{
+       afb_req_t request = closure;
+       in_after (request);
+       afb_req_unref(request);
+       return NULL;
+}
+
+static void after (afb_req_t request)
+{
+       int rc;
+       pthread_t tid;
+       pthread_attr_t attr;
+
+       afb_req_addref(request);
+
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       rc =pthread_create(&tid, &attr, thread_after, request);
+       pthread_attr_destroy(&attr);
+
+       if (rc != 0) {
+               afb_req_unref(request);
+               afb_req_reply(request, NULL, "cant-start", NULL);
+       }
+}
+
+static void api (afb_req_t request);
+
+/**
+ * Definition of an authorization entry
+ */
+static struct afb_auth auths[] = {
+       {       /* 0 */
+               .type = afb_auth_Or,
+               .first = &auths[1],
+               .next = &auths[9],
+       },
+       {       /* 1 */
+               .type = afb_auth_And,
+               .first = &auths[2],
+               .next = &auths[3],
+       },
+       {       /* 2 */
+               .type = afb_auth_Yes
+       },
+       {       /* 3 */
+               .type = afb_auth_And,
+               .first = &auths[4],
+               .next = &auths[5],
+       },
+       {       /* 4 */
+               .type = afb_auth_LOA,
+               .loa = 0
+       },
+       {       /* 5 */
+               .type = afb_auth_Or,
+               .first = &auths[6],
+               .next = &auths[7],
+       },
+       {       /* 6 */
+               .type = afb_auth_No
+       },
+       {       /* 7 */
+               .type = afb_auth_Not,
+               .first = &auths[8]
+       },
+       {       /* 8 */
+               .type = afb_auth_Yes
+       },
+       {       /* 9 */
+               .type = afb_auth_And,
+               .first = &auths[10],
+               .next = &auths[13],
+       },
+       {       /* 10 */
+               .type = afb_auth_Or,
+               .first = &auths[12],
+               .next = &auths[11],
+       },
+       {       /* 11 */
+               .type = afb_auth_Not,
+               .first = &auths[13]
+       },
+       {       /* 12 */
+               .type = afb_auth_Token
+       },
+       {       /* 13 */
+               .type = afb_auth_And,
+               .first = &auths[14],
+               .next = &auths[17],
+       },
+       {       /* 14 */
+               .type = afb_auth_Or,
+               .first = &auths[16],
+               .next = &auths[15],
+       },
+       {       /* 15 */
+               .type = afb_auth_Not,
+               .first = &auths[16]
+       },
+       {       /* 16 */
+               .type = afb_auth_Permission,
+               .text = "permission"
+       },
+       {       /* 17 */
+               .type = afb_auth_Yes
+       }
+};
+
+
 // NOTE: this sample does not use session to keep test a basic as possible
 //       in real application most APIs should be protected with AFB_SESSION_CHECK
 static const struct afb_verb_v3 verbs[]= {
@@ -463,12 +801,203 @@ static const struct afb_verb_v3 verbs[]= {
   { .verb="appid",       .callback=appid },
   { .verb="uid",         .callback=uid },
   { .verb="exit",        .callback=exitnow },
+  { .verb="close",       .callback=closess, .session=AFB_SESSION_CLOSE },
+  { .verb="set-loa",     .callback=setloa, .auth = &auths[0] },
+  { .verb="has-loa-1",   .callback=ok, .session=AFB_SESSION_LOA_1 },
+  { .verb="has-loa-2",   .callback=ok, .session=AFB_SESSION_LOA_2 },
+  { .verb="has-loa-3",   .callback=ok, .session=AFB_SESSION_LOA_3 },
+  { .verb="setctx",      .callback=setctx, .vcbdata = (void*)(intptr_t)1 },
+  { .verb="setctxif",    .callback=setctx, .vcbdata = (void*)(intptr_t)0 },
+  { .verb="getctx",      .callback=getctx },
+  { .verb="checktok",    .callback=ok, .session=AFB_SESSION_CHECK },
+  { .verb="reftok",      .callback=ok, .session=AFB_SESSION_CHECK | AFB_SESSION_REFRESH },
+  { .verb="info",        .callback=info },
+  { .verb="eventloop",   .callback=eventloop },
+  { .verb="dbus",        .callback=dbus },
+  { .verb="reply-count", .callback=replycount },
+  { .verb="get",         .callback=get},
+  { .verb="ref",         .callback=ref},
+  { .verb="rootdir",     .callback=rootdir},
+  { .verb="locale",      .callback=locale},
+  { .verb="api",         .callback=api},
+  { .verb="mute",        .callback=mute},
+  { .verb="mutebug",     .callback=mutebug},
+  { .verb="queue",       .callback=queue},
+  { .verb="settings",    .callback=settings},
+  { .verb="after",       .callback=after},
   { .verb=NULL}
 };
 
-#if !defined(APINAME)
-#define APINAME "hello3"
+static void pingSample2 (struct afb_req_x1 req)
+{
+       pingSample(req.closure);
+}
+
+static const struct afb_verb_v2 apiverbs2[]= {
+  { .verb="ping",        .callback=pingSample2 },
+  { .verb="ping2",        .callback=pingSample2 },
+  { .verb=NULL }
+};
+
+static int apipreinit(void *closure, afb_api_t api)
+{
+       afb_api_set_verbs_v2(api, apiverbs2);
+       afb_api_set_verbs_v3(api, verbs);
+       return 0;
+}
+
+static void apiverb (afb_req_t request)
+{
+       afb_req_reply_f(request, json_object_get(afb_req_json(request)), NULL, "api: %s, verb: %s",
+               afb_req_get_called_api(request), afb_req_get_called_verb(request));
+}
+
+static void apievhndl(void *closure, const char *event, struct json_object *args, afb_api_t api)
+{
+       struct json_object *obj = closure;
+       afb_api_verbose(api, 0, NULL, 0, NULL, "the handler of closure(%s) received the event %s(%s)",
+               json_object_get_string(obj), event, json_object_get_string(args));
+}
+
+static void api (afb_req_t request)
+{
+       struct api *sapi, **psapi;
+       const char *action, *apiname, *verbname, *pattern;
+       json_object *json = afb_req_json(request), *x, *closure;
+       afb_api_t api = afb_req_get_api(request), oapi;
+
+       /* get the action */
+       if (!json_object_object_get_ex(json, "action", &x)) {
+               afb_req_reply(request, NULL, "invalid", "no action");
+               goto end;
+       }
+       action = json_object_get_string(x);
+
+       /* get the verb */
+       verbname = json_object_object_get_ex(json, "verb", &x) ?
+               json_object_get_string(x) : NULL;
+
+       /* get the pattern */
+       pattern = json_object_object_get_ex(json, "pattern", &x) ?
+               json_object_get_string(x) : NULL;
+
+       /* get the closure */
+       closure = NULL;
+       json_object_object_get_ex(json, "closure", &closure);
+
+       /* get the api */
+       if (json_object_object_get_ex(json, "api", &x)) {
+               apiname = json_object_get_string(x);
+               sapi = searchapi(apiname, &psapi);
+               oapi = sapi ? sapi->api : NULL;
+       } else {
+               oapi = api;
+               apiname = afb_api_name(api);
+               sapi = searchapi(apiname, &psapi);
+       }
+
+       /* search the sapi */
+       if (!strcasecmp(action, "create")) {
+               if (!apiname) {
+                       afb_req_reply(request, NULL, "invalid", "no api");
+                       goto end;
+               }
+               if (sapi) {
+                       afb_req_reply(request, NULL, "already-exist", NULL);
+                       goto end;
+               }
+               sapi = malloc (sizeof * sapi + strlen(apiname));
+               if (!sapi) {
+                       afb_req_reply(request, NULL, "out-of-memory", NULL);
+                       goto end;
+               }
+               sapi->api = afb_api_new_api(api, apiname, NULL, 1, apipreinit, NULL);
+               if (!sapi->api) {
+                       free(sapi);
+                       afb_req_reply_f(request, NULL, "cant-create", "%m");
+                       goto end;
+               }
+               strcpy(sapi->name, apiname);
+               sapi->next = NULL;
+               *psapi = sapi;
+       } else {
+               if (!oapi) {
+                       afb_req_reply(request, NULL, "cant-find-api", NULL);
+                       goto end;
+               }
+               if (!strcasecmp(action, "destroy")) {
+                       if (!sapi) {
+                               afb_req_reply(request, NULL, "cant-destroy", NULL);
+                               goto end;
+                       }
+                       afb_api_delete_api(oapi);
+                       *psapi = sapi->next;
+                       free(sapi);
+               } else if (!strcasecmp(action, "addverb")) {
+                       if (!verbname){
+                               afb_req_reply(request, NULL, "invalid", "no verb");
+                               goto end;
+                       }
+                       afb_api_add_verb(oapi, verbname, NULL, apiverb, NULL, NULL, 0, !!strchr(verbname, '*'));
+               } else if (!strcasecmp(action, "delverb")) {
+                       if (!verbname){
+                               afb_req_reply(request, NULL, "invalid", "no verb");
+                               goto end;
+                       }
+                       afb_api_del_verb(oapi, verbname, NULL);
+               } else if (!strcasecmp(action, "addhandler")) {
+                       if (!pattern){
+                               afb_req_reply(request, NULL, "invalid", "no pattern");
+                               goto end;
+                       }
+                       afb_api_event_handler_add(oapi, pattern, apievhndl, json_object_get(closure));
+               } else if (!strcasecmp(action, "delhandler")) {
+                       if (!pattern){
+                               afb_req_reply(request, NULL, "invalid", "no pattern");
+                               goto end;
+                       }
+                       closure = NULL;
+                       afb_api_event_handler_del(oapi, pattern, (void**)&closure);
+                       json_object_put(closure);
+               } else if (!strcasecmp(action, "seal")) {
+                       afb_api_seal(oapi);
+               } else {
+                       afb_req_reply_f(request, NULL, "invalid", "unknown action %s", action ?: "NULL");
+                       goto end;
+               }
+       }
+       afb_req_reply(request, NULL, NULL, NULL);
+end:   return;
+}
+
+/*************************************************************/
+
+static int preinit(afb_api_t api)
+{
+       AFB_NOTICE("hello binding comes to live");
+#if defined(PREINIT_PROVIDE_CLASS)
+       afb_api_provide_class(api, PREINIT_PROVIDE_CLASS);
+#endif
+#if defined(PREINIT_REQUIRE_CLASS)
+       afb_api_require_class(api, PREINIT_REQUIRE_CLASS);
+#endif
+       return 0;
+}
+
+static int init(afb_api_t api)
+{
+       AFB_NOTICE("hello binding starting");
+#if defined(INIT_REQUIRE_API)
+       afb_api_require_api(api, INIT_REQUIRE_API, 1);
 #endif
+       afb_api_add_alias(api, api->apiname, "fakename");
+       return 0;
+}
+
+static void onevent(afb_api_t api, const char *event, struct json_object *object)
+{
+       AFB_NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
+}
 
 const struct afb_binding_v3 afbBindingV3 = {
        .api = APINAME,