ad3a4f2238a597961af3233fc4e8a6d2cb7b4a05
[src/app-framework-binder.git] / src / rest-api.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  * 
18  * Contain all generic part to handle REST/API
19  * 
20  *  https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'FILE *fp']
21  */
22
23 #include "../include/local-def.h"
24
25 #include <setjmp.h>
26 #include <signal.h>
27
28 #define AFB_MSG_JTYPE "AJB_reply"
29
30
31 // handle to hold queryAll values
32 typedef struct {
33      char    *msg;
34      int     idx;
35      size_t  len;
36 } queryHandleT;
37
38 static json_object     *afbJsonType;
39
40
41 // Sample Generic Ping Debug API
42 PUBLIC json_object* apiPingTest(AFB_request *request) {
43     static pingcount = 0;
44     json_object *response;
45     char query  [256];
46     char session[256];
47
48     int len;
49     AFB_clientCtx *client=request->client; // get client context from request
50     
51     // request all query key/value
52     len = getQueryAll (request, query, sizeof(query));
53     if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query));
54     
55     // check if we have some post data
56     if (request->post == NULL)  request->post->data="NoData"; 
57     
58     // check is we have a session and a plugin handle
59     if (client == NULL) strcpy (session,"NoSession");       
60     else snprintf(session, sizeof(session),"uuid=%s token=%s ctx=0x%x handle=0x%x", client->uuid, client->token, client->ctx, client->ctx); 
61         
62     // return response to caller
63     response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d CtxtId=%d query={%s} session={%s} PostData: [%s] "
64                , pingcount++, request->client->cid, query, session, request->post->data);
65     return (response);
66 }
67
68 // Helper to retrieve argument from  connection
69 PUBLIC const char* getQueryValue(AFB_request * request, char *name) {
70     const char *value;
71
72     value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name);
73     return (value);
74 }
75
76 STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) {
77     queryHandleT *query = (queryHandleT*)handle;
78         
79     query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value);
80 }
81
82 // Helper to retrieve argument from  connection
83 PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) {
84     queryHandleT query;
85     buffer[0] = '\0'; // start with an empty string
86     query.msg= buffer;
87     query.len= len;
88     query.idx= 0;
89
90     MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query);
91     return (len);
92 }
93
94 // Because of POST call multiple time requestApi we need to free POST handle here
95 PUBLIC void endPostRequest(AFB_PostHandle *postHandle) {
96
97     if (postHandle->type == AFB_POST_JSON) {
98         if (verbose) fprintf(stderr, "End PostJson Request UID=%d\n", postHandle->uid);
99     }
100
101     if (postHandle->type == AFB_POST_FORM) {
102         AFB_PostHandle *postform = (AFB_PostHandle*) postHandle->private;
103         if (verbose) fprintf(stderr, "End PostForm Request UID=%d\n", postHandle->uid);
104
105         // call API termination callback
106         if (!postHandle->private) {
107             if (!postHandle->completeCB) postHandle->completeCB (postHandle->private);
108         }
109     }
110     free(postHandle->private);
111     free(postHandle);
112 }
113
114 // Check of apiurl is declare in this plugin and call it
115 STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request, void *context) {
116     json_object *jresp, *jcall;
117     int idx, status, sig;
118     int signals[]= {SIGALRM, SIGSEGV, SIGFPE, 0};
119     
120     /*---------------------------------------------------------------
121     | Signal handler defined inside CallPluginApi to access Request
122     +---------------------------------------------------------------- */
123     void pluginError (int signum) {
124       sigset_t sigset;
125       AFB_clientCtx *context;
126               
127       // unlock signal to allow a new signal to come
128       sigemptyset (&sigset);
129       sigaddset   (&sigset, signum);
130       sigprocmask (SIG_UNBLOCK, &sigset, 0);
131
132       fprintf (stderr, "Oops:%s Plugin Api Timeout timeout\n", configTime());
133       longjmp (request->checkPluginCall, signum);
134     }
135
136     
137     // If a plugin hold this urlpath call its callback
138     for (idx = 0; plugin->apis[idx].callback != NULL; idx++) {
139         if (!strcmp(plugin->apis[idx].name, request->api)) {
140             
141             // Request was found and at least partially executed
142             request->jresp  = json_object_new_object();
143             json_object_get (afbJsonType);  // increate jsontype reference count
144             json_object_object_add (request->jresp, "jtype", afbJsonType);
145             
146             // prepare an object to store calling values
147             jcall=json_object_new_object();
148             json_object_object_add(jcall, "prefix", json_object_new_string (plugin->prefix));
149             json_object_object_add(jcall, "api"   , json_object_new_string (plugin->apis[idx].name));
150             
151             // save context before calling the API
152             status = setjmp (request->checkPluginCall);
153             if (status != 0) {    
154                 
155                 // Plugin aborted somewhere during its execution
156                 json_object_object_add(jcall, "status", json_object_new_string ("abort"));
157                 json_object_object_add(jcall, "info" ,  json_object_new_string ("Plugin broke during execution"));
158                 json_object_object_add(request->jresp, "request", jcall);
159                 
160             } else {
161                 
162                 // If timeout protection==0 we are in debug and we do not apply signal protection
163                 if (request->config->apiTimeout > 0) {
164                     for (sig=0; signals[sig] != 0; sig++) {
165                        if (signal (signals[sig], pluginError) == SIG_ERR) {
166                             request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
167                             json_object_object_add(jcall, "status", json_object_new_string ("fail"));
168                             json_object_object_add(jcall, "info", json_object_new_string ("Setting Timeout Handler Failed"));
169                             json_object_object_add(request->jresp, "request", jcall);
170                             return AFB_DONE;
171                        }
172                     }
173                     // Trigger a timer to protect from unacceptable long time execution
174                     alarm (request->config->apiTimeout);
175                 }
176
177                 // Out of SessionNone every call get a client context session
178                 if (AFB_SESSION_NONE != plugin->apis[idx].session) {
179                     
180                     // add client context to request
181                     ctxClientGet(request, plugin);
182                     
183                     if (verbose) fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[0x%x] Uuid=[%s] Token=[%s]\n"
184                            , request->plugin, request->api, plugin->apis[idx].session, request->client, request->client->uuid, request->client->token);                        
185                     
186                     switch(plugin->apis[idx].session) {
187
188                         case AFB_SESSION_CREATE:
189                             if (request->client->token[0] != '\0') {
190                                 request->errcode=MHD_HTTP_UNAUTHORIZED;
191                                 json_object_object_add(jcall, "status", json_object_new_string ("exist"));
192                                 json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CREATE Session already exist"));
193                                 json_object_object_add(request->jresp, "request", jcall);
194                                 return (AFB_DONE);                              
195                             }
196                         
197                             if (AFB_SUCCESS != ctxTokenCreate (request)) {
198                                 request->errcode=MHD_HTTP_UNAUTHORIZED;
199                                 json_object_object_add(jcall, "status", json_object_new_string ("fail"));
200                                 json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CREATE Invalid Initial Token"));
201                                 json_object_object_add(request->jresp, "request", jcall);
202                                 return (AFB_DONE);
203                             } else {
204                                 json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid));                                
205                                 json_object_object_add(jcall, "token", json_object_new_string (request->client->token));                                
206                             }
207                             break;
208
209
210                         case AFB_SESSION_RENEW:
211                             if (AFB_SUCCESS != ctxTokenRefresh (request)) {
212                                 request->errcode=MHD_HTTP_UNAUTHORIZED;
213                                 json_object_object_add(jcall, "status", json_object_new_string ("fail"));
214                                 json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_REFRESH Broken Exchange Token Chain"));
215                                 json_object_object_add(request->jresp, "request", jcall);
216                                 return (AFB_DONE);
217                             } else {
218                                 json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid));                                
219                                 json_object_object_add(jcall, "token", json_object_new_string (request->client->token));                                
220                             }
221                             break;
222
223                         case AFB_SESSION_CLOSE:
224                             if (AFB_SUCCESS != ctxTokenCheck (request)) {
225                                 request->errcode=MHD_HTTP_UNAUTHORIZED;
226                                 json_object_object_add(jcall, "status", json_object_new_string ("empty"));
227                                 json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CLOSE Not a Valid Access Token"));
228                                 json_object_object_add(request->jresp, "request", jcall);
229                                 return (AFB_DONE);
230                             } else {
231                                 json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid));                                
232                             }
233                             break;
234                         
235                         case AFB_SESSION_CHECK:
236                         default: 
237                             // default action is check
238                             if (AFB_SUCCESS != ctxTokenCheck (request)) {
239                                 request->errcode=MHD_HTTP_UNAUTHORIZED;
240                                 json_object_object_add(jcall, "status", json_object_new_string ("fail"));
241                                 json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CHECK Invalid Active Token"));
242                                 json_object_object_add(request->jresp, "request", jcall);
243                                 return (AFB_DONE);
244                             }
245                             break;
246                     }
247                 }
248                 
249                 // Effectively call the API with a subset of the context
250                 jresp = plugin->apis[idx].callback(request, context);
251
252                 // Session close is done after the API call so API can still use session in closing API
253                 if (AFB_SESSION_CLOSE == plugin->apis[idx].session) ctxTokenReset (request);                    
254                 
255                 // API should return NULL of a valid Json Object
256                 if (jresp == NULL) {
257                     json_object_object_add(jcall, "status", json_object_new_string ("null"));
258                     json_object_object_add(request->jresp, "request", jcall);
259                     request->errcode = MHD_HTTP_NO_RESPONSE;
260                     
261                 } else {
262                     json_object_object_add(jcall, "status", json_object_new_string ("processed"));
263                     json_object_object_add(request->jresp, "request", jcall);
264                     json_object_object_add(request->jresp, "response", jresp);
265                 }
266                 // cancel timeout and plugin signal handle before next call
267                 if (request->config->apiTimeout > 0) {
268                     alarm (0);
269                     for (sig=0; signals[sig] != 0; sig++) {
270                        signal (signals[sig], SIG_DFL);
271                     }
272                 }              
273             }       
274             return (AFB_DONE);
275         }
276     }
277     
278     return (AFB_FAIL);
279 }
280
281 STATIC AFB_error findAndCallApi (AFB_request *request, void *context) {
282     int idx;
283     AFB_error status;
284     
285    
286     // Search for a plugin with this urlpath
287     for (idx = 0; request->plugins[idx] != NULL; idx++) {
288         if (!strcmp(request->plugins[idx]->prefix, request->plugin)) {
289             status =callPluginApi(request->plugins[idx], request, context);
290             break;
291         }
292     }
293     // No plugin was found
294     if (request->plugins[idx] == NULL) {
295         request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s]", request->plugin);
296         goto ExitOnError;
297     }  
298     
299     // plugin callback did not return a valid Json Object
300     if (status != AFB_DONE) {
301         request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s]", request->api, request->plugin);
302         goto ExitOnError;
303     }
304
305
306
307     
308     // Everything look OK
309     return (status);
310     
311 ExitOnError:
312     request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
313     return (AFB_FAIL);
314 }
315
316 // This CB is call for every item with a form post it reformat iterator values
317 // and callback Plugin API for each Item within PostForm.
318 doPostIterate (void *cls, enum MHD_ValueKind kind, const char *key,
319               const char *filename, const char *mimetype,
320               const char *encoding, const char *data, uint64_t offset,
321               size_t size) {
322   
323   AFB_error    status;
324   AFB_PostItem item;
325     
326   // retrieve API request from Post iterator handle  
327   AFB_PostHandle *postHandle  = (AFB_PostHandle*)cls;
328   AFB_request *request = (AFB_request*)postHandle->private;
329   AFB_PostRequest postRequest;
330   
331    
332   // Create and Item value for Plugin API
333   item.kind     = kind;
334   item.key      = key;
335   item.filename = filename;
336   item.mimetype = mimetype;
337   item.encoding = encoding;
338   item.len      = size;
339   item.data     = data;
340   item.offset   = offset;
341   
342   // Reformat Request to make it somehow similar to GET/PostJson case
343   postRequest.data= (char*) postHandle;
344   postRequest.len = size;
345   postRequest.type= AFB_POST_FORM;;
346   request->post = &postRequest;
347   
348   // effectively call plugin API                 
349   status = findAndCallApi (request, &item);
350   
351   // when returning no processing of postform stop
352   if (status != AFB_SUCCESS) return MHD_NO;
353   
354   // let's allow iterator to move to next item
355   return (MHD_YES);
356 }
357
358 STATIC void freeRequest (AFB_request *request) {
359
360  free (request->plugin);    
361  free (request->api);    
362  free (request);    
363 }
364
365 STATIC AFB_request *createRequest (struct MHD_Connection *connection, AFB_session *session, const char* url) {
366     
367     AFB_request *request;
368     
369     // Start with a clean request
370     request = calloc (1, sizeof (AFB_request));
371     char *urlcpy1, *urlcpy2;
372     char *baseapi, *baseurl;  
373       
374     // Extract plugin urlpath from request and make two copy because strsep overload copy
375     urlcpy1 = urlcpy2 = strdup(url);
376     baseurl = strsep(&urlcpy2, "/");
377     if (baseurl == NULL) {
378         request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url);
379     }
380
381     // let's compute URL and call API
382     baseapi = strsep(&urlcpy2, "/");
383     if (baseapi == NULL) {
384         request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url);
385     }
386     
387     // build request structure
388     request->connection = connection;
389     request->config = session->config;
390     request->url    = url;
391     request->plugin = strdup (baseurl);
392     request->api    = strdup (baseapi);
393     request->plugins= session->plugins;
394     
395     free(urlcpy1);
396     return (request);
397 }
398
399 // process rest API query
400 PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char* url, const char *method
401     , const char *upload_data, size_t *upload_data_size, void **con_cls) {
402     
403     static int postcount = 0; // static counter to debug POST protocol
404     json_object *errMessage;
405     AFB_error status;
406     struct MHD_Response *webResponse;
407     const char *serialized;
408     AFB_request *request;
409     AFB_PostHandle *postHandle;
410     AFB_PostRequest postRequest;
411     int ret;
412   
413     // if post data may come in multiple calls
414     if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
415         const char *encoding, *param;
416         int contentlen = -1;
417         postHandle = *con_cls;
418
419         // This is the initial post event let's create form post structure POST datas come in multiple events
420         if (postHandle == NULL) {
421
422             // allocate application POST processor handle to zero
423             postHandle = calloc(1, sizeof (AFB_PostHandle));
424             postHandle->uid = postcount++; // build a UID for DEBUG
425             *con_cls = postHandle;         // attache POST handle to current HTTP request
426             
427             // Let make sure we have the right encoding and a valid length
428             encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE);
429         
430             // Form post is handle through a PostProcessor and call API once per form key
431             if (strcasestr(encoding, FORM_CONTENT) != NULL) {
432                 if (verbose) fprintf(stderr, "Create PostForm[uid=%d]\n", postHandle->uid);
433
434                 request = createRequest (connection, session, url);
435                 if (request->jresp != NULL) {
436                     errMessage = request->jresp;
437                     goto ExitOnError;
438                 }
439                 postHandle = malloc(sizeof (AFB_PostHandle)); // allocate application POST processor handle
440                 postHandle->type   = AFB_POST_FORM;
441                 postHandle->pp     = MHD_create_post_processor (connection, MAX_POST_SIZE, doPostIterate, postHandle);
442                 postHandle->private= (void*)request;
443                 
444                 if (NULL == postHandle->pp) {
445                     fprintf(stderr,"OOPS: Internal error fail to allocate MHD_create_post_processor\n");
446                     free (postHandle);
447                     return MHD_NO;
448                 }
449                 return MHD_YES;
450             }           
451         
452             // POST json is store into a buffer and present in one piece to API
453             if (strcasestr(encoding, JSON_CONTENT) != NULL) {
454
455                 param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
456                 if (param) sscanf(param, "%i", &contentlen);
457
458                 // Because PostJson are build in RAM size is constrained
459                 if (contentlen > MAX_POST_SIZE) {
460                     errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE);
461                     goto ExitOnError;
462                 }
463
464                 // Size is OK, let's allocate a buffer to hold post data
465                 postHandle->type = AFB_POST_JSON;
466                 postHandle->private = malloc(contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string
467
468                 if (verbose) fprintf(stderr, "Create PostJson[uid=%d] Size=%d\n", postHandle->uid, contentlen);
469                 return MHD_YES;
470
471             } else {
472                 // We only support Json and Form Post format
473                 errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT);
474                 goto ExitOnError;
475                 
476             }   
477         }
478
479         // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless
480         // return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with
481         // 'Internal application error, closing connection'.            
482         if (*upload_data_size) {
483     
484             if (postHandle->type == AFB_POST_FORM) {
485                 if (verbose) fprintf(stderr, "Processing PostForm[uid=%d]\n", postHandle->uid);
486                 MHD_post_process (postHandle->pp, upload_data, *upload_data_size);
487             }
488             
489             // Process JsonPost request when buffer is completed let's call API    
490             if (postHandle->type == AFB_POST_JSON) {
491                 if (verbose) fprintf(stderr, "Updating PostJson[uid=%d]\n", postHandle->uid);
492
493                 memcpy(&postHandle->private[postHandle->len], upload_data, *upload_data_size);
494                 postHandle->len = postHandle->len + *upload_data_size;
495                 *upload_data_size = 0;
496             }
497             return MHD_YES;
498             
499         } else {  // we have finish with Post reception let's finish the work
500             
501             // Create a request structure to finalise the request
502             request= createRequest (connection, session, url);
503             if (request->jresp != NULL) {
504                 errMessage = request->jresp;
505                 goto ExitOnError;
506             }
507
508             
509             if (postHandle->type == AFB_POST_JSON) {
510                 if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid);
511
512                 param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
513                 if (param) sscanf(param, "%i", &contentlen);
514
515                 // At this level we're may verify that we got everything and process DATA
516                 if (postHandle->len != contentlen) {
517                     errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %d", postHandle->uid, contentlen, postHandle->len);
518                     goto ExitOnError;
519                 }
520
521                 // Before processing data, make sure buffer string is properly ended
522                 postHandle->private[postHandle->len] = '\0';
523                 postRequest.data = postHandle->private;
524                 postRequest.type = postHandle->type;
525                 request->post = &postRequest;
526
527                 if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", postHandle->uid, request->post->data);
528             }
529         }
530     } else {
531         // this is a get we only need a request
532         request= createRequest (connection, session, url);
533     };
534     
535     // Request is ready let's call API without any extra handle
536     status = findAndCallApi (request, NULL);
537     
538 ExitOnResponse:
539     serialized = json_object_to_json_string(request->jresp);
540     webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY);
541     
542     // client did not pass token on URI let's use cookies 
543     if ((!request->restfull) && (request->client != NULL)) {
544        char cookie[64]; 
545        snprintf (cookie, sizeof (cookie), "%s=%s", COOKIE_NAME,  request->client->uuid); 
546        MHD_add_response_header (webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie);
547     }
548     
549     // if requested add an error status
550     if (request->errcode != 0)  ret=MHD_queue_response (connection, request->errcode, webResponse);
551     else MHD_queue_response(connection, MHD_HTTP_OK, webResponse);
552     
553     MHD_destroy_response(webResponse);
554     json_object_put(request->jresp); // decrease reference rqtcount to free the json object
555     freeRequest (request);
556     return MHD_YES;
557
558 ExitOnError:
559     freeRequest (request);
560     serialized = json_object_to_json_string(errMessage);
561     webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY);
562     MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse);
563     MHD_destroy_response(webResponse);
564     json_object_put(errMessage); // decrease reference rqtcount to free the json object
565     return MHD_YES;
566 }
567
568
569 // Loop on plugins. Check that they have the right type, prepare a JSON object with prefix
570 STATIC AFB_plugin ** RegisterJsonPlugins(AFB_plugin **plugins) {
571     int idx, jdx;
572
573     for (idx = 0; plugins[idx] != NULL; idx++) {
574         if (plugins[idx]->type != AFB_PLUGIN_JSON) {
575             fprintf(stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN_JSON, plugins[idx]->type);
576         } else {
577             // some sanity controls
578             if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)) {
579                 if (plugins[idx]->prefix == NULL) plugins[idx]->prefix = "No URL prefix for APIs";
580                 if (plugins[idx]->info == NULL) plugins[idx]->info = "No Info describing plugin APIs";
581                 fprintf(stderr, "ERROR: plugin[%d] invalid prefix=%s info=%s", idx, plugins[idx]->prefix, plugins[idx]->info);
582                 return NULL;
583             }
584
585             if (verbose) fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info);
586             
587             // Prebuild plugin jtype to boost API response
588             plugins[idx]->jtype = json_object_new_string(plugins[idx]->prefix);
589             json_object_get(plugins[idx]->jtype); // increase reference count to make it permanent
590             plugins[idx]->prefixlen = strlen(plugins[idx]->prefix);
591             
592               
593             // Prebuild each API jtype to boost API json response
594             for (jdx = 0; plugins[idx]->apis[jdx].name != NULL; jdx++) {
595                 AFB_privateApi *private = malloc (sizeof (AFB_privateApi));
596                 if (plugins[idx]->apis[jdx].private != NULL) {
597                     fprintf (stderr, "WARNING: plugin=%s api=%s private handle should be NULL=0x%x\n"
598                             ,plugins[idx]->prefix,plugins[idx]->apis[jdx].name, plugins[idx]->apis[jdx].private);
599                 }
600                 private->len = strlen (plugins[idx]->apis[jdx].name);
601                 private->jtype=json_object_new_string(plugins[idx]->apis[jdx].name);
602                 json_object_get(private->jtype); // increase reference count to make it permanent
603                 plugins[idx]->apis[jdx].private = private;
604             }
605         }
606     }
607     return (plugins);
608 }
609
610 void initPlugins(AFB_session *session) {
611     static AFB_plugin * plugins[10];
612     afbJsonType = json_object_new_string (AFB_MSG_JTYPE);
613     int i = 0;
614
615     plugins[i++] = afsvRegister(session),
616     plugins[i++] = dbusRegister(session),
617     plugins[i++] = alsaRegister(session),
618 #ifdef HAVE_RADIO_PLUGIN
619     plugins[i++] = radioRegister(session),
620 #endif
621     plugins[i++] = NULL;
622     
623     // complete plugins and save them within current sessions    
624     session->plugins = RegisterJsonPlugins(plugins);
625 }