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