From c2bda43c447016a20ed061078b0dff3e64d09940 Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Mon, 7 Dec 2015 01:39:27 +0100 Subject: [PATCH] Almost working --- include/local-def.h | 16 ++-- include/proto-def.h | 23 +++-- nbproject/Makefile-Debug.mk | 2 +- nbproject/configurations.xml | 1 + src/afbs-api.c | 28 +++--- src/alsa-api.c | 18 ++-- src/config.c | 59 +++++++------ src/dbus-api.c | 15 ++-- src/http-svc.c | 197 ++++++++++++++++++++++++++----------------- src/main.c | 4 +- src/rest-api.c | 61 +++++++++----- src/session.c | 2 +- 12 files changed, 252 insertions(+), 174 deletions(-) diff --git a/include/local-def.h b/include/local-def.h index f69ddcbe..3f0a26a0 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -17,7 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: $ */ #define _GNU_SOURCE @@ -35,7 +34,7 @@ #include #include #include - +#include #define AJQ_VERSION "0.1" @@ -54,7 +53,8 @@ typedef int BOOL; #define FAILED -1 // 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_ERROR; +typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS} AFB_error; + extern char *ERROR_LABEL[]; #define ERROR_LABEL_DEF {"false", "true","fatal", "fail", "warning", "empty", "success"} @@ -70,7 +70,7 @@ typedef struct { int level; char* label; json_object *json; -} AFB_ErrorT; +} AFB_errorT; // Post handler typedef struct { @@ -79,6 +79,11 @@ typedef struct { int uid; } AFB_HttpPost; +typedef struct { + char path[512]; + int fd; +} AFB_staticfile; + // some usefull static object initialized when entering listen loop. extern int verbose; @@ -112,7 +117,6 @@ typedef struct { char *configfile; // where to store configuration on gateway exit uid_t setuid; int cacheTimeout; - AFB_redirect_msg html5; // html5 redirect message } AFB_config; // Command line structure hold cli --command + help text @@ -137,6 +141,7 @@ typedef struct { AFB_type type; char *info; char *prefix; + size_t prefixlen; json_object *jtype; AFB_restapi *apis; } AFB_plugin; @@ -154,6 +159,7 @@ typedef struct { int fakemod; // respond to GET/POST request without interacting with sndboard int forceexit; // when autoconfig from script force exit before starting server AFB_plugin **plugins; // pointer to REST/API plugins + magic_t magic; // Mime type file magic lib } AFB_session; diff --git a/include/proto-def.h b/include/proto-def.h index 934bf30f..d1b0c3fb 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -23,31 +23,38 @@ // Rest-api PUBLIC json_object* pingSample (AFB_plugin *plugin, AFB_session *session, AFB_request *post); PUBLIC const char* getQueryValue (AFB_request * request, char *name); -PUBLIC AFB_plugin *afsvRegister (AFB_session *session); PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char *method, const char* url); +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); + + // Session handling -PUBLIC AFB_ERROR sessionCheckdir (AFB_session *session); +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); // Httpd server -PUBLIC AFB_ERROR httpdStart (AFB_session *session); -PUBLIC AFB_ERROR httpdLoop (AFB_session *session); +PUBLIC AFB_error httpdStart (AFB_session *session); +PUBLIC AFB_error httpdLoop (AFB_session *session); PUBLIC void httpdStop (AFB_session *session); // config management PUBLIC char *configTime (void); PUBLIC AFB_session *configInit (void); -PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...); -PUBLIC json_object *jsonNewStatus (AFB_ERROR level); +PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...); +PUBLIC json_object *jsonNewStatus (AFB_error level); PUBLIC json_object *jsonNewjtype (void); -PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...); +PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...); PUBLIC void jsonDumpObject (json_object * jObject); -PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig); +PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig); PUBLIC void configStoreFile (AFB_session * session); diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index fb778c72..1866ff18 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -59,7 +59,7 @@ FFLAGS= ASFLAGS= # Link Libraries and Options -LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` +LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` -lmagic # Build Targets .build-conf: ${BUILD_SUBPROJECTS} diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index f30d8c0b..c0793702 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -52,6 +52,7 @@ `pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` + magic diff --git a/src/afbs-api.c b/src/afbs-api.c index 5b7d4896..40e89d02 100644 --- a/src/afbs-api.c +++ b/src/afbs-api.c @@ -36,23 +36,23 @@ STATIC json_object* pingAfbs (AFB_session *session, AFB_request *request) { STATIC AFB_restapi pluginApis[]= { - {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"}, - {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, - {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ping" , (AFB_apiCB)pingSample ,"Ping Service"}, + {"get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, + {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, {0,0,0} }; PUBLIC AFB_plugin *afsvRegister (AFB_session *session) { - AFB_plugin plugin; - plugin.type = AFB_PLUGIN; - plugin.info = "Application Framework Binder Service"; - plugin.prefix= "afbs"; // url base - plugin.apis = pluginApis; + AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); + plugin->type = AFB_PLUGIN; + plugin->info = "Application Framework Binder Service"; + plugin->prefix= "afbs"; // url base + plugin->apis = pluginApis; - return (&plugin); + return (plugin); }; \ No newline at end of file diff --git a/src/alsa-api.c b/src/alsa-api.c index dca372ab..ab7dced3 100644 --- a/src/alsa-api.c +++ b/src/alsa-api.c @@ -29,20 +29,20 @@ STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct M STATIC AFB_restapi pluginApis[]= { - {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"}, - {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, - {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ping" , (AFB_apiCB)pingSample ,"Ping Service"}, + {"get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, + {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, {0,0,0} }; PUBLIC AFB_plugin *alsaRegister (AFB_session *session) { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); - + plugin->type = AFB_PLUGIN; plugin->info = "Application Framework Binder Service"; plugin->prefix = "alsa"; plugin->apis = pluginApis; diff --git a/src/config.c b/src/config.c index 893b3a5f..6691ca03 100644 --- a/src/config.c +++ b/src/config.c @@ -33,8 +33,8 @@ PUBLIC char *ERROR_LABEL[]=ERROR_LABEL_DEF; PUBLIC int verbose; -STATIC AFB_ErrorT AFB_Error [AFB_SUCCESS+1]; -STATIC json_object *AFBJsonType; +STATIC AFB_errorT AFBerr [AFB_SUCCESS+1]; +STATIC json_object *jTypeStatic; /* ------------------------------------------------------------------------------ * Get localtime and return in a string @@ -56,15 +56,11 @@ PUBLIC char * configTime (void) { } // load config from disk and merge with CLI option -PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) { +PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { static char cacheTimeout [10]; int fd; json_object * AFBConfig, *value; - // fix config redirect message - session->config->html5.msg = "Angular/HTML5 redirect"; - session->config->html5.len = strlen(session->config->html5.msg); - // default HTTP port if (cliconfig->httpdPort == 0) session->config->httpdPort=1234; else session->config->httpdPort=cliconfig->httpdPort; @@ -88,16 +84,27 @@ PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) { // if no Angular/HTML5 rootbase let's try '/' as default if (cliconfig->rootbase == NULL) { - session->config->rootbase = "/"; + session->config->rootbase = "/opa"; } else { - session->config->console= cliconfig->console; + session->config->rootbase= cliconfig->rootbase; } - // if no rootapi use '/api' - if (cliconfig->rootbase == NULL) { - session->config->rootbase = "/api"; + if (cliconfig->rootapi == NULL) { + session->config->rootapi = "/api"; } else { - session->config->console= cliconfig->console; + session->config->rootapi= cliconfig->rootapi; + } + + if (cliconfig->smack == NULL) { + session->config->smack = "demo"; + } else { + session->config->smack= cliconfig->smack; + } + + if (cliconfig->smack == NULL) { + session->config->plugins = "all"; + } else { + session->config->plugins= cliconfig->plugins; } @@ -204,6 +211,8 @@ PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) { session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version json_object_put (AFBConfig); // decrease reference count to free the json object + + return AFB_SUCCESS; } @@ -258,29 +267,31 @@ PUBLIC AFB_session *configInit () { // stack config handle into session session->config = config; - AFBJsonType = json_object_new_string ("AFB_message"); + jTypeStatic = json_object_new_string ("AFB_message"); // initialise JSON constant messages and increase reference count to make them permanent verbosesav = verbose; verbose = 0; // run initialisation in silent mode - for (idx = 0; idx <= AFB_SUCCESS; idx++) { - AFB_Error[idx].level = idx; - AFB_Error[idx].label = ERROR_LABEL [idx]; - AFB_Error[idx].json = jsonNewMessage (idx, NULL); + AFBerr[idx].level = idx; + AFBerr[idx].label = ERROR_LABEL [idx]; + AFBerr[idx].json = jsonNewMessage (idx, NULL); } verbose = verbosesav; + // Load Plugins + initPlugins (session); + return (session); } // get JSON object from error level and increase its reference count -PUBLIC json_object *jsonNewStatus (AFB_ERROR level) { +PUBLIC json_object *jsonNewStatus (AFB_error level) { - json_object *target = AFB_Error[level].json; + json_object *target = AFBerr[level].json; json_object_get (target); return (target); @@ -288,12 +299,12 @@ PUBLIC json_object *jsonNewStatus (AFB_ERROR level) { // get AFB object type with adequate usage count PUBLIC json_object *jsonNewjtype (void) { - json_object_get (AFBJsonType); // increase reference count - return (AFBJsonType); + json_object_get (jTypeStatic); // increase reference count + return (jTypeStatic); } // build an ERROR message and return it as a valid json object -PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...) { +PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...) { static int count = 0; json_object * AFBResponse; va_list args; @@ -313,7 +324,7 @@ PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...) { json_object_object_add (AFBResponse, "info" , json_object_new_string (message)); } if (verbose) { - fprintf (stderr, "AFB:%-6s [%3d]: ", AFB_Error [level].label, count++); + fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++); if (format != NULL) { fprintf (stderr, "%s", message); } else { diff --git a/src/dbus-api.c b/src/dbus-api.c index febe87d9..9d34e436 100644 --- a/src/dbus-api.c +++ b/src/dbus-api.c @@ -19,7 +19,7 @@ #include "local-def.h" -STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct MHD_Connection *connection, AFB_request *request) { +STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, AFB_request *request) { static pingcount=0; json_object *response; response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++); @@ -29,20 +29,15 @@ STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct M STATIC AFB_restapi pluginApis[]= { - {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"}, - {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, - {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ping" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, + {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, {0,0,0} }; PUBLIC AFB_plugin *dbusRegister (AFB_session *session) { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); - + plugin->type = AFB_PLUGIN; plugin->info = "Application Framework Binder Service"; plugin->prefix= "dbus"; plugin->apis = pluginApis; diff --git a/src/http-svc.c b/src/http-svc.c index b29a4bb8..d638f6e9 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -31,16 +31,43 @@ #include + #include #include "../include/local-def.h" +// let's compute fixed URL length only once +static apiUrlLen=0; +static baseUrlLen=0; + // proto missing from GCC char *strcasestr(const char *haystack, const char *needle); static int rqtcount = 0; // dummy request rqtcount to make each message be different static int postcount = 0; -static int aipUrlLen=0; // do not compute apiurl for each call -static int baseUrlLen=0; // do not compute baseurl for each call + +// try to open libmagic to handle mine types +static AFB_error initLibMagic (AFB_session *session) { + const char *magic_full; + + /*MAGIC_MIME tells magic to return a mime of the file, but you can specify different things*/ + if (verbose) printf("Loading mimetype default magic database\n"); + + session->magic = magic_open(MAGIC_MIME); + if (session->magic == NULL) { + fprintf(stderr,"ERROR: unable to initialize magic library\n"); + return AFB_FAIL; + } + if (magic_load(session->magic, NULL) != 0) { + fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic)); + magic_close(session->magic); + return AFB_FAIL; + } + + //usage + //magic_full = magic_file(magic_cookie, actual_file); + //printf("%s\n", magic_full); + return AFB_SUCCESS; +} // 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) { @@ -62,86 +89,93 @@ STATIC void computeEtag(char *etag, int maxlen, struct stat *sbuf) { snprintf(etag, maxlen, "%d", time); } -STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, const char *url, char *filepath, int fd) { - const char *etagCache; +STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, const char *url, AFB_staticfile *staticfile) { + const char *etagCache, *mimetype; char etagValue[15]; struct MHD_Response *response; struct stat sbuf; int ret; - if (fstat (fd, &sbuf) != 0) { - fprintf(stderr, "Fail to stat file: [%s] error:%s\n", filepath, strerror(errno)); + if (fstat (staticfile->fd, &sbuf) != 0) { + fprintf(stderr, "Fail to stat file: [%s] error:%s\n", staticfile->path, strerror(errno)); return (FAILED); } // if url is a directory let's add index.html and redirect client if (S_ISDIR (sbuf.st_mode)) { - strncpy (filepath, url, sizeof (filepath)); - - if (url [strlen (url) -1] != '/') strncat (filepath, "/", sizeof (filepath)); - strncat (filepath, "index.html", sizeof (filepath)); - close (fd); - response = MHD_create_response_from_buffer (0,"", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header (response,MHD_HTTP_HEADER_LOCATION, filepath); - ret = MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); - + if (url [strlen (url) -1] != '/') strncat (staticfile->path, "/", sizeof (staticfile->path)); + strncat (staticfile->path, "index.html", sizeof (staticfile->path)); + close (staticfile->fd); // close directory try to open index.html + 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); + return (FAILED); + } + } else if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused. - fprintf (stderr, "Fail file: [%s] is not a regular file\n", filepath); + fprintf (stderr, "Fail file: [%s] is not a regular file\n", staticfile->path); const char *errorstr = "Alsa-Json-Gateway Invalid file type"; response = MHD_create_response_from_buffer (strlen (errorstr), (void *) errorstr, MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); + MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); + goto finishJob; - } else { + } - // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=fr - // ftp://ftp.heanet.ie/disk1/www.gnu.org/software/libmicrohttpd/doxygen/dc/d0c/microhttpd_8h.html - - // Check etag value and load file only when modification date changes - etagCache = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_NONE_MATCH); - computeEtag(etagValue, sizeof (etagValue), &sbuf); - - if (etagCache != NULL && strcmp(etagValue, etagCache) == 0) { - close(fd); // file did not change since last upload - if (verbose) fprintf(stderr, "Not Modify: [%s]\n", filepath); - response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache - MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue); - ret = MHD_queue_response(connection, MHD_HTTP_NOT_MODIFIED, response); - - } else { // it's a new file, we need to upload it to client - if (verbose) fprintf(stderr, "Serving: [%s]\n", filepath); - response = MHD_create_response_from_fd(sbuf.st_size, fd); - MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache - MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue); - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - } + // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=fr + // ftp://ftp.heanet.ie/disk1/www.gnu.org/software/libmicrohttpd/doxygen/dc/d0c/microhttpd_8h.html + + // Check etag value and load file only when modification date changes + etagCache = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_NONE_MATCH); + computeEtag(etagValue, sizeof (etagValue), &sbuf); + + if (etagCache != NULL && strcmp(etagValue, etagCache) == 0) { + close(staticfile->fd); // file did not change since last upload + if (verbose) fprintf(stderr, "Not Modify: [%s]\n", staticfile->path); + response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache + MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue); + MHD_queue_response(connection, MHD_HTTP_NOT_MODIFIED, response); + + } else { // it's a new file, we need to upload it to client + // if we have magic let's try to guest mime type + if (session->magic) { + mimetype="Unknown"; + mimetype= magic_descriptor(session->magic, staticfile->fd); + if (mimetype != NULL) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype); + }; + if (verbose) fprintf(stderr, "Serving: [%s] mine=%s\n", staticfile->path, mimetype); + response = MHD_create_response_from_fd(sbuf.st_size, staticfile->fd); + MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache + MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue); + MHD_queue_response(connection, MHD_HTTP_OK, response); } + +finishJob: MHD_destroy_response(response); - return (ret); - + return (MHD_YES); } // minimal httpd file server for static HTML,JS,CSS,etc... STATIC int requestFile(struct MHD_Connection *connection, AFB_session *session, const char* url) { int fd; int ret; - - char filepath [512]; + AFB_staticfile staticfile; // build full path from rootdir + url - strncpy(filepath, session->config->rootdir, sizeof (filepath)); - strncat(filepath, url, 511); + + + strncpy(staticfile.path, session->config->rootdir, sizeof (staticfile.path)); + strncat(staticfile.path, url, sizeof (staticfile.path)); // try to open file and get its size - if (-1 == (fd = open(filepath, O_RDONLY))) { - fprintf(stderr, "Fail to open file: [%s] error:%s\n", filepath, strerror(errno)); + if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) { + fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno)); return (FAILED); } // open file is OK let use it - ret = servFile (connection, session, url, filepath, fd); + ret = servFile (connection, session, url, &staticfile); return ret; } @@ -151,34 +185,35 @@ STATIC int checkHTML5(struct MHD_Connection *connection, AFB_session *session, c int fd; int ret; struct MHD_Response *response; - char filepath [512]; + AFB_staticfile staticfile; - // if requesting '/' serve index.html - if (strlen (url) == 0) { - strncpy(filepath, session->config->rootdir, sizeof (filepath)); - strncat(filepath, "/index.html", sizeof (filepath)); + // Any URL prefixed with /rootbase is served with index.html ex: /opa,/opa/,/opa/#!xxxxxx + if ( url[0] == '\0' || url[1] == '\0' || url[1] == '#') { + strncpy(staticfile.path, session->config->rootdir, sizeof (staticfile.path)); + strncat(staticfile.path, "/index.html", sizeof (staticfile.path)); // try to open file and get its size - if (-1 == (fd = open(filepath, O_RDONLY))) { - fprintf(stderr, "Fail to open file: [%s] error:%s\n", filepath, strerror(errno)); + if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) { + fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno)); // Nothing respond to this request Files, API, Angular Base - const char *errorstr = "Alsa-Json-Gateway Unknown or Not readable file"; + const char *errorstr = "Application Framework OPA/index.html Not found"; response = MHD_create_response_from_buffer(strlen(errorstr),(void *)errorstr, MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); - ret = MHD_YES; - return (FAILED); + MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); + return (MHD_YES); } else { - ret = servFile (connection, session, url, filepath, fd); + ret = servFile (connection, session, url, &staticfile); return ret; } } - // we are facing a internal route within the HTML5 OnePageApp let's redirect ex: /myapp/#!user/login - strncpy(filepath, session->config->rootbase, sizeof (filepath)); - strncat(filepath, "#!", sizeof (filepath)); - strncat(filepath, url, sizeof (filepath)); - response = MHD_create_response_from_buffer(session->config->html5.len,(void *)session->config->html5.msg, MHD_RESPMEM_PERSISTENT); - MHD_add_response_header (response, "Location", "http://somesite.com/page.html"); - MHD_queue_response (connection, MHD_HTTP_OK, response); + // Url match /opa/xxxx but not /opa#!xxxx we redirect the URL to /opa#!/xxxx to force index.html reload + strncpy(staticfile.path, session->config->rootbase, sizeof (staticfile.path)); + strncat(staticfile.path, "#!", sizeof (staticfile.path)); + strncat(staticfile.path, url, sizeof (staticfile.path)); + response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header (response, "Location", staticfile.path); + MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); + if (verbose) fprintf (stderr,"checkHTML5 redirect to [%s]\n",staticfile.path); + return (MHD_YES); } // Check and Dispatch HTTP request @@ -193,9 +228,9 @@ STATIC int newRequest(void *cls, struct MHD_Response *response; int ret; - // this is an Angular request we change URL /!#xxxxx - if (0 == strncmp(url, session->config->rootapi, baseUrlLen)) { - ret = doRestApi(connection, session, method, &url[baseUrlLen]); + // this is a REST API let's check for plugins + if (0 == strncmp(url, session->config->rootapi, apiUrlLen)) { + ret = doRestApi(connection, session, method, &url[apiUrlLen+1]); return ret; } @@ -220,17 +255,21 @@ STATIC int newRequest(void *cls, } STATIC int newClient(void *cls, const struct sockaddr * addr, socklen_t addrlen) { - // check if client is comming from an acceptable IP + // check if client is coming from an acceptable IP return (MHD_YES); // MHD_NO } -PUBLIC AFB_ERROR httpdStart(AFB_session *session) { - - // do this only once - aipUrlLen = strlen (session->config->rootapi); - baseUrlLen = strlen (session->config->rootbase); - +PUBLIC AFB_error httpdStart(AFB_session *session) { + + // compute fixed URL length at startup time + apiUrlLen = strlen (session->config->rootapi); + baseUrlLen= strlen (session->config->rootbase); + + // open libmagic cache + initLibMagic (session); + + if (verbose) { printf("AFB:notice Waiting port=%d rootdir=%s\n", session->config->httpdPort, session->config->rootdir); printf("AFB:notice Browser URL= http://localhost:%d\n", session->config->httpdPort); @@ -253,7 +292,7 @@ PUBLIC AFB_ERROR httpdStart(AFB_session *session) { } // infinite loop -PUBLIC AFB_ERROR httpdLoop(AFB_session *session) { +PUBLIC AFB_error httpdLoop(AFB_session *session) { static int count = 0; if (verbose) fprintf(stderr, "AFB:notice entering httpd waiting loop\n"); diff --git a/src/main.c b/src/main.c index 795c2a7b..a86de1c5 100644 --- a/src/main.c +++ b/src/main.c @@ -88,7 +88,7 @@ static AFB_options cliOptions [] = { {SET_TCP_PORT ,1,"port" , "HTTP listening TCP port [default 1234]"}, {SET_ROOT_DIR ,1,"rootdir" , "HTTP Root Directory [default $HOME/.AFB"}, - {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /"}, + {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /opa"}, {SET_ROOT_API ,1,"rootapi" , "HTML Root API URL [default /api"}, {SET_CACHE_TO ,1,"cache-eol" , "Client cache end of live [default 3600s]"}, @@ -227,7 +227,7 @@ static void closeSession (AFB_session *session) { | Main listening HTTP loop +--------------------------------------------------------- */ static void listenLoop (AFB_session *session) { - AFB_ERROR err; + AFB_error err; if (signal (SIGABRT, signalFail) == SIG_ERR) { fprintf (stderr, "%s ERR: main fail to install Signal handler\n", configTime()); diff --git a/src/rest-api.c b/src/rest-api.c index 3b5c53e1..74237adc 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -110,13 +110,15 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co for (idx=0; session->plugins[idx] != NULL; idx++) { if (!strcmp (session->plugins[idx]->prefix, baseurl)) { jsonResponse = callPluginApi (session->plugins[idx], session, &request ); - free (urlcpy); + // free (urlcpy); break; } + } + // No plugin was found + if (session->plugins[idx] == NULL) { errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl); free (urlcpy); goto ExitOnError; - } // plugin callback did not return a valid Json Object @@ -150,26 +152,43 @@ PUBLIC const char* getQueryValue (AFB_request * request, char *name) { return (value); } - -void *initPlugins (AFB_session *session) { - static AFB_plugin *plugins[10]; // no more than 10 plugins !!! - AFB_plugin *plugin; +// Loop on plugins. Check that they have the right type, prepare a JSON object with prefix +STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { int idx; - // simulate dynamic library load for plugins - // need to implement mods argument to activate only requested mods - idx=0; - - // Minimal check before accepting a new plugin - plugin = afsvRegister (session); - if (plugin->type != AFB_PLUGIN) { - fprintf (stderr, "ERROR: AFSV plugin invalid type=%d!=%d\n", plugin->type, AFB_PLUGIN); - } else { - // Prepare Plugin name to be added to API response - plugin->jtype = json_object_new_string (plugin->prefix); - json_object_get (plugin->jtype); // increase reference count to make it permanent - plugins[idx++]= plugin; + for (idx=0; plugins[idx] != NULL; idx++) { + if (plugins[idx]->type != AFB_PLUGIN) { + fprintf (stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN, plugins[idx]->type); + } else { + // some sanity controls + if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)){ + if (plugins[idx]->prefix == NULL) plugins[idx]->prefix = "No URL prefix for APIs"; + if (plugins[idx]->info == NULL) plugins[idx]->info = "No Info describing plugin APIs"; + fprintf (stderr, "ERROR: plugin[%d] invalid prefix=%s info=%s", idx,plugins[idx]->prefix, plugins[idx]->info); + return NULL; + } + + if (verbose) fprintf (stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info); + + // Prepare Plugin name to be added into each API response + plugins[idx]->jtype = json_object_new_string (plugins[idx]->prefix); + json_object_get (plugins[idx]->jtype); // increase reference count to make it permanent + + // compute urlprefix lenght + plugins[idx]->prefixlen = strlen (plugins[idx]->prefix); + } } - - session->plugins= plugins; + return (plugins); +} + +void initPlugins (AFB_session *session) { + static AFB_plugin *plugins[10]; + + plugins[0]= afsvRegister (session), + plugins[1]= dbusRegister (session), + plugins[2]= alsaRegister (session), + plugins[3]= NULL; + + // complete plugins and save them within current sessions + session->plugins= RegisterPlugins (plugins); } \ No newline at end of file diff --git a/src/session.c b/src/session.c index 2bb5b442..aade418e 100644 --- a/src/session.c +++ b/src/session.c @@ -35,7 +35,7 @@ // verify we can read/write in session dir -PUBLIC AFB_ERROR sessionCheckdir (AFB_session *session) { +PUBLIC AFB_error sessionCheckdir (AFB_session *session) { int err; -- 2.16.6