My initial commit message
authorFulup Ar Foll <fulup@iot.bzh>
Sun, 6 Dec 2015 17:04:47 +0000 (18:04 +0100)
committerFulup Ar Foll <fulup@iot.bzh>
Sun, 6 Dec 2015 17:04:47 +0000 (18:04 +0100)
19 files changed:
Makefile [new file with mode: 0644]
include/local-def.h [new file with mode: 0644]
include/proto-def.h [new file with mode: 0644]
nbproject/Makefile-Debug.mk [new file with mode: 0644]
nbproject/Makefile-Release.mk [new file with mode: 0644]
nbproject/Makefile-impl.mk [new file with mode: 0644]
nbproject/Makefile-variables.mk [new file with mode: 0644]
nbproject/Package-Debug.bash [new file with mode: 0644]
nbproject/Package-Release.bash [new file with mode: 0644]
nbproject/configurations.xml [new file with mode: 0644]
nbproject/project.xml [new file with mode: 0644]
src/afbs-api.c [new file with mode: 0644]
src/alsa-api.c [new file with mode: 0644]
src/config.c [new file with mode: 0644]
src/dbus-api.c [new file with mode: 0644]
src/http-svc.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/rest-api.c [new file with mode: 0644]
src/session.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..180d7a1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,129 @@
+#
+#  There exist several targets which are by default empty and which can be 
+#  used for execution of your targets. These targets are usually executed 
+#  before and after some main targets. They are: 
+#
+#     .build-pre:              called before 'build' target
+#     .build-post:             called after 'build' target
+#     .clean-pre:              called before 'clean' target
+#     .clean-post:             called after 'clean' target
+#     .clobber-pre:            called before 'clobber' target
+#     .clobber-post:           called after 'clobber' target
+#     .all-pre:                called before 'all' target
+#     .all-post:               called after 'all' target
+#     .help-pre:               called before 'help' target
+#     .help-post:              called after 'help' target
+#
+#  Targets beginning with '.' are not intended to be called on their own.
+#
+#  Main targets can be executed directly, and they are:
+#  
+#     build                    build a specific configuration
+#     clean                    remove built files from a configuration
+#     clobber                  remove all built files
+#     all                      build all configurations
+#     help                     print help mesage
+#  
+#  Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and
+#  .help-impl are implemented in nbproject/makefile-impl.mk.
+#
+#  Available make variables:
+#
+#     CND_BASEDIR                base directory for relative paths
+#     CND_DISTDIR                default top distribution directory (build artifacts)
+#     CND_BUILDDIR               default top build directory (object files, ...)
+#     CONF                       name of current configuration
+#     CND_PLATFORM_${CONF}       platform name (current configuration)
+#     CND_ARTIFACT_DIR_${CONF}   directory of build artifact (current configuration)
+#     CND_ARTIFACT_NAME_${CONF}  name of build artifact (current configuration)
+#     CND_ARTIFACT_PATH_${CONF}  path to build artifact (current configuration)
+#     CND_PACKAGE_DIR_${CONF}    directory of package (current configuration)
+#     CND_PACKAGE_NAME_${CONF}   name of package (current configuration)
+#     CND_PACKAGE_PATH_${CONF}   path to package (current configuration)
+#
+# NOCDDL
+CND_BUILDDIR=./build
+CND_DISTDIR=./build/dist
+
+# Environment 
+MKDIR=mkdir
+CP=cp
+CCADMIN=CCadmin
+
+
+# build
+build: .build-post
+
+.build-pre:
+# Add your pre 'build' code here...
+
+.build-post: .build-impl
+# Add your post 'build' code here...
+
+
+# clean
+clean: .clean-post
+
+.clean-pre:
+# Add your pre 'clean' code here...
+
+.clean-post: .clean-impl
+# Add your post 'clean' code here...
+
+
+# clobber
+clobber: .clobber-post
+
+.clobber-pre:
+# Add your pre 'clobber' code here...
+
+.clobber-post: .clobber-impl
+# Add your post 'clobber' code here...
+
+
+# all
+all: .all-post
+
+.all-pre:
+# Add your pre 'all' code here...
+
+.all-post: .all-impl
+# Add your post 'all' code here...
+
+
+# build tests
+build-tests: .build-tests-post
+
+.build-tests-pre:
+# Add your pre 'build-tests' code here...
+
+.build-tests-post: .build-tests-impl
+# Add your post 'build-tests' code here...
+
+
+# run tests
+test: .test-post
+
+.test-pre: build-tests
+# Add your pre 'test' code here...
+
+.test-post: .test-impl
+# Add your post 'test' code here...
+
+
+# help
+help: .help-post
+
+.help-pre:
+# Add your pre 'help' code here...
+
+.help-post: .help-impl
+# Add your post 'help' code here...
+
+
+
+# include project implementation makefile
+include nbproject/Makefile-impl.mk
+
+# include project make variables
+include nbproject/Makefile-variables.mk
diff --git a/include/local-def.h b/include/local-def.h
new file mode 100644 (file)
index 0000000..f69ddcb
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+   alsajson-gw -- provide a REST/HTTP interface to ALSA-Mixer
+
+   Copyright (C) 2015, Fulup Ar Foll
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   $Id: $
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <time.h>
+#include <json.h>
+#include <microhttpd.h>
+
+
+#define AJQ_VERSION "0.1"
+
+/* other definitions --------------------------------------------------- */
+
+typedef int BOOL;
+#ifndef FALSE
+  #define FALSE 0
+#endif
+#ifndef TRUE
+  #define TRUE 1
+#endif
+
+#define PUBLIC
+#define STATIC    static
+#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;
+extern char *ERROR_LABEL[];
+#define ERROR_LABEL_DEF {"false", "true","fatal", "fail", "warning", "empty", "success"}
+
+#define BANNER "<html><head><title>Application Framework Binder</title></head><body>Application Framework </body></html>"
+#define JSON_CONTENT  "application/json"
+#define MAX_POST_SIZE  4096   // maximum size for POST data
+
+// use to check anonymous data when using dynamic loadable lib
+typedef enum  {AFB_PLUGIN=1234, AFB_REQUEST=5678} AFB_type;
+
+// Error code are requested through function to manage json usage count
+typedef struct {
+  int   level;
+  char* label;
+  json_object *json;
+} AFB_ErrorT;
+
+// Post handler
+typedef struct {
+  char* data;
+  int   len;
+  int   uid;
+} AFB_HttpPost;
+
+
+// some usefull static object initialized when entering listen loop.
+extern int verbose;
+// MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value");
+typedef struct {
+  const char *url;
+  char *plugin;
+  char *api;
+  char *post;
+  struct MHD_Connection *connection;
+} AFB_request;
+
+typedef struct {
+     char *msg;
+     int  len;
+} AFB_redirect_msg;
+
+// main config structure
+typedef struct {
+  char *logname;           // logfile path for info & error log
+  char *console;           // console device name (can be a file or a tty)
+  int  localhostOnly;
+  int   httpdPort;
+  char *smack;             // smack label
+  char *plugins;           // list of requested plugins
+  char *rootdir;           // base dir for httpd file download
+  char *rootbase;          // Angular HTML5 base URL
+  char *rootapi;           // Base URL for REST APIs
+  char *pidfile;           // where to store pid when running background
+  char *sessiondir;        // where to store mixer session files
+  char *configfile;        // where to store configuration on gateway exit
+  uid_t setuid;
+  int  cacheTimeout;
+  AFB_redirect_msg html5;  // html5 redirect message
+} AFB_config;
+
+// Command line structure hold cli --command + help text
+typedef struct {
+  int  val;        // command number within application
+  int  has_arg;    // command number within application
+  char *name;      // command as used in --xxxx cli
+  char *help;      // help text
+} AFB_options;
+
+typedef json_object* (*AFB_apiCB)();
+
+// API definition
+typedef struct {
+  char *name;
+  AFB_apiCB callback;
+  char *info;
+} AFB_restapi;
+
+// Plugin definition
+typedef struct {
+  AFB_type type;  
+  char *info;
+  char *prefix;
+  json_object *jtype;
+  AFB_restapi *apis;
+} AFB_plugin;
+
+typedef struct {
+  AFB_config  *config;   // pointer to current config
+  // List of commands to execute
+  int  killPrevious;
+  int  background;        // run in backround mode
+  int  foreground;        // run in forground mode
+  int  checkAlsa;         // Display active Alsa Board
+  int  configsave;        // Save config on disk on start
+  char *cacheTimeout;     // http require timeout to be a string
+  void *httpd;            // anonymous structure for httpd handler
+  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 
+} AFB_session;
+
+
+#include "proto-def.h"
diff --git a/include/proto-def.h b/include/proto-def.h
new file mode 100644 (file)
index 0000000..934bf30
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+   alsajson-gw -- provide a REST/HTTP interface to ALSA-Mixer
+
+   Copyright (C) 2015, Fulup Ar Foll
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   $Id: $
+*/
+
+// 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);
+
+// Session handling
+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 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 *jsonNewjtype (void);
+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 void configStoreFile (AFB_session * session);
+
+
diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk
new file mode 100644 (file)
index 0000000..fb778c7
--- /dev/null
@@ -0,0 +1,126 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a -pre and a -post target defined where you can add customized code.
+#
+# This makefile implements configuration specific macros and targets.
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+GREP=grep
+NM=nm
+CCADMIN=CCadmin
+RANLIB=ranlib
+CC=gcc
+CCC=g++
+CXX=g++
+FC=gfortran
+AS=as
+
+# Macros
+CND_PLATFORM=GNU-Linux
+CND_DLIB_EXT=so
+CND_CONF=Debug
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+
+# Include project Makefile
+include Makefile
+
+# Object Directory
+OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}
+
+# Object Files
+OBJECTFILES= \
+       ${OBJECTDIR}/src/afbs-api.o \
+       ${OBJECTDIR}/src/alsa-api.o \
+       ${OBJECTDIR}/src/config.o \
+       ${OBJECTDIR}/src/dbus-api.o \
+       ${OBJECTDIR}/src/http-svc.o \
+       ${OBJECTDIR}/src/main.o \
+       ${OBJECTDIR}/src/rest-api.o \
+       ${OBJECTDIR}/src/session.o
+
+
+# C Compiler Flags
+CFLAGS=
+
+# CC Compiler Flags
+CCFLAGS=
+CXXFLAGS=
+
+# Fortran Compiler Flags
+FFLAGS=
+
+# Assembler Flags
+ASFLAGS=
+
+# Link Libraries and Options
+LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c`  
+
+# Build Targets
+.build-conf: ${BUILD_SUBPROJECTS}
+       "${MAKE}"  -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+
+${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder: ${OBJECTFILES}
+       ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}
+       ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder ${OBJECTFILES} ${LDLIBSOPTIONS}
+
+${OBJECTDIR}/src/afbs-api.o: src/afbs-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/afbs-api.o src/afbs-api.c
+
+${OBJECTDIR}/src/alsa-api.o: src/alsa-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/alsa-api.o src/alsa-api.c
+
+${OBJECTDIR}/src/config.o: src/config.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/config.o src/config.c
+
+${OBJECTDIR}/src/dbus-api.o: src/dbus-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/dbus-api.o src/dbus-api.c
+
+${OBJECTDIR}/src/http-svc.o: src/http-svc.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-svc.o src/http-svc.c
+
+${OBJECTDIR}/src/main.o: src/main.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/main.o src/main.c
+
+${OBJECTDIR}/src/rest-api.o: src/rest-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/rest-api.o src/rest-api.c
+
+${OBJECTDIR}/src/session.o: src/session.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -g -I/usr/include/json-c -Iinclude `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c`   -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/session.o src/session.c
+
+# Subprojects
+.build-subprojects:
+
+# Clean Targets
+.clean-conf: ${CLEAN_SUBPROJECTS}
+       ${RM} -r ${CND_BUILDDIR}/${CND_CONF}
+       ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+
+# Subprojects
+.clean-subprojects:
+
+# Enable dependency checking
+.dep.inc: .depcheck-impl
+
+include .dep.inc
diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk
new file mode 100644 (file)
index 0000000..6df0bda
--- /dev/null
@@ -0,0 +1,126 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a -pre and a -post target defined where you can add customized code.
+#
+# This makefile implements configuration specific macros and targets.
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+GREP=grep
+NM=nm
+CCADMIN=CCadmin
+RANLIB=ranlib
+CC=gcc
+CCC=g++
+CXX=g++
+FC=gfortran
+AS=as
+
+# Macros
+CND_PLATFORM=GNU-Linux
+CND_DLIB_EXT=so
+CND_CONF=Release
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+
+# Include project Makefile
+include Makefile
+
+# Object Directory
+OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}
+
+# Object Files
+OBJECTFILES= \
+       ${OBJECTDIR}/src/afbs-api.o \
+       ${OBJECTDIR}/src/alsa-api.o \
+       ${OBJECTDIR}/src/config.o \
+       ${OBJECTDIR}/src/dbus-api.o \
+       ${OBJECTDIR}/src/http-svc.o \
+       ${OBJECTDIR}/src/main.o \
+       ${OBJECTDIR}/src/rest-api.o \
+       ${OBJECTDIR}/src/session.o
+
+
+# C Compiler Flags
+CFLAGS=
+
+# CC Compiler Flags
+CCFLAGS=
+CXXFLAGS=
+
+# Fortran Compiler Flags
+FFLAGS=
+
+# Assembler Flags
+ASFLAGS=
+
+# Link Libraries and Options
+LDLIBSOPTIONS=
+
+# Build Targets
+.build-conf: ${BUILD_SUBPROJECTS}
+       "${MAKE}"  -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+
+${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder: ${OBJECTFILES}
+       ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}
+       ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder ${OBJECTFILES} ${LDLIBSOPTIONS}
+
+${OBJECTDIR}/src/afbs-api.o: src/afbs-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/afbs-api.o src/afbs-api.c
+
+${OBJECTDIR}/src/alsa-api.o: src/alsa-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/alsa-api.o src/alsa-api.c
+
+${OBJECTDIR}/src/config.o: src/config.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/config.o src/config.c
+
+${OBJECTDIR}/src/dbus-api.o: src/dbus-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/dbus-api.o src/dbus-api.c
+
+${OBJECTDIR}/src/http-svc.o: src/http-svc.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-svc.o src/http-svc.c
+
+${OBJECTDIR}/src/main.o: src/main.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/main.o src/main.c
+
+${OBJECTDIR}/src/rest-api.o: src/rest-api.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/rest-api.o src/rest-api.c
+
+${OBJECTDIR}/src/session.o: src/session.c 
+       ${MKDIR} -p ${OBJECTDIR}/src
+       ${RM} "$@.d"
+       $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/session.o src/session.c
+
+# Subprojects
+.build-subprojects:
+
+# Clean Targets
+.clean-conf: ${CLEAN_SUBPROJECTS}
+       ${RM} -r ${CND_BUILDDIR}/${CND_CONF}
+       ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+
+# Subprojects
+.clean-subprojects:
+
+# Enable dependency checking
+.dep.inc: .depcheck-impl
+
+include .dep.inc
diff --git a/nbproject/Makefile-impl.mk b/nbproject/Makefile-impl.mk
new file mode 100644 (file)
index 0000000..185e4af
--- /dev/null
@@ -0,0 +1,133 @@
+# 
+# Generated Makefile - do not edit! 
+# 
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a pre- and a post- target defined where you can add customization code.
+#
+# This makefile implements macros and targets common to all configurations.
+#
+# NOCDDL
+
+
+# Building and Cleaning subprojects are done by default, but can be controlled with the SUB
+# macro. If SUB=no, subprojects will not be built or cleaned. The following macro
+# statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf
+# and .clean-reqprojects-conf unless SUB has the value 'no'
+SUB_no=NO
+SUBPROJECTS=${SUB_${SUB}}
+BUILD_SUBPROJECTS_=.build-subprojects
+BUILD_SUBPROJECTS_NO=
+BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}}
+CLEAN_SUBPROJECTS_=.clean-subprojects
+CLEAN_SUBPROJECTS_NO=
+CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}}
+
+
+# Project Name
+PROJECTNAME=AppFrameworkBinder
+
+# Active Configuration
+DEFAULTCONF=Debug
+CONF=${DEFAULTCONF}
+
+# All Configurations
+ALLCONFS=Debug Release 
+
+
+# build
+.build-impl: .build-pre .validate-impl .depcheck-impl
+       @#echo "=> Running $@... Configuration=$(CONF)"
+       "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf
+
+
+# clean
+.clean-impl: .clean-pre .validate-impl .depcheck-impl
+       @#echo "=> Running $@... Configuration=$(CONF)"
+       "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf
+
+
+# clobber 
+.clobber-impl: .clobber-pre .depcheck-impl
+       @#echo "=> Running $@..."
+       for CONF in ${ALLCONFS}; \
+       do \
+           "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \
+       done
+
+# all 
+.all-impl: .all-pre .depcheck-impl
+       @#echo "=> Running $@..."
+       for CONF in ${ALLCONFS}; \
+       do \
+           "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \
+       done
+
+# build tests
+.build-tests-impl: .build-impl .build-tests-pre
+       @#echo "=> Running $@... Configuration=$(CONF)"
+       "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf
+
+# run tests
+.test-impl: .build-tests-impl .test-pre
+       @#echo "=> Running $@... Configuration=$(CONF)"
+       "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf
+
+# dependency checking support
+.depcheck-impl:
+       @echo "# This code depends on make tool being used" >.dep.inc
+       @if [ -n "${MAKE_VERSION}" ]; then \
+           echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES} \$${TESTOBJECTFILES}))" >>.dep.inc; \
+           echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \
+           echo "include \$${DEPFILES}" >>.dep.inc; \
+           echo "endif" >>.dep.inc; \
+       else \
+           echo ".KEEP_STATE:" >>.dep.inc; \
+           echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \
+       fi
+
+# configuration validation
+.validate-impl:
+       @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \
+       then \
+           echo ""; \
+           echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \
+           echo "See 'make help' for details."; \
+           echo "Current directory: " `pwd`; \
+           echo ""; \
+       fi
+       @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \
+       then \
+           exit 1; \
+       fi
+
+
+# help
+.help-impl: .help-pre
+       @echo "This makefile supports the following configurations:"
+       @echo "    ${ALLCONFS}"
+       @echo ""
+       @echo "and the following targets:"
+       @echo "    build  (default target)"
+       @echo "    clean"
+       @echo "    clobber"
+       @echo "    all"
+       @echo "    help"
+       @echo ""
+       @echo "Makefile Usage:"
+       @echo "    make [CONF=<CONFIGURATION>] [SUB=no] build"
+       @echo "    make [CONF=<CONFIGURATION>] [SUB=no] clean"
+       @echo "    make [SUB=no] clobber"
+       @echo "    make [SUB=no] all"
+       @echo "    make help"
+       @echo ""
+       @echo "Target 'build' will build a specific configuration and, unless 'SUB=no',"
+       @echo "    also build subprojects."
+       @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no',"
+       @echo "    also clean subprojects."
+       @echo "Target 'clobber' will remove all built files from all configurations and,"
+       @echo "    unless 'SUB=no', also from subprojects."
+       @echo "Target 'all' will will build all configurations and, unless 'SUB=no',"
+       @echo "    also build subprojects."
+       @echo "Target 'help' prints this message."
+       @echo ""
+
diff --git a/nbproject/Makefile-variables.mk b/nbproject/Makefile-variables.mk
new file mode 100644 (file)
index 0000000..4e44a21
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Generated - do not edit!
+#
+# NOCDDL
+#
+CND_BASEDIR=`pwd`
+CND_BUILDDIR=build
+CND_DISTDIR=dist
+# Debug configuration
+CND_PLATFORM_Debug=GNU-Linux
+CND_ARTIFACT_DIR_Debug=dist/Debug/GNU-Linux
+CND_ARTIFACT_NAME_Debug=appframeworkbinder
+CND_ARTIFACT_PATH_Debug=dist/Debug/GNU-Linux/appframeworkbinder
+CND_PACKAGE_DIR_Debug=dist/Debug/GNU-Linux/package
+CND_PACKAGE_NAME_Debug=appframeworkbinder.tar
+CND_PACKAGE_PATH_Debug=dist/Debug/GNU-Linux/package/appframeworkbinder.tar
+# Release configuration
+CND_PLATFORM_Release=GNU-Linux
+CND_ARTIFACT_DIR_Release=dist/Release/GNU-Linux
+CND_ARTIFACT_NAME_Release=appframeworkbinder
+CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux/appframeworkbinder
+CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux/package
+CND_PACKAGE_NAME_Release=appframeworkbinder.tar
+CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux/package/appframeworkbinder.tar
+#
+# include compiler specific variables
+#
+# dmake command
+ROOT:sh = test -f nbproject/private/Makefile-variables.mk || \
+       (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk)
+#
+# gmake command
+.PHONY: $(shell test -f nbproject/private/Makefile-variables.mk || (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk))
+#
+include nbproject/private/Makefile-variables.mk
diff --git a/nbproject/Package-Debug.bash b/nbproject/Package-Debug.bash
new file mode 100644 (file)
index 0000000..2635983
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/bash -x
+
+#
+# Generated - do not edit!
+#
+
+# Macros
+TOP=`pwd`
+CND_PLATFORM=GNU-Linux
+CND_CONF=Debug
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+CND_DLIB_EXT=so
+NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging
+TMPDIRNAME=tmp-packaging
+OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+OUTPUT_BASENAME=appframeworkbinder
+PACKAGE_TOP_DIR=appframeworkbinder/
+
+# Functions
+function checkReturnCode
+{
+    rc=$?
+    if [ $rc != 0 ]
+    then
+        exit $rc
+    fi
+}
+function makeDirectory
+# $1 directory path
+# $2 permission (optional)
+{
+    mkdir -p "$1"
+    checkReturnCode
+    if [ "$2" != "" ]
+    then
+      chmod $2 "$1"
+      checkReturnCode
+    fi
+}
+function copyFileToTmpDir
+# $1 from-file path
+# $2 to-file path
+# $3 permission
+{
+    cp "$1" "$2"
+    checkReturnCode
+    if [ "$3" != "" ]
+    then
+        chmod $3 "$2"
+        checkReturnCode
+    fi
+}
+
+# Setup
+cd "${TOP}"
+mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package
+rm -rf ${NBTMPDIR}
+mkdir -p ${NBTMPDIR}
+
+# Copy files and create directories and links
+cd "${TOP}"
+makeDirectory "${NBTMPDIR}/appframeworkbinder/bin"
+copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755
+
+
+# Generate tar file
+cd "${TOP}"
+rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar
+cd ${NBTMPDIR}
+tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar *
+checkReturnCode
+
+# Cleanup
+cd "${TOP}"
+rm -rf ${NBTMPDIR}
diff --git a/nbproject/Package-Release.bash b/nbproject/Package-Release.bash
new file mode 100644 (file)
index 0000000..4ef9af3
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/bash -x
+
+#
+# Generated - do not edit!
+#
+
+# Macros
+TOP=`pwd`
+CND_PLATFORM=GNU-Linux
+CND_CONF=Release
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+CND_DLIB_EXT=so
+NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging
+TMPDIRNAME=tmp-packaging
+OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder
+OUTPUT_BASENAME=appframeworkbinder
+PACKAGE_TOP_DIR=appframeworkbinder/
+
+# Functions
+function checkReturnCode
+{
+    rc=$?
+    if [ $rc != 0 ]
+    then
+        exit $rc
+    fi
+}
+function makeDirectory
+# $1 directory path
+# $2 permission (optional)
+{
+    mkdir -p "$1"
+    checkReturnCode
+    if [ "$2" != "" ]
+    then
+      chmod $2 "$1"
+      checkReturnCode
+    fi
+}
+function copyFileToTmpDir
+# $1 from-file path
+# $2 to-file path
+# $3 permission
+{
+    cp "$1" "$2"
+    checkReturnCode
+    if [ "$3" != "" ]
+    then
+        chmod $3 "$2"
+        checkReturnCode
+    fi
+}
+
+# Setup
+cd "${TOP}"
+mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package
+rm -rf ${NBTMPDIR}
+mkdir -p ${NBTMPDIR}
+
+# Copy files and create directories and links
+cd "${TOP}"
+makeDirectory "${NBTMPDIR}/appframeworkbinder/bin"
+copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755
+
+
+# Generate tar file
+cd "${TOP}"
+rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar
+cd ${NBTMPDIR}
+tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar *
+checkReturnCode
+
+# Cleanup
+cd "${TOP}"
+rm -rf ${NBTMPDIR}
diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml
new file mode 100644 (file)
index 0000000..f30d8c0
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configurationDescriptor version="97">
+  <logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT">
+    <logicalFolder name="HeaderFiles"
+                   displayName="Header Files"
+                   projectFiles="true">
+    </logicalFolder>
+    <logicalFolder name="ResourceFiles"
+                   displayName="Resource Files"
+                   projectFiles="true">
+    </logicalFolder>
+    <logicalFolder name="SourceFiles"
+                   displayName="Source Files"
+                   projectFiles="true">
+      <itemPath>src/afbs-api.c</itemPath>
+      <itemPath>src/alsa-api.c</itemPath>
+      <itemPath>src/config.c</itemPath>
+      <itemPath>src/dbus-api.c</itemPath>
+      <itemPath>src/http-svc.c</itemPath>
+      <itemPath>src/main.c</itemPath>
+      <itemPath>src/rest-api.c</itemPath>
+      <itemPath>src/session.c</itemPath>
+    </logicalFolder>
+    <logicalFolder name="TestFiles"
+                   displayName="Test Files"
+                   projectFiles="false"
+                   kind="TEST_LOGICAL_FOLDER">
+    </logicalFolder>
+    <logicalFolder name="ExternalFiles"
+                   displayName="Important Files"
+                   projectFiles="false"
+                   kind="IMPORTANT_FILES_FOLDER">
+      <itemPath>Makefile</itemPath>
+    </logicalFolder>
+  </logicalFolder>
+  <projectmakefile>Makefile</projectmakefile>
+  <confs>
+    <conf name="Debug" type="1">
+      <toolsSet>
+        <compilerSet>default</compilerSet>
+        <dependencyChecking>true</dependencyChecking>
+        <rebuildPropChanged>false</rebuildPropChanged>
+      </toolsSet>
+      <compileType>
+        <cTool>
+          <incDir>
+            <pElem>/usr/include/json-c</pElem>
+            <pElem>include</pElem>
+          </incDir>
+        </cTool>
+        <linkerTool>
+          <linkerLibItems>
+            <linkerOptionItem>`pkg-config --libs libmicrohttpd`</linkerOptionItem>
+            <linkerOptionItem>`pkg-config --libs json-c`</linkerOptionItem>
+          </linkerLibItems>
+        </linkerTool>
+      </compileType>
+      <item path="src/afbs-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/alsa-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/config.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/dbus-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/http-svc.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/main.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/rest-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/session.c" ex="false" tool="0" flavor2="0">
+      </item>
+    </conf>
+    <conf name="Release" type="1">
+      <toolsSet>
+        <compilerSet>default</compilerSet>
+        <dependencyChecking>true</dependencyChecking>
+        <rebuildPropChanged>false</rebuildPropChanged>
+      </toolsSet>
+      <compileType>
+        <cTool>
+          <developmentMode>5</developmentMode>
+        </cTool>
+        <ccTool>
+          <developmentMode>5</developmentMode>
+        </ccTool>
+        <fortranCompilerTool>
+          <developmentMode>5</developmentMode>
+        </fortranCompilerTool>
+        <asmTool>
+          <developmentMode>5</developmentMode>
+        </asmTool>
+      </compileType>
+      <item path="src/afbs-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/alsa-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/config.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/dbus-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/http-svc.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/main.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/rest-api.c" ex="false" tool="0" flavor2="0">
+      </item>
+      <item path="src/session.c" ex="false" tool="0" flavor2="0">
+      </item>
+    </conf>
+  </confs>
+</configurationDescriptor>
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644 (file)
index 0000000..394601f
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.cnd.makeproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/make-project/1">
+            <name>AppFrameworkBinder</name>
+            <c-extensions>c</c-extensions>
+            <cpp-extensions/>
+            <header-extensions/>
+            <sourceEncoding>UTF-8</sourceEncoding>
+            <make-dep-projects/>
+            <sourceRootList/>
+            <confList>
+                <confElem>
+                    <name>Debug</name>
+                    <type>1</type>
+                </confElem>
+                <confElem>
+                    <name>Release</name>
+                    <type>1</type>
+                </confElem>
+            </confList>
+            <formatting>
+                <project-formatting-style>false</project-formatting-style>
+            </formatting>
+        </data>
+    </configuration>
+</project>
diff --git a/src/afbs-api.c b/src/afbs-api.c
new file mode 100644 (file)
index 0000000..5b7d489
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "local-def.h"
+
+STATIC json_object* pingAfbs (AFB_session *session, AFB_request *request) {
+    static pingcount=0;
+    json_object *response;
+    const char * argval;
+    
+    argval=getQueryValue (request, "arg");
+    if (argval == NULL) {
+        argval="No present in query";
+    };
+    
+    response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d [arg=%s]", pingcount++, argval);
+    if (verbose) fprintf(stderr, "%d: \n", pingcount);
+    return (response);
+};
+
+
+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"},
+  {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;
+    
+    return (&plugin);
+};
\ No newline at end of file
diff --git a/src/alsa-api.c b/src/alsa-api.c
new file mode 100644 (file)
index 0000000..dca372a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "local-def.h"
+
+STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct MHD_Connection *connection, AFB_request *request) {
+    static pingcount=0;
+    json_object *response;
+    response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++);
+    if (verbose) fprintf(stderr, "%d: \n", pingcount);
+    return (response);
+};
+
+
+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"},
+  {0,0,0}
+};
+
+PUBLIC AFB_plugin *alsaRegister (AFB_session *session) {
+    AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
+    
+    plugin->info  = "Application Framework Binder Service";
+    plugin->prefix  = "alsa";        
+    plugin->apis  = pluginApis;
+    
+    return (plugin);
+};
\ No newline at end of file
diff --git a/src/config.c b/src/config.c
new file mode 100644 (file)
index 0000000..893b3a5
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   References:
+     https://www.gnu.org/software/libmicrohttpd/manual/html_node/index.html#Top
+     http://www-01.ibm.com/support/knowledgecenter/SSB23S_1.1.0.9/com.ibm.ztpf-ztpfdf.doc_put.09/gtpc2/cpp_vsprintf.html?cp=SSB23S_1.1.0.9%2F0-3-8-1-0-16-8
+
+*/
+
+
+#include "../include/local-def.h"
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+#define AFB_CONFIG_JTYPE "AFB_config"
+
+PUBLIC  char *ERROR_LABEL[]=ERROR_LABEL_DEF;
+
+PUBLIC int verbose;
+STATIC AFB_ErrorT  AFB_Error [AFB_SUCCESS+1];
+STATIC json_object *AFBJsonType;
+
+/* ------------------------------------------------------------------------------
+ * Get localtime and return in a string
+ * ------------------------------------------------------------------------------ */
+
+PUBLIC char * configTime (void) {
+  static char reqTime [26];
+  time_t tt;
+  struct tm *rt;
+
+  /* Get actual Date and Time */
+  time (&tt);
+  rt = localtime (&tt);
+
+  strftime (reqTime, sizeof (reqTime), "(%d-%b %H:%M)",rt);
+
+  // return pointer on static data
+  return (reqTime);
+}
+
+// load config from disk and merge with CLI option
+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;
+
+   // cache timeout default one hour
+   if (cliconfig->cacheTimeout == 0) session->config->cacheTimeout=3600;
+   else session->config->cacheTimeout=cliconfig->cacheTimeout;
+
+   if (cliconfig->rootdir == NULL) {
+       session->config->rootdir = getenv("AFBDIR");
+       if (session->config->rootdir == NULL) {
+           session->config->rootdir = malloc (512);
+           strncpy  (session->config->rootdir, getenv("HOME"),512);
+           strncat (session->config->rootdir, "/.AFB",512);
+       }
+       // if directory does not exist createit
+       mkdir (session->config->rootdir,  O_RDWR | S_IRWXU | S_IRGRP);
+   } else {
+       session->config->rootdir =  cliconfig->rootdir;
+   }
+   
+   // if no Angular/HTML5 rootbase let's try '/' as default
+   if  (cliconfig->rootbase == NULL) {
+       session->config->rootbase = "/";
+   } else {
+       session->config->console= cliconfig->console;
+   }
+   
+   // if no rootapi use '/api'
+   if  (cliconfig->rootbase == NULL) {
+       session->config->rootbase = "/api";
+   } else {
+       session->config->console= cliconfig->console;
+   }
+
+
+
+   // if no session dir create a default path from rootdir
+   if  (cliconfig->sessiondir == NULL) {
+       session->config->sessiondir = malloc (512);
+       strncpy (session->config->sessiondir, session->config->rootdir, 512);
+       strncat (session->config->sessiondir, "/sessions",512);
+   } else {
+       session->config->sessiondir = cliconfig->sessiondir;
+   }
+
+   // if no config dir create a default path from sessiondir
+   if  (cliconfig->configfile == NULL) {
+       session->config->configfile = malloc (512);
+       strncpy (session->config->configfile, session->config->sessiondir, 512);
+       strncat (session->config->configfile, "/AFB-config.json",512);
+   } else {
+       session->config->configfile = cliconfig->configfile;
+   }
+
+   // if no config dir create a default path from sessiondir
+   if  (cliconfig->pidfile == NULL) {
+       session->config->pidfile = malloc (512);
+       strncpy (session->config->pidfile, session->config->sessiondir, 512);
+       strncat (session->config->pidfile, "/AFB-process.pid",512);
+   } else {
+       session->config->pidfile= cliconfig->pidfile;
+   }
+
+   // if no config dir create a default path from sessiondir
+   if  (cliconfig->console == NULL) {
+       session->config->console = malloc (512);
+       strncpy (session->config->console, session->config->sessiondir, 512);
+       strncat (session->config->console, "/AFB-console.out",512);
+   } else {
+       session->config->console= cliconfig->console;
+   }
+   
+   // just upload json object and return without any further processing
+   if((fd = open(session->config->configfile, O_RDONLY)) < 0) {
+      if (verbose) fprintf (stderr, "AFB:notice: config at %s: %s\n", session->config->configfile, strerror(errno));
+      return AFB_EMPTY;
+   }
+
+   // openjson from FD is not public API we need to reopen it !!!
+   close(fd);
+   AFBConfig = json_object_from_file (session->config->configfile);
+
+   // check it is an AFB_config
+   if (json_object_object_get_ex (AFBConfig, "jtype", &value)) {
+      if (strcmp (AFB_CONFIG_JTYPE, json_object_get_string (value))) {
+         fprintf (stderr,"AFB: Error file [%s] is not a valid [%s] type\n ", session->config->configfile, AFB_CONFIG_JTYPE);
+         return AFB_FAIL;
+      }
+   }
+
+   if (!cliconfig->rootdir && json_object_object_get_ex (AFBConfig, "rootdir", &value)) {
+      session->config->rootdir =  strdup (json_object_get_string (value));
+   }
+   
+   if (!cliconfig->rootbase && json_object_object_get_ex (AFBConfig, "rootbase", &value)) {
+      session->config->rootbase =  strdup (json_object_get_string (value));
+   }
+   
+   if (!cliconfig->rootapi && json_object_object_get_ex (AFBConfig, "rootapi", &value)) {
+      session->config->rootapi =  strdup (json_object_get_string (value));
+   }
+   
+   if (!cliconfig->smack && json_object_object_get_ex (AFBConfig, "smack", &value)) {
+      session->config->smack =  strdup (json_object_get_string (value));
+   }
+   
+   if (!cliconfig->plugins && json_object_object_get_ex (AFBConfig, "plugins", &value)) {
+      session->config->plugins =  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));
+   }
+   
+   if (!cliconfig->pidfile && json_object_object_get_ex (AFBConfig, "pidfile", &value)) {
+      session->config->pidfile = strdup (json_object_get_string (value));
+   }
+
+   if (!cliconfig->httpdPort && json_object_object_get_ex (AFBConfig, "httpdPort", &value)) {
+      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);
+   }
+   
+   if (!cliconfig->cacheTimeout && json_object_object_get_ex (AFBConfig, "cachetimeout", &value)) {
+      session->config->cacheTimeout = json_object_get_int (value);
+   }
+   // cacheTimeout is an interger but HTTPd wants it as a string
+   snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout);
+   session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version
+   json_object_put   (AFBConfig);    // decrease reference count to free the json object
+
+   return AFB_SUCCESS;
+}
+
+// Save the config on disk
+PUBLIC void configStoreFile (AFB_session * session) {
+   json_object * AFBConfig;
+   time_t rawtime;
+   struct tm * timeinfo;
+   int err;
+
+   AFBConfig =  json_object_new_object();
+
+   // add a timestamp and store session on disk
+   time ( &rawtime );  timeinfo = localtime ( &rawtime );
+   // A copy of the string is made and the memory is managed by the json_object
+   json_object_object_add (AFBConfig, "jtype"         , json_object_new_string (AFB_CONFIG_JTYPE));
+   json_object_object_add (AFBConfig, "timestamp"     , json_object_new_string (asctime (timeinfo)));
+   json_object_object_add (AFBConfig, "rootdir"       , json_object_new_string (session->config->rootdir));
+   json_object_object_add (AFBConfig, "rootapi"       , json_object_new_string (session->config->rootapi));
+   json_object_object_add (AFBConfig, "rootbase"      , json_object_new_string (session->config->rootbase));
+   json_object_object_add (AFBConfig, "smack"         , json_object_new_string (session->config->smack));
+   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, "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));
+
+   err = json_object_to_file (session->config->configfile, AFBConfig);
+   json_object_put   (AFBConfig);    // decrease reference count to free the json object
+   if (err < 0) {
+      fprintf(stderr, "AFB: Fail to save config on disk [%s]\n ", session->config->configfile);
+   }
+}
+
+
+PUBLIC AFB_session *configInit () {
+
+  AFB_session *session;
+  AFB_config  *config;
+  int idx, verbosesav;
+
+
+  session = malloc (sizeof (AFB_session));
+  memset (session,0, sizeof (AFB_session));
+
+  // create config handle
+  config = malloc (sizeof (AFB_config));
+  memset (config,0, sizeof (AFB_config));
+
+  // stack config handle into session
+  session->config = config;
+
+  AFBJsonType = 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);
+  }
+  verbose = verbosesav;
+  
+  return (session);
+}
+
+
+// get JSON object from error level and increase its reference count
+PUBLIC json_object *jsonNewStatus (AFB_ERROR level) {
+
+  json_object *target =  AFB_Error[level].json;
+  json_object_get (target);
+
+  return (target);
+}
+
+// get AFB object type with adequate usage count
+PUBLIC json_object *jsonNewjtype (void) {
+  json_object_get (AFBJsonType); // increase reference count
+  return (AFBJsonType);
+}
+
+// build an ERROR message and return it as a valid json object
+PUBLIC  json_object *jsonNewMessage (AFB_ERROR level, char* format, ...) {
+   static int count = 0;
+   json_object * AFBResponse;
+   va_list args;
+   char message [512];
+
+   // format message
+   if (format != NULL) {
+       va_start(args, format);
+       vsnprintf (message, sizeof (message), format, args);
+       va_end(args);
+   }
+
+   AFBResponse = json_object_new_object();
+   json_object_object_add (AFBResponse, "jtype", jsonNewjtype ());
+   json_object_object_add (AFBResponse, "status" , json_object_new_string (ERROR_LABEL[level]));
+   if (format != NULL) {
+        json_object_object_add (AFBResponse, "info"   , json_object_new_string (message));
+   }
+   if (verbose) {
+        fprintf (stderr, "AFB:%-6s [%3d]: ", AFB_Error [level].label, count++);
+        if (format != NULL) {
+            fprintf (stderr, "%s", message);
+        } else {
+            fprintf (stderr, "No Message");
+        }
+        fprintf (stderr, "\n");
+   }
+
+   return (AFBResponse);
+}
+
+// Dump a message on stderr
+PUBLIC void jsonDumpObject (json_object * jObject) {
+
+   if (verbose) {
+        fprintf (stderr, "AFB:dump [%s]\n", json_object_to_json_string(jObject));
+   }
+}
+
diff --git a/src/dbus-api.c b/src/dbus-api.c
new file mode 100644 (file)
index 0000000..febe87d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "local-def.h"
+
+STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct MHD_Connection *connection, AFB_request *request) {
+    static pingcount=0;
+    json_object *response;
+    response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++);
+    if (verbose) fprintf(stderr, "%d: \n", pingcount);
+    return (response);
+};
+
+
+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"},
+  {0,0,0}
+};
+
+PUBLIC AFB_plugin *dbusRegister (AFB_session *session) {
+    AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
+    
+    plugin->info  = "Application Framework Binder Service";
+    plugin->prefix= "dbus";        
+    plugin->apis  = pluginApis;
+    
+    return (plugin);
+};
\ No newline at end of file
diff --git a/src/http-svc.c b/src/http-svc.c
new file mode 100644 (file)
index 0000000..b29a4bb
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Handle standard HTTP request
+ *    Features/Restriction:
+    - handle ETAG to limit upload to modified/new files [cache default 3600s]
+    - handles redirect to index.htlm when path is a directory [code 301]
+    - only support GET method
+    - does not follow link.
+
+   References: https://www.gnu.org/software/libmicrohttpd/manual/html_node/index.html#Top
+   http://libmicrohttpd.sourcearchive.com/documentation/0.4.2/microhttpd_8h.html
+   https://gnunet.org/svn/libmicrohttpd/src/examples/fileserver_example_external_select.c
+   https://github.com/json-c/json-c
+   POST https://www.gnu.org/software/libmicrohttpd/manual/html_node/microhttpd_002dpost.html#microhttpd_002dpost
+ */
+
+
+#include <microhttpd.h>
+#include <sys/stat.h>
+#include "../include/local-def.h"
+
+// 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
+
+// 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;
+
+  // if post handle was used let's free everything
+  if (posthandle) {
+     if (verbose) fprintf (stderr, "End Post Request UID=%d\n", posthandle->uid);
+     free (posthandle->data);
+     free (posthandle);
+  }
+}
+
+
+// Create check etag value
+STATIC void computeEtag(char *etag, int maxlen, struct stat *sbuf) {
+    int time;
+    time = sbuf->st_mtim.tv_sec;
+    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;
+    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));
+        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);
+
+    } 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);
+        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);
+
+    } 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);
+        }
+    }
+    MHD_destroy_response(response);
+    return (ret);
+
+}
+
+// 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];
+
+    // build full path from rootdir + url
+    strncpy(filepath, session->config->rootdir, sizeof (filepath));
+    strncat(filepath, url, 511);
+
+    // 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));
+        return (FAILED);
+
+    }
+    // open file is OK let use it
+    ret = servFile (connection, session, url, filepath, fd);
+    return ret;
+}
+
+// this function return either Index.htlm or a redirect to /#!route to make angular happy
+STATIC int checkHTML5(struct MHD_Connection *connection, AFB_session *session, const char* url) {
+
+    int fd;
+    int ret;
+    struct MHD_Response *response;
+    char filepath [512];
+
+    // if requesting '/' serve index.html
+    if (strlen (url) == 0) {
+        strncpy(filepath, session->config->rootdir, sizeof (filepath));
+        strncat(filepath, "/index.html", sizeof (filepath));
+        // 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));
+            // Nothing respond to this request Files, API, Angular Base
+            const char *errorstr = "<html><body>Alsa-Json-Gateway Unknown or Not readable file</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);
+       } else {
+            ret = servFile (connection, session, url, filepath, fd);
+            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);
+}
+
+// Check and Dispatch HTTP request
+STATIC int newRequest(void *cls,
+        struct MHD_Connection *connection,
+        const char *url,
+        const char *method,
+        const char *version,
+        const char *upload_data, size_t *upload_data_size, void **con_cls) {
+
+    AFB_session *session = 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]);
+        return ret;
+    }
+    
+    // From here only accept get request
+    if (0 != strcmp(method, MHD_HTTP_METHOD_GET)) return MHD_NO; /* unexpected method */
+   
+    // If a static file exist serve it now
+    ret = requestFile(connection, session, url);
+    if (ret != FAILED) return ret;
+    
+    // no static was served let check for Angular redirect
+    if (0 == strncmp(url, session->config->rootbase, baseUrlLen)) {
+        ret = checkHTML5(connection, session, &url[baseUrlLen]);
+        return ret;
+    }
+
+     // Nothing respond to this request Files, API, Angular Base
+    const char *errorstr = "<html><body>Alsa-Json-Gateway Unknown or Not readable file</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);
+    return (MHD_YES);
+}
+
+STATIC int newClient(void *cls, const struct sockaddr * addr, socklen_t addrlen) {
+    // check if client is comming 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);
+
+    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);
+    }
+
+    session->httpd = (void*) MHD_start_daemon(
+            MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, // use request and not threads
+            session->config->httpdPort, // port
+            &newClient, NULL, // Tcp Accept call back + extra attribute
+            &newRequest, session, // Http Request Call back + extra attribute
+            MHD_OPTION_NOTIFY_COMPLETED, &endRequest, NULL,
+            MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15, MHD_OPTION_END); // 15s + options-end
+    // TBD: MHD_OPTION_SOCK_ADDR
+
+    if (session->httpd == NULL) {
+        printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort);
+        return AFB_FATAL;
+    }
+    return AFB_SUCCESS;
+}
+
+// infinite loop
+PUBLIC AFB_ERROR httpdLoop(AFB_session *session) {
+    static int  count = 0;
+
+    if (verbose) fprintf(stderr, "AFB:notice entering httpd waiting loop\n");
+    if (session->foreground) {
+
+        while (TRUE) {
+            fprintf(stderr, "AFB:notice Use Ctrl-C to quit");
+            (void) getc(stdin);
+        }
+    } else {
+        while (TRUE) {
+            sleep(3600);
+            if (verbose) fprintf(stderr, "AFB:notice httpd alive [%d]\n", count++);
+        }
+    }
+
+    // should never return from here
+    return AFB_FATAL;
+}
+
+PUBLIC int httpdStatus(AFB_session *session) {
+    return (MHD_run(session->httpd));
+}
+
+PUBLIC void httpdStop(AFB_session *session) {
+    MHD_stop_daemon(session->httpd);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..795c2a7
--- /dev/null
@@ -0,0 +1,611 @@
+/* 
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* 
+ * File:   main.c
+ * Author: "Fulup Ar Foll"
+ *
+ * Created on 05 December 2015, 15:38
+ */
+
+#include "local-def.h"
+
+#include <syslog.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <getopt.h>
+
+static sigjmp_buf exitpoint; // context save for set/longjmp
+static sigjmp_buf restartpoint; // context save for set/longjmp
+
+/*----------------------------------------------------------
+ | printversion
+ |   print version and copyright
+ +--------------------------------------------------------- */
+ static void printVersion (void) {
+
+   fprintf (stderr,"\n----------------------------------------- \n");
+   fprintf (stderr,"|  AFB [Application Framework Binder] version=%s |\n", AJQ_VERSION);
+   fprintf (stderr,"----------------------------------------- \n");
+   fprintf (stderr,"|  Copyright(C) 2015 Fulup Ar Foll /IoT.bzh [fulup -at- iot.bzh]\n");
+   fprintf (stderr,"|  AFB comes with ABSOLUTELY NO WARRANTY.\n");
+   fprintf (stderr,"|  Licence [what ever makes you happy] until you fix bugs by yourself :)\n\n");
+   exit (0);
+ } // end printVersion
+
+
+// Define command line option
+ #define SET_VERBOSE        101
+ #define SET_BACKGROUND     105
+ #define SET_FORGROUND      106
+ #define KILL_PREV_EXIT     107
+ #define KILL_PREV_REST     108
+ #define SET_FAKE_MOD       109
+
+ #define SET_TCP_PORT       120
+ #define SET_ROOT_DIR       121
+ #define SET_ROOT_BASE      122
+ #define SET_ROOT_API       123
+
+ #define SET_CACHE_TO       130
+ #define SET_cardid         131
+ #define SET_PID_FILE       132
+ #define SET_SESSION_DIR    133
+ #define SET_CONFIG_FILE    134
+ #define SET_CONFIG_SAVE    135
+ #define SET_CONFIG_EXIT    138
+
+ #define SET_SMACK          140
+ #define SET_PLUGINS        141
+
+ #define DISPLAY_VERSION    150
+ #define DISPLAY_HELP       151
+
+
+// Supported option
+static  AFB_options cliOptions [] = {
+  {SET_VERBOSE      ,0,"verbose"         , "Verbose Mode"},
+
+  {SET_FORGROUND    ,0,"foreground"      , "Get all in foreground mode"},
+  {SET_BACKGROUND   ,0,"daemon"          , "Get all in background mode"},
+  {KILL_PREV_EXIT   ,0,"kill"            , "Kill active process if any and exit"},
+  {KILL_PREV_REST   ,0,"restart"         , "Kill active process if any and restart"},
+
+  {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_API     ,1,"rootapi"         , "HTML Root API URL [default /api"},
+
+  {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_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]"},
+  {SET_CONFIG_SAVE  ,0,"save"            , "Save config on disk [default no]"},
+  {SET_CONFIG_EXIT  ,0,"saveonly"        , "Save config on disk and then exit"},
+
+  {SET_SMACK        ,1,"smack"           , "Set Smack Label [default=demo"},
+  {SET_PLUGINS      ,1,"mods"            , "Enable module [default=all"},
+  
+  {DISPLAY_VERSION  ,0,"version"         , "Display version and copyright"},
+  {DISPLAY_HELP     ,0,"help"            , "Display this help"},
+  {0, 0, 0}
+ };
+
+/*----------------------------------------------------------
+ | signalQuit
+ |  return to intitial exitpoint on order to close backend
+ |  before exiting.
+ +--------------------------------------------------------- */
+void signalQuit (int signum)
+{
+  if (verbose) printf ("INF:signalQuit received signal to quit\n");
+  longjmp (exitpoint, signum);
+}
+
+/*----------------------------------------------------------
+ | timeout signalQuit
+ |
+ +--------------------------------------------------------- */
+void signalFail (int signum) {
+
+  sigset_t sigset;
+
+  // unlock timeout signal to allow a new signal to come
+  sigemptyset (&sigset);
+  sigaddset   (&sigset, SIGABRT);
+  sigprocmask (SIG_UNBLOCK, &sigset, 0);
+
+  fprintf (stderr, "%s ERR:getAllBlock acquisition timeout\n",configTime());
+  syslog (LOG_ERR, "Daemon fail and restart [please report bug]");
+  longjmp (restartpoint, signum);
+}
+
+
+/*----------------------------------------------------------
+ | printHelp
+ |   print information from long option array
+ +--------------------------------------------------------- */
+
+ static void printHelp(char *name) {
+    int ind;
+    char command[20];
+
+    fprintf (stderr,"%s:\nallowed options\n", name);
+    for (ind=0; cliOptions [ind].name != NULL;ind++)
+    {
+      // display options
+      if (cliOptions [ind].has_arg == 0 )
+      {
+            fprintf (stderr,"  --%-15s %s\n", cliOptions [ind].name, cliOptions[ind].help);
+      } else {
+         sprintf(command,"%s=xxxx", cliOptions [ind].name);
+         fprintf (stderr,"  --%-15s %s\n", command, cliOptions[ind].help);
+      }
+    }
+    fprintf (stderr,"Example:\n  %s\\\n  --verbose --port=1234 --smack=xxxx --mods=alsa:dbus\n", name);
+} // end printHelp
+
+/*----------------------------------------------------------
+ | writePidFile
+ |   write a file in /var/run/AFB with pid
+ +--------------------------------------------------------- */
+static int writePidFile (AFB_config *config, int pid) {
+  FILE *file;
+
+  // if no pid file configure just return
+  if (config->pidfile == NULL) return 0;
+
+  // open pid file in write mode
+  file = fopen(config->pidfile,"w");
+  if (file == NULL) {
+    fprintf (stderr,"%s ERR:writePidFile fail to open [%s]\n",configTime(), config->pidfile);
+    return -1;
+  }
+
+  // write pid in file and close
+  fprintf (file, "%d\n", pid);
+  fclose  (file);
+  return 0;
+}
+
+/*----------------------------------------------------------
+ | readPidFile
+ |   read file in /var/run/AFB with pid
+ +--------------------------------------------------------- */
+static int readPidFile (AFB_config *config) {
+  int  pid;
+  FILE *file;
+  int  status;
+
+  if (config->pidfile == NULL) return -1;
+
+  // open pid file in write mode
+  file = fopen(config->pidfile,"r");
+  if (file == NULL) {
+    fprintf (stderr,"%s ERR:readPidFile fail to open [%s]\n",configTime(), config->pidfile);
+    return -1;
+  }
+
+  // write pid in file and close
+  status = fscanf  (file, "%d\n", &pid);
+  fclose  (file);
+
+  // never kill pid 0
+  if (status != 1) return -1;
+
+  return (pid);
+}
+
+/*----------------------------------------------------------
+ | closeSession
+ |   try to close everything before leaving
+ +--------------------------------------------------------- */
+static void closeSession (AFB_session *session) {
+
+
+}
+
+
+/*----------------------------------------------------------
+ | listenLoop
+ |   Main listening HTTP loop
+ +--------------------------------------------------------- */
+static void listenLoop (AFB_session *session) {
+  AFB_ERROR  err;
+
+  if (signal (SIGABRT, signalFail) == SIG_ERR) {
+        fprintf (stderr, "%s ERR: main fail to install Signal handler\n", configTime());
+        return;
+  }
+
+  // ------ Start httpd server
+  if (session->config->httpdPort > 0) {
+
+        err = httpdStart (session);
+        if (err != AFB_SUCCESS) return;
+
+        // infinite loop
+        httpdLoop(session);
+
+        fprintf (stderr, "hoops returned from infinite loop [report bug]\n");
+  }
+}
+
+
+/*---------------------------------------------------------
+ | main
+ |   Parse option and launch action
+ +--------------------------------------------------------- */
+
+int main(int argc, char *argv[])  {
+  AFB_session    *session;
+  char*          programName = argv [0];
+  int            optionIndex = 0;
+  int            optc, ind, consoleFD;
+  int            pid, nbcmd, status;
+  AFB_config     cliconfig; // temp structure to store CLI option before file config upload
+
+  // ------------- Build session handler & init config -------
+  session =  configInit ();
+  memset (&cliconfig,0,sizeof(cliconfig));
+
+  // GNU CLI getopts nterface.
+  struct option ggcOption;
+  struct option *gnuOptions;
+
+  // ------------------ Process Command Line -----------------------
+
+  // if no argument print help and return
+  if (argc < 2) {
+       printHelp(programName);
+       return (-1);
+  }
+
+  // build GNU getopt info from cliOptions
+  nbcmd = sizeof (cliOptions) / sizeof (AFB_options);
+  gnuOptions = malloc (sizeof (ggcOption) * nbcmd);
+  for (ind=0; ind < nbcmd;ind++) {
+    gnuOptions [ind].name    = cliOptions[ind].name;
+    gnuOptions [ind].has_arg = cliOptions[ind].has_arg;
+    gnuOptions [ind].flag    = 0;
+    gnuOptions [ind].val     = cliOptions[ind].val;
+  }
+
+  // get all options from command line
+  while ((optc = getopt_long (argc, argv, "vsp?", gnuOptions, &optionIndex))
+        != EOF)
+  {
+    switch (optc)
+    {
+     case SET_VERBOSE:
+       verbose = 1;
+       break;
+
+    case SET_TCP_PORT:
+       if (optarg == 0) goto needValueForOption;
+       if (!sscanf (optarg, "%d", &cliconfig.httpdPort)) goto notAnInteger;
+       break;
+
+    case SET_ROOT_DIR:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.rootdir   = optarg;
+       break;       
+       
+    case SET_ROOT_BASE:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.rootbase   = optarg;
+       break;
+
+    case SET_ROOT_API:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.rootapi   = optarg;
+       break;
+       
+    case SET_SMACK:
+       if (optarg == 0) goto needValueForOption;
+       fprintf (stderr, "Not Implemented yet\n");
+       cliconfig.smack   = optarg;
+       break;
+
+    case SET_PLUGINS:
+       if (optarg == 0) goto needValueForOption;
+       fprintf (stderr, "Not Implemented yet\n");
+       cliconfig.plugins = optarg;
+       break;
+
+    case SET_PID_FILE:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.pidfile   = optarg;
+       break;
+
+    case SET_SESSION_DIR:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.sessiondir   = optarg;
+       break;
+
+    case  SET_CONFIG_FILE:
+       if (optarg == 0) goto needValueForOption;
+       cliconfig.configfile   = optarg;
+       break;
+
+    case  SET_CACHE_TO:
+       if (optarg == 0) goto needValueForOption;
+       if (!sscanf (optarg, "%d", &cliconfig.cacheTimeout)) goto notAnInteger;
+       break;
+
+    case SET_CONFIG_EXIT:
+       if (optarg != 0) goto noValueForOption;
+       session->configsave  = 1;
+       session->forceexit   = 1;
+       break;
+
+    case SET_CONFIG_SAVE:
+       if (optarg != 0) goto noValueForOption;
+       session->configsave  = 1;
+       break;
+
+    case SET_cardid:
+       if (optarg == 0) goto needValueForOption;
+       if (!sscanf (optarg, "%d", &cliconfig.setuid)) goto notAnInteger;
+       break;
+
+    case SET_FAKE_MOD:
+       if (optarg != 0) goto noValueForOption;
+       session->fakemod  = 1;
+       break;
+
+    case SET_FORGROUND:
+       if (optarg != 0) goto noValueForOption;
+       session->foreground  = 1;
+       break;
+
+    case SET_BACKGROUND:
+       if (optarg != 0) goto noValueForOption;
+       session->background  = 1;
+       break;
+
+     case KILL_PREV_REST:
+       if (optarg != 0) goto noValueForOption;
+       session->killPrevious  = 1;
+       break;
+
+     case KILL_PREV_EXIT:
+       if (optarg != 0) goto noValueForOption;
+       session->killPrevious  = 2;
+       break;
+
+    case DISPLAY_VERSION:
+       if (optarg != 0) goto noValueForOption;
+       printVersion();
+       goto normalExit;
+
+    case DISPLAY_HELP:
+     default:
+       printHelp(programName);
+       goto normalExit;
+
+  }
+  }
+  // Create session config
+  configInit (/* session & config are initialized globally */);
+
+  // if exist merge config file with CLI arguments
+  configLoadFile  (session, &cliconfig);
+
+  // ------------------ sanity check ----------------------------------------
+  if  ((session->background) && (session->foreground)) {
+    fprintf (stderr, "%s ERR: cannot select foreground & background at the same time\n",configTime());
+     exit (-1);
+  }
+
+  // ------------------ Some useful default values -------------------------
+  if  ((session->background == 0) && (session->foreground == 0)) session->foreground=1;
+
+  // open syslog if ever needed
+  openlog("AGB-log", 0, LOG_DAEMON);
+
+  // -------------- Try to kill any previsou process if asked ---------------------
+  if (session->killPrevious) {
+    pid = readPidFile (session->config);  // enforce commandline option
+    switch (pid) {
+    case -1:
+      fprintf (stderr, "%s ERR:main --kill ignored no PID file [%s]\n",configTime(), session->config->pidfile);
+      break;
+    case 0:
+      fprintf (stderr, "%s ERR:main --kill ignored no active AFB process\n",configTime());
+      break;
+    default:
+      status = kill (pid,SIGINT );
+      if (status == 0) {
+            if (verbose) printf ("%s INF:main signal INTR sent to pid:%d \n", configTime(), pid);
+      } else {
+         // try kill -9
+         status = kill (pid,9);
+         if (status != 0)  fprintf (stderr, "%s ERR:main failled to killed pid=%d \n",configTime(), pid);
+      }
+    } // end switch pid
+
+    if (session->killPrevious >= 2) goto normalExit;
+  } // end killPrevious
+
+
+  // ------------------ clean exit on CTR-C signal ------------------------
+  if (signal (SIGINT, signalQuit) == SIG_ERR) {
+    fprintf (stderr, "%s Quit Signal received.",configTime());
+    return (-1);
+  }
+
+  // save exitpoint context when returning from longjmp closeSession and exit
+  status = setjmp (exitpoint); // return !+ when coming from longjmp
+  if (status != 0) {
+    if (verbose) printf ("INF:main returning from longjump after signal [%d]\n", status);
+    closeSession (session);
+    goto exitOnSignal;
+  }
+
+  // let's run this program with a low priority
+  status=nice (20);
+
+
+  // ------------------ Finaly Process Commands -----------------------------
+
+
+
+   // if --save then store config on disk upfront
+   if (session->configsave) configStoreFile (session);
+   if (session->forceexit)  exit (0);
+
+    if (session->config->setuid) {
+        int err;
+
+        err = setuid(session->config->setuid);
+        if (err) fprintf (stderr, "Fail to change program cardid error=%s", strerror(err));
+    }
+
+    // let's not take the risk to run as ROOT
+    if (getuid() == 0)  status=setuid(65534);  // run as nobody
+
+    // check session dir and create if it does not exist
+    if (sessionCheckdir (session) != AFB_SUCCESS) goto errSessiondir;
+    if (verbose) fprintf (stderr, "AFB:notice Init config done\n");
+
+
+
+    // ---- run in foreground mode --------------------
+    if (session->foreground) {
+
+        if (verbose) fprintf (stderr,"AFB:notice Foreground mode\n");
+
+        // write a pid file for --kill-previous and --raise-debug option
+        status = writePidFile (session->config, getpid());
+        if (status == -1) goto errorPidFile;
+
+        // enter listening loop in foreground
+        listenLoop(session);
+        goto exitInitLoop;
+  } // end foreground
+
+
+  // --------- run in background mode -----------
+  if (session->background) {
+
+       // if (status != 0) goto errorCommand;
+      if (verbose) printf ("AFB: Entering background mode\n");
+
+      // open /dev/console to redirect output messAFBes
+      consoleFD = open(session->config->console, O_WRONLY | O_APPEND | O_CREAT , 0640);
+      if (consoleFD < 0) goto errConsole;
+
+      // fork process when running background mode
+      pid = fork ();
+
+      // son process get all data in standalone mode
+      if (pid == 0) {
+
+            printf ("\nAFB: background mode [pid:%d console:%s]\n", getpid(),session->config->console);
+            if (verbose) printf ("AFB:info use '%s --restart --rootdir=%s # [--pidfile=%s] to restart daemon\n", programName,session->config->rootdir, session->config->pidfile);
+
+         // redirect default I/O on console
+         close (2); status=dup(consoleFD);  // redirect stderr
+         close (1); status=dup(consoleFD);  // redirect stdout
+         close (0);           // no need for stdin
+         close (consoleFD);
+
+        setsid();   // allow father process to fully exit
+            sleep (2);  // allow main to leave and release port
+
+         fprintf (stderr, "----------------------------\n");
+         fprintf (stderr, "%s INF:main background pid=%d\n", configTime(), getpid());
+         fflush  (stderr);
+
+         // if everything look OK then look forever
+         syslog (LOG_ERR, "AFB: Entering infinite loop in background mode");
+
+         // should normally never return from this loop
+         listenLoop(session);
+         syslog (LOG_ERR, "AFB:FAIL background infinite loop exited check [%s]\n", session->config->console);
+
+         goto exitInitLoop;
+      }
+
+      // if fail nothing much to do
+      if (pid == -1) goto errorFork;
+
+      // fork worked and we are in father process
+      status = writePidFile (session->config, pid);
+      if (status == -1) goto errorPidFile;
+
+      // we are in father process, we don't need this one
+      exit (0);
+
+  } // end background-foreground
+
+normalExit:
+  closeSession (session);   // try to close everything before leaving
+  if (verbose) printf ("\n---- Application Framework Binder Normal End ------\n");
+  exit (0);
+
+// ------------- Fatal ERROR display error and quit  -------------
+errorPidFile:
+  fprintf (stderr,"\nERR:main Failled to write pid file [%s]\n\n", session->config->pidfile);
+  exit (-1);
+
+errorFork:
+  fprintf (stderr,"\nERR:main Failled to fork son process\n\n");
+  exit (-1);
+
+needValueForOption:
+  fprintf (stderr,"\nERR:main 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"
+          ,gnuOptions[optionIndex].name);
+  exit (-1);
+
+notAnInteger:
+  fprintf (stderr,"\nERR:main 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"
+                 ,configTime(), getpid());
+  exit (-1);
+
+errConsole:
+  fprintf (stderr,"\nERR:cannot open /dev/console (use --foreground)\n\n");
+  exit (-1);
+
+errSessiondir:
+  fprintf (stderr,"\nERR:cannot read/write session dir\n\n");
+  exit (-1);
+
+errSoundCard:
+  fprintf (stderr,"\nERR:fail to probe sound cards\n\n");
+  exit (-1);
+
+exitInitLoop:
+  // try to unlink pid file if any
+  if (session->background && session->config->pidfile != NULL)  unlink (session->config->pidfile);
+  exit (-1);
+
+}; /* END main() */
+
diff --git a/src/rest-api.c b/src/rest-api.c
new file mode 100644 (file)
index 0000000..3b5c53e
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Contain all generic part to handle REST/API
+ */
+
+
+#include <microhttpd.h>
+#include <sys/stat.h>
+#include "../include/local-def.h"
+
+// proto missing from GCC
+char *strcasestr(const char *haystack, const char *needle);
+
+
+// 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;
+
+    // if post handle was used let's free everything
+    if (posthandle) {
+        if (verbose) fprintf(stderr, "End Post Request UID=%d\n", posthandle->uid);
+        free(posthandle->data);
+        free(posthandle);
+    }
+}
+
+
+PUBLIC json_object* pingSample (AFB_plugin *plugin, AFB_session *session, AFB_request *post) {
+    static pingcount=0;
+    json_object *response;
+    response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d", pingcount++);
+    if (verbose) fprintf(stderr, "%d: \n", pingcount);
+    return (response);
+}
+
+// Check of apiurl is declare in this plugin and call it
+STATIC json_object * callPluginApi (AFB_plugin *plugin, AFB_session *session,  AFB_request *request) {
+    json_object *response;
+    int idx;
+    
+    // If a plugin hold this urlpath call its callback
+    for (idx=0; plugin->apis[idx].callback != NULL; idx++) {
+        if (!strcmp (plugin->apis[idx].name, request->api)) {
+           response = plugin->apis[idx].callback (session, request);
+           if (response != NULL) {
+               json_object_object_add (response, "jtype" ,plugin->jtype);
+           }
+           return (response);
+        }   
+    }
+    return (NULL);
+}
+
+
+// process rest API query
+PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char *method, const char* url) {
+
+    char *baseurl, *baseapi, *urlcpy;
+    json_object *jsonResponse, *errMessage;
+    struct MHD_Response *webResponse;
+    const char *serialized, parsedurl;
+    AFB_request request;
+    int  idx, ret;  
+
+    // Extract plugin urlpath from request
+    urlcpy=strdup (url);
+    baseurl = strsep(&urlcpy, "/");
+    if (baseurl == NULL) {
+        errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s", url);
+        goto ExitOnError;
+    }
+    
+    baseapi = strsep(&urlcpy, "/");
+    if (baseapi == NULL) {
+        errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s/%s", baseurl, url);
+        goto ExitOnError;
+    }
+    
+    // build request structure
+    memset (&request, 0, sizeof (request));
+    request.connection = connection;
+    request.url        = url;
+    request.plugin     = baseurl;
+    request.api        = baseapi;
+
+    // if post wait as data may come in multiple calls
+    if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) {
+   
+        request.post="TO Be DONE"; 
+    } else {
+        request.post=NULL;
+    };
+    
+    // Search for a plugin with this urlpath
+    for (idx=0; session->plugins[idx] != NULL; idx++) {
+        if (!strcmp (session->plugins[idx]->prefix, baseurl)) {
+           jsonResponse = callPluginApi (session->plugins[idx], session, &request );
+           free (urlcpy);
+           break;
+        }
+        errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl);
+        free (urlcpy);
+        goto ExitOnError;
+
+    }
+
+    // plugin callback did not return a valid Json Object
+    if (jsonResponse == NULL) {
+       errMessage = jsonNewMessage(AFB_FATAL, "No Plugin/API for %s/%s", baseurl, baseapi);
+       goto ExitOnError;
+    }
+
+    serialized = json_object_to_json_string(jsonResponse);
+    webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY);
+
+    ret = MHD_queue_response(connection, MHD_HTTP_OK, webResponse);
+    MHD_destroy_response(webResponse);
+    json_object_put(jsonResponse); // decrease reference rqtcount to free the json object
+    return ret;
+
+ExitOnError:
+    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);
+    MHD_destroy_response(webResponse);
+    json_object_put(errMessage); // decrease reference rqtcount to free the json object
+    return ret;
+}
+
+// Helper to retreive argument from  connection
+PUBLIC const char* getQueryValue (AFB_request * request, char *name) {
+    const char *value;
+    
+    value=MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name);
+    return (value);
+}
+
+
+void *initPlugins (AFB_session *session) {
+    static AFB_plugin *plugins[10]; // no more than 10 plugins !!!
+    AFB_plugin *plugin;
+    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;
+    }
+    
+    session->plugins= plugins;   
+}
\ No newline at end of file
diff --git a/src/session.c b/src/session.c
new file mode 100644 (file)
index 0000000..2bb5b44
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "local-def.h"
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define AFB_SESSION_JTYPE "AFB_session"
+#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
+
+
+
+
+// verify we can read/write in session dir
+PUBLIC AFB_ERROR sessionCheckdir (AFB_session *session) {
+
+   int err;
+
+   // in case session dir would not exist create one
+   if (verbose) fprintf (stderr, "AFB:notice checking session dir [%s]\n", session->config->sessiondir);
+   mkdir(session->config->sessiondir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+
+   // change for session directory
+   err = chdir(session->config->sessiondir);
+   if (err) {
+     fprintf(stderr,"AFB: Fail to chdir to %s error=%s\n", session->config->sessiondir, strerror(err));
+     return err;
+   }
+
+   // verify we can write session in directory
+   json_object *dummy= json_object_new_object();
+   json_object_object_add (dummy, "checked"  , json_object_new_int (getppid()));
+   err = json_object_to_file ("./AFB-probe.json", dummy);
+   if (err < 0) return err;
+
+   return AFB_SUCCESS;
+}
+
+// let's return only sessions files
+STATIC int fileSelect (const struct dirent *entry) {
+   return (strstr (entry->d_name, ".afb") != NULL);
+}
+
+STATIC  json_object *checkCardDirExit (AFB_session *session, AFB_request *request ) {
+    int  sessionDir, cardDir;
+
+    // card name should be more than 3 character long !!!!
+    if (strlen (request->plugin) < 3) {
+       return (jsonNewMessage (AFB_FAIL,"Fail invalid plugin=%s", request->plugin));
+    }
+
+    // open session directory
+    sessionDir = open (session->config->sessiondir, O_DIRECTORY);
+    if (sessionDir < 0) {
+          return (jsonNewMessage (AFB_FAIL,"Fail to open directory [%s] error=%s", session->config->sessiondir, strerror(sessionDir)));
+    }
+
+   // create session sndcard directory if it does not exit
+    cardDir = openat (sessionDir, request->plugin,  O_DIRECTORY);
+    if (cardDir < 0) {
+          cardDir  = mkdirat (sessionDir, request->plugin, O_RDWR | S_IRWXU | S_IRGRP);
+          if (cardDir < 0) {
+              return (jsonNewMessage (AFB_FAIL,"Fail to create directory [%s/%s] error=%s", session->config->sessiondir, request->plugin, strerror(cardDir)));
+          }
+    }
+    close (sessionDir);
+    return NULL;
+}
+
+// create a session in current directory
+PUBLIC json_object *sessionList (AFB_session *session, AFB_request *request) {
+    json_object *sessionsJ, *ajgResponse;
+    struct stat fstat;
+    struct dirent **namelist;
+    int  count, sessionDir;
+
+    // if directory for card's sessions does not exist create it
+    ajgResponse = checkCardDirExit (session, request);
+    if (ajgResponse != NULL) return ajgResponse;
+
+    // open session directory
+    sessionDir = open (session->config->sessiondir, O_DIRECTORY);
+    if (sessionDir < 0) {
+          return (jsonNewMessage (AFB_FAIL,"Fail to open directory [%s] error=%s", session->config->sessiondir, strerror(sessionDir)));
+    }
+
+    count = scandirat (sessionDir, request->plugin, &namelist, fileSelect, alphasort);
+    close (sessionDir);
+
+    if (count < 0) {
+        return (jsonNewMessage (AFB_FAIL,"Fail to scan sessions directory [%s/%s] error=%s", session->config->sessiondir, request->plugin, strerror(sessionDir)));
+    }
+    if (count == 0) return (jsonNewMessage (AFB_EMPTY,"[%s] no session at [%s]", request->plugin, session->config->sessiondir));
+
+    // loop on each session file, retrieve its date and push it into json response object
+    sessionsJ = json_object_new_array();
+    while (count--) {
+         json_object *sessioninfo;
+         char timestamp [64];
+         char *filename;
+
+         // extract file name and last modification date
+         filename = namelist[count]->d_name;
+         printf("%s\n", filename);
+         stat(filename,&fstat);
+         strftime (timestamp, sizeof(timestamp), "%c", localtime (&fstat.st_mtime));
+         filename[strlen(filename)-4] = '\0'; // remove .afb extension from filename
+
+         // create an object by session with last update date
+         sessioninfo = json_object_new_object();
+         json_object_object_add (sessioninfo, "date" , json_object_new_string (timestamp));
+         json_object_object_add (sessioninfo, "session" , json_object_new_string (filename));
+         json_object_array_add (sessionsJ, sessioninfo);
+
+         free(namelist[count]);
+    }
+
+    // free scandir structure
+    free(namelist);
+
+    // everything is OK let's build final response
+    ajgResponse = json_object_new_object();
+    json_object_object_add (ajgResponse, "jtype" , json_object_new_string (AFB_SESSION_JLIST));
+    json_object_object_add (ajgResponse, "status"  , jsonNewStatus(AFB_SUCCESS));
+    json_object_object_add (ajgResponse, "data"    , sessionsJ);
+
+    return (ajgResponse);
+}
+
+// Create a link toward last used sessionname within sndcard directory
+STATIC void makeSessionLink (const char *cardname, const char *sessionname) {
+   char linkname [256], filename [256];
+   int err;
+   // create a link to keep track of last uploaded sessionname for this card
+   strncpy (filename, sessionname, sizeof(filename));
+   strncat (filename, ".afb", sizeof(filename));
+
+   strncpy (linkname, cardname, sizeof(linkname));
+   strncat (linkname, "/", sizeof(filename));
+   strncat (linkname, AFB_CURRENT_SESSION, sizeof(linkname));
+   strncat (linkname, ".afb", sizeof(filename));
+   unlink (linkname); // remove previous link if any
+   err = symlink (filename, linkname);
+   if (err < 0) fprintf (stderr, "Fail to create link %s->%s error=%s\n", linkname, filename, strerror(errno));
+}
+
+// Load Json session object from disk
+PUBLIC json_object *sessionFromDisk (AFB_session *session, AFB_request *request, char *name) {
+    json_object *jsonSession, *jtype, *response;
+    const char *ajglabel;
+    char filename [256];
+    int defsession;
+
+    if (name == NULL) {
+        return  (jsonNewMessage (AFB_FATAL,"session name missing &session=MySessionName"));
+    }
+
+    // check for current session request
+    defsession = (strcmp (name, AFB_DEFAULT_SESSION) ==0);
+
+    // if directory for card's sessions does not exist create it
+    response = checkCardDirExit (session, request);
+    if (response != NULL) return response;
+
+    // add name and file extension to session name
+    strncpy (filename, request->plugin, sizeof(filename));
+    strncat (filename, "/", sizeof(filename));
+    if (defsession) strncat (filename, AFB_CURRENT_SESSION, sizeof(filename)-1);
+    else strncat (filename, name, sizeof(filename)-1);
+    strncat (filename, ".afb", sizeof(filename));
+
+    // just upload json object and return without any further processing
+    jsonSession = json_object_from_file (filename);
+
+    if (jsonSession == NULL)  return (jsonNewMessage (AFB_EMPTY,"File [%s] not found", filename));
+
+    // verify that file is a JSON ALSA session type
+    if (!json_object_object_get_ex (jsonSession, "jtype", &jtype)) {
+        json_object_put   (jsonSession);
+        return  (jsonNewMessage (AFB_EMPTY,"File [%s] 'jtype' descriptor not found", filename));
+    }
+
+    // check type value is AFB_SESSION_JTYPE
+    ajglabel = json_object_get_string (jtype);
+    if (strcmp (AFB_SESSION_JTYPE, ajglabel)) {
+       json_object_put   (jsonSession);
+       return  (jsonNewMessage (AFB_FATAL,"File [%s] jtype=[%s] != [%s]", filename, ajglabel, AFB_SESSION_JTYPE));
+    }
+
+    // create a link to keep track of last uploaded session for this card
+    if (!defsession) makeSessionLink (request->plugin, name);
+
+    return (jsonSession);
+}
+
+// push Json session object to disk
+PUBLIC json_object * sessionToDisk (AFB_session *session, AFB_request *request, char *name, json_object *jsonSession) {
+   char filename [256];
+   time_t rawtime;
+   struct tm * timeinfo;
+   int err, defsession;
+   static json_object *response;
+
+   // we should have a session name
+   if (name == NULL) return (jsonNewMessage (AFB_FATAL,"session name missing &session=MySessionName"));
+
+   // check for current session request
+   defsession = (strcmp (name, AFB_DEFAULT_SESSION) ==0);
+
+   // if directory for card's sessions does not exist create it
+   response = checkCardDirExit (session, request);
+   if (response != NULL) return response;
+
+   // add cardname and file extension to session name
+   strncpy (filename, request->plugin, sizeof(filename));
+   strncat (filename, "/", sizeof(filename));
+   if (defsession) strncat (filename, AFB_CURRENT_SESSION, sizeof(filename)-1);
+   else strncat (filename, name, sizeof(filename)-1);
+   strncat (filename, ".afb", sizeof(filename)-1);
+
+
+   json_object_object_add(jsonSession, "jtype", json_object_new_string (AFB_SESSION_JTYPE));
+
+   // add a timestamp and store session on disk
+   time ( &rawtime );  timeinfo = localtime ( &rawtime );
+   // A copy of the string is made and the memory is managed by the json_object
+   json_object_object_add (jsonSession, "timestamp", json_object_new_string (asctime (timeinfo)));
+
+
+   // do we have extra session info ?
+   if (request->post) {
+       static json_object *info, *jtype;
+       const char  *ajglabel;
+
+       // extract session info from args
+       info = json_tokener_parse (request->post);
+       if (!info) {
+            response = jsonNewMessage (AFB_FATAL,"sndcard=%s session=%s invalid json args=%s", request->plugin, name, request->post);
+            goto OnErrorExit;
+       }
+
+       // info is a valid AFB_info type
+       if (!json_object_object_get_ex (info, "jtype", &jtype)) {
+            response = jsonNewMessage (AFB_EMPTY,"sndcard=%s session=%s No 'AFB_type' args=%s", request->plugin, name, request->post);
+            goto OnErrorExit;
+       }
+
+       // check type value is AFB_INFO_JTYPE
+       ajglabel = json_object_get_string (jtype);
+       if (strcmp (AFB_SESSION_JINFO, ajglabel)) {
+              json_object_put   (info); // release info json object
+              response = jsonNewMessage (AFB_FATAL,"File [%s] jtype=[%s] != [%s] data=%s", filename, ajglabel, AFB_SESSION_JTYPE, request->post);
+              goto OnErrorExit;
+       }
+
+       // this is valid info data for our session
+       json_object_object_add (jsonSession, "info", info);
+   }
+
+   // Finally save session on disk
+   err = json_object_to_file (filename, jsonSession);
+   if (err < 0) {
+        response = jsonNewMessage (AFB_FATAL,"Fail save session = [%s] to disk", filename);
+        goto OnErrorExit;
+   }
+
+
+   // create a link to keep track of last uploaded session for this card
+   if (!defsession) makeSessionLink (request->plugin, name);
+
+   // we're donne let's return status message
+   response = jsonNewMessage (AFB_SUCCESS,"Session= [%s] saved on disk", filename);
+   json_object_put (jsonSession);
+   return (response);
+
+OnErrorExit:
+   json_object_put (jsonSession);
+   return response;
+}