Merge origin/master
[src/app-framework-binder.git] / src / rest-api.c
index 1635d44..8146e14 100644 (file)
 
 #include "../include/local-def.h"
 
+#include <dirent.h>
+#include <dlfcn.h>
 #include <setjmp.h>
 #include <signal.h>
 
 #define AFB_MSG_JTYPE "AJB_reply"
 
 
-// handle to hold queryAll values
-typedef struct {
-     char    *msg;
-     int     idx;
-     size_t  len;
-} queryHandleT;
 
 static json_object     *afbJsonType;
 
 
-// Sample Generic Ping Debug API
-PUBLIC json_object* apiPingTest(AFB_request *request) {
-    static pingcount = 0;
-    json_object *response;
-    char query  [256];
-    char session[256];
-
-    int len;
-    AFB_clientCtx *client=request->client; // get client context from request
-    
-    // request all query key/value
-    len = getQueryAll (request, query, sizeof(query));
-    if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query));
-    
-    // check if we have some post data
-    if (request->post == NULL)  request->post->data="NoData"; 
-    
-    // check is we have a session and a plugin handle
-    if (client == NULL) strcpy (session,"NoSession");       
-    else snprintf(session, sizeof(session),"uuid=%s token=%s ctx=0x%x handle=0x%x", client->uuid, client->token, client->ctx, client->ctx); 
-        
-    // return response to caller
-    response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d CtxtId=%d query={%s} session={%s} PostData: [%s] "
-               , pingcount++, request->client->cid, query, session, request->post->data);
-    return (response);
-}
-
-
-// Helper to retrieve argument from  connection
-PUBLIC const char* getQueryValue(AFB_request * request, char *name) {
-    const char *value;
-
-    value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name);
-    return (value);
-}
-
-STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) {
-    queryHandleT *query = (queryHandleT*)handle;
-        
-    query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value);
-}
-
-// Helper to retrieve argument from  connection
-PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) {
-    queryHandleT query;
-    buffer[0] = '\0'; // start with an empty string
-    query.msg= buffer;
-    query.len= len;
-    query.idx= 0;
-
-    MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query);
-    return (len);
-}
-
-
-// Helper to retreive POST handle
-PUBLIC AFB_PostHandle* getPostHandle (AFB_request *request) {
-    if (request->post == NULL) return (NULL);
-    return ((AFB_PostHandle*) request->post->data);
-}
-
 // Because of POST call multiple time requestApi we need to free POST handle here
 // Note this method is called from http-svc just before closing session
 PUBLIC void endPostRequest(AFB_PostHandle *postHandle) {
@@ -295,6 +230,7 @@ STATIC AFB_error findAndCallApi (AFB_request *request, void *context) {
     int idx;
     AFB_error status;
     
+    if (!request->api || !request->plugin) return (AFB_FAIL);
    
     // Search for a plugin with this urlpath
     for (idx = 0; request->plugins[idx] != NULL; idx++) {
@@ -305,13 +241,13 @@ STATIC AFB_error findAndCallApi (AFB_request *request, void *context) {
     }
     // No plugin was found
     if (request->plugins[idx] == NULL) {
-        request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s]", request->plugin);
+        request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s] Url=%s", request->plugin, request->url);
         goto ExitOnError;
     }  
     
     // plugin callback did not return a valid Json Object
     if (status == AFB_FAIL) {
-        request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s]", request->api, request->plugin);
+        request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->api, request->plugin, request->url);
         goto ExitOnError;
     }
     
@@ -386,12 +322,16 @@ STATIC AFB_request *createRequest (struct MHD_Connection *connection, AFB_sessio
     baseurl = strsep(&urlcpy2, "/");
     if (baseurl == NULL) {
         request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url);
+        request->errcode = MHD_HTTP_BAD_REQUEST;
+        goto Done;
     }
 
     // let's compute URL and call API
     baseapi = strsep(&urlcpy2, "/");
     if (baseapi == NULL) {
-        request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url);
+        request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call plugin=[%s] url=[%s]", baseurl, url);
+        request->errcode = MHD_HTTP_BAD_REQUEST;
+        goto Done;
     }
     
     // build request structure
@@ -401,7 +341,8 @@ STATIC AFB_request *createRequest (struct MHD_Connection *connection, AFB_sessio
     request->plugin = strdup (baseurl);
     request->api    = strdup (baseapi);
     request->plugins= session->plugins;
-    
+
+Done:    
     free(urlcpy1);
     return (request);
 }
