From e7c246a1b0d30b8156c7033061a61ecb5d2bdfc8 Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Sat, 12 Dec 2015 03:10:34 +0100 Subject: [PATCH] Added Session Management --- .gitignore | 1 + CMakeLists.txt | 4 +- include/local-def.h | 91 +++++++++++----- include/proto-def.h | 16 ++- nbproject/configurations.xml | 120 ++++++++++++++++++--- nbproject/private/Default.properties | 7 -- nbproject/private/configurations.xml | 15 +-- src/afbs-api.c | 105 ++++++++++++++---- src/alsa-api.c | 20 ++-- src/config.c | 22 ++-- src/dbus-api.c | 40 ++++--- src/http-svc.c | 4 +- src/main.c | 59 ++++++---- src/rest-api.c | 128 ++++++++++++---------- src/session.c | 203 +++++++++++++++++++++++++++++++++++ 15 files changed, 642 insertions(+), 193 deletions(-) diff --git a/.gitignore b/.gitignore index 3bd40a1f..f49e1601 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist/** .dep.inc CMakeFiles/ CMakeCache.txt +nbproject/private/*.log diff --git a/CMakeLists.txt b/CMakeLists.txt index b297cbfc..4e9314d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,8 @@ PKG_CHECK_MODULES(json-c REQUIRED json-c) PKG_CHECK_MODULES(libmicrohttpd REQUIRED libmicrohttpd) # Optional plugin dependencies PKG_CHECK_MODULES(librtlsdr librtlsdr>=0.5.0) +PKG_CHECK_MODULES(uuid REQUIRED uuid) + IF(librtlsdr_FOUND) MESSAGE(STATUS "librtlsdr found ; will compile Radio plugin... (PLUGIN)") @@ -46,6 +48,6 @@ INCLUDE(FindThreads) FIND_PACKAGE(Threads) SET(include_dirs ${INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/include ${json-c_INCLUDE_DIRS} ${libmicrohttpd_INCLUDE_DIRS} ${librtlsdr_INCLUDE_DIRS}) -SET(link_libraries ${json-c_LIBRARIES} ${libmicrohttpd_LIBRARIES} ${librtlsdr_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${libefence_LIBRARIES} -lmagic -lm) +SET(link_libraries ${json-c_LIBRARIES} ${libmicrohttpd_LIBRARIES} ${uuid_LIBRARIES} ${librtlsdr_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${libefence_LIBRARIES} -lmagic -lm) ADD_SUBDIRECTORY(src) diff --git a/include/local-def.h b/include/local-def.h index d8010c1c..df66a09e 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -39,6 +39,8 @@ #include #include #include +#include + @@ -49,7 +51,14 @@ // Note: because of a bug in libmagic MAGIC_DB NULL should not be used for default #define MAGIC_DB "/usr/share/misc/magic.mgc" #define OPA_INDEX "index.html" -#define MAX_ALIAS 10 // max number of aliases +#define MAX_ALIAS 10 // max number of aliases +#define COOKIE_NAME "AJB_session" + + +#define DEFLT_CNTX_TIMEOUT 3600 // default Client Connection Timeout +#define DEFLT_API_TIMEOUT 0 // default Plugin API Timeout [0=NoLimit for Debug Only] +#define DEFLT_API_TIMEOUT 0 // default Plugin API Timeout +#define DEFLT_CACHE_TIMEOUT 100000 // default Static File Chache [Client Side Cache 100000~=1day] typedef int BOOL; #ifndef FALSE @@ -63,6 +72,8 @@ typedef int BOOL; #define STATIC static #define FAILED -1 +extern int verbose; // this is the only global variable + // prebuild json error are constructed in config.c typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS, AFB_DONE} AFB_error; @@ -72,9 +83,11 @@ extern char *ERROR_LABEL[]; #define BANNER "Application Framework BinderApplication Framework " #define JSON_CONTENT "application/json" #define MAX_POST_SIZE 4096 // maximum size for POST data +#define CTX_NBCLIENTS 10 // allow a default of 10 authenticated clients // use to check anonymous data when using dynamic loadable lib typedef enum {AFB_PLUGIN=1234, AFB_REQUEST=5678} AFB_type; +typedef json_object* (*AFB_apiCB)(); // Error code are requested through function to manage json usage count typedef struct { @@ -101,24 +114,13 @@ typedef struct { size_t len; } AFB_aliasdir; - -// some usefull static object initialized when entering listen loop. -extern int verbose; -// MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value"); -typedef struct { - const char *url; - char *plugin; - char *api; - char *post; - json_object *jresp; - struct MHD_Connection *connection; - sigjmp_buf checkPluginCall; // context save for timeout set/longjmp -} AFB_request; - +// Command line structure hold cli --command + help text typedef struct { - char *msg; - size_t len; -} AFB_redirect_msg; + int val; // command number within application + int has_arg; // command number within application + char *name; // command as used in --xxxx cli + char *help; // help text +} AFB_options; // main config structure typedef struct { @@ -134,36 +136,64 @@ typedef struct { char *pidfile; // where to store pid when running background char *sessiondir; // where to store mixer session files char *configfile; // where to store configuration on gateway exit - uid_t setuid; + char *setuid; int cacheTimeout; int apiTimeout; + int cntxTimeout; // Client Session Context timeout AFB_aliasdir *aliasdir; // alias mapping for icons,apps,... } AFB_config; -// Command line structure hold cli --command + help text -typedef struct { - int val; // command number within application - int has_arg; // command number within application - char *name; // command as used in --xxxx cli - char *help; // help text -} AFB_options; + typedef struct { int len; // command number within application json_object *jtype; } AFB_privateApi; -typedef json_object* (*AFB_apiCB)(); + +typedef struct { + char *msg; + size_t len; +} AFB_redirect_msg; // API definition typedef struct { char *name; AFB_apiCB callback; char *info; - void * handle; AFB_privateApi *private; } AFB_restapi; + +// User Client Session Context +typedef struct { + int cid; // index 0 if global + char uuid[37]; // long term authentication of remote client + char token[37]; // short term authentication of remote client + time_t timeStamp; // last time token was refresh + int restfull; // client does not use cookie + void *handle; // application specific context + AFB_apiCB freeHandleCB; // callback to free application handle [null for standard free] +} AFB_clientCtx; + + +// MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value"); +typedef struct { + const char *url; + char *plugin; + char *api; + char *post; + int loa; + json_object *jresp; + AFB_clientCtx *client; // needed because libmicrohttp cannot create an empty response + 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 MHD_Connection *connection; +} AFB_request; + + // Plugin definition typedef struct { AFB_type type; @@ -172,8 +202,12 @@ typedef struct { size_t prefixlen; json_object *jtype; AFB_restapi *apis; + void *handle; + int ctxCount; + AFB_clientCtx *ctxGlobal; } AFB_plugin; + typedef struct { AFB_config *config; // pointer to current config // List of commands to execute @@ -192,4 +226,5 @@ typedef struct { } AFB_session; + #include "proto-def.h" diff --git a/include/proto-def.h b/include/proto-def.h index f5b9f79c..25834524 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -22,7 +22,7 @@ // Rest-api -PUBLIC json_object* apiPingTest(AFB_session *session, AFB_request *request, void* handle); +PUBLIC json_object* apiPingTest(AFB_request *request); PUBLIC const char* getQueryValue (AFB_request * request, char *name); PUBLIC int getQueryAll(AFB_request * request, char *query, size_t len); @@ -32,10 +32,10 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co void initPlugins (AFB_session *session); -typedef AFB_plugin* (*AFB_pluginCB)(AFB_session *session); -PUBLIC AFB_plugin* afsvRegister (AFB_session *session); -PUBLIC AFB_plugin* dbusRegister (AFB_session *session); -PUBLIC AFB_plugin* alsaRegister (AFB_session *session); +typedef AFB_plugin* (*AFB_pluginCB)(); +PUBLIC AFB_plugin* afsvRegister (); +PUBLIC AFB_plugin* dbusRegister (); +PUBLIC AFB_plugin* alsaRegister (); PUBLIC AFB_plugin* radioRegister (AFB_session *session); @@ -45,6 +45,12 @@ PUBLIC AFB_error sessionCheckdir (AFB_session *session); PUBLIC json_object *sessionList (AFB_session *session, AFB_request *request); PUBLIC json_object *sessionToDisk (AFB_session *session, AFB_request *request, char *name,json_object *jsonSession); PUBLIC json_object *sessionFromDisk (AFB_session *session, AFB_request *request, char *name); +PUBLIC char* ctxTokenRefresh (AFB_request *request); +PUBLIC char* ctxTokenCreate (AFB_request *request); +PUBLIC AFB_error ctxTokenCheck (AFB_request *request); +PUBLIC int ctxTokenReset (AFB_request *request); +PUBLIC int ctxClientGet (AFB_request *request); + // Httpd server diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 3d743b11..4b821789 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -47,6 +47,7 @@ + @@ -56,13 +57,6 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean build/src/afb-daemon - - - include - /usr/include/json-c - build/src - - build @@ -72,34 +66,134 @@ + + include + /usr/include/json-c + build/src + + + include + /usr/include/json-c + build/src + - + + + src + /usr/include/json-c + include + /usr/include/uuid + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + + + src + /usr/include/json-c + include + /usr/include/uuid + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + + + src + /usr/include/json-c + include + /usr/include/uuid + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + + + src + /usr/include/json-c + include + /usr/include/uuid + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + + + src + /usr/include/json-c + include + /usr/include/uuid + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + + + src + /usr/include/json-c + /usr/include/uuid + include + build/src + + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + diff --git a/nbproject/private/Default.properties b/nbproject/private/Default.properties index de73bf9e..0f8ddbcc 100644 --- a/nbproject/private/Default.properties +++ b/nbproject/private/Default.properties @@ -1,8 +1 @@ -/home/fulup/Workspace/afb-daemon/src/session.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/session.c.o -c /home/fulup/Workspace/afb-daemon/src/session.c -/home/fulup/Workspace/afb-daemon/src/alsa-api.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/alsa-api.c.o -c /home/fulup/Workspace/afb-daemon/src/alsa-api.c -/home/fulup/Workspace/afb-daemon/src/main.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/main.c.o -c /home/fulup/Workspace/afb-daemon/src/main.c -/home/fulup/Workspace/afb-daemon/src/dbus-api.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/dbus-api.c.o -c /home/fulup/Workspace/afb-daemon/src/dbus-api.c -/home/fulup/Workspace/afb-daemon/src/http-svc.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/http-svc.c.o -c /home/fulup/Workspace/afb-daemon/src/http-svc.c /home/fulup/Workspace/afb-daemon/src/afbs-api.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/afbs-api.c.o -c /home/fulup/Workspace/afb-daemon/src/afbs-api.c -/home/fulup/Workspace/afb-daemon/src/rest-api.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/rest-api.c.o -c /home/fulup/Workspace/afb-daemon/src/rest-api.c -/home/fulup/Workspace/afb-daemon/src/config.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/config.c.o -c /home/fulup/Workspace/afb-daemon/src/config.c diff --git a/nbproject/private/configurations.xml b/nbproject/private/configurations.xml index 457c443c..4cc0c9f9 100644 --- a/nbproject/private/configurations.xml +++ b/nbproject/private/configurations.xml @@ -20,12 +20,6 @@ - - - - - - local-def.h proto-def.h @@ -37,6 +31,7 @@ dbus-api.c http-svc.c main.c + radio-api.c rest-api.c session.c @@ -58,12 +53,6 @@ - - - - - - @@ -95,6 +84,8 @@ + + diff --git a/src/afbs-api.c b/src/afbs-api.c index b55ebf60..f5a55a43 100644 --- a/src/afbs-api.c +++ b/src/afbs-api.c @@ -19,39 +19,98 @@ #include "local-def.h" +// Dummy sample of Client Application Context +typedef struct { + int something; + void *whateveryouwant; +} MyClientApplicationHandle; -STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { - static pingcount = 0; - json_object *response; - char query [512]; - // request all query key/value - getQueryAll (request, query, sizeof(query)); - - // check if we have some post data - if (request->post == NULL) request->post="NoData"; +// Request Creation of new context if it does not exist +PUBLIC json_object* clientContextCreate (AFB_request *request) { + json_object *jresp; + int res; + char *token; + AFB_clientCtx *client=request->client; // get client context from request + + // check we do not already have a session + if (client->handle != NULL) { + request->errcode=MHD_HTTP_FORBIDDEN; + return (jsonNewMessage(AFB_FAIL, "Token exist use refresh")); + } - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); + // request a new client context token and check result + ctxTokenCreate (request); + + // add a client handle to session + client->handle = malloc (sizeof (MyClientApplicationHandle)); + + // Send response to UI + jresp = json_object_new_object(); + json_object_object_add(jresp, "token", json_object_new_string (client->token)); + + return (jresp); +} + +// Renew an existing context +PUBLIC json_object* clientContextRefresh (AFB_request *request) { + json_object *jresp; + + // check we do not already have a session + if (request->client == NULL) return (jsonNewMessage(AFB_FAIL, "No Previous Token use Create")); + + // note: we do not need to parse the old token as clientContextRefresh doit for us + if (ctxTokenRefresh (request)) { + jresp = json_object_new_object(); + json_object_object_add(jresp, "token", json_object_new_string (request->client->token)); + } else { + request->errcode=MHD_HTTP_UNAUTHORIZED; + jresp= jsonNewMessage(AFB_FAIL, "Token Exchange Broken Refresh Refused"); + } + + return (jresp); +} + + +// Verify a context is still valid +PUBLIC json_object* clientContextCheck (AFB_request *request) { + json_object *jresp; + int isvalid; + + // check is token is valid + isvalid= ctxTokenCheck (request); + + // add an error code to respond + if (!isvalid) request->errcode=MHD_HTTP_UNAUTHORIZED; + + // prepare response for client side application + jresp = json_object_new_object(); + json_object_object_add(jresp, "isvalid", json_object_new_boolean (isvalid)); + + return (jresp); +} + +// Close and Free context +PUBLIC json_object* clientContextReset (AFB_request *request) { + json_object *jresp; + + jresp = json_object_new_object(); + json_object_object_add(jresp, "done", json_object_new_boolean (ctxTokenReset (request))); - if (verbose) fprintf(stderr, "%d: \n", pingcount); - return (response); + return (jresp); } STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample ,"Ping Service", NULL}, - {"get-all" , (AFB_apiCB)pingSample ,"Ping Application Framework", NULL}, - {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, - {0,0,0} + {"ping" , (AFB_apiCB)apiPingTest ,"Ping Rest Test Service", NULL}, + {"token-create" , (AFB_apiCB)clientContextCreate ,"Request Client Context Creation",NULL}, + {"token-refresh" , (AFB_apiCB)clientContextRefresh,"Refresh Client Context Token",NULL}, + {"token-check" , (AFB_apiCB)clientContextCheck ,"Check Client Context Token",NULL}, + {"token-reset" , (AFB_apiCB)clientContextReset ,"Close Client Context and Free resources",NULL}, + {0,0,0,0} }; -PUBLIC AFB_plugin *afsvRegister (AFB_session *session) { +PUBLIC AFB_plugin *afsvRegister () { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN; plugin->info = "Application Framework Binder Service"; diff --git a/src/alsa-api.c b/src/alsa-api.c index 01341ce0..18529d93 100644 --- a/src/alsa-api.c +++ b/src/alsa-api.c @@ -19,7 +19,7 @@ #include "local-def.h" -STATIC json_object* wrongApi (AFB_session *session, AFB_request *request, void* handle) { +STATIC json_object* wrongApi (AFB_request *request, void* handle) { int zero=0; int bug=1234; int impossible; @@ -27,13 +27,13 @@ STATIC json_object* wrongApi (AFB_session *session, AFB_request *request, void* impossible=bug/zero; } -STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { +STATIC json_object* pingSample (AFB_request *request, void* handle) { static pingcount = 0; json_object *response; char query [512]; // request all query key/value - getQueryAll (request, query, sizeof(query)); + getQueryAll (request,query, sizeof(query)); // check if we have some post data if (request->post == NULL) request->post="NoData"; @@ -52,18 +52,18 @@ STATIC struct { STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework", NULL}, - {"error" , (AFB_apiCB)wrongApi , "Ping Application Framework", NULL}, - {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode", NULL}, - {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode", NULL}, - {0,0,0} + {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework",NULL}, + {"error" , (AFB_apiCB)wrongApi , "Ping Application Framework",NULL}, + {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode",NULL}, + {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode",NULL}, + {0,0,0,0} }; -PUBLIC AFB_plugin *alsaRegister (AFB_session *session) { +PUBLIC AFB_plugin *alsaRegister () { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN; plugin->info = "Application Framework Binder Service"; - plugin->prefix = "alsa"; + plugin->prefix= "alsa"; plugin->apis = pluginApis; return (plugin); diff --git a/src/config.c b/src/config.c index e30a687b..29d7241d 100644 --- a/src/config.c +++ b/src/config.c @@ -69,13 +69,17 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { else session->config->httpdPort=cliconfig->httpdPort; // default Plugin API timeout - if (cliconfig->apiTimeout == 0) session->config->apiTimeout=0; + if (cliconfig->apiTimeout == 0) session->config->apiTimeout=DEFLT_API_TIMEOUT; else session->config->apiTimeout=cliconfig->apiTimeout; // cache timeout default one hour - if (cliconfig->cacheTimeout == 0) session->config->cacheTimeout=3600; + if (cliconfig->cacheTimeout == 0) session->config->cacheTimeout=DEFLT_CACHE_TIMEOUT; else session->config->cacheTimeout=cliconfig->cacheTimeout; + // cache timeout default one hour + if (cliconfig->cntxTimeout == 0) session->config->cntxTimeout=DEFLT_CNTX_TIMEOUT; + else session->config->cntxTimeout=cliconfig->cntxTimeout; + if (cliconfig->rootdir == NULL) { session->config->rootdir = getenv("AFBDIR"); if (session->config->rootdir == NULL) { @@ -188,6 +192,10 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { session->config->plugins = strdup (json_object_get_string (value)); } + if (!cliconfig->setuid && json_object_object_get_ex (AFBConfig, "setuid", &value)) { + session->config->setuid = strdup (json_object_get_string (value)); + } + if (!cliconfig->sessiondir && json_object_object_get_ex (AFBConfig, "sessiondir", &value)) { session->config->sessiondir = strdup (json_object_get_string (value)); } @@ -200,9 +208,6 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { session->config->httpdPort = json_object_get_int (value); } - if (!cliconfig->setuid && json_object_object_get_ex (AFBConfig, "setuid", &value)) { - session->config->setuid = json_object_get_int (value); - } if (!cliconfig->localhostOnly && json_object_object_get_ex (AFBConfig, "localhostonly", &value)) { session->config->localhostOnly = json_object_get_int (value); @@ -216,6 +221,10 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { session->config->apiTimeout = json_object_get_int (value); } + if (!cliconfig->cntxTimeout && json_object_object_get_ex (AFBConfig, "cntxtimeout", &value)) { + session->config->cntxTimeout = json_object_get_int (value); + } + // cacheTimeout is an integer but HTTPd wants it as a string snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout); session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version @@ -248,11 +257,12 @@ PUBLIC void configStoreFile (AFB_session * session) { json_object_object_add (AFBConfig, "plugins" , json_object_new_string (session->config->plugins)); json_object_object_add (AFBConfig, "sessiondir" , json_object_new_string (session->config->sessiondir)); json_object_object_add (AFBConfig, "pidfile" , json_object_new_string (session->config->pidfile)); + json_object_object_add (AFBConfig, "setuid" , json_object_new_string (session->config->setuid)); json_object_object_add (AFBConfig, "httpdPort" , json_object_new_int (session->config->httpdPort)); - json_object_object_add (AFBConfig, "setuid" , json_object_new_int (session->config->setuid)); json_object_object_add (AFBConfig, "localhostonly" , json_object_new_int (session->config->localhostOnly)); json_object_object_add (AFBConfig, "cachetimeout" , json_object_new_int (session->config->cacheTimeout)); json_object_object_add (AFBConfig, "apitimeout" , json_object_new_int (session->config->apiTimeout)); + json_object_object_add (AFBConfig, "cntxtimeout" , json_object_new_int (session->config->cntxTimeout)); err = json_object_to_file (session->config->configfile, AFBConfig); json_object_put (AFBConfig); // decrease reference count to free the json object diff --git a/src/dbus-api.c b/src/dbus-api.c index 90fa3b50..f7f64fab 100644 --- a/src/dbus-api.c +++ b/src/dbus-api.c @@ -19,7 +19,7 @@ #include "local-def.h" -STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { +STATIC json_object* pingSample (AFB_request *request) { static pingcount = 0; json_object *response; char query [512]; @@ -39,11 +39,11 @@ STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void return (response); } -STATIC json_object* pingFail (AFB_session *session, AFB_request *request, void* handle) { +STATIC json_object* pingFail (AFB_request *request) { return NULL; } -STATIC json_object* pingBug (AFB_session *session, AFB_request *request, void* handle) { +STATIC json_object* pingBug (AFB_request *request) { int a,b,c; fprintf (stderr, "Use --timeout=10 to trap error\n"); @@ -55,27 +55,43 @@ STATIC json_object* pingBug (AFB_session *session, AFB_request *request, void* h return NULL; } -STATIC struct { - void * somedata; -} handle; + +// For samples https://linuxprograms.wordpress.com/2010/05/20/json-c-libjson-tutorial/ +STATIC json_object* pingJson (AFB_session *session, AFB_request *request, void* handle) { + json_object *jresp, *embed; + + jresp = json_object_new_object(); + json_object_object_add(jresp, "myString", json_object_new_string ("Some String")); + json_object_object_add(jresp, "myInt", json_object_new_int (1234)); + + embed = json_object_new_object(); + json_object_object_add(embed, "subObjString", json_object_new_string ("Some String")); + json_object_object_add(embed, "subObjInt", json_object_new_int (5678)); + + json_object_object_add(jresp,"eobj", embed); + + return jresp; +} STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework",NULL}, - {"pingnull" , (AFB_apiCB)pingFail , "Return NULL", NULL}, - {"pingbug" , (AFB_apiCB)pingBug , "Do a Memory Violation", NULL}, - {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode", NULL}, - {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode", NULL}, + {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework"}, + {"pingnull" , (AFB_apiCB)pingFail , "Return NULL"}, + {"pingbug" , (AFB_apiCB)pingBug , "Do a Memory Violation"}, + {"pingJson" , (AFB_apiCB)pingJson , "Return a JSON object"}, + {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode"}, + {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode"}, {0,0,0} }; -PUBLIC AFB_plugin *dbusRegister (AFB_session *session) { +PUBLIC AFB_plugin *dbusRegister () { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN; plugin->info = "Application Framework Binder Service"; plugin->prefix= "dbus"; plugin->apis = pluginApis; + plugin->handle= (void*) "Any you Want"; return (plugin); }; \ No newline at end of file diff --git a/src/http-svc.c b/src/http-svc.c index e8f2c0e5..ac5cd789 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -118,7 +118,7 @@ STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, co if (-1 == (staticfile->fd = open(staticfile->path, O_RDONLY)) || (fstat (staticfile->fd, &sbuf) != 0)) { fprintf(stderr, "No Index.html in direcory [%s]\n", staticfile->path); goto abortRequest; - } + } } else if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused. close (staticfile->fd); // nothing useful to do with this file fprintf (stderr, "Fail file: [%s] is not a regular file\n", staticfile->path); @@ -149,7 +149,7 @@ STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, co if (session->magic) { mimetype= magic_descriptor(session->magic, staticfile->fd); if (mimetype != NULL) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype); - } else mimetype="Unknown"; + } else mimetype="application/unknown"; if (verbose) fprintf(stderr, "Serving: [%s] mime=%s\n", staticfile->path, mimetype); response = MHD_create_response_from_fd(sbuf.st_size, staticfile->fd); diff --git a/src/main.c b/src/main.c index c825e587..89b7023f 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,7 @@ #include #include #include +#include static sigjmp_buf exitPoint; // context save for set/longjmp @@ -63,7 +64,7 @@ static sigjmp_buf exitPoint; // context save for set/longjmp #define SET_ROOT_ALIAS 124 #define SET_CACHE_TO 130 - #define SET_cardid 131 + #define SET_USERID 131 #define SET_PID_FILE 132 #define SET_SESSION_DIR 133 #define SET_CONFIG_FILE 134 @@ -73,6 +74,7 @@ static sigjmp_buf exitPoint; // context save for set/longjmp #define SET_SMACK 140 #define SET_PLUGINS 141 #define SET_APITIMEOUT 142 + #define SET_CNTXTIMEOUT 143 #define DISPLAY_VERSION 150 #define DISPLAY_HELP 151 @@ -92,10 +94,12 @@ static AFB_options cliOptions [] = { {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /opa]"}, {SET_ROOT_API ,1,"rootapi" , "HTML Root API URL [default /api]"}, {SET_ROOT_ALIAS ,1,"alias" , "Muliple url map outside of rootdir [eg: --alias=/icons:/usr/share/icons]"}, + {SET_APITIMEOUT ,1,"apitimeout" , "Plugin API timeout in seconds [default 10]"}, - + {SET_CNTXTIMEOUT ,1,"cntxtimeout" , "Client Session Context Timeout [default 900]"}, {SET_CACHE_TO ,1,"cache-eol" , "Client cache end of live [default 3600s]"}, - {SET_cardid ,1,"setuid" , "Change user id [default don't change]"}, + + {SET_USERID ,1,"setuid" , "Change user id [default don't change]"}, {SET_PID_FILE ,1,"pidfile" , "PID file path [default none]"}, {SET_SESSION_DIR ,1,"sessiondir" , "Sessions file path [default rootdir/sessions]"}, {SET_CONFIG_FILE ,1,"config" , "Config Filename [default rootdir/sessions/configs/default.AFB]"}, @@ -302,6 +306,11 @@ int main(int argc, char *argv[]) { if (!sscanf (optarg, "%d", &cliconfig.apiTimeout)) goto notAnInteger; break; + case SET_CNTXTIMEOUT: + if (optarg == 0) goto needValueForOption; + if (!sscanf (optarg, "%d", &cliconfig.cntxTimeout)) goto notAnInteger; + break; + case SET_ROOT_DIR: if (optarg == 0) goto needValueForOption; cliconfig.rootdir = optarg; @@ -376,9 +385,9 @@ int main(int argc, char *argv[]) { session->configsave = 1; break; - case SET_cardid: + case SET_USERID: if (optarg == 0) goto needValueForOption; - if (!sscanf (optarg, "%d", &cliconfig.setuid)) goto notAnInteger; + if (!sscanf (optarg, "%s", &cliconfig.setuid)) goto notAnInteger; break; case SET_FAKE_MOD: @@ -485,13 +494,17 @@ int main(int argc, char *argv[]) { if (session->config->setuid) { int err; - - err = setuid(session->config->setuid); - if (err) fprintf (stderr, "Fail to change program cardid error=%s", strerror(err)); + struct passwd *passwd; + passwd=getpwnam(session->config->setuid); + + if (passwd == NULL) goto errorSetuid; + + err = setuid(passwd->pw_uid); + if (err) goto errorSetuid; } // let's not take the risk to run as ROOT - if (getuid() == 0) status=setuid(65534); // run as nobody + if (getuid() == 0) goto errorNoRoot; // check session dir and create if it does not exist if (sessionCheckdir (session) != AFB_SUCCESS) goto errSessiondir; @@ -574,44 +587,52 @@ normalExit: exit (0); // ------------- Fatal ERROR display error and quit ------------- +errorSetuid: + fprintf (stderr,"\nERR:AFB-daemon Failed to change UID to username=[%s]\n\n", session->config->setuid); + exit (-1); + +errorNoRoot: + fprintf (stderr,"\nERR:AFB-daemon Not allow to run as root [use --seteuid=username option]\n\n"); + exit (-1); + errorPidFile: - fprintf (stderr,"\nERR:main Failled to write pid file [%s]\n\n", session->config->pidfile); + fprintf (stderr,"\nERR:AFB-daemon Failed to write pid file [%s]\n\n", session->config->pidfile); exit (-1); errorFork: - fprintf (stderr,"\nERR:main Failled to fork son process\n\n"); + fprintf (stderr,"\nERR:AFB-daemon Failed to fork son process\n\n"); exit (-1); needValueForOption: - fprintf (stderr,"\nERR:main option [--%s] need a value i.e. --%s=xxx\n\n" + fprintf (stderr,"\nERR:AFB-daemon option [--%s] need a value i.e. --%s=xxx\n\n" ,gnuOptions[optionIndex].name, gnuOptions[optionIndex].name); exit (-1); noValueForOption: - fprintf (stderr,"\nERR:main option [--%s] don't take value\n\n" + fprintf (stderr,"\nERR:AFB-daemon option [--%s] don't take value\n\n" ,gnuOptions[optionIndex].name); exit (-1); notAnInteger: - fprintf (stderr,"\nERR:main option [--%s] requirer an interger i.e. --%s=9\n\n" + fprintf (stderr,"\nERR:AFB-daemon option [--%s] requirer an interger i.e. --%s=9\n\n" ,gnuOptions[optionIndex].name, gnuOptions[optionIndex].name); exit (-1); exitOnSignal: - fprintf (stderr,"\n%s INF:main pid=%d received exit signal (Hopefully crtl-C or --kill-previous !!!)\n\n" + fprintf (stderr,"\n%s INF:AFB-daemon pid=%d received exit signal (Hopefully crtl-C or --kill-previous !!!)\n\n" ,configTime(), getpid()); exit (-1); errConsole: - fprintf (stderr,"\nERR:cannot open /dev/console (use --foreground)\n\n"); + fprintf (stderr,"\nERR:AFB-daemon cannot open /dev/console (use --foreground)\n\n"); exit (-1); errSessiondir: - fprintf (stderr,"\nERR:cannot read/write session dir\n\n"); + fprintf (stderr,"\nERR:AFB-daemon cannot read/write session dir\n\n"); exit (-1); errSoundCard: - fprintf (stderr,"\nERR:fail to probe sound cards\n\n"); + fprintf (stderr,"\nERR:AFB-daemon fail to probe sound cards\n\n"); exit (-1); exitInitLoop: @@ -619,5 +640,5 @@ exitInitLoop: if (session->background && session->config->pidfile != NULL) unlink (session->config->pidfile); exit (-1); -}; /* END main() */ +}; /* END AFB-daemon() */ diff --git a/src/rest-api.c b/src/rest-api.c index b06d70ad..4f06066e 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -33,7 +33,28 @@ typedef struct { size_t len; } queryHandleT; -static json_object *afbJsonType; +static json_object *afbJsonType; + + +// Sample Generic Ping Debug API +PUBLIC json_object* apiPingTest(AFB_request *request) { + static pingcount = 0; + json_object *response; + char query [512]; + int len; + + // request all query key/value + len = getQueryAll (request, query, sizeof(query)); + if (len == 0) strcpy (query,"NoSearchQueryList"); + + // check if we have some post data + if (request->post == NULL) request->post="NoData"; + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d CtxtId=%d Loa=%d query={%s} PostData: \'%s\' " + , pingcount++, request->client->cid, request->loa, query, request->post); + return (response); +} // Helper to retrieve argument from connection PUBLIC const char* getQueryValue(AFB_request * request, char *name) { @@ -52,7 +73,7 @@ STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, co // 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; @@ -61,29 +82,7 @@ PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) { return (len); } - -// Sample Generic Ping Debug API -PUBLIC json_object* apiPingTest(AFB_session *session, AFB_request *request, void* handle) { - static pingcount = 0; - json_object *response; - char query [512]; - int len; - - // request all query key/value - len = getQueryAll (request, query, sizeof(query)); - if (len == 0) strcpy (query,"NoSearchQueryList"); - - // check if we have some post data - if (request->post == NULL) request->post="NoData"; - - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); - return (response); -} - - // Because of POST call multiple time requestApi we need to free POST handle here - STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { AFB_HttpPost *posthandle = *con_cls; @@ -96,7 +95,7 @@ STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_ } // Check of apiurl is declare in this plugin and call it -STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_request *request) { +STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request) { json_object *jresp, *jcall; int idx, status, sig; int signals[]= {SIGALRM, SIGSEGV, SIGFPE, 0}; @@ -106,7 +105,8 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_req +---------------------------------------------------------------- */ void pluginError (int signum) { sigset_t sigset; - + AFB_clientCtx *context; + // unlock signal to allow a new signal to come sigemptyset (&sigset); sigaddset (&sigset, signum); @@ -138,40 +138,43 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_req } else { // If timeout protection==0 we are in debug and we do not apply signal protection - if (session->config->apiTimeout > 0) { + if (request->config->apiTimeout > 0) { for (sig=0; signals[sig] != 0; sig++) { if (signal (signals[sig], pluginError) == SIG_ERR) { - fprintf (stderr, "%s ERR: main no Signal/timeout handler installed.", configTime()); - return AFB_FAIL; + request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + fprintf (stderr, "%s ERR: main no Signal/timeout handler installed.", configTime()); + return AFB_FAIL; } } - // Trigger a timer to protect from inacceptable long time execution - alarm (session->config->apiTimeout); + // Trigger a timer to protect from unacceptable long time execution + alarm (request->config->apiTimeout); } - - // Effectively call the API - jresp = plugin->apis[idx].callback(session, request, plugin->apis[idx].handle); + + // add client context to request + ctxClientGet(request); + + // Effectively call the API with a subset of the context + jresp = plugin->apis[idx].callback(request); // API should return NULL of a valid Json Object if (jresp == NULL) { - json_object_object_add(jcall, "status", json_object_new_string ("fail")); + json_object_object_add(jcall, "status", json_object_new_string ("null")); json_object_object_add(request->jresp, "request", jcall); + request->errcode = MHD_HTTP_NO_RESPONSE; } else { - json_object_object_add(jcall, "status", json_object_new_string ("success")); + json_object_object_add(jcall, "status", json_object_new_string ("processed")); json_object_object_add(request->jresp, "request", jcall); json_object_object_add(request->jresp, "response", jresp); - } // cancel timeout and plugin signal handle before next call - if (session->config->apiTimeout > 0) { + if (request->config->apiTimeout > 0) { alarm (0); for (sig=0; signals[sig] != 0; sig++) { signal (signals[sig], SIG_DFL); } } - } - + } return (AFB_DONE); } } @@ -180,31 +183,31 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_req // process rest API query - PUBLIC 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) { static int postcount = 0; // static counter to debug POST protocol - char *baseurl, *baseapi, *urlcpy1, *urlcpy2, *query; + char *baseurl, *baseapi, *urlcpy1, *urlcpy2, *query, *token, *uuid; json_object *errMessage; AFB_error status; struct MHD_Response *webResponse; const char *serialized, parsedurl; AFB_request request; AFB_HttpPost *posthandle = *con_cls; + AFB_clientCtx clientCtx; int idx, ret; // Extract plugin urlpath from request and make two copy because strsep overload copy urlcpy1 = urlcpy2 = strdup(url); baseurl = strsep(&urlcpy2, "/"); if (baseurl == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s", url); + errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); goto ExitOnError; } baseapi = strsep(&urlcpy2, "/"); if (baseapi == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s", url); + errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); goto ExitOnError; } @@ -271,11 +274,11 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co } else { request.post = NULL; }; - - + // build request structure memset(&request, 0, sizeof (request)); request.connection = connection; + request.config = session->config; request.url = url; request.plugin = baseurl; request.api = baseapi; @@ -288,33 +291,44 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co // Search for a plugin with this urlpath for (idx = 0; session->plugins[idx] != NULL; idx++) { if (!strcmp(session->plugins[idx]->prefix, baseurl)) { - status =callPluginApi(session->plugins[idx], session, &request); - free(urlcpy1); + status =callPluginApi(session->plugins[idx], &request); break; } } // No plugin was found if (session->plugins[idx] == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl); - free(urlcpy1); + errMessage = jsonNewMessage(AFB_FATAL, "No Plugin=[%s]", request.plugin); goto ExitOnError; } // plugin callback did not return a valid Json Object if (status != AFB_DONE) { - errMessage = jsonNewMessage(AFB_FATAL, "No Plugin/API for %s/%s", baseurl, baseapi); + errMessage = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s]", request.api, request.plugin); goto ExitOnError; } serialized = json_object_to_json_string(request.jresp); webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY); - - ret = MHD_queue_response(connection, MHD_HTTP_OK, webResponse); + free(urlcpy1); + + // client did not pass token on URI let's use cookies + if (!request.restfull) { + char cookie[64]; + snprintf (cookie, sizeof (cookie), "%s=%s", COOKIE_NAME, request.client->uuid); + MHD_add_response_header (webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); + // if(verbose) fprintf(stderr,"Cookie: [%s]\n", cookie); + } + + // if requested add an error status + if (request.errcode != 0) ret=MHD_queue_response (connection, request.errcode, webResponse); + else ret = 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 return ret; ExitOnError: + free(urlcpy1); serialized = json_object_to_json_string(errMessage); webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY); ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse); @@ -341,6 +355,10 @@ STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { } if (verbose) fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info); + + // register private to plugin global context [cid=0] + plugins[idx]->ctxGlobal= malloc (sizeof (AFB_clientCtx)); + plugins[idx]->ctxGlobal->cid=0; // Prebuild plugin jtype to boost API response plugins[idx]->jtype = json_object_new_string(plugins[idx]->prefix); @@ -370,14 +388,14 @@ void initPlugins(AFB_session *session) { afbJsonType = json_object_new_string (AFB_MSG_JTYPE); int i = 0; - plugins[i] = afsvRegister(session), + plugins[i++] = afsvRegister(session), plugins[i++] = dbusRegister(session), plugins[i++] = alsaRegister(session), #ifdef HAVE_RADIO_PLUGIN plugins[i++] = radioRegister(session), #endif plugins[i++] = NULL; - + // complete plugins and save them within current sessions session->plugins = RegisterPlugins(plugins); } diff --git a/src/session.c b/src/session.c index aade418e..cd5b09f7 100644 --- a/src/session.c +++ b/src/session.c @@ -14,6 +14,10 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Reference: + * https://github.com/json-c/json-c/blob/master/linkhash.c + * https://github.com/json-c/json-c/blob/master/linkhash.h */ @@ -28,10 +32,12 @@ #define AFB_SESSION_JLIST "AFB_sessions" #define AFB_SESSION_JINFO "AFB_infos" + #define AFB_CURRENT_SESSION "active-session" // file link name within sndcard dir #define AFB_DEFAULT_SESSION "current-session" // should be in sync with UI +static struct lh_table *clientCtxs=NULL; // let's use JsonObject Hashtable to Store Sessions // verify we can read/write in session dir @@ -300,3 +306,200 @@ OnErrorExit: json_object_put (jsonSession); return response; } + + +// Function to handle Cookies and Client session context it relies on json low level +// linked list functionalities https://github.com/json-c/json-c/blob/master/linkhash.c + +// Hash client UUID before storing in table +STATIC unsigned long ctxUuidHashCB (const void *k1) { + unsigned long hash; + + AFB_clientCtx *ctx = (AFB_clientCtx*) k1; + hash = lh_char_hash(ctx->uuid); + return (hash); +} + +// Compare client UUIDs within table +STATIC int ctxUuidCompCB (const void *k1, const void *k2) { + int res; + AFB_clientCtx *ctx1 = (AFB_clientCtx*) k1; + AFB_clientCtx *ctx2 = (AFB_clientCtx*) k2; + + res = lh_char_equal(ctx1->uuid, ctx2->uuid); + return (res); +} + +// Free context [XXXX Should be protected again memory abort XXXX] +STATIC void ctxUuidFreeCB (struct lh_entry *entry) { + AFB_clientCtx *ctx = (AFB_clientCtx*) entry->v; + + // If application add a handle let's free it now + if (ctx->handle != NULL) { + + // Free client handle with a standard Free function, with app callback or ignore it + if (ctx->freeHandleCB == NULL) free (ctx->handle); + else if (ctx->freeHandleCB != (void*)-1) ctx->freeHandleCB(ctx->handle); + } + free ((void*)entry->v); +} + +// Create a new store in RAM, not that is too small it will be automatically extended +STATIC struct lh_table *ctxStoreCreate (int nbSession) { + lh_table *table; + + // function will exit process in case of error !!! + table=lh_table_new (nbSession, "CtxClient", ctxUuidFreeCB, ctxUuidHashCB, ctxUuidCompCB); + return (table); +} + +// Check if context timeout or not +STATIC int ctxStoreToOld (const void *k1, int timeout) { + int res; + AFB_clientCtx *ctx = (AFB_clientCtx*) k1; + + res = ((ctx->timeStamp + timeout) < time(NULL)); + return (res); +} + +// Loop on every entry and remove old context sessions +PUBLIC int ctxStoreGarbage (struct lh_table *lht, const int timeout) { + struct lh_entry *c; + + // Loop on every entry within table + for(c = lht->head; c != NULL; c = c->next) { + if(lht->free_fn) { + if(c->k == LH_EMPTY) return lht->count; + if(c->k != LH_FREED && ctxStoreToOld(c->v, timeout)) lh_table_delete_entry (lht, c); + } + } + + // return current size after cleanup + return (lht->count); +} + +// This function will return exiting client context or newly created client context +PUBLIC int ctxClientGet (AFB_request *request) { + static int cid=0; + AFB_clientCtx *clientCtx=NULL; + const char *uuid; + uuid_t newuuid; + int ret; + + // if client session store is null create it + if (clientCtxs == NULL) { + clientCtxs= ctxStoreCreate(CTX_NBCLIENTS); + } + + // Check if client as a context or not inside the URL + uuid = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, "uuid"); + + // if UUID in query we're restfull with no cookies otherwise check for cookie + if (uuid != NULL) request->restfull = TRUE; + else { + request->restfull = FALSE; + uuid = MHD_lookup_connection_value (request->connection, MHD_COOKIE_KIND, COOKIE_NAME); + }; + + + if (uuid != NULL) { + // search if client context exist and it not timeout let's use it + if ((lh_table_lookup_ex (clientCtxs, uuid, (void**) &clientCtx)) + && ! ctxStoreToOld (clientCtx, request->config->cntxTimeout)) { + request->client=clientCtx; + if (verbose) fprintf (stderr, "ctxClientGet Old uuid=[%s] token=[%s] timestamp=%d\n" + ,request->client->uuid, request->client->token, request->client->timeStamp); + return; + } + } + + + // 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 + uuid_generate(newuuid); // create a new UUID + uuid_unparse_lower(newuuid, clientCtx->uuid); + clientCtx->cid=cid++; + + // if table is full at 50% let's clean it up + if(clientCtxs->count > (clientCtxs->size*0.5)) ctxStoreGarbage(clientCtxs, request->config->cntxTimeout); + + // finally add uuid into hashtable + ret= lh_table_insert (clientCtxs, (void*)clientCtx->uuid, clientCtx); + + if (verbose) fprintf (stderr, "ctxClientGet New uuid=[%s] token=[%s] timestamp=%d\n", clientCtx->uuid, clientCtx->token, clientCtx->timeStamp); + + request->client = clientCtx; + return (ret); +} + +// Sample Generic Ping Debug API +PUBLIC AFB_error ctxTokenCheck (AFB_request *request) { + const char *token; + + // this time have to extract token from query list + token = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, "token"); + + // if not token is providing we refuse the exchange + if ((token == NULL) || (request->client->token == NULL)) return (AFB_FALSE); + + // compare current token with previous one + if ((0 == strcmp (token, request->client->token)) && (!ctxStoreToOld (request->client, request->config->cntxTimeout))) { + return (AFB_TRUE); + } + + // Token is not valid let move level of assurance to zero and free attached client handle + return (AFB_FALSE); +} + +// Free Client Session Context +PUBLIC int ctxTokenReset (AFB_request *request) { + struct lh_entry* entry; + int ret; + + entry = lh_table_lookup_entry (clientCtxs, request->client->uuid); + if (entry == NULL) return FALSE; + + lh_table_delete_entry (clientCtxs, entry); + + return (TRUE); +} + +// generate a new token +PUBLIC char* ctxTokenCreate (AFB_request *request) { + int oldTnkValid; + const char *ornew; + uuid_t newuuid; + + // create a UUID as token value + uuid_generate(newuuid); + uuid_unparse_lower(newuuid, request->client->token); + + // keep track of time for session timeout and further clean up + request->client->timeStamp=time(NULL); + + // Token is also store in context but it might be convenient for plugin to access it directly + return (request->client->token); +} + + +// generate a new token and update client context +PUBLIC char* ctxTokenRefresh (AFB_request *request) { + int oldTnkValid; + const char *oldornew; + uuid_t newuuid; + + // Check if the old token is valid + oldTnkValid= ctxTokenCheck (request); + + // if token is not valid let check for query argument "oldornew" + if (!oldTnkValid) { + oldornew = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, "oldornew"); + if (oldornew != NULL) oldTnkValid= TRUE; + } + + // No existing token and no request to create one + if (oldTnkValid != TRUE) return NULL; + + return (ctxTokenCreate (request)); +} + -- 2.16.6