Added Session Management
authorFulup Ar Foll <fulup@iot.bzh>
Sat, 12 Dec 2015 02:10:34 +0000 (03:10 +0100)
committerFulup Ar Foll <fulup@iot.bzh>
Sat, 12 Dec 2015 02:10:34 +0000 (03:10 +0100)
15 files changed:
.gitignore
CMakeLists.txt
include/local-def.h
include/proto-def.h
nbproject/configurations.xml
nbproject/private/Default.properties
nbproject/private/configurations.xml
src/afbs-api.c
src/alsa-api.c
src/config.c
src/dbus-api.c
src/http-svc.c
src/main.c
src/rest-api.c
src/session.c

index 3bd40a1..f49e160 100644 (file)
@@ -7,3 +7,4 @@ dist/**
 .dep.inc
 CMakeFiles/
 CMakeCache.txt
+nbproject/private/*.log
index b297cbf..4e9314d 100644 (file)
@@ -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)
index d8010c1..df66a09 100644 (file)
@@ -39,6 +39,8 @@
 #include <magic.h>
 #include <setjmp.h>
 #include <signal.h>
+#include <uuid/uuid.h>
+
 
 
 
 // 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 "<html><head><title>Application Framework Binder</title></head><body>Application Framework </body></html>"
 #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"
index f5b9f79..2583452 100644 (file)
@@ -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
index 3d743b1..4b82178 100644 (file)
@@ -47,6 +47,7 @@
       </toolsSet>
       <flagsDictionary>
         <element flagsID="0" commonFlags="-fPIE"/>
+        <element flagsID="1" commonFlags="-mtune=generic -march=x86-64 -fPIE"/>
       </flagsDictionary>
       <codeAssistance>
       </codeAssistance>
           <buildCommand>${MAKE} -f Makefile</buildCommand>
           <cleanCommand>${MAKE} -f Makefile clean</cleanCommand>
           <executablePath>build/src/afb-daemon</executablePath>
-          <cTool>
-            <incDir>
-              <pElem>include</pElem>
-              <pElem>/usr/include/json-c</pElem>
-              <pElem>build/src</pElem>
-            </incDir>
-          </cTool>
         </makeTool>
         <preBuild>
           <preBuildCommandWorkingDir>build</preBuildCommandWorkingDir>
       </makefileType>
       <item path="src/afbs-api.c" ex="false" tool="0" flavor2="2">
         <cTool flags="0">
+          <incDir>
+            <pElem>include</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
         </cTool>
       </item>
       <item path="src/alsa-api.c" ex="false" tool="0" flavor2="2">
         <cTool flags="0">
+          <incDir>
+            <pElem>include</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
         </cTool>
       </item>
       <item path="src/config.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
       <item path="src/dbus-api.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
       <item path="src/http-svc.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
       <item path="src/main.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
       <item path="src/rest-api.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
       <item path="src/session.c" ex="false" tool="0" flavor2="2">
-        <cTool flags="0">
+        <cTool flags="1">
+          <incDir>
+            <pElem>src</pElem>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>/usr/include/uuid</pElem>
+            <pElem>include</pElem>
+            <pElem>build/src</pElem>
+          </incDir>
+          <preprocessorList>
+            <Elem>__PIC__=2</Elem>
+            <Elem>__PIE__=2</Elem>
+            <Elem>__REGISTER_PREFIX__=</Elem>
+            <Elem>__USER_LABEL_PREFIX__=</Elem>
+            <Elem>__pic__=2</Elem>
+            <Elem>__pie__=2</Elem>
+          </preprocessorList>
         </cTool>
       </item>
     </conf>
index de73bf9..0f8ddbc 100644 (file)
@@ -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
index 457c443..4cc0c9f 100644 (file)
           </df>
         </df>
       </df>
-      <df name="dist">
-        <df name="Debug">
-          <df name="GNU-Linux">
-          </df>
-        </df>
-      </df>
       <df name="include">
         <in>local-def.h</in>
         <in>proto-def.h</in>
@@ -37,6 +31,7 @@
         <in>dbus-api.c</in>
         <in>http-svc.c</in>
         <in>main.c</in>
+        <in>radio-api.c</in>
         <in>rest-api.c</in>
         <in>session.c</in>
       </df>
           </df>
         </df>
       </df>
-      <df name="dist">
-        <df name="Debug">
-          <df name="GNU-Linux">
-          </df>
-        </df>
-      </df>
       <df name="include">
       </df>
       <df name="src">
@@ -95,6 +84,8 @@
         <gdb_interceptlist>
           <gdbinterceptoptions gdb_all="false" gdb_unhandled="true" gdb_unexpected="true"/>
         </gdb_interceptlist>
+        <gdb_signals>
+        </gdb_signals>
         <gdb_options>
           <DebugOptions>
           </DebugOptions>
index b55ebf6..f5a55a4 100644 (file)
 
 #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";
index 01341ce..18529d9 100644 (file)
@@ -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);
index e30a687..29d7241 100644 (file)
@@ -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
index 90fa3b5..f7f64fa 100644 (file)
@@ -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
index e8f2c0e..ac5cd78 100644 (file)
@@ -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);
index c825e58..89b7023 100644 (file)
@@ -29,6 +29,7 @@
 #include <setjmp.h>
 #include <signal.h>
 #include <getopt.h>
+#include <pwd.h>
 
 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() */
 
index b06d70a..4f06066 100644 (file)
@@ -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);
 }
index aade418..cd5b09f 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Reference: 
+ * https://github.com/json-c/json-c/blob/master/linkhash.c
+ * https://github.com/json-c/json-c/blob/master/linkhash.h
  */
 
 
 #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));
+}
+