Add a closure argument to dynamic verbs
[src/app-framework-binder.git] / src / afb-api-dyn.c
1 /*
2  * Copyright (C) 2016, 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 <string.h>
22 #include <assert.h>
23 #include <errno.h>
24
25 #include <json-c/json.h>
26
27 #define AFB_BINDING_VERSION 0
28 #include <afb/afb-binding.h>
29
30 #include "afb-api.h"
31 #include "afb-api-dyn.h"
32 #include "afb-apiset.h"
33 #include "afb-auth.h"
34 #include "afb-export.h"
35 #include "afb-xreq.h"
36 #include "verbose.h"
37
38 /*
39  * Description of a binding
40  */
41 struct afb_api_dyn {
42         int count;
43         struct afb_api_dyn_verb **verbs;
44         const struct afb_verb_v2 *verbsv2;
45         struct afb_export *export;
46         char info[1];
47 };
48
49 void afb_api_dyn_set_verbs_v2(
50                 struct afb_api_dyn *dynapi,
51                 const struct afb_verb_v2 *verbs)
52 {
53         dynapi->verbsv2 = verbs;
54 }
55
56 int afb_api_dyn_add_verb(
57                 struct afb_api_dyn *dynapi,
58                 const char *verb,
59                 const char *info,
60                 void (*callback)(struct afb_request *request),
61                 void *vcbdata,
62                 const struct afb_auth *auth,
63                 uint32_t session)
64 {
65         struct afb_api_dyn_verb *v, **vv;
66
67         afb_api_dyn_sub_verb(dynapi, verb);
68
69         vv = realloc(dynapi->verbs, (1 + dynapi->count) * sizeof *vv);
70         if (!vv)
71                 goto oom;
72         dynapi->verbs = vv;
73
74         v = malloc(sizeof *v + strlen(verb) + (info ? 1 + strlen(info) : 0));
75         if (!v)
76                 goto oom;
77
78         v->callback = callback;
79         v->vcbdata = vcbdata;
80         v->auth = auth;
81         v->session = session;
82
83         v->info = 1 + stpcpy(v->verb, verb);
84         if (info)
85                 strcpy((char*)v->info, info);
86         else
87                 v->info = NULL;
88
89         dynapi->verbs[dynapi->count++] = v;
90         return 0;
91 oom:
92         errno = ENOMEM;
93         return -1;
94 }
95
96 int afb_api_dyn_sub_verb(
97                 struct afb_api_dyn *dynapi,
98                 const char *verb)
99 {
100         struct afb_api_dyn_verb *v;
101         int i;
102
103         /* look first in dyna mic verbs */
104         for (i = 0 ; i < dynapi->count ; i++) {
105                 v = dynapi->verbs[i];
106                 if (!strcasecmp(v->verb, verb)) {
107                         if (i != --dynapi->count)
108                                 dynapi->verbs[i] = dynapi->verbs[dynapi->count];
109                         free(v);
110                         return 0;
111                 }
112         }
113
114         errno = ENOENT;
115         return -1;
116 }
117
118 static void call_cb(void *closure, struct afb_xreq *xreq)
119 {
120         struct afb_api_dyn *dynapi = closure;
121         struct afb_api_dyn_verb **verbs, *v;
122         const struct afb_verb_v2 *verbsv2;
123         int i;
124         const char *name;
125
126         name = xreq->verb;
127         xreq->request.dynapi = (void*)dynapi->export; /* hack: this avoids to export afb_export structure */
128
129         /* look first in dyna mic verbs */
130         verbs = dynapi->verbs;
131         i = dynapi->count;
132         while (i) {
133                 v = verbs[--i];
134                 if (!strcasecmp(v->verb, name)) {
135                         xreq->request.vcbdata = v->vcbdata;
136                         afb_xreq_call_verb_vdyn(xreq, verbs[i]);
137                         return;
138                 }
139         }
140
141         verbsv2 = dynapi->verbsv2;
142         if (verbsv2) {
143                 while (verbsv2->verb) {
144                         if (strcasecmp(verbsv2->verb, name))
145                                 verbsv2++;
146                         else {
147                                 afb_xreq_call_verb_v2(xreq, verbsv2);
148                                 return;
149                         }
150                 }
151         }
152
153         afb_xreq_fail_unknown_verb(xreq);
154 }
155
156 static int service_start_cb(void *closure, int share_session, int onneed, struct afb_apiset *apiset)
157 {
158         struct afb_api_dyn *dynapi = closure;
159         return afb_export_start(dynapi->export, share_session, onneed, apiset);
160 }
161
162 static void update_hooks_cb(void *closure)
163 {
164         struct afb_api_dyn *dynapi = closure;
165         afb_export_update_hook(dynapi->export);
166 }
167
168 static int get_verbosity_cb(void *closure)
169 {
170         struct afb_api_dyn *dynapi = closure;
171         return afb_export_verbosity_get(dynapi->export);
172 }
173
174 static void set_verbosity_cb(void *closure, int level)
175 {
176         struct afb_api_dyn *dynapi = closure;
177         afb_export_verbosity_set(dynapi->export, level);
178 }
179
180 static struct json_object *make_description_openAPIv3(struct afb_api_dyn *dynapi)
181 {
182         char buffer[256];
183         struct afb_api_dyn_verb **iter, **end, *verb;
184         struct json_object *r, *f, *a, *i, *p, *g;
185
186         r = json_object_new_object();
187         json_object_object_add(r, "openapi", json_object_new_string("3.0.0"));
188
189         i = json_object_new_object();
190         json_object_object_add(r, "info", i);
191         json_object_object_add(i, "title", json_object_new_string(afb_export_apiname(dynapi->export)));
192         json_object_object_add(i, "version", json_object_new_string("0.0.0"));
193         json_object_object_add(i, "description", json_object_new_string(dynapi->info));
194
195         p = json_object_new_object();
196         json_object_object_add(r, "paths", p);
197         iter = dynapi->verbs;
198         end = iter + dynapi->count;
199         while (iter != end) {
200                 verb = *iter++;
201                 buffer[0] = '/';
202                 strncpy(buffer + 1, verb->verb, sizeof buffer - 1);
203                 buffer[sizeof buffer - 1] = 0;
204                 f = json_object_new_object();
205                 json_object_object_add(p, buffer, f);
206                 g = json_object_new_object();
207                 json_object_object_add(f, "get", g);
208
209                 a = afb_auth_json_v2(verb->auth, verb->session);
210                 if (a)
211                         json_object_object_add(g, "x-permissions", a);
212
213                 a = json_object_new_object();
214                 json_object_object_add(g, "responses", a);
215                 f = json_object_new_object();
216                 json_object_object_add(a, "200", f);
217                 json_object_object_add(f, "description", json_object_new_string(verb->info?:verb->verb));
218         }
219         return r;
220 }
221
222 static struct json_object *describe_cb(void *closure)
223 {
224         struct afb_api_dyn *dynapi = closure;
225         struct json_object *r = make_description_openAPIv3(dynapi);
226         return r;
227 }
228
229 static struct afb_api_itf dyn_api_itf = {
230         .call = call_cb,
231         .service_start = service_start_cb,
232         .update_hooks = update_hooks_cb,
233         .get_verbosity = get_verbosity_cb,
234         .set_verbosity = set_verbosity_cb,
235         .describe = describe_cb
236 };
237
238 int afb_api_dyn_add(struct afb_apiset *apiset, const char *name, const char *info, int (*preinit)(void*, struct afb_dynapi*), void *closure)
239 {
240         int rc;
241         struct afb_api_dyn *dynapi;
242         struct afb_api afb_api;
243         struct afb_export *export;
244
245         INFO("Starting creation of dynamic API %s", name);
246
247         /* allocates the description */
248         info = info ?: "";
249         dynapi = calloc(1, sizeof *dynapi + strlen(info));
250         export = afb_export_create_vdyn(apiset, name, dynapi);
251         if (!dynapi || !export) {
252                 ERROR("out of memory");
253                 goto error;
254         }
255         strcpy(dynapi->info, info);
256         dynapi->export = export;
257
258         /* preinit the api */
259         rc = afb_export_preinit_vdyn(export, preinit, closure);
260         if (rc < 0) {
261                 ERROR("dynamic api %s preinit function failed, ABORTING it!",
262                                 afb_export_apiname(dynapi->export));
263                 goto error;
264         }
265
266         /* records the binding */
267         afb_api.closure = dynapi;
268         afb_api.itf = &dyn_api_itf;
269         afb_api.group = NULL;
270         if (afb_apiset_add(apiset, afb_export_apiname(dynapi->export), afb_api) < 0) {
271                 ERROR("dynamic api %s can't be registered to set %s, ABORTING it!",
272                                 afb_export_apiname(dynapi->export),
273                                 afb_apiset_name(apiset));
274                 goto error;
275         }
276         INFO("binding %s added to set %s", afb_export_apiname(dynapi->export), afb_apiset_name(apiset));
277         return 1;
278
279 error:
280         afb_export_destroy(export);
281         free(dynapi);
282
283         return -1;
284 }
285