Almost working
authorFulup Ar Foll <fulup@iot.bzh>
Mon, 7 Dec 2015 00:39:27 +0000 (01:39 +0100)
committerFulup Ar Foll <fulup@iot.bzh>
Mon, 7 Dec 2015 00:39:27 +0000 (01:39 +0100)
12 files changed:
include/local-def.h
include/proto-def.h
nbproject/Makefile-Debug.mk
nbproject/configurations.xml
src/afbs-api.c
src/alsa-api.c
src/config.c
src/dbus-api.c
src/http-svc.c
src/main.c
src/rest-api.c
src/session.c

index f69ddcb..3f0a26a 100644 (file)
@@ -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 <time.h>
 #include <json.h>
 #include <microhttpd.h>
-
+#include <magic.h>
 
 #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;
 
 
index 934bf30..d1b0c3f 100644 (file)
 // 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);
 
 
index fb778c7..1866ff1 100644 (file)
@@ -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}
index f30d8c0..c079370 100644 (file)
@@ -52,6 +52,7 @@
           <linkerLibItems>
             <linkerOptionItem>`pkg-config --libs libmicrohttpd`</linkerOptionItem>
             <linkerOptionItem>`pkg-config --libs json-c`</linkerOptionItem>
+            <linkerLibLibItem>magic</linkerLibLibItem>
           </linkerLibItems>
         </linkerTool>
       </compileType>
index 5b7d489..40e89d0 100644 (file)
@@ -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
index dca372a..ab7dced 100644 (file)
@@ -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;
index 893b3a5..6691ca0 100644 (file)
@@ -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 {
index febe87d..9d34e43 100644 (file)
@@ -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;
index b29a4bb..d638f6e 100644 (file)
 
 
 #include <microhttpd.h>
+
 #include <sys/stat.h>
 #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 = "<html><body>Alsa-Json-Gateway Invalid file type</body></html>";
         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 = "<html><body>Alsa-Json-Gateway Unknown or Not readable file</body></html>";
+            const char *errorstr = "<html><body>Application Framework OPA/index.html Not found</body></html>";
             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");
index 795c2a7..a86de1c 100644 (file)
@@ -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());
index 3b5c53e..74237ad 100644 (file)
@@ -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
index 2bb5b44..aade418 100644 (file)
@@ -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;