summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile129
-rw-r--r--include/local-def.h160
-rw-r--r--include/proto-def.h53
-rw-r--r--nbproject/Makefile-Debug.mk126
-rw-r--r--nbproject/Makefile-Release.mk126
-rw-r--r--nbproject/Makefile-impl.mk133
-rw-r--r--nbproject/Makefile-variables.mk35
-rw-r--r--nbproject/Package-Debug.bash76
-rw-r--r--nbproject/Package-Release.bash76
-rw-r--r--nbproject/configurations.xml113
-rw-r--r--nbproject/project.xml28
-rw-r--r--src/afbs-api.c58
-rw-r--r--src/alsa-api.c51
-rw-r--r--src/config.c335
-rw-r--r--src/dbus-api.c51
-rw-r--r--src/http-svc.c283
-rw-r--r--src/main.c611
-rw-r--r--src/rest-api.c175
-rw-r--r--src/session.c302
19 files changed, 2921 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..180d7a16
--- /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
index 00000000..f69ddcbe
--- /dev/null
+++ b/include/local-def.h
@@ -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
index 00000000..934bf30f
--- /dev/null
+++ b/include/proto-def.h
@@ -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
index 00000000..fb778c72
--- /dev/null
+++ b/nbproject/Makefile-Debug.mk
@@ -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
index 00000000..6df0bda6
--- /dev/null
+++ b/nbproject/Makefile-Release.mk
@@ -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
index 00000000..185e4af2
--- /dev/null
+++ b/nbproject/Makefile-impl.mk
@@ -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
index 00000000..4e44a219
--- /dev/null
+++ b/nbproject/Makefile-variables.mk
@@ -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
index 00000000..26359837
--- /dev/null
+++ b/nbproject/Package-Debug.bash
@@ -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
index 00000000..4ef9af39
--- /dev/null
+++ b/nbproject/Package-Release.bash
@@ -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
index 00000000..f30d8c0b
--- /dev/null
+++ b/nbproject/configurations.xml
@@ -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
index 00000000..394601fb
--- /dev/null
+++ b/nbproject/project.xml
@@ -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
index 00000000..5b7d4896
--- /dev/null
+++ b/src/afbs-api.c
@@ -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
index 00000000..dca372ab
--- /dev/null
+++ b/src/alsa-api.c
@@ -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
index 00000000..893b3a5f
--- /dev/null
+++ b/src/config.c
@@ -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
index 00000000..febe87d9
--- /dev/null
+++ b/src/dbus-api.c
@@ -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
index 00000000..b29a4bb8
--- /dev/null
+++ b/src/http-svc.c
@@ -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
index 00000000..795c2a7b
--- /dev/null
+++ b/src/main.c
@@ -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
index 00000000..3b5c53e1
--- /dev/null
+++ b/src/rest-api.c
@@ -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
index 00000000..2bb5b442
--- /dev/null
+++ b/src/session.c
@@ -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;
+}