work in progress (tbf)
[src/app-framework-binder.git] / src / afb-rest-api.c
index febe19f..28346b1 100644 (file)
 #include <signal.h>
 
 #include "afb-apis.h"
+#include "session.h"
 
 #define AFB_MSG_JTYPE "AJB_reply"
 
 #define JSON_CONTENT  "application/json"
-#define FORM_CONTENT  "multipart/form-data"    /* TODO: replace with MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA */
+#define FORM_CONTENT  MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
 
 static json_object *afbJsonType;
 
@@ -66,46 +67,44 @@ static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx,
 
        // Request was found and at least partially executed
        jreqt = json_object_new_object();
-       json_object_get(afbJsonType);   // increate jsontype reference count
-       json_object_object_add(jreqt, "jtype", afbJsonType);
+       json_object_object_add(jreqt, "jtype", json_object_get(afbJsonType));
 
        // prepare an object to store calling values
        jcall = json_object_new_object();
        json_object_object_add(jcall, "prefix", json_object_new_string(request->prefix));
-       json_object_object_add(jcall, "api", json_object_new_string(request->api));
+       json_object_object_add(jcall, "api", json_object_new_string(request->method));
 
        // Out of SessionNone every call get a client context session
        session = afb_apis_get(apiidx, verbidx)->session;
        if (AFB_SESSION_NONE != session) {
 
                // add client context to request
-               clientCtx = ctxClientGet(request, apiidx);
+               clientCtx = ctxClientGet(request);
                if (clientCtx == NULL) {
                        request->errcode = MHD_HTTP_INSUFFICIENT_STORAGE;
-                       json_object_object_add(jcall, "status", json_object_new_string("fail"));
-                       json_object_object_add(jcall, "info", json_object_new_string("Client Session Context Full !!!"));
+                       json_add_status(jcall, "fail", "Client Session Context Full !!!");
                        json_object_object_add(jreqt, "request", jcall);
                        goto ExitOnDone;
-               };
+               }
+               request->context = clientCtx->contexts[apiidx];
+               request->uuid = clientCtx->uuid;
 
                if (verbose)
-                       fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[%p] Uuid=[%s] Token=[%s]\n", request->prefix, request->api, session, clientCtx, clientCtx->uuid, clientCtx->token);
+                       fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[%p] Uuid=[%s] Token=[%s]\n", request->prefix, request->method, session, clientCtx, clientCtx->uuid, clientCtx->token);
 
                switch (session) {
 
                case AFB_SESSION_CREATE:
                        if (clientCtx->token[0] != '\0' && request->config->token[0] != '\0') {
                                request->errcode = MHD_HTTP_UNAUTHORIZED;
-                               json_object_object_add(jcall, "status", json_object_new_string("exist"));
-                               json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CREATE Session already exist"));
+                               json_add_status(jcall, "exist", "AFB_SESSION_CREATE Session already exist");
                                json_object_object_add(jreqt, "request", jcall);
                                goto ExitOnDone;
                        }
 
                        if (AFB_SUCCESS != ctxTokenCreate(clientCtx, request)) {
                                request->errcode = MHD_HTTP_UNAUTHORIZED;
-                               json_object_object_add(jcall, "status", json_object_new_string("fail"));
-                               json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CREATE Invalid Initial Token"));
+                               json_add_status(jcall, "fail", "AFB_SESSION_CREATE Invalid Initial Token");
                                json_object_object_add(jreqt, "request", jcall);
                                goto ExitOnDone;
                        } else {
@@ -118,8 +117,7 @@ static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx,
                case AFB_SESSION_RENEW:
                        if (AFB_SUCCESS != ctxTokenRefresh(clientCtx, request)) {
                                request->errcode = MHD_HTTP_UNAUTHORIZED;
-                               json_object_object_add(jcall, "status", json_object_new_string("fail"));
-                               json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_REFRESH Broken Exchange Token Chain"));
+                               json_add_status(jcall, "fail", "AFB_SESSION_REFRESH Broken Exchange Token Chain");
                                json_object_object_add(jreqt, "request", jcall);
                                goto ExitOnDone;
                        } else {
@@ -132,8 +130,7 @@ static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx,
                case AFB_SESSION_CLOSE:
                        if (AFB_SUCCESS != ctxTokenCheck(clientCtx, request)) {
                                request->errcode = MHD_HTTP_UNAUTHORIZED;
-                               json_object_object_add(jcall, "status", json_object_new_string("empty"));
-                               json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CLOSE Not a Valid Access Token"));
+                               json_add_status(jcall, "fail", "AFB_SESSION_CLOSE Not a Valid Access Token"));
                                json_object_object_add(jreqt, "request", jcall);
                                goto ExitOnDone;
                        } else {
@@ -146,14 +143,14 @@ static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx,
                        // default action is check
                        if (AFB_SUCCESS != ctxTokenCheck(clientCtx, request)) {
                                request->errcode = MHD_HTTP_UNAUTHORIZED;
-                               json_object_object_add(jcall, "status", json_object_new_string("fail"));
-                               json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CHECK Invalid Active Token"));
+                               json_add_status(jcall, "fail", "AFB_SESSION_CHECK Invalid Active Token"));
                                json_object_object_add(jreqt, "request", jcall);
                                goto ExitOnDone;
                        }
                        break;
                }
        }
+
        // Effectively CALL PLUGIN API with a subset of the context
        jresp = afb_apis_get(apiidx, verbidx)->callback(request, context);
 
@@ -190,7 +187,7 @@ ExitOnDone:
 extern __thread sigjmp_buf *error_handler;
 static AFB_error callPluginApi(AFB_request * request, int apiidx, int verbidx, void *context)
 {
-       sigjmp_buf jmpbuf;
+       sigjmp_buf jmpbuf, *older;
 
        json_object *jcall, *jreqt;
        int status;
@@ -201,36 +198,32 @@ static AFB_error callPluginApi(AFB_request * request, int apiidx, int verbidx, v
 
                // Request was found and at least partially executed
                jreqt = json_object_new_object();
-               json_object_get(afbJsonType);   // increate jsontype reference count
-               json_object_object_add(jreqt, "jtype", afbJsonType);
+               json_object_object_add(jreqt, "jtype", json_object_get(afbJsonType));
 
                // prepare an object to store calling values
                jcall = json_object_new_object();
                json_object_object_add(jcall, "prefix", json_object_new_string(request->prefix));
-               json_object_object_add(jcall, "api", json_object_new_string(request->api));
+               json_object_object_add(jcall, "api", json_object_new_string(request->method));
 
                // Plugin aborted somewhere during its execution
                json_object_object_add(jcall, "status", json_object_new_string("abort"));
                json_object_object_add(jcall, "info", json_object_new_string("Plugin broke during execution"));
                json_object_object_add(jreqt, "request", jcall);
                request->jresp = jreqt;
-               goto ExitOnDone;
-
        } else {
 
                // Trigger a timer to protect from unacceptable long time execution
                if (request->config->apiTimeout > 0)
                        alarm((unsigned)request->config->apiTimeout);
 
+               older = error_handler;
                error_handler = &jmpbuf;
                doCallPluginApi(request, apiidx, verbidx, context);
-               error_handler = NULL;
+               error_handler = older;
 
                // cancel timeout and plugin signal handle before next call
                alarm(0);
        }
-
-ExitOnDone:
        return AFB_DONE;
 }
 
@@ -239,7 +232,7 @@ STATIC AFB_error findAndCallApi(AFB_request * request, void *context)
        int apiidx, verbidx;
        AFB_error status;
 
-       if (!request->api || !request->prefix)
+       if (!request->method || !request->prefix)
                return AFB_FAIL;
 
        /* get the plugin if any */
@@ -251,9 +244,9 @@ STATIC AFB_error findAndCallApi(AFB_request * request, void *context)
        }
 
        /* get the verb if any */
-       verbidx = afb_apis_get_verbidx(apiidx, request->api);
+       verbidx = afb_apis_get_verbidx(apiidx, request->method);
        if (verbidx < 0) {
-               request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->api, request->prefix, request->url);
+               request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->method, request->prefix, request->url);
                request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
                return AFB_FAIL;
        }
@@ -263,7 +256,7 @@ STATIC AFB_error findAndCallApi(AFB_request * request, void *context)
 
        /* plugin callback did not return a valid Json Object */
        if (status == AFB_FAIL) {
-               request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->api, request->prefix, request->url);
+               request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->method, request->prefix, request->url);
                request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
                return AFB_FAIL;
        }
@@ -316,8 +309,8 @@ STATIC int doPostIterate(void *cls, enum MHD_ValueKind kind, const char *key, co
 STATIC void freeRequest(AFB_request * request)
 {
 
-       free(request->prefix);
-       free(request->api);
+       free((void*)request->prefix);
+       free((void*)request->method);
        free(request);
 }
 
@@ -347,11 +340,11 @@ STATIC AFB_request *createRequest(struct MHD_Connection *connection, AFB_session
                goto Done;
        }
        // build request structure
-       request->connection = connection;
+//     request->connection = connection;
        request->config = session->config;
        request->url = url;
        request->prefix = strdup(baseurl);
-       request->api = strdup(baseapi);
+       request->method = strdup(baseapi);
 
  Done:
        free(urlcpy1);
@@ -408,6 +401,7 @@ static int doRestApiPost(struct MHD_Connection *connection, AFB_session * sessio
                        postHandle->type = AFB_POST_EMPTY;
                        return MHD_YES;
                }
+
                // Form post is handle through a PostProcessor and call API once per form key
                if (strcasestr(encoding, FORM_CONTENT) != NULL) {
                        if (verbose)
@@ -446,11 +440,10 @@ static int doRestApiPost(struct MHD_Connection *connection, AFB_session * sessio
                        // if (verbose) fprintf(stderr, "Create PostJson[uid=%d] Size=%d\n", postHandle->uid, contentlen);
                        return MHD_YES;
 
-               } else {
-                       // We only support Json and Form Post format
-                       errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT);
-                       goto ExitOnError;
                }
+               // We only support Json and Form Post format
+               errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT);
+               goto ExitOnError;
        }
 
        // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless
@@ -472,41 +465,44 @@ static int doRestApiPost(struct MHD_Connection *connection, AFB_session * sessio
                *upload_data_size = 0;
                return MHD_YES;
 
-       } else {        // we have finish with Post reception let's finish the work
+       }
 
+       if (postHandle->type == AFB_POST_FORM)
+               request = postHandle->privatebuf;
+       else
                // Create a request structure to finalise the request
                request = createRequest(connection, session, url);
-               if (request->jresp != NULL) {
-                       errMessage = request->jresp;
-                       goto ExitOnError;
-               }
-               postRequest.type = postHandle->type;
 
-               // Postform add application context handle to request
-               if (postHandle->type == AFB_POST_FORM) {
-                       postRequest.data = (char *)postHandle;
-                       request->post = &postRequest;
-               }
+       if (request->jresp != NULL) {
+               errMessage = request->jresp;
+               goto ExitOnError;
+       }
+       postRequest.type = postHandle->type;
 
-               if (postHandle->type == AFB_POST_JSON) {
-                       // if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid);
+       // Postform add application context handle to request
+       if (postHandle->type == AFB_POST_FORM) {
+               postRequest.data = (char *)postHandle;
+               request->post = &postRequest;
+       }
 
-                       param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
-                       if (param)
-                               sscanf(param, "%i", &contentlen);
+       if (postHandle->type == AFB_POST_JSON) {
+               // if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid);
 
-                       // At this level we're may verify that we got everything and process DATA
-                       if (postHandle->len != contentlen) {
-                               errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %d", postHandle->uid, contentlen, postHandle->len);
-                               goto ExitOnError;
-                       }
-                       // Before processing data, make sure buffer string is properly ended
-                       postHandle->privatebuf[postHandle->len] = '\0';
-                       postRequest.data = postHandle->privatebuf;
-                       request->post = &postRequest;
+               param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
+               if (param)
+                       sscanf(param, "%i", &contentlen);
 
-                       // if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", postHandle->uid, request->post->data);
+               // At this level we're may verify that we got everything and process DATA
+               if (postHandle->len != contentlen) {
+                       errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %d", postHandle->uid, contentlen, postHandle->len);
+                       goto ExitOnError;
                }
+               // Before processing data, make sure buffer string is properly ended
+               postHandle->privatebuf[postHandle->len] = '\0';
+               postRequest.data = postHandle->privatebuf;
+               request->post = &postRequest;
+
+               // if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", postHandle->uid, request->post->data);
        }
 
  ProcessApiCall: