summaryrefslogtreecommitdiffstats
path: root/src
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 /src
parent335eeec7aaf944d66cac87b5bb3f64f8fc7e385e (diff)
Added Session Management
Diffstat (limited to 'src')
-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
8 files changed, 454 insertions, 127 deletions
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));
+}
+