summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFulup Ar Foll <fulup@iot.bzh>2015-12-12 03:10:34 +0100
committerFulup Ar Foll <fulup@iot.bzh>2015-12-12 03:10:34 +0100
commite7c246a1b0d30b8156c7033061a61ecb5d2bdfc8 (patch)
treeeedbde29dfd073934c6b985d6cb213621708cd51
parent335eeec7aaf944d66cac87b5bb3f64f8fc7e385e (diff)
Added Session Management
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt4
-rw-r--r--include/local-def.h91
-rw-r--r--include/proto-def.h16
-rw-r--r--nbproject/configurations.xml120
-rw-r--r--nbproject/private/Default.properties7
-rw-r--r--nbproject/private/configurations.xml15
-rw-r--r--src/afbs-api.c105
-rw-r--r--src/alsa-api.c20
-rw-r--r--src/config.c22
-rw-r--r--src/dbus-api.c40
-rw-r--r--src/http-svc.c4
-rw-r--r--src/main.c59
-rw-r--r--src/rest-api.c128
-rw-r--r--src/session.c203
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 <magic.h>
#include <setjmp.h>
#include <signal.h>
+#include <uuid/uuid.h>
+
@@ -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 "<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"
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 @@
</toolsSet>
<flagsDictionary>
<element flagsID="0" commonFlags="-fPIE"/>
+ <element flagsID="1" commonFlags="-mtune=generic -march=x86-64 -fPIE"/>
</flagsDictionary>
<codeAssistance>
</codeAssistance>
@@ -56,13 +57,6 @@
<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>
@@ -72,34 +66,134 @@
</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>
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 @@
</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>
@@ -58,12 +53,6 @@
</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>
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 <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() */
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 <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
*/
@@ -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));
+}
+