Introduce typedef for clients
[src/app-framework-binder.git] / bindings / samples / HelloWorld.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "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
22 #include <json-c/json.h>
23
24 #define AFB_BINDING_VERSION 2
25 #include <afb/afb-binding.h>
26
27 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
28
29 struct event
30 {
31         struct event *next;
32         struct afb_event event;
33         char tag[1];
34 };
35
36 static struct event *events = 0;
37
38 /* searchs the event of tag */
39 static struct event *event_get(const char *tag)
40 {
41         struct event *e = events;
42         while(e && strcmp(e->tag, tag))
43                 e = e->next;
44         return e;
45 }
46
47 /* deletes the event of tag */
48 static int event_del(const char *tag)
49 {
50         struct event *e, **p;
51
52         /* check exists */
53         e = event_get(tag);
54         if (!e) return -1;
55
56         /* unlink */
57         p = &events;
58         while(*p != e) p = &(*p)->next;
59         *p = e->next;
60
61         /* destroys */
62         afb_event_drop(e->event);
63         free(e);
64         return 0;
65 }
66
67 /* creates the event of tag */
68 static int event_add(const char *tag, const char *name)
69 {
70         struct event *e;
71
72         /* check valid tag */
73         e = event_get(tag);
74         if (e) return -1;
75
76         /* creation */
77         e = malloc(strlen(tag) + sizeof *e);
78         if (!e) return -1;
79         strcpy(e->tag, tag);
80
81         /* make the event */
82         e->event = afb_daemon_make_event(name);
83         if (!e->event.closure) { free(e); return -1; }
84
85         /* link */
86         e->next = events;
87         events = e;
88         return 0;
89 }
90
91 static int event_subscribe(afb_req request, const char *tag)
92 {
93         struct event *e;
94         e = event_get(tag);
95         return e ? afb_req_subscribe(request, e->event) : -1;
96 }
97
98 static int event_unsubscribe(afb_req request, const char *tag)
99 {
100         struct event *e;
101         e = event_get(tag);
102         return e ? afb_req_unsubscribe(request, e->event) : -1;
103 }
104
105 static int event_push(struct json_object *args, const char *tag)
106 {
107         struct event *e;
108         e = event_get(tag);
109         return e ? afb_event_push(e->event, json_object_get(args)) : -1;
110 }
111
112 // Sample Generic Ping Debug API
113 static void ping(afb_req request, json_object *jresp, const char *tag)
114 {
115         static int pingcount = 0;
116         json_object *query = afb_req_json(request);
117         afb_req_success_f(request, jresp, "Ping Binder Daemon tag=%s count=%d query=%s", tag, ++pingcount, json_object_to_json_string(query));
118 }
119
120 static void pingSample (afb_req request)
121 {
122         ping(request, json_object_new_string ("Some String"), "pingSample");
123 }
124
125 static void pingFail (afb_req request)
126 {
127         afb_req_fail(request, "failed", "Ping Binder Daemon fails");
128 }
129
130 static void pingNull (afb_req request)
131 {
132         ping(request, NULL, "pingNull");
133 }
134
135 static void pingBug (afb_req request)
136 {
137         ping((afb_req){NULL,NULL}, NULL, "pingBug");
138 }
139
140 static void pingEvent(afb_req request)
141 {
142         json_object *query = afb_req_json(request);
143         afb_daemon_broadcast_event("event", json_object_get(query));
144         ping(request, json_object_get(query), "event");
145 }
146
147
148 // For samples https://linuxprograms.wordpress.com/2010/05/20/json-c-libjson-tutorial/
149 static void pingJson (afb_req request) {
150     json_object *jresp, *embed;
151
152     jresp = json_object_new_object();
153     json_object_object_add(jresp, "myString", json_object_new_string ("Some String"));
154     json_object_object_add(jresp, "myInt", json_object_new_int (1234));
155
156     embed  = json_object_new_object();
157     json_object_object_add(embed, "subObjString", json_object_new_string ("Some String"));
158     json_object_object_add(embed, "subObjInt", json_object_new_int (5678));
159
160     json_object_object_add(jresp,"eobj", embed);
161
162     ping(request, jresp, "pingJson");
163 }
164
165 static void subcallcb (void *prequest, int iserror, json_object *object)
166 {
167         afb_req request = afb_req_unstore(prequest);
168         if (iserror)
169                 afb_req_fail(request, "failed", json_object_to_json_string(object));
170         else
171                 afb_req_success(request, json_object_get(object), NULL);
172         afb_req_unref(request);
173 }
174
175 static void subcall (afb_req request)
176 {
177         const char *api = afb_req_value(request, "api");
178         const char *verb = afb_req_value(request, "verb");
179         const char *args = afb_req_value(request, "args");
180         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
181
182         if (object == NULL)
183                 afb_req_fail(request, "failed", "bad arguments");
184         else
185                 afb_req_subcall(request, api, verb, object, subcallcb, afb_req_store(request));
186 }
187
188 static void subcallsync (afb_req request)
189 {
190         int rc;
191         const char *api = afb_req_value(request, "api");
192         const char *verb = afb_req_value(request, "verb");
193         const char *args = afb_req_value(request, "args");
194         json_object *result, *object = api && verb && args ? json_tokener_parse(args) : NULL;
195
196         if (object == NULL)
197                 afb_req_fail(request, "failed", "bad arguments");
198         else {
199                 rc = afb_req_subcall_sync(request, api, verb, object, &result);
200                 if (rc)
201                         afb_req_success(request, result, NULL);
202                 else {
203                         afb_req_fail(request, "failed", json_object_to_json_string(result));
204                         json_object_put(result);
205                 }
206         }
207 }
208
209 static void eventadd (afb_req request)
210 {
211         const char *tag = afb_req_value(request, "tag");
212         const char *name = afb_req_value(request, "name");
213
214         pthread_mutex_lock(&mutex);
215         if (tag == NULL || name == NULL)
216                 afb_req_fail(request, "failed", "bad arguments");
217         else if (0 != event_add(tag, name))
218                 afb_req_fail(request, "failed", "creation error");
219         else
220                 afb_req_success(request, NULL, NULL);
221         pthread_mutex_unlock(&mutex);
222 }
223
224 static void eventdel (afb_req request)
225 {
226         const char *tag = afb_req_value(request, "tag");
227
228         pthread_mutex_lock(&mutex);
229         if (tag == NULL)
230                 afb_req_fail(request, "failed", "bad arguments");
231         else if (0 != event_del(tag))
232                 afb_req_fail(request, "failed", "deletion error");
233         else
234                 afb_req_success(request, NULL, NULL);
235         pthread_mutex_unlock(&mutex);
236 }
237
238 static void eventsub (afb_req request)
239 {
240         const char *tag = afb_req_value(request, "tag");
241
242         pthread_mutex_lock(&mutex);
243         if (tag == NULL)
244                 afb_req_fail(request, "failed", "bad arguments");
245         else if (0 != event_subscribe(request, tag))
246                 afb_req_fail(request, "failed", "subscription error");
247         else
248                 afb_req_success(request, NULL, NULL);
249         pthread_mutex_unlock(&mutex);
250 }
251
252 static void eventunsub (afb_req request)
253 {
254         const char *tag = afb_req_value(request, "tag");
255
256         pthread_mutex_lock(&mutex);
257         if (tag == NULL)
258                 afb_req_fail(request, "failed", "bad arguments");
259         else if (0 != event_unsubscribe(request, tag))
260                 afb_req_fail(request, "failed", "unsubscription error");
261         else
262                 afb_req_success(request, NULL, NULL);
263         pthread_mutex_unlock(&mutex);
264 }
265
266 static void eventpush (afb_req request)
267 {
268         const char *tag = afb_req_value(request, "tag");
269         const char *data = afb_req_value(request, "data");
270         json_object *object = data ? json_tokener_parse(data) : NULL;
271
272         pthread_mutex_lock(&mutex);
273         if (tag == NULL)
274                 afb_req_fail(request, "failed", "bad arguments");
275         else if (0 > event_push(object, tag))
276                 afb_req_fail(request, "failed", "push error");
277         else
278                 afb_req_success(request, NULL, NULL);
279         pthread_mutex_unlock(&mutex);
280         json_object_put(object);
281 }
282
283 static void callcb (void *prequest, int iserror, json_object *object)
284 {
285         afb_req request = afb_req_unstore(prequest);
286         if (iserror)
287                 afb_req_fail(request, "failed", json_object_to_json_string(object));
288         else
289                 afb_req_success(request, json_object_get(object), NULL);
290         afb_req_unref(request);
291 }
292
293 static void call (afb_req request)
294 {
295         const char *api = afb_req_value(request, "api");
296         const char *verb = afb_req_value(request, "verb");
297         const char *args = afb_req_value(request, "args");
298         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
299
300         if (object == NULL)
301                 afb_req_fail(request, "failed", "bad arguments");
302         else
303                 afb_service_call(api, verb, object, callcb, afb_req_store(request));
304 }
305
306 static void callsync (afb_req request)
307 {
308         int rc;
309         const char *api = afb_req_value(request, "api");
310         const char *verb = afb_req_value(request, "verb");
311         const char *args = afb_req_value(request, "args");
312         json_object *result, *object = api && verb && args ? json_tokener_parse(args) : NULL;
313
314         if (object == NULL)
315                 afb_req_fail(request, "failed", "bad arguments");
316         else {
317                 rc = afb_service_call_sync(api, verb, object, &result);
318                 if (rc)
319                         afb_req_success(request, result, NULL);
320                 else {
321                         afb_req_fail(request, "failed", json_object_to_json_string(result));
322                         json_object_put(result);
323                 }
324         }
325 }
326
327 static void verbose (afb_req request)
328 {
329         int level = 5;
330         json_object *query = afb_req_json(request), *l;
331
332         if (json_object_is_type(query,json_type_int))
333                 level = json_object_get_int(query);
334         else if (json_object_object_get_ex(query, "level", &l) && json_object_is_type(l, json_type_int))
335                 level = json_object_get_int(l);
336
337         if (!json_object_object_get_ex(query,"message",&l))
338                 l = query;
339
340         AFB_REQ_VERBOSE(request, level, "verbose called for %s", json_object_get_string(l));
341         afb_req_success(request, NULL, NULL);
342 }
343
344 static void exitnow (afb_req request)
345 {
346         int code = 0;
347         json_object *query = afb_req_json(request), *l;
348
349         if (json_object_is_type(query,json_type_int))
350                 code = json_object_get_int(query);
351         else if (json_object_object_get_ex(query, "code", &l) && json_object_is_type(l, json_type_int))
352                 code = json_object_get_int(l);
353
354         if (!json_object_object_get_ex(query,"reason",&l))
355                 l = NULL;
356
357         REQ_NOTICE(request, "in phase of exiting with code %d, reason: %s", code, l ? json_object_get_string(l) : "unknown");
358         afb_req_success(request, NULL, NULL);
359         exit(code);
360 }
361
362 static int preinit()
363 {
364         NOTICE("hello binding comes to live");
365         return 0;
366 }
367
368 static int init()
369 {
370         NOTICE("hello binding starting");
371         return 0;
372 }
373
374 static void onevent(const char *event, struct json_object *object)
375 {
376         NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
377 }
378
379 // NOTE: this sample does not use session to keep test a basic as possible
380 //       in real application most APIs should be protected with AFB_SESSION_CHECK
381 static const afb_verb_v2 verbs[]= {
382   { "ping"     ,    pingSample , NULL, AFB_SESSION_NONE },
383   { "pingfail" ,    pingFail   , NULL, AFB_SESSION_NONE },
384   { "pingnull" ,    pingNull   , NULL, AFB_SESSION_NONE },
385   { "pingbug"  ,    pingBug    , NULL, AFB_SESSION_NONE },
386   { "pingJson" ,    pingJson   , NULL, AFB_SESSION_NONE },
387   { "pingevent",    pingEvent  , NULL, AFB_SESSION_NONE },
388   { "subcall",      subcall    , NULL, AFB_SESSION_NONE },
389   { "subcallsync",  subcallsync, NULL, AFB_SESSION_NONE },
390   { "eventadd",     eventadd   , NULL, AFB_SESSION_NONE },
391   { "eventdel",     eventdel   , NULL, AFB_SESSION_NONE },
392   { "eventsub",     eventsub   , NULL, AFB_SESSION_NONE },
393   { "eventunsub",   eventunsub , NULL, AFB_SESSION_NONE },
394   { "eventpush",    eventpush  , NULL, AFB_SESSION_NONE },
395   { "call",         call       , NULL, AFB_SESSION_NONE },
396   { "callsync",     callsync   , NULL, AFB_SESSION_NONE },
397   { "verbose",      verbose    , NULL, AFB_SESSION_NONE },
398   { "exit",         exitnow    , NULL, AFB_SESSION_NONE },
399   { NULL}
400 };
401
402 const afb_binding_v2 afbBindingV2 = {
403         .api = "hello",
404         .specification = NULL,
405         .verbs = verbs,
406         .preinit = preinit,
407         .init = init,
408         .onevent = onevent
409 };
410