@@ -432,6 +373,7 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co
             // allocate application POST processor handle to zero
             postHandle = calloc(1, sizeof (AFB_PostHandle));
             postHandle->uid = postcount++; // build a UID for DEBUG
+            *con_cls = postHandle;  // update context with posthandle
             
             // Let make sure we have the right encoding and a valid length
             encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE);
@@ -447,15 +389,12 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co
                 if (verbose) fprintf(stderr, "Create PostForm[uid=%d]\n", postHandle->uid);
 
                 request = createRequest (connection, session, url);
-                if (request->jresp != NULL) {
-                    errMessage = request->jresp;
-                    goto ExitOnError;
-                }
+                if (request->jresp != NULL) goto ProcessApiCall;
+
                 postHandle = malloc(sizeof (AFB_PostHandle)); // allocate application POST processor handle
                 postHandle->type   = AFB_POST_FORM;
                 postHandle->pp     = MHD_create_post_processor (connection, MAX_POST_SIZE, doPostIterate, postHandle);
                 postHandle->private= (void*)request;
-                *con_cls = postHandle;  // update context with posthandle
                 
                 if (NULL == postHandle->pp) {
                     fprintf(stderr,"OOPS: Internal error fail to allocate MHD_create_post_processor\n");
@@ -629,21 +568,58 @@ STATIC AFB_plugin ** RegisterJsonPlugins(AFB_plugin **plugins) {
 }
 
 void initPlugins(AFB_session *session) {
-    static AFB_plugin * plugins[10];
+    static AFB_plugin **plugins;
+    AFB_plugin* (*pluginRegisterFct)(void);
+    void *plugin;
+    char *pluginPath;
+    struct dirent *pluginDir;
+    DIR *dir;
     afbJsonType = json_object_new_string (AFB_MSG_JTYPE);
-    int i = 0;
-
-    plugins[i++] = tokenRegister(session);
-    plugins[i++] = helloWorldRegister(session);
-    plugins[i++] = samplePostRegister(session);
-#ifdef HAVE_AUDIO_PLUGIN
-    plugins[i++] = audioRegister(session);
-#endif
-#ifdef HAVE_RADIO_PLUGIN
-    plugins[i++] = radioRegister(session),
-#endif
-    plugins[i++] = NULL;
-    
+    int num = 0;
+
+    /* pre-allocate for 20 plugins, we will downsize if necessary */
+    plugins = (AFB_plugin **) malloc (20*sizeof(AFB_plugin));
+
+    if ((dir = opendir(session->config->plugins)) == NULL) {
+        fprintf(stderr, "Could not open plugin directory [%s], exiting...\n", session->config->plugins);
+        exit (-1);
+    }
+
+    while ((pluginDir = readdir(dir)) != NULL) {
+
+        if (!strstr (pluginDir->d_name, ".so"))
+            continue;
+
+        asprintf (&pluginPath, "%s/%s", session->config->plugins, pluginDir->d_name);
+        plugin = dlopen (pluginPath, RTLD_NOW | RTLD_LOCAL);
+        pluginRegisterFct = dlsym (plugin, "pluginRegister");
+        free (pluginPath);
+        if (!plugin) {
+            if (verbose) fprintf(stderr, "[%s] is not loadable, continuing...\n", pluginDir->d_name);
+            continue;
+        } else if (!pluginRegisterFct) {
+            if (verbose) fprintf(stderr, "[%s] is not an AFB plugin, continuing...\n", pluginDir->d_name);
+            continue;
+        }
+
+        if (verbose) fprintf(stderr, "[%s] is a valid AFB plugin, loading it\n", pluginDir->d_name);
+        plugins[num] = (AFB_plugin *) malloc (sizeof(AFB_plugin));
+        plugins[num] = (**pluginRegisterFct)();
+        num++;
+        /* only 20 plugins are supported at that time */
+        if (num == 20) break;
+    }
+    plugins = (AFB_plugin **) realloc (plugins, (num+1)*sizeof(AFB_plugin));
+    plugins[num] = NULL;
+
+    closedir (dir);
+
+    if (plugins[0] == NULL) {
+        fprintf(stderr, "No plugins found, afb-daemon is unlikely to work in this configuration, exiting...\n");
+        exit (-1);
+    }
+
     // complete plugins and save them within current sessions    
     session->plugins = RegisterJsonPlugins(plugins);
+    session->pluginCount = num;
 }