diff options
-rw-r--r-- | include/local-def.h | 112 | ||||
-rw-r--r-- | include/proto-def.h | 7 | ||||
-rw-r--r-- | plugins/media/media-api.c | 2 | ||||
-rw-r--r-- | plugins/radio/radio-api.c | 12 | ||||
-rw-r--r-- | plugins/samples/ClientCtx.c | 21 | ||||
-rw-r--r-- | plugins/samples/HelloWorld.c | 2 | ||||
-rw-r--r-- | plugins/samples/SamplePost.c | 1 | ||||
-rw-r--r-- | plugins/session/token-api.c | 5 | ||||
-rw-r--r-- | src/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/afb-api.c | 634 | ||||
-rw-r--r-- | src/afb-apis.c (renamed from src/afb-plugins.c) | 140 | ||||
-rw-r--r-- | src/afb-apis.h (renamed from src/afb-plugins.h) | 19 | ||||
-rw-r--r-- | src/afb-hreq.c | 2 | ||||
-rw-r--r-- | src/afb-req-itf.h | 3 | ||||
-rw-r--r-- | src/afb-rest-api.c | 617 | ||||
-rw-r--r-- | src/afb-websock.c | 4 | ||||
-rw-r--r-- | src/main.c | 39 | ||||
-rw-r--r-- | src/session.c | 40 |
18 files changed, 871 insertions, 793 deletions
diff --git a/include/local-def.h b/include/local-def.h index 280ab0bb..ace9249a 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -75,9 +75,6 @@ extern int verbose; // this is the only global variable -// Plugin Type -typedef enum {AFB_PLUGIN_JSON=123456789, AFB_PLUGIN_JSCRIPT=987654321, AFB_PLUGIN_RAW=987123546} AFB_pluginE; - // prebuild json error are constructed in helper-api.c typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS, AFB_DONE, AFB_UNAUTH} AFB_error; @@ -85,12 +82,74 @@ typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY #define CTX_NBCLIENTS 10 // allow a default of 10 authenticated clients + + + + + + +// Plugin Type +enum AFB_pluginE +{ + AFB_PLUGIN_JSON = 123456789, + AFB_PLUGIN_JSCRIPT = 987654321, + AFB_PLUGIN_RAW = 987123546 +}; + +// Enum for Session/Token/Authentication middleware +enum AFB_sessionE +{ + AFB_SESSION_NONE, + AFB_SESSION_CREATE, + AFB_SESSION_CLOSE, + AFB_SESSION_RENEW, + AFB_SESSION_CHECK +}; + +// API definition +struct AFB_restapi +{ + const char *name; + enum AFB_sessionE session; + json_object* (*callback)(); + const char *info; +}; + +// Plugin definition +struct AFB_plugin +{ + enum AFB_pluginE type; + const char *info; + const char *prefix; + const struct AFB_restapi *apis; + void (*freeCtxCB)(void*); // callback to free application context [null for standard free] +}; + +typedef enum AFB_pluginE AFB_pluginE; +typedef enum AFB_sessionE AFB_sessionE; typedef json_object* (*AFB_apiCB)(); -typedef void (*AFB_freeCtxCB)(void*, void*, char*); +typedef void (*AFB_freeCtxCB)(void*); +typedef struct AFB_restapi AFB_restapi; +typedef struct AFB_plugin AFB_plugin; + + + + + + + + + + + + + typedef enum {AFB_POST_NONE=0, AFB_POST_JSON, AFB_POST_FORM, AFB_POST_EMPTY} AFB_PostType; typedef enum {AFB_MODE_LOCAL=0, AFB_MODE_REMOTE, AFB_MODE_GLOBAL} AFB_Mode; + + // Post Upload File Handle typedef struct { int fd; @@ -143,31 +202,6 @@ typedef struct { size_t len; } AFB_redirect_msg; -// Enum for Session/Token/Authentication middleware -typedef enum {AFB_SESSION_NONE, AFB_SESSION_CREATE, AFB_SESSION_CLOSE, AFB_SESSION_RENEW, AFB_SESSION_CHECK} AFB_sessionE; - -// API definition -typedef struct { - char *name; - AFB_sessionE session; - AFB_apiCB callback; - char *info; -} AFB_restapi; - -// Plugin definition -typedef struct { - AFB_pluginE type; - char *info; - char *prefix; - size_t prefixlen; - json_object *jtype; - AFB_restapi *apis; - void *handle; - int ctxCount; - AFB_freeCtxCB freeCtxCB; // callback to free application context [null for standard free] -} AFB_plugin; - - // User Client Session Context typedef struct { char uuid[37]; // long term authentication of remote client @@ -175,11 +209,11 @@ typedef struct { time_t timeStamp; // last time token was refresh int restfull; // client does not use cookie void **contexts; // application specific context [one per plugin]] - AFB_plugin **plugins; // we need plugins reference to cleanup session outside of call context } AFB_clientCtx; // main config structure -typedef struct { +struct AFB_config +{ char *console; // console device name (can be a file or a tty) int httpdPort; char *ldpaths; // list of plugins directories @@ -191,10 +225,9 @@ typedef struct { int cacheTimeout; int apiTimeout; int cntxTimeout; // Client Session Context timeout - int pluginCount; // loaded plugins count AFB_Mode mode; // mode of listening AFB_aliasdir *aliasdir; // alias mapping for icons,apps,... -} AFB_config; +}; // MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value"); typedef struct { @@ -205,20 +238,18 @@ typedef struct { AFB_PostRequest *post; json_object *jresp; void *context; // Hold Client Context when using session - void *handle; // provide callback and easy access to plugin int restfull; // request is resfull [uuid token provided] int errcode; // http error code sigjmp_buf checkPluginCall; // context save for timeout set/longjmp - AFB_config *config; // plugin may need access to config + struct AFB_config *config; // plugin may need access to config struct MHD_Connection *connection; - AFB_plugin **plugins; } AFB_request; struct afb_hsrv_handler; struct MHD_Daemon; -typedef struct { - AFB_config *config; // pointer to current config +struct AFB_session { + struct AFB_config *config; // pointer to current config // List of commands to execute int background; // run in backround mode int foreground; // run in forground mode @@ -226,12 +257,13 @@ typedef struct { struct MHD_Daemon *httpd; // structure for httpd handler int fakemod; // respond to GET/POST request without interacting with sndboard int readyfd; // a #fd to signal when ready to serve - AFB_plugin **plugins; // pointer to REST/API plugins magic_t magic; // Mime type file magic lib struct afb_hsrv_handler *handlers; -} AFB_session; +}; +typedef struct AFB_config AFB_config; +typedef struct AFB_session AFB_session; #include "proto-def.h" diff --git a/include/proto-def.h b/include/proto-def.h index e96b0229..87bc63b0 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -39,13 +39,6 @@ extern int doRestApi(struct MHD_Connection *connection, AFB_session *session, co , const char *upload_data, size_t *upload_data_size, void **con_cls); // Session handling -#if defined(ALLOWS_SESSION_FILES) -extern AFB_error sessionCheckdir (AFB_session *session); -extern json_object *sessionList (AFB_session *session, AFB_request *request); -extern json_object *sessionToDisk (AFB_session *session, AFB_request *request, char *name,json_object *jsonSession); -extern json_object *sessionFromDisk (AFB_session *session, AFB_request *request, char *name); -#endif - extern AFB_error ctxTokenRefresh (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_error ctxTokenCreate (AFB_clientCtx *clientCtx, AFB_request *request); extern AFB_error ctxTokenCheck (AFB_clientCtx *clientCtx, AFB_request *request); diff --git a/plugins/media/media-api.c b/plugins/media/media-api.c index 92e195a9..4e064bc2 100644 --- a/plugins/media/media-api.c +++ b/plugins/media/media-api.c @@ -33,7 +33,7 @@ STATIC mediaCtxHandleT* initMediaCtx () { } /* called when client session dies [e.g. client quits for more than 15mns] */ -STATIC void freeMedia (void *context, void *handle) { +STATIC void freeMedia (void *context) { free (context); } diff --git a/plugins/radio/radio-api.c b/plugins/radio/radio-api.c index ea17eb51..bdb1768f 100644 --- a/plugins/radio/radio-api.c +++ b/plugins/radio/radio-api.c @@ -26,6 +26,8 @@ /* ------ LOCAL HELPER FUNCTIONS --------- */ +static pluginHandleT *the_radio = NULL; + /* detect new radio devices */ STATIC void updateRadioDevList(pluginHandleT *handle) { @@ -116,9 +118,9 @@ STATIC AFB_error releaseRadio (pluginHandleT *handle, radioCtxHandleT *ctx) { } /* called when client session dies [e.g. client quits for more than 15mns] */ -STATIC void freeRadio (void *context, void *handle) { +STATIC void freeRadio (void *context) { - releaseRadio (handle, context); + releaseRadio (the_radio, context); free (context); } @@ -140,7 +142,7 @@ STATIC json_object* init (AFB_request *request) { /* AFB_SESSION_CHECK */ STATIC json_object* power (AFB_request *request) { /* AFB_SESSION_CHECK */ - pluginHandleT *handle = (pluginHandleT*)request->handle; + pluginHandleT *handle = the_radio; radioCtxHandleT *ctx = (radioCtxHandleT*)request->context; const char *value = getQueryValue (request, "value"); json_object *jresp; @@ -325,8 +327,8 @@ PUBLIC AFB_plugin* pluginRegister () { plugin->prefix = "radio"; plugin->apis = pluginApis; - plugin->handle = initRadioPlugin(); plugin->freeCtxCB = (AFB_freeCtxCB)freeRadio; - return (plugin); + radio = initRadioPlugin(); + return plugin; }; diff --git a/plugins/samples/ClientCtx.c b/plugins/samples/ClientCtx.c index b2479f50..b59400e1 100644 --- a/plugins/samples/ClientCtx.c +++ b/plugins/samples/ClientCtx.c @@ -56,7 +56,9 @@ typedef struct { } MyClientContextT; - +// Plugin handle should not be in stack (malloc or static) +STATIC MyPluginHandleT global_handle; + // This function is call at session open time. Any client trying to // call it with an already open session will be denied. // Ex: http://localhost:1234/api/context/create?token=123456789 @@ -64,7 +66,7 @@ STATIC json_object* myCreate (AFB_request *request) { json_object *jresp; MyClientContextT *ctx= malloc (sizeof (MyClientContextT)); - MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; // store something in our plugin private client context ctx->count = 0; @@ -82,7 +84,7 @@ STATIC json_object* myCreate (AFB_request *request) { // ex: http://localhost:1234/api/context/action?token=xxxxxx-xxxxxx-xxxxx-xxxxx-xxxxxx STATIC json_object* myAction (AFB_request *request) { json_object* jresp; - MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; MyClientContextT *ctx= (MyClientContextT*) request->context; // store something in our plugin private client context @@ -98,7 +100,7 @@ STATIC json_object* myAction (AFB_request *request) { // ex: http://localhost:1234/api/context/close?token=xxxxxx-xxxxxx-xxxxx-xxxxx-xxxxxx STATIC json_object* myClose (AFB_request *request) { json_object* jresp; - MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; MyClientContextT *ctx= (MyClientContextT*) request->context; // store something in our plugin private client context @@ -109,8 +111,9 @@ STATIC json_object* myClose (AFB_request *request) { return jresp; } -STATIC void freeCtxCB (MyClientContextT *ctx, MyPluginHandleT *handle, char *uuid) { - fprintf (stderr, "FreeCtxCB uuid=[%s] Plugin=[%s] count=[%d]", uuid, (char*)handle->anythingYouWant, ctx->count); +STATIC void freeCtxCB (MyClientContextT *ctx) { + MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; + fprintf (stderr, "FreeCtxCB Plugin=[%s] count=[%d]", (char*)handle->anythingYouWant, ctx->count); free (ctx); // Note: handle should be free it is a static resource attached to plugin and not to session @@ -127,19 +130,15 @@ STATIC AFB_restapi pluginApis[]= { PUBLIC AFB_plugin *pluginRegister () { - // Plugin handle should not be in stack (malloc or static) - STATIC MyPluginHandleT handle; - AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN_JSON; plugin->info = "Sample of Client Context Usage"; plugin->prefix = "context"; plugin->apis = pluginApis; - plugin->handle = &handle; plugin->freeCtxCB= (AFB_freeCtxCB) freeCtxCB; // feed plugin handle before returning from registration - handle.anythingYouWant = "My Plugin Handle"; + global_handle.anythingYouWant = "My Plugin Handle"; return (plugin); }; diff --git a/plugins/samples/HelloWorld.c b/plugins/samples/HelloWorld.c index 05bec957..067365dd 100644 --- a/plugins/samples/HelloWorld.c +++ b/plugins/samples/HelloWorld.c @@ -30,7 +30,7 @@ STATIC json_object* pingSample (AFB_request *request) { if (len == 0) strcpy (query,"NoSearchQueryList"); // check if we have some post data - if (request->post == NULL) request->post->data="NoData"; + if (request->post != NULL) request->post->data="NoData"; // return response to caller response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); diff --git a/plugins/samples/SamplePost.c b/plugins/samples/SamplePost.c index 22acd095..d29fb05a 100644 --- a/plugins/samples/SamplePost.c +++ b/plugins/samples/SamplePost.c @@ -123,7 +123,6 @@ PUBLIC AFB_plugin *pluginRegister () { plugin->info = "Sample with Post Upload Files"; plugin->prefix= "post"; // url base plugin->apis = pluginApis; - plugin->handle= (void*) "What ever you want"; return (plugin); }; diff --git a/plugins/session/token-api.c b/plugins/session/token-api.c index 7073dac0..9a078328 100644 --- a/plugins/session/token-api.c +++ b/plugins/session/token-api.c @@ -91,8 +91,8 @@ STATIC json_object* clientGetPing (AFB_request *request) { // This function is call when Client Session Context is removed // Note: when freeCtxCB==NULL standard free/malloc is called -STATIC void clientContextFree(void *context, char* uuid) { - fprintf (stderr,"Plugin[token] Closing Session uuid=[%s]\n", uuid); +STATIC void clientContextFree(void *context) { + fprintf (stderr,"Plugin[token] Closing Session\n"); free (context); } @@ -111,7 +111,6 @@ PUBLIC AFB_plugin *pluginRegister () { plugin->info = "Application Framework Binder Service"; plugin->prefix= "token"; // url base plugin->apis = pluginApis; - plugin->handle= (void*) "What ever you want"; plugin->freeCtxCB= (void*) clientContextFree; return (plugin); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6eeb431d..03878dc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,8 +3,8 @@ ADD_LIBRARY(src OBJECT main.c session.c http-svc.c - afb-api.c - afb-plugins.c + afb-rest-api.c + afb-apis.c afb-method.c afb-hreq.c afb-websock.c diff --git a/src/afb-api.c b/src/afb-api.c deleted file mode 100644 index a3ff07fd..00000000 --- a/src/afb-api.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author "Fulup Ar Foll" - * Author José Bollo <jose.bollo@iot.bzh> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Contain all generic part to handle REST/API - * - * https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c'] - */ - -#define _GNU_SOURCE - -#include "../include/local-def.h" - -#include <dirent.h> -#include <dlfcn.h> -#include <setjmp.h> -#include <signal.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 */ - -static json_object *afbJsonType; - -// 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) -{ - - if (postHandle->type == AFB_POST_JSON) { - // if (verbose) fprintf(stderr, "End PostJson Request UID=%d\n", postHandle->uid); - } - - if (postHandle->type == AFB_POST_FORM) { - if (verbose) - fprintf(stderr, "End PostForm Request UID=%d\n", postHandle->uid); - } - if (postHandle->privatebuf) - free(postHandle->privatebuf); - free(postHandle); -} - -// Check of apiurl is declare in this plugin and call it -STATIC AFB_error callPluginApi(AFB_request * request, int plugidx, void *context) -{ - json_object *jresp, *jcall, *jreqt; - int idx, status, sig; - AFB_clientCtx *clientCtx = NULL; - AFB_plugin *plugin = request->plugins[plugidx]; - int signals[] = { SIGALRM, SIGSEGV, SIGFPE, 0 }; - - /*--------------------------------------------------------------- - | Signal handler defined inside CallPluginApi to access Request - +---------------------------------------------------------------- */ - void pluginError(int signum) { - sigset_t sigset; - - // unlock signal to allow a new signal to come - sigemptyset(&sigset); - sigaddset(&sigset, signum); - sigprocmask(SIG_UNBLOCK, &sigset, 0); - - fprintf(stderr, "Oops: Plugin Api Timeout timeout\n"); - longjmp(request->checkPluginCall, signum); - } - - // If a plugin hold this urlpath call its callback - for (idx = 0; plugin->apis[idx].callback != NULL; idx++) { - if (!strcmp(plugin->apis[idx].name, request->api)) { - - // 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); - - // prepare an object to store calling values - jcall = json_object_new_object(); - json_object_object_add(jcall, "prefix", json_object_new_string(plugin->prefix)); - json_object_object_add(jcall, "api", json_object_new_string(plugin->apis[idx].name)); - - // save context before calling the API - status = setjmp(request->checkPluginCall); - if (status != 0) { - - // 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); - - } else { - - // If timeout protection==0 we are in debug and we do not apply signal protection - if (request->config->apiTimeout > 0) { - for (sig = 0; signals[sig] != 0; sig++) { - if (signal(signals[sig], pluginError) == SIG_ERR) { - request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; - json_object_object_add(jcall, "status", json_object_new_string("fail")); - json_object_object_add(jcall, "info", json_object_new_string("Setting Timeout Handler Failed")); - json_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - } - } - // Trigger a timer to protect from unacceptable long time execution - alarm((unsigned)request->config->apiTimeout); - } - // Out of SessionNone every call get a client context session - if (AFB_SESSION_NONE != plugin->apis[idx].session) { - - // add client context to request - clientCtx = ctxClientGet(request, plugidx); - 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_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - }; - - if (verbose) - fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[%p] Uuid=[%s] Token=[%s]\n", request->prefix, request->api, plugin->apis[idx].session, clientCtx, clientCtx->uuid, clientCtx->token); - - switch (plugin->apis[idx].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_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_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - } else { - json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); - json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token)); - json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout)); - } - break; - - 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_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - } else { - json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); - json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token)); - json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout)); - } - break; - - 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_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - } else { - json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); - } - break; - - case AFB_SESSION_CHECK: - default: - // 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_object_object_add(jreqt, "request", jcall); - goto ExitOnDone; - } - break; - } - } - // Effectively CALL PLUGIN API with a subset of the context - jresp = plugin->apis[idx].callback(request, context); - - // Store context in case it was updated by plugins - if (request->context != NULL) - clientCtx->contexts[plugidx] = request->context; - - // handle intermediary Post Iterates out of band - if ((jresp == NULL) - && (request->errcode == MHD_HTTP_OK)) - return (AFB_SUCCESS); - - // Session close is done after the API call so API can still use session in closing API - if (AFB_SESSION_CLOSE == plugin->apis[idx].session) - ctxTokenReset(clientCtx, request); - - // API should return NULL of a valid Json Object - if (jresp == NULL) { - json_object_object_add(jcall, "status", json_object_new_string("null")); - json_object_object_add(jreqt, "request", jcall); - request->errcode = MHD_HTTP_NO_RESPONSE; - - } else { - json_object_object_add(jcall, "status", json_object_new_string("processed")); - json_object_object_add(jreqt, "request", jcall); - json_object_object_add(jreqt, "response", jresp); - } - // cancel timeout and plugin signal handle before next call - if (request->config->apiTimeout > 0) { - alarm(0); - for (sig = 0; signals[sig] != 0; sig++) { - signal(signals[sig], SIG_DFL); - } - } - } - goto ExitOnDone; - } - } - return (AFB_FAIL); - - ExitOnDone: - request->jresp = jreqt; - return (AFB_DONE); -} - -STATIC AFB_error findAndCallApi(AFB_request * request, void *context) -{ - int idx; - AFB_error status; - - if (!request->api || !request->prefix) - return (AFB_FAIL); - - // Search for a plugin with this urlpath - for (idx = 0; request->plugins[idx] != NULL; idx++) { - if (!strcmp(request->plugins[idx]->prefix, request->prefix)) { - status = callPluginApi(request, idx, context); - break; - } - } - // No plugin was found - if (request->plugins[idx] == NULL) { - request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s] Url=%s", request->prefix, 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] url=[%s]", request->api, request->prefix, request->url); - goto ExitOnError; - } - // Everything look OK - return (status); - - ExitOnError: - request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; - return (AFB_FAIL); -} - -// This CB is call for every item with a form post it reformat iterator values -// and callback Plugin API for each Item within PostForm. -STATIC int doPostIterate(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *mimetype, const char *encoding, const char *data, uint64_t offset, size_t size) -{ - - AFB_error status; - AFB_PostItem item; - - // retrieve API request from Post iterator handle - AFB_PostHandle *postHandle = (AFB_PostHandle *) cls; - AFB_request *request = (AFB_request *) postHandle->privatebuf; - AFB_PostRequest postRequest; - - if (verbose) - fprintf(stderr, "postHandle key=%s filename=%s len=%zu mime=%s\n", key, filename, size, mimetype); - - // Create and Item value for Plugin API - item.kind = kind; - item.key = key; - item.filename = filename; - item.mimetype = mimetype; - item.encoding = encoding; - item.len = size; - item.data = data; - item.offset = offset; - - // Reformat Request to make it somehow similar to GET/PostJson case - postRequest.data = (char *)postHandle; - postRequest.len = size; - postRequest.type = AFB_POST_FORM;; - request->post = &postRequest; - - // effectively call plugin API - status = findAndCallApi(request, &item); - // when returning no processing of postform stop - if (status != AFB_SUCCESS) - return MHD_NO; - - // let's allow iterator to move to next item - return MHD_YES; -} - -STATIC void freeRequest(AFB_request * request) -{ - - free(request->prefix); - free(request->api); - free(request); -} - -STATIC AFB_request *createRequest(struct MHD_Connection *connection, AFB_session * session, const char *url) -{ - - AFB_request *request; - - // Start with a clean request - request = calloc(1, sizeof(AFB_request)); - char *urlcpy1, *urlcpy2; - char *baseapi, *baseurl; - - // Extract plugin urlpath from request and make two copy because strsep overload copy - urlcpy1 = urlcpy2 = strdup(url); - 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 plugin=[%s] url=[%s]", baseurl, url); - request->errcode = MHD_HTTP_BAD_REQUEST; - goto Done; - } - // build request structure - request->connection = connection; - request->config = session->config; - request->url = url; - request->prefix = strdup(baseurl); - request->api = strdup(baseapi); - request->plugins = session->plugins; - // note request->handle is fed with request->context in ctxClientGet - - Done: - free(urlcpy1); - return (request); -} - - - - - - - - - - - - - - - -static int doRestApiPost(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) -{ - - static int postcount = 0; // static counter to debug POST protocol - json_object *errMessage; - AFB_error status; - struct MHD_Response *webResponse; - const char *serialized; - AFB_request *request = NULL; - AFB_PostHandle *postHandle; - AFB_PostRequest postRequest; - int ret; - - // fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls); - - // if post data may come in multiple calls - const char *encoding, *param; - int contentlen = -1; - postHandle = *con_cls; - - // This is the initial post event let's create form post structure POST data come in multiple events - if (postHandle == NULL) { - - // 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); - - // We are facing an empty post let's process it as a get - if (encoding == NULL) { - 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) - fprintf(stderr, "Create doPostIterate[uid=%d posthandle=%p]\n", postHandle->uid, postHandle); - - request = createRequest(connection, session, url); - if (request->jresp != NULL) - goto ProcessApiCall; - postHandle->type = AFB_POST_FORM; - postHandle->privatebuf = (void *)request; - postHandle->pp = MHD_create_post_processor(connection, MAX_POST_SIZE, &doPostIterate, postHandle); - - if (NULL == postHandle->pp) { - fprintf(stderr, "OOPS: Internal error fail to allocate MHD_create_post_processor\n"); - free(postHandle); - return MHD_NO; - } - return MHD_YES; - } - // POST json is store into a buffer and present in one piece to API - if (strcasestr(encoding, JSON_CONTENT) != NULL) { - - param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); - if (param) - sscanf(param, "%i", &contentlen); - - // Because PostJson are build in RAM size is constrained - if (contentlen > MAX_POST_SIZE) { - errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE); - goto ExitOnError; - } - // Size is OK, let's allocate a buffer to hold post data - postHandle->type = AFB_POST_JSON; - postHandle->privatebuf = malloc((unsigned)contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string - - // 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; - } - } - // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless - // return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with - // 'Internal application error, closing connection'. - if (*upload_data_size) { - - if (postHandle->type == AFB_POST_FORM) { - // if (verbose) fprintf(stderr, "Processing PostForm[uid=%d]\n", postHandle->uid); - MHD_post_process(postHandle->pp, upload_data, *upload_data_size); - } - // Process JsonPost request when buffer is completed let's call API - if (postHandle->type == AFB_POST_JSON) { - // if (verbose) fprintf(stderr, "Updating PostJson[uid=%d]\n", postHandle->uid); - memcpy(&postHandle->privatebuf[postHandle->len], upload_data, *upload_data_size); - postHandle->len = postHandle->len + *upload_data_size; - } - - *upload_data_size = 0; - return MHD_YES; - - } else { // we have finish with Post reception let's finish the work - - // 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 (postHandle->type == AFB_POST_JSON) { - // if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid); - - param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); - if (param) - sscanf(param, "%i", &contentlen); - - // 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: - // Request is ready let's call API without any extra handle - status = findAndCallApi(request, NULL); - - serialized = json_object_to_json_string(request->jresp); - webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); - - // client did not pass token on URI let's use cookies - if ((!request->restfull) && (request->context != NULL)) { - char cookie[256]; - snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout); - MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); - } - // if requested add an error status - if (request->errcode != 0) - ret = MHD_queue_response(connection, request->errcode, webResponse); - else - MHD_queue_response(connection, MHD_HTTP_OK, webResponse); - - MHD_destroy_response(webResponse); - json_object_put(request->jresp); // decrease reference rqtcount to free the json object - freeRequest(request); - return MHD_YES; - - ExitOnError: - freeRequest(request); - serialized = json_object_to_json_string(errMessage); - webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); - MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse); - MHD_destroy_response(webResponse); - json_object_put(errMessage); // decrease reference rqtcount to free the json object - return MHD_YES; -} - - - - - - - - - - - - - - - - - - - - -static int doRestApiGet(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) -{ - - static int postcount = 0; // static counter to debug POST protocol - json_object *errMessage; - AFB_error status; - struct MHD_Response *webResponse; - const char *serialized; - AFB_request *request = NULL; - AFB_PostHandle *postHandle; - AFB_PostRequest postRequest; - int ret; - - // fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls); - - // if post data may come in multiple calls - // this is a get we only need a request - request = createRequest(connection, session, url); - - ProcessApiCall: - // Request is ready let's call API without any extra handle - status = findAndCallApi(request, NULL); - - serialized = json_object_to_json_string(request->jresp); - webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); - - // client did not pass token on URI let's use cookies - if ((!request->restfull) && (request->context != NULL)) { - char cookie[256]; - snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout); - MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); - } - // if requested add an error status - if (request->errcode != 0) - ret = MHD_queue_response(connection, request->errcode, webResponse); - else - MHD_queue_response(connection, MHD_HTTP_OK, webResponse); - - MHD_destroy_response(webResponse); - json_object_put(request->jresp); // decrease reference rqtcount to free the json object - freeRequest(request); - return MHD_YES; - - ExitOnError: - freeRequest(request); - serialized = json_object_to_json_string(errMessage); - webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); - MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse); - MHD_destroy_response(webResponse); - json_object_put(errMessage); // decrease reference rqtcount to free the json object - return MHD_YES; -} - -int doRestApi(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) -{ - int rc; - - if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) { - rc = doRestApiPost(connection, session, url, method, upload_data, upload_data_size, con_cls); - } else { - rc = doRestApiGet(connection, session, url, method, upload_data, upload_data_size, con_cls); - } - return rc; -} - diff --git a/src/afb-plugins.c b/src/afb-apis.c index 1658fc57..3dcad651 100644 --- a/src/afb-plugins.c +++ b/src/afb-apis.c @@ -24,6 +24,7 @@ #define _GNU_SOURCE #include <stdio.h> +#include <assert.h> #include <string.h> #include <dirent.h> #include <dlfcn.h> @@ -34,32 +35,76 @@ #include "../include/local-def.h" -#include "afb-plugins.h" +#include "afb-apis.h" + +struct api_desc { + AFB_plugin *plugin; /* descriptor */ + size_t prefixlen; + const char *prefix; + void *handle; /* context of dlopen */ +}; + +static struct api_desc *apis_array = NULL; +static int apis_count = 0; static const char plugin_register_function[] = "pluginRegister"; -AFB_plugin *afb_plugins_search(AFB_session *session, const char *prefix, size_t length) +int afb_apis_count() +{ + return apis_count; +} + +void afb_apis_free_context(int apiidx, void *context) +{ + void (*cb)(void*); + + assert(0 <= apiidx && apiidx < apis_count); + cb = apis_array[apiidx].plugin->freeCtxCB; + if (cb) + cb(context); + else + free(context); +} + +const struct AFB_restapi *afb_apis_get(int apiidx, int verbidx) +{ + assert(0 <= apiidx && apiidx < apis_count); + return &apis_array[apiidx].plugin->apis[verbidx]; +} + +int afb_apis_get_verbidx(int apiidx, const char *name) +{ + const struct AFB_restapi *apis; + int idx; + + assert(0 <= apiidx && apiidx < apis_count); + apis = apis_array[apiidx].plugin->apis; + for (idx = 0 ; apis[idx].name ; idx++) + if (!strcasecmp(apis[idx].name, name)) + return idx; + return -1; +} + +int afb_apis_get_apiidx(const char *prefix, size_t length) { - int i, n; - AFB_plugin **plugins, *p; + int i; + const struct api_desc *a; if (!length) length = strlen(prefix); - n = session->config->pluginCount; - plugins = session->plugins; - - for (i = 0 ; i < n ; i++) { - p = plugins[i]; - if (p->prefixlen == length && !strcmp(p->prefix, prefix)) - return p; + for (i = 0 ; i < apis_count ; i++) { + a = &apis_array[i]; + if (a->prefixlen == length && !strcasecmp(a->prefix, prefix)) + return i; } - return NULL; + return -1; } -int afb_plugins_add_plugin(AFB_session *session, const char *path) +int afb_apis_add_plugin(const char *path) { - AFB_plugin *desc, *check, **plugins; + struct api_desc *apis; + AFB_plugin *plugin; AFB_plugin *(*pluginRegisterFct) (void); void *handle; size_t len; @@ -81,56 +126,54 @@ int afb_plugins_add_plugin(AFB_session *session, const char *path) fprintf(stderr, "[%s] is a valid AFB plugin\n", path); /* allocates enough memory */ - plugins = realloc(session->plugins, ((unsigned)session->config->pluginCount + 2) * sizeof(AFB_plugin*)); - if (plugins == NULL) { + apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis); + if (apis == NULL) { fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path); goto error2; } - session->plugins = plugins; + apis_array = apis; /* init the plugin */ - desc = pluginRegisterFct(); - if (desc == NULL) { + plugin = pluginRegisterFct(); + if (plugin == NULL) { fprintf(stderr, "ERROR: plugin [%s] register function failed. continuing...\n", path); goto error2; } /* check the returned structure */ - if (desc->type != AFB_PLUGIN_JSON) { - fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, desc->type); + if (plugin->type != AFB_PLUGIN_JSON) { + fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, plugin->type); goto error2; } - if (desc->prefix == NULL || *desc->prefix == 0) { + if (plugin->prefix == NULL || *plugin->prefix == 0) { fprintf(stderr, "ERROR: plugin [%s] bad prefix...\n", path); goto error2; } - if (desc->info == NULL || *desc->info == 0) { + if (plugin->info == NULL || *plugin->info == 0) { fprintf(stderr, "ERROR: plugin [%s] bad description...\n", path); goto error2; } - if (desc->apis == NULL) { + if (plugin->apis == NULL) { fprintf(stderr, "ERROR: plugin [%s] no APIs...\n", path); goto error2; } /* check previously existing plugin */ - len = strlen(desc->prefix); - check = afb_plugins_search(session, desc->prefix, len); - if (check != NULL) { - fprintf(stderr, "ERROR: plugin [%s] prefix %s duplicated...\n", path, desc->prefix); + len = strlen(plugin->prefix); + if (afb_apis_get_apiidx(plugin->prefix, len) >= 0) { + fprintf(stderr, "ERROR: plugin [%s] prefix %s duplicated...\n", path, plugin->prefix); goto error2; } - /* Prebuild plugin jtype to boost API response */ - desc->jtype = json_object_new_string(desc->prefix); - desc->prefixlen = len; - /* record the plugin */ - session->plugins[session->config->pluginCount] = desc; - session->plugins[++session->config->pluginCount] = NULL; - if (verbose) - fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", session->config->pluginCount, desc->prefix, desc->info); + fprintf(stderr, "Loading plugin[%lu] prefix=[%s] info=%s\n", (unsigned long)apis_count, plugin->prefix, plugin->info); + apis = &apis_array[apis_count]; + apis->plugin = plugin; + apis->prefixlen = len; + apis->prefix = plugin->prefix; + apis->handle = handle; + apis_count++; return 0; @@ -140,7 +183,7 @@ error: return -1; } -static int adddirs(AFB_session * session, char path[PATH_MAX], size_t end) +static int adddirs(char path[PATH_MAX], size_t end) { int rc; DIR *dir; @@ -178,19 +221,19 @@ static int adddirs(AFB_session * session, char path[PATH_MAX], size_t end) if (ent.d_name[1] == '.' && len == 2) continue; } - rc = adddirs(session, path, end+len);; + rc = adddirs(path, end+len);; } else if (ent.d_type == DT_REG) { /* case of files */ if (!strstr(ent.d_name, ".so")) continue; - rc = afb_plugins_add_plugin(session, path); + rc = afb_apis_add_plugin(path); } } closedir(dir); return 0; } -int afb_plugins_add_directory(AFB_session * session, const char *path) +int afb_apis_add_directory(const char *path) { size_t length; char buffer[PATH_MAX]; @@ -202,10 +245,10 @@ int afb_plugins_add_directory(AFB_session * session, const char *path) } memcpy(buffer, path, length + 1); - return adddirs(session, buffer, length); + return adddirs(buffer, length); } -int afb_plugins_add_path(AFB_session * session, const char *path) +int afb_apis_add_path(const char *path) { struct stat st; int rc; @@ -214,13 +257,13 @@ int afb_plugins_add_path(AFB_session * session, const char *path) if (rc < 0) fprintf(stderr, "Invalid plugin path [%s]: %m\n", path); else if (S_ISDIR(st.st_mode)) - rc = afb_plugins_add_directory(session, path); + rc = afb_apis_add_directory(path); else - rc = afb_plugins_add_plugin(session, path); + rc = afb_apis_add_plugin(path); return rc; } -int afb_plugins_add_pathset(AFB_session * session, const char *pathset) +int afb_apis_add_pathset(const char *pathset) { static char sep[] = ":"; char *ps, *p; @@ -231,12 +274,7 @@ int afb_plugins_add_pathset(AFB_session * session, const char *pathset) p = strsep(&ps, sep); if (!p) return 0; - rc = afb_plugins_add_path(session, p); + rc = afb_apis_add_path(p); }; } -void initPlugins(AFB_session * session) -{ - int rc = afb_plugins_add_pathset(session, session->config->ldpaths); -} - diff --git a/src/afb-plugins.h b/src/afb-apis.h index c2db2e4c..a37df5d3 100644 --- a/src/afb-plugins.h +++ b/src/afb-apis.h @@ -16,15 +16,22 @@ */ -AFB_plugin *afb_plugins_search(AFB_session *session, const char *prefix, size_t length); +extern int afb_apis_count(); -int afb_plugins_add_plugin(AFB_session *session, const char *path); +extern void afb_apis_free_context(int apiidx, void *context); -int afb_plugins_add_directory(AFB_session * session, const char *path); +extern const struct AFB_restapi *afb_apis_get(int apiidx, int verbidx); -int afb_plugins_add_path(AFB_session * session, const char *path); +extern int afb_apis_get_verbidx(int apiidx, const char *name); -int afb_plugins_add_pathset(AFB_session * session, const char *pathset); +extern int afb_apis_get_apiidx(const char *prefix, size_t length); + +extern int afb_apis_add_plugin(const char *path); + +extern int afb_apis_add_directory(const char *path); + +extern int afb_apis_add_path(const char *path); + +extern int afb_apis_add_pathset(const char *pathset); -void initPlugins(AFB_session * session); diff --git a/src/afb-hreq.c b/src/afb-hreq.c index 7bee69b3..f7f3bb15 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -75,7 +75,7 @@ int afb_hreq_unprefix(struct afb_hreq *hreq, const char *prefix, size_t length) { /* check the prefix ? */ if (length > hreq->lentail || (hreq->tail[length] && hreq->tail[length] != '/') - || memcmp(prefix, hreq->tail, length)) + || strncasecmp(prefix, hreq->tail, length)) return 0; /* removes successives / */ diff --git a/src/afb-req-itf.h b/src/afb-req-itf.h index e1d9dc84..6fa854c2 100644 --- a/src/afb-req-itf.h +++ b/src/afb-req-itf.h @@ -19,6 +19,9 @@ struct afb_req_itf { const char *(*get_cookie)(void *data, const char *name); const char *(*get_argument)(void *data, const char *name); +#if 0 + int (*set_cookie)(void *data, const char *name, const char *value); +#endif }; struct afb_req { diff --git a/src/afb-rest-api.c b/src/afb-rest-api.c new file mode 100644 index 00000000..febe19f8 --- /dev/null +++ b/src/afb-rest-api.c @@ -0,0 +1,617 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author "Fulup Ar Foll" + * Author José Bollo <jose.bollo@iot.bzh> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contain all generic part to handle REST/API + * + * https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c'] + */ + +#define _GNU_SOURCE + +#include "../include/local-def.h" + +#include <dirent.h> +#include <dlfcn.h> +#include <setjmp.h> +#include <signal.h> + +#include "afb-apis.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 */ + +static json_object *afbJsonType; + +// 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) +{ + + if (postHandle->type == AFB_POST_JSON) { + // if (verbose) fprintf(stderr, "End PostJson Request UID=%d\n", postHandle->uid); + } + + if (postHandle->type == AFB_POST_FORM) { + if (verbose) + fprintf(stderr, "End PostForm Request UID=%d\n", postHandle->uid); + } + if (postHandle->privatebuf) + free(postHandle->privatebuf); + free(postHandle); +} + +// Check of apiurl is declare in this plugin and call it +static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx, void *context) +{ + enum AFB_sessionE session; + json_object *jresp, *jcall, *jreqt; + AFB_clientCtx *clientCtx = NULL; + + // 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); + + // 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)); + + // 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); + 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_object_object_add(jreqt, "request", jcall); + goto ExitOnDone; + }; + + 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); + + 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_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_object_object_add(jreqt, "request", jcall); + goto ExitOnDone; + } else { + json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); + json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token)); + json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout)); + } + break; + + 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_object_object_add(jreqt, "request", jcall); + goto ExitOnDone; + } else { + json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); + json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token)); + json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout)); + } + break; + + 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_object_object_add(jreqt, "request", jcall); + goto ExitOnDone; + } else { + json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid)); + } + break; + + case AFB_SESSION_CHECK: + default: + // 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_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); + + // Store context in case it was updated by plugins + if (request->context != NULL) + clientCtx->contexts[apiidx] = request->context; + + // handle intermediary Post Iterates out of band + if ((jresp == NULL) && (request->errcode == MHD_HTTP_OK)) + return AFB_SUCCESS; + + // Session close is done after the API call so API can still use session in closing API + if (AFB_SESSION_CLOSE == session) + ctxTokenReset(clientCtx, request); + + // API should return NULL of a valid Json Object + if (jresp == NULL) { + json_object_object_add(jcall, "status", json_object_new_string("null")); + json_object_object_add(jreqt, "request", jcall); + request->errcode = MHD_HTTP_NO_RESPONSE; + + } else { + json_object_object_add(jcall, "status", json_object_new_string("processed")); + json_object_object_add(jreqt, "request", jcall); + json_object_object_add(jreqt, "response", jresp); + } + +ExitOnDone: + request->jresp = jreqt; + return AFB_DONE; +} + +// Check of apiurl is declare in this plugin and call it +extern __thread sigjmp_buf *error_handler; +static AFB_error callPluginApi(AFB_request * request, int apiidx, int verbidx, void *context) +{ + sigjmp_buf jmpbuf; + + json_object *jcall, *jreqt; + int status; + + // save context before calling the API + status = setjmp(jmpbuf); + if (status != 0) { + + // 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); + + // 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)); + + // 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); + + error_handler = &jmpbuf; + doCallPluginApi(request, apiidx, verbidx, context); + error_handler = NULL; + + // cancel timeout and plugin signal handle before next call + alarm(0); + } + +ExitOnDone: + return AFB_DONE; +} + +STATIC AFB_error findAndCallApi(AFB_request * request, void *context) +{ + int apiidx, verbidx; + AFB_error status; + + if (!request->api || !request->prefix) + return AFB_FAIL; + + /* get the plugin if any */ + apiidx = afb_apis_get_apiidx(request->prefix, 0); + if (apiidx < 0) { + request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s] Url=%s", request->prefix, request->url); + request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + return AFB_FAIL; + } + + /* get the verb if any */ + verbidx = afb_apis_get_verbidx(apiidx, request->api); + if (verbidx < 0) { + request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->api, request->prefix, request->url); + request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + return AFB_FAIL; + } + + /* Search for a plugin with this urlpath */ + status = callPluginApi(request, apiidx, verbidx, 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->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + return AFB_FAIL; + } + // Everything look OK + return status; +} + +// This CB is call for every item with a form post it reformat iterator values +// and callback Plugin API for each Item within PostForm. +STATIC int doPostIterate(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *mimetype, const char *encoding, const char *data, uint64_t offset, size_t size) +{ + + AFB_error status; + AFB_PostItem item; + + // retrieve API request from Post iterator handle + AFB_PostHandle *postHandle = (AFB_PostHandle *) cls; + AFB_request *request = (AFB_request *) postHandle->privatebuf; + AFB_PostRequest postRequest; + + if (verbose) + fprintf(stderr, "postHandle key=%s filename=%s len=%zu mime=%s\n", key, filename, size, mimetype); + + // Create and Item value for Plugin API + item.kind = kind; + item.key = key; + item.filename = filename; + item.mimetype = mimetype; + item.encoding = encoding; + item.len = size; + item.data = data; + item.offset = offset; + + // Reformat Request to make it somehow similar to GET/PostJson case + postRequest.data = (char *)postHandle; + postRequest.len = size; + postRequest.type = AFB_POST_FORM;; + request->post = &postRequest; + + // effectively call plugin API + status = findAndCallApi(request, &item); + // when returning no processing of postform stop + if (status != AFB_SUCCESS) + return MHD_NO; + + // let's allow iterator to move to next item + return MHD_YES; +} + +STATIC void freeRequest(AFB_request * request) +{ + + free(request->prefix); + free(request->api); + free(request); +} + +STATIC AFB_request *createRequest(struct MHD_Connection *connection, AFB_session * session, const char *url) +{ + + AFB_request *request; + + // Start with a clean request + request = calloc(1, sizeof(AFB_request)); + char *urlcpy1, *urlcpy2; + char *baseapi, *baseurl; + + // Extract plugin urlpath from request and make two copy because strsep overload copy + urlcpy1 = urlcpy2 = strdup(url); + 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 plugin=[%s] url=[%s]", baseurl, url); + request->errcode = MHD_HTTP_BAD_REQUEST; + goto Done; + } + // build request structure + request->connection = connection; + request->config = session->config; + request->url = url; + request->prefix = strdup(baseurl); + request->api = strdup(baseapi); + + Done: + free(urlcpy1); + return (request); +} + + + + + + + + + + + + + + + +static int doRestApiPost(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) +{ + + static int postcount = 0; // static counter to debug POST protocol + json_object *errMessage; + AFB_error status; + struct MHD_Response *webResponse; + const char *serialized; + AFB_request *request = NULL; + AFB_PostHandle *postHandle; + AFB_PostRequest postRequest; + int ret; + + // fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls); + + // if post data may come in multiple calls + const char *encoding, *param; + int contentlen = -1; + postHandle = *con_cls; + + // This is the initial post event let's create form post structure POST data come in multiple events + if (postHandle == NULL) { + + // 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); + + // We are facing an empty post let's process it as a get + if (encoding == NULL) { + 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) + fprintf(stderr, "Create doPostIterate[uid=%d posthandle=%p]\n", postHandle->uid, postHandle); + + request = createRequest(connection, session, url); + if (request->jresp != NULL) + goto ProcessApiCall; + postHandle->type = AFB_POST_FORM; + postHandle->privatebuf = (void *)request; + postHandle->pp = MHD_create_post_processor(connection, MAX_POST_SIZE, &doPostIterate, postHandle); + + if (NULL == postHandle->pp) { + fprintf(stderr, "OOPS: Internal error fail to allocate MHD_create_post_processor\n"); + free(postHandle); + return MHD_NO; + } + return MHD_YES; + } + // POST json is store into a buffer and present in one piece to API + if (strcasestr(encoding, JSON_CONTENT) != NULL) { + + param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); + if (param) + sscanf(param, "%i", &contentlen); + + // Because PostJson are build in RAM size is constrained + if (contentlen > MAX_POST_SIZE) { + errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE); + goto ExitOnError; + } + // Size is OK, let's allocate a buffer to hold post data + postHandle->type = AFB_POST_JSON; + postHandle->privatebuf = malloc((unsigned)contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string + + // 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; + } + } + + // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless + // return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with + // 'Internal application error, closing connection'. + if (*upload_data_size) { + + if (postHandle->type == AFB_POST_FORM) { + // if (verbose) fprintf(stderr, "Processing PostForm[uid=%d]\n", postHandle->uid); + MHD_post_process(postHandle->pp, upload_data, *upload_data_size); + } + // Process JsonPost request when buffer is completed let's call API + if (postHandle->type == AFB_POST_JSON) { + // if (verbose) fprintf(stderr, "Updating PostJson[uid=%d]\n", postHandle->uid); + memcpy(&postHandle->privatebuf[postHandle->len], upload_data, *upload_data_size); + postHandle->len = postHandle->len + *upload_data_size; + } + + *upload_data_size = 0; + return MHD_YES; + + } else { // we have finish with Post reception let's finish the work + + // 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 (postHandle->type == AFB_POST_JSON) { + // if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid); + + param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); + if (param) + sscanf(param, "%i", &contentlen); + + // 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: + // Request is ready let's call API without any extra handle + status = findAndCallApi(request, NULL); + + serialized = json_object_to_json_string(request->jresp); + webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); + + // client did not pass token on URI let's use cookies + if ((!request->restfull) && (request->context != NULL)) { + char cookie[256]; + snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout); + MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); + } + // if requested add an error status + if (request->errcode != 0) + ret = MHD_queue_response(connection, request->errcode, webResponse); + else + MHD_queue_response(connection, MHD_HTTP_OK, webResponse); + + MHD_destroy_response(webResponse); + json_object_put(request->jresp); // decrease reference rqtcount to free the json object + freeRequest(request); + return MHD_YES; + + ExitOnError: + freeRequest(request); + serialized = json_object_to_json_string(errMessage); + webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); + MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse); + MHD_destroy_response(webResponse); + json_object_put(errMessage); // decrease reference rqtcount to free the json object + return MHD_YES; +} + + + + + + + + + + + + + + + + + + + + +static int doRestApiGet(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) +{ + AFB_error status; + struct MHD_Response *webResponse; + const char *serialized; + AFB_request *request = NULL; + int ret; + + // fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls); + + // if post data may come in multiple calls + // this is a get we only need a request + request = createRequest(connection, session, url); + + // Request is ready let's call API without any extra handle + status = findAndCallApi(request, NULL); + + serialized = json_object_to_json_string(request->jresp); + webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY); + + // client did not pass token on URI let's use cookies + if ((!request->restfull) && (request->context != NULL)) { + char cookie[256]; + snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout); + MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); + } + // if requested add an error status + if (request->errcode != 0) + ret = MHD_queue_response(connection, request->errcode, webResponse); + else + MHD_queue_response(connection, MHD_HTTP_OK, webResponse); + + MHD_destroy_response(webResponse); + json_object_put(request->jresp); // decrease reference rqtcount to free the json object + freeRequest(request); + return MHD_YES; +} + +int doRestApi(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls) +{ + int rc; + + if (afbJsonType == NULL) + afbJsonType = json_object_new_string (AFB_MSG_JTYPE); + + if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) { + rc = doRestApiPost(connection, session, url, method, upload_data, upload_data_size, con_cls); + } else { + rc = doRestApiGet(connection, session, url, method, upload_data, upload_data_size, con_cls); + } + return rc; +} + diff --git a/src/afb-websock.c b/src/afb-websock.c index 90148ecf..b6056adc 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -152,13 +152,11 @@ int afb_websock_check(struct afb_hreq *hreq, int *later) /* is an upgrade to websocket ? */ upgrade = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_UPGRADE); -printf("upgrade %s\n", upgrade); if (upgrade == NULL || strcasecmp(upgrade, websocket_s)) return 0; /* is a connection for upgrade ? */ connection = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONNECTION); -printf("connection %s\n", connection); if (connection == NULL || !headerhas (connection, MHD_HTTP_HEADER_UPGRADE)) return 0; @@ -169,8 +167,6 @@ printf("connection %s\n", connection); /* has a key and a version ? */ key = afb_hreq_get_header(hreq, sec_websocket_key_s); version = afb_hreq_get_header(hreq, sec_websocket_version_s); -printf("key %s\n", key); -printf("version %s\n", connection); if (key == NULL || version == NULL) return 0; @@ -21,11 +21,12 @@ #include <signal.h> #include <getopt.h> #include <pwd.h> +#include <pthread.h> #include <sys/stat.h> #include <sys/types.h> #include "local-def.h" -#include "afb-plugins.h" +#include "afb-apis.h" #if !defined(PLUGIN_INSTALL_DIR) #error "you should define PLUGIN_INSTALL_DIR" @@ -416,6 +417,36 @@ void signalQuit (int signum) { /*---------------------------------------------------------- + | Error signals + | + +--------------------------------------------------------- */ +__thread sigjmp_buf *error_handler; +static void signalError(int signum) +{ + sigset_t sigset; + + // unlock signal to allow a new signal to come + sigemptyset(&sigset); + sigaddset(&sigset, signum); + sigprocmask(SIG_UNBLOCK, &sigset, 0); + if (error_handler != NULL) { + longjmp(*error_handler, signum); + } +} + +static void install_error_handlers() +{ + int i, signals[] = { SIGALRM, SIGSEGV, SIGFPE, 0 }; + + for (i = 0; signals[i] != 0; i++) { + if (signal(signals[i], signalError) == SIG_ERR) { + fprintf(stderr, "Signal handler error\n"); + exit(1); + } + } +} + +/*---------------------------------------------------------- | listenLoop | Main listening HTTP loop +--------------------------------------------------------- */ @@ -514,9 +545,13 @@ int main(int argc, char *argv[]) { exit (1); } - initPlugins(session); + if (session->config->ldpaths) + afb_apis_add_pathset(session->config->ldpaths); + ctxStoreInit(CTX_NBCLIENTS); + install_error_handlers(); + // ------------------ Some useful default values ------------------------- if ((session->background == 0) && (session->foreground == 0)) session->foreground=1; diff --git a/src/session.c b/src/session.c index 743f8254..9569b46c 100644 --- a/src/session.c +++ b/src/session.c @@ -30,6 +30,7 @@ #include <pthread.h> #include <search.h> +#include "afb-apis.h" // Session UUID are store in a simple array [for 10 sessions this should be enough] static struct { @@ -45,22 +46,16 @@ static const char key_token[] = "token"; // Free context [XXXX Should be protected again memory abort XXXX] static void ctxUuidFreeCB (AFB_clientCtx *client) { - - AFB_plugin **plugins = client->plugins; - AFB_freeCtxCB freeCtxCB; - int idx; + int idx, cnt; // If application add a handle let's free it now if (client->contexts != NULL) { + cnt = afb_apis_count(); // Free client handle with a standard Free function, with app callback or ignore it - for (idx=0; client->plugins[idx] != NULL; idx ++) { + for (idx=0; idx < cnt; idx ++) { if (client->contexts[idx] != NULL) { - freeCtxCB = client->plugins[idx]->freeCtxCB; - if (freeCtxCB == NULL) - free (client->contexts[idx]); - else if (freeCtxCB != (void*)-1) - freeCtxCB(client->contexts[idx], plugins[idx]->handle, client->uuid); + afb_apis_free_context(idx, client->contexts[idx]); } } } @@ -150,7 +145,7 @@ static AFB_error ctxStoreAdd (AFB_clientCtx *client) } // Check if context timeout or not -static int ctxStoreToOld (AFB_clientCtx *ctx, int timeout) +static int ctxStoreTooOld (AFB_clientCtx *ctx, int timeout) { int res; time_t now = time(NULL); @@ -166,15 +161,15 @@ void ctxStoreGarbage (const int timeout) // Loop on Sessions Table and remove anything that is older than timeout for (idx=0; idx < sessions.max; idx++) { - ctx=sessions.store[idx]; - if ((ctx != NULL) && (ctxStoreToOld(ctx, timeout))) { + ctx = sessions.store[idx]; + if ((ctx != NULL) && (ctxStoreTooOld(ctx, timeout))) { ctxStoreDel (ctx); } } } // This function will return exiting client context or newly created client context -AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) +AFB_clientCtx *ctxClientGet (AFB_request *request, int apiidx) { AFB_clientCtx *clientCtx=NULL; const char *uuid; @@ -201,14 +196,13 @@ AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) clientCtx = ctxStoreSearch (uuid); if (clientCtx) { - if (ctxStoreToOld (clientCtx, request->config->cntxTimeout)) { + if (ctxStoreTooOld (clientCtx, request->config->cntxTimeout)) { // this session is too old let's delete it ctxStoreDel (clientCtx); clientCtx = NULL; } else { - request->context=clientCtx->contexts[idx]; - request->handle = clientCtx->plugins[idx]->handle; - request->uuid= uuid; + request->context = clientCtx->contexts[apiidx]; + request->uuid = uuid; return clientCtx; } } @@ -217,8 +211,7 @@ AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) // we have no session let's create one otherwise let's clean any exiting values if (clientCtx == NULL) { clientCtx = calloc(1, sizeof(AFB_clientCtx)); // init NULL clientContext - clientCtx->contexts = calloc (1, (unsigned)request->config->pluginCount * (sizeof (void*))); - clientCtx->plugins = request->plugins; + clientCtx->contexts = calloc ((unsigned)afb_apis_count(), sizeof (void*)); } uuid_generate(newuuid); // create a new UUID @@ -234,9 +227,8 @@ AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) } // if (verbose) fprintf (stderr, "ctxClientGet New uuid=[%s] token=[%s] timestamp=%d\n", clientCtx->uuid, clientCtx->token, clientCtx->timeStamp); - request->context = clientCtx->contexts[idx]; - request->handle = clientCtx->plugins[idx]->handle; - request->uuid=clientCtx->uuid; + request->context = clientCtx->contexts[apiidx]; + request->uuid = clientCtx->uuid; return clientCtx; } @@ -256,7 +248,7 @@ AFB_error ctxTokenCheck (AFB_clientCtx *clientCtx, AFB_request *request) return AFB_FALSE; // compare current token with previous one - if ((0 == strcmp (token, clientCtx->token)) && (!ctxStoreToOld (clientCtx, request->config->cntxTimeout))) { + if ((0 == strcmp (token, clientCtx->token)) && (!ctxStoreTooOld (clientCtx, request->config->cntxTimeout))) { return AFB_SUCCESS; } |