From b55efc33fb8df8b0518570b2584b6da9abb3221b Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Wed, 9 Dec 2015 01:27:23 +0100 Subject: Added POST, Plugin API signal protection, refactor HTML5 rewrite --- .gitignore | 8 ++ include/local-def.h | 10 +- include/proto-def.h | 10 +- nbproject/Makefile-Debug.mk | 26 ++-- nbproject/Makefile-Release.mk | 8 +- nbproject/Makefile-impl.mk | 2 +- nbproject/Makefile-variables.mk | 16 +-- nbproject/Package-Debug.bash | 12 +- nbproject/Package-Release.bash | 12 +- nbproject/configurations.xml | 2 + nbproject/project.xml | 2 +- src/afbs-api.c | 38 ++--- src/alsa-api.c | 43 ++++-- src/config.c | 13 +- src/dbus-api.c | 30 +++- src/http-svc.c | 134 +++++++++--------- src/main.c | 7 + src/rest-api.c | 299 ++++++++++++++++++++++++++++++---------- 18 files changed, 446 insertions(+), 226 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6e3b026e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/nbproject/private/ +amixer +autom4te.cache +config.log +build/** +dist/** +!.gitignore +.dep.inc \ No newline at end of file diff --git a/include/local-def.h b/include/local-def.h index 3f0a26a0..72c51dea 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -40,6 +40,10 @@ /* other definitions --------------------------------------------------- */ +// Note: because of a bug in libmagic MAGIC_DB NULL should not be used for default +#define MAGIC_DB "/usr/share/misc/magic.mgc" +#define OPA_INDEX "index.html" + typedef int BOOL; #ifndef FALSE #define FALSE 0 @@ -97,8 +101,8 @@ typedef struct { } AFB_request; typedef struct { - char *msg; - int len; + char *msg; + size_t len; } AFB_redirect_msg; // main config structure @@ -117,6 +121,7 @@ typedef struct { char *configfile; // where to store configuration on gateway exit uid_t setuid; int cacheTimeout; + int apiTimeout; } AFB_config; // Command line structure hold cli --command + help text @@ -134,6 +139,7 @@ typedef struct { char *name; AFB_apiCB callback; char *info; + void * handle; } AFB_restapi; // Plugin definition diff --git a/include/proto-def.h b/include/proto-def.h index d1b0c3fb..3e1d1c38 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -21,9 +21,15 @@ */ // Rest-api -PUBLIC json_object* pingSample (AFB_plugin *plugin, AFB_session *session, AFB_request *post); + +PUBLIC json_object* apiPingTest(AFB_session *session, AFB_request *request, void* handle); PUBLIC const char* getQueryValue (AFB_request * request, char *name); -PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char *method, const char* url); +PUBLIC const char* getQueryAll(AFB_request * request, char *query, size_t len); + + +PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char* url, const char *method + , const char *upload_data, size_t *upload_data_size, void **con_cls); + void initPlugins (AFB_session *session); typedef AFB_plugin* (*AFB_pluginCB)(AFB_session *session); diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index 1866ff18..39692f38 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -59,55 +59,55 @@ FFLAGS= ASFLAGS= # Link Libraries and Options -LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` -lmagic +LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` -lefence -lmagic # Build Targets .build-conf: ${BUILD_SUBPROJECTS} - "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder + "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon -${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder: ${OBJECTFILES} +${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon: ${OBJECTFILES} ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} - ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder ${OBJECTFILES} ${LDLIBSOPTIONS} + ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon ${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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `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 + $(COMPILE.c) -g -I/usr/include/json-c -Iinclude -I/opt/libmagic/include `pkg-config --cflags libmicrohttpd` `pkg-config --cflags json-c` -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/session.o src/session.c # Subprojects .build-subprojects: @@ -115,7 +115,7 @@ ${OBJECTDIR}/src/session.o: src/session.c # Clean Targets .clean-conf: ${CLEAN_SUBPROJECTS} ${RM} -r ${CND_BUILDDIR}/${CND_CONF} - ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder + ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon # Subprojects .clean-subprojects: diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk index 6df0bda6..7cbdfe86 100644 --- a/nbproject/Makefile-Release.mk +++ b/nbproject/Makefile-Release.mk @@ -63,11 +63,11 @@ LDLIBSOPTIONS= # Build Targets .build-conf: ${BUILD_SUBPROJECTS} - "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder + "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon -${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder: ${OBJECTFILES} +${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon: ${OBJECTFILES} ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} - ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder ${OBJECTFILES} ${LDLIBSOPTIONS} + ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon ${OBJECTFILES} ${LDLIBSOPTIONS} ${OBJECTDIR}/src/afbs-api.o: src/afbs-api.c ${MKDIR} -p ${OBJECTDIR}/src @@ -115,7 +115,7 @@ ${OBJECTDIR}/src/session.o: src/session.c # Clean Targets .clean-conf: ${CLEAN_SUBPROJECTS} ${RM} -r ${CND_BUILDDIR}/${CND_CONF} - ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/appframeworkbinder + ${RM} ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon # Subprojects .clean-subprojects: diff --git a/nbproject/Makefile-impl.mk b/nbproject/Makefile-impl.mk index 185e4af2..a2d5e848 100644 --- a/nbproject/Makefile-impl.mk +++ b/nbproject/Makefile-impl.mk @@ -24,7 +24,7 @@ CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} # Project Name -PROJECTNAME=AppFrameworkBinder +PROJECTNAME=afb-daemon # Active Configuration DEFAULTCONF=Debug diff --git a/nbproject/Makefile-variables.mk b/nbproject/Makefile-variables.mk index 4e44a219..60514673 100644 --- a/nbproject/Makefile-variables.mk +++ b/nbproject/Makefile-variables.mk @@ -9,19 +9,19 @@ 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_ARTIFACT_NAME_Debug=afb-daemon +CND_ARTIFACT_PATH_Debug=dist/Debug/GNU-Linux/afb-daemon 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 +CND_PACKAGE_NAME_Debug=afb-daemon.tar +CND_PACKAGE_PATH_Debug=dist/Debug/GNU-Linux/package/afb-daemon.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_ARTIFACT_NAME_Release=afb-daemon +CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux/afb-daemon 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 +CND_PACKAGE_NAME_Release=afb-daemon.tar +CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux/package/afb-daemon.tar # # include compiler specific variables # diff --git a/nbproject/Package-Debug.bash b/nbproject/Package-Debug.bash index 26359837..b7d25543 100644 --- a/nbproject/Package-Debug.bash +++ b/nbproject/Package-Debug.bash @@ -13,9 +13,9 @@ 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/ +OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon +OUTPUT_BASENAME=afb-daemon +PACKAGE_TOP_DIR=afb-daemon/ # Functions function checkReturnCode @@ -60,15 +60,15 @@ mkdir -p ${NBTMPDIR} # Copy files and create directories and links cd "${TOP}" -makeDirectory "${NBTMPDIR}/appframeworkbinder/bin" +makeDirectory "${NBTMPDIR}/afb-daemon/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 +rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/afb-daemon.tar cd ${NBTMPDIR} -tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar * +tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/afb-daemon.tar * checkReturnCode # Cleanup diff --git a/nbproject/Package-Release.bash b/nbproject/Package-Release.bash index 4ef9af39..627cc4c8 100644 --- a/nbproject/Package-Release.bash +++ b/nbproject/Package-Release.bash @@ -13,9 +13,9 @@ 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/ +OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/afb-daemon +OUTPUT_BASENAME=afb-daemon +PACKAGE_TOP_DIR=afb-daemon/ # Functions function checkReturnCode @@ -60,15 +60,15 @@ mkdir -p ${NBTMPDIR} # Copy files and create directories and links cd "${TOP}" -makeDirectory "${NBTMPDIR}/appframeworkbinder/bin" +makeDirectory "${NBTMPDIR}/afb-daemon/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 +rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/afb-daemon.tar cd ${NBTMPDIR} -tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/appframeworkbinder.tar * +tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/afb-daemon.tar * checkReturnCode # Cleanup diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index c0793702..ce722f75 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -46,12 +46,14 @@ /usr/include/json-c include + /opt/libmagic/include `pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` + efence magic diff --git a/nbproject/project.xml b/nbproject/project.xml index 394601fb..91920333 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -3,7 +3,7 @@ org.netbeans.modules.cnd.makeproject - AppFrameworkBinder + afb-daemon c diff --git a/src/afbs-api.c b/src/afbs-api.c index 40e89d02..b55ebf60 100644 --- a/src/afbs-api.c +++ b/src/afbs-api.c @@ -19,31 +19,35 @@ #include "local-def.h" -STATIC json_object* pingAfbs (AFB_session *session, AFB_request *request) { - static pingcount=0; + +STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { + static pingcount = 0; json_object *response; - const char * argval; + char query [512]; + + // request all query key/value + getQueryAll (request, query, sizeof(query)); - argval=getQueryValue (request, "arg"); - if (argval == NULL) { - argval="No present in query"; - }; + // check if we have some post data + if (request->post == NULL) request->post="NoData"; + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); - 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"}, + {"ping" , (AFB_apiCB)pingSample ,"Ping Service", NULL}, + {"get-all" , (AFB_apiCB)pingSample ,"Ping Application Framework", NULL}, + {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, + {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, + {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, + {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, + {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, + {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode", NULL}, {0,0,0} }; diff --git a/src/alsa-api.c b/src/alsa-api.c index ab7dced3..01341ce0 100644 --- a/src/alsa-api.c +++ b/src/alsa-api.c @@ -19,24 +19,43 @@ #include "local-def.h" -STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct MHD_Connection *connection, AFB_request *request) { - static pingcount=0; +STATIC json_object* wrongApi (AFB_session *session, AFB_request *request, void* handle) { + int zero=0; + int bug=1234; + int impossible; + + impossible=bug/zero; +} + +STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { + static pingcount = 0; json_object *response; - response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++); + char query [512]; + + // request all query key/value + getQueryAll (request, query, sizeof(query)); + + // check if we have some post data + if (request->post == NULL) request->post="NoData"; + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); + if (verbose) fprintf(stderr, "%d: \n", pingcount); return (response); -}; +} + + +STATIC struct { + void * somedata; +} handle; STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample ,"Ping Service"}, - {"get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, - {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework", NULL}, + {"error" , (AFB_apiCB)wrongApi , "Ping Application Framework", NULL}, + {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode", NULL}, + {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode", NULL}, {0,0,0} }; diff --git a/src/config.c b/src/config.c index 6691ca03..ae1830d1 100644 --- a/src/config.c +++ b/src/config.c @@ -64,6 +64,10 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { // default HTTP port if (cliconfig->httpdPort == 0) session->config->httpdPort=1234; else session->config->httpdPort=cliconfig->httpdPort; + + // default Plugin API timeout + if (cliconfig->apiTimeout == 0) session->config->apiTimeout=10; + else session->config->apiTimeout=cliconfig->apiTimeout; // cache timeout default one hour if (cliconfig->cacheTimeout == 0) session->config->cacheTimeout=3600; @@ -206,9 +210,15 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { 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 + + if (!cliconfig->apiTimeout && json_object_object_get_ex (AFBConfig, "apitimeout", &value)) { + session->config->apiTimeout = json_object_get_int (value); + } + + // cacheTimeout is an integer but HTTPd wants it as a string snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout); session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version + json_object_put (AFBConfig); // decrease reference count to free the json object @@ -241,6 +251,7 @@ PUBLIC void configStoreFile (AFB_session * session) { json_object_object_add (AFBConfig, "setuid" , json_object_new_int (session->config->setuid)); json_object_object_add (AFBConfig, "localhostonly" , json_object_new_int (session->config->localhostOnly)); json_object_object_add (AFBConfig, "cachetimeout" , json_object_new_int (session->config->cacheTimeout)); + json_object_object_add (AFBConfig, "apitimeout" , json_object_new_int (session->config->apiTimeout)); err = json_object_to_file (session->config->configfile, AFBConfig); json_object_put (AFBConfig); // decrease reference count to free the json object diff --git a/src/dbus-api.c b/src/dbus-api.c index 9d34e436..1f81bb2f 100644 --- a/src/dbus-api.c +++ b/src/dbus-api.c @@ -19,22 +19,38 @@ #include "local-def.h" -STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, AFB_request *request) { - static pingcount=0; +STATIC json_object* pingSample (AFB_session *session, AFB_request *request, void* handle) { + static pingcount = 0; json_object *response; - response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++); + char query [512]; + + // request all query key/value + getQueryAll (request, query, sizeof(query)); + + // check if we have some post data + if (request->post == NULL) request->post="NoData"; + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); + if (verbose) fprintf(stderr, "%d: \n", pingcount); return (response); -}; +} + + +STATIC struct { + void * somedata; +} handle; STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"}, - {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"}, - {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"}, + {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework", NULL}, + {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode", NULL}, + {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode", NULL}, {0,0,0} }; + PUBLIC AFB_plugin *dbusRegister (AFB_session *session) { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN; diff --git a/src/http-svc.c b/src/http-svc.c index 836915a7..cd488961 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -47,25 +47,23 @@ static int postcount = 0; // try to open libmagic to handle mime types static AFB_error initLibMagic (AFB_session *session) { - const char *magic_full; /*MAGIC_MIME tells magic to return a mime of the file, but you can specify different things*/ if (verbose) printf("Loading mimetype default magic database\n"); - session->magic = magic_open(MAGIC_MIME); + session->magic = magic_open(MAGIC_MIME_TYPE); if (session->magic == NULL) { fprintf(stderr,"ERROR: unable to initialize magic library\n"); return AFB_FAIL; } - if (magic_load(session->magic, NULL) != 0) { + + // Warning: should not use NULL for DB [libmagic bug wont pass efence check] + if (magic_load(session->magic, MAGIC_DB) != 0) { fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic)); magic_close(session->magic); return AFB_FAIL; } - - //usage - //magic_full = magic_file(magic_cookie, actual_file); - //printf("%s\n", magic_full); + return AFB_SUCCESS; } @@ -98,30 +96,40 @@ STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, co if (fstat (staticfile->fd, &sbuf) != 0) { fprintf(stderr, "Fail to stat file: [%s] error:%s\n", staticfile->path, strerror(errno)); - return (FAILED); + goto abortRequest; } - // if url is a directory let's add index.html and redirect client - if (S_ISDIR (sbuf.st_mode)) { - if (url [strlen (url) -1] != '/') strncat (staticfile->path, "/", sizeof (staticfile->path)); - strncat (staticfile->path, "index.html", sizeof (staticfile->path)); - close (staticfile->fd); // close directory try to open index.html - if (-1 == (staticfile->fd = open(staticfile->path, O_RDONLY)) || (fstat (staticfile->fd, &sbuf) != 0)) { - fprintf(stderr, "No Index.html in direcory [%s]\n", staticfile->path); - return (FAILED); - } - - } else if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused. - + if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused. + close (staticfile->fd); // nothing useful to do with this file fprintf (stderr, "Fail file: [%s] is not a regular file\n", staticfile->path); const char *errorstr = "Alsa-Json-Gateway Invalid file type"; response = MHD_create_response_from_buffer (strlen (errorstr), (void *) errorstr, MHD_RESPMEM_PERSISTENT); MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); - goto finishJob; - + goto sendRequest; } + // if url is a directory let's add index.html and redirect client + if (S_ISDIR (sbuf.st_mode)) { + close (staticfile->fd); // close directory check for Index + + // No trailing '/'. Let's add one and redirect for relative paths to work + if (url [strlen (url) -1] != '/') { + response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT); + strncat(staticfile->path, "/", sizeof (staticfile->path)); + MHD_add_response_header (response, "Location", staticfile->path); + MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); + if (verbose) fprintf (stderr,"Adding trailing '/' [%s]\n",staticfile->path); + goto sendRequest; + } + + strncat (staticfile->path, OPA_INDEX, sizeof (staticfile->path)); + if (-1 == (staticfile->fd = open(staticfile->path, O_RDONLY)) || (fstat (staticfile->fd, &sbuf) != 0)) { + fprintf(stderr, "No Index.html in direcory [%s]\n", staticfile->path); + goto abortRequest; + } + } + // 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 @@ -139,23 +147,47 @@ STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, co } else { // it's a new file, we need to upload it to client // if we have magic let's try to guest mime type - if (session->magic) { - mimetype="Unknown"; + if (session->magic) { mimetype= magic_descriptor(session->magic, staticfile->fd); if (mimetype != NULL) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype); - }; - if (verbose) fprintf(stderr, "Serving: [%s] mine=%s\n", staticfile->path, mimetype); + } else mimetype="Unknown"; + + if (verbose) fprintf(stderr, "Serving: [%s] mime=%s\n", staticfile->path, mimetype); response = MHD_create_response_from_fd(sbuf.st_size, staticfile->fd); MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue); MHD_queue_response(connection, MHD_HTTP_OK, response); } -finishJob: +sendRequest: MHD_destroy_response(response); return (MHD_YES); + +abortRequest: + return (FAILED); +} + + +// this function return either Index.htlm or a redirect to /#!route to make angular happy +STATIC int redirectHTML5(struct MHD_Connection *connection, AFB_session *session, const char* url) { + + int fd; + int ret; + struct MHD_Response *response; + AFB_staticfile staticfile; + + // Url match /opa/xxxx should redirect to "/opa/#!page" to force index.html reload + strncpy(staticfile.path, session->config->rootbase, sizeof (staticfile.path)); + strncat(staticfile.path, "/#!", sizeof (staticfile.path)); + strncat(staticfile.path, &url[1], sizeof (staticfile.path)); + response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header (response, "Location", staticfile.path); + MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); + if (verbose) fprintf (stderr,"checkHTML5 redirect to [%s]\n",staticfile.path); + return (MHD_YES); } + // 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; @@ -172,50 +204,12 @@ STATIC int requestFile(struct MHD_Connection *connection, AFB_session *session, if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) { fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno)); return (FAILED); - } // open file is OK let use it ret = servFile (connection, session, url, &staticfile); 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; - AFB_staticfile staticfile; - - // Any URL prefixed with /rootbase is served with index.html ex: /opa,/opa/,/opa/#!xxxxxx - if ( url[0] == '\0' || url[1] == '\0' || url[1] == '#') { - strncpy(staticfile.path, session->config->rootdir, sizeof (staticfile.path)); - strncat(staticfile.path, "/index.html", sizeof (staticfile.path)); - // try to open file and get its size - if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) { - fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno)); - // Nothing respond to this request Files, API, Angular Base - const char *errorstr = "Application Framework OPA/index.html Not found"; - response = MHD_create_response_from_buffer(strlen(errorstr),(void *)errorstr, MHD_RESPMEM_PERSISTENT); - MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); - return (MHD_YES); - } else { - ret = servFile (connection, session, url, &staticfile); - return ret; - } - } - - // Url match /opa/xxxx but not /opa#!xxxx we redirect the URL to /opa#!/xxxx to force index.html reload - strncpy(staticfile.path, session->config->rootbase, sizeof (staticfile.path)); - strncat(staticfile.path, "#!", sizeof (staticfile.path)); - strncat(staticfile.path, url, sizeof (staticfile.path)); - response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header (response, "Location", staticfile.path); - MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); - if (verbose) fprintf (stderr,"checkHTML5 redirect to [%s]\n",staticfile.path); - return (MHD_YES); -} - // Check and Dispatch HTTP request STATIC int newRequest(void *cls, struct MHD_Connection *connection, @@ -230,7 +224,7 @@ STATIC int newRequest(void *cls, // this is a REST API let's check for plugins if (0 == strncmp(url, session->config->rootapi, apiUrlLen)) { - ret = doRestApi(connection, session, method, &url[apiUrlLen+1]); + ret = doRestApi(connection, session, &url[apiUrlLen+1], method, upload_data, upload_data_size, con_cls); return ret; } @@ -241,9 +235,9 @@ STATIC int newRequest(void *cls, ret = requestFile(connection, session, url); if (ret != FAILED) return ret; - // no static was served let check for Angular redirect + // no static was served let's try HTML5 OPA redirect if (0 == strncmp(url, session->config->rootbase, baseUrlLen)) { - ret = checkHTML5(connection, session, &url[baseUrlLen]); + ret = redirectHTML5(connection, session, &url[baseUrlLen]); return ret; } @@ -267,7 +261,7 @@ PUBLIC AFB_error httpdStart(AFB_session *session) { baseUrlLen= strlen (session->config->rootbase); // open libmagic cache - initLibMagic (session); + // initLibMagic (session); if (verbose) { @@ -299,7 +293,7 @@ PUBLIC AFB_error httpdLoop(AFB_session *session) { if (session->foreground) { while (TRUE) { - fprintf(stderr, "AFB:notice Use Ctrl-C to quit"); + fprintf(stderr, "AFB:notice Use Ctrl-C to quit\n"); (void) getc(stdin); } } else { diff --git a/src/main.c b/src/main.c index a86de1c5..9586bb56 100644 --- a/src/main.c +++ b/src/main.c @@ -72,6 +72,7 @@ static sigjmp_buf restartpoint; // context save for set/longjmp #define SET_SMACK 140 #define SET_PLUGINS 141 + #define SET_APITIMEOUT 142 #define DISPLAY_VERSION 150 #define DISPLAY_HELP 151 @@ -90,6 +91,7 @@ static AFB_options cliOptions [] = { {SET_ROOT_DIR ,1,"rootdir" , "HTTP Root Directory [default $HOME/.AFB"}, {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /opa"}, {SET_ROOT_API ,1,"rootapi" , "HTML Root API URL [default /api"}, + {SET_APITIMEOUT ,1,"apitimeout" , "Plugin API timeout in seconds [default 10]"}, {SET_CACHE_TO ,1,"cache-eol" , "Client cache end of live [default 3600s]"}, {SET_cardid ,1,"setuid" , "Change user id [default don't change]"}, @@ -301,6 +303,11 @@ int main(int argc, char *argv[]) { if (optarg == 0) goto needValueForOption; if (!sscanf (optarg, "%d", &cliconfig.httpdPort)) goto notAnInteger; break; + + case SET_APITIMEOUT: + if (optarg == 0) goto needValueForOption; + if (!sscanf (optarg, "%d", &cliconfig.apiTimeout)) goto notAnInteger; + break; case SET_ROOT_DIR: if (optarg == 0) goto needValueForOption; diff --git a/src/rest-api.c b/src/rest-api.c index 74237adc..39a7286d 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -18,16 +18,69 @@ * Contain all generic part to handle REST/API */ - -#include -#include #include "../include/local-def.h" -// proto missing from GCC -char *strcasestr(const char *haystack, const char *needle); +#include +#include + +// context save for timeout set/longjmp +static sigjmp_buf checkPluginCall; + +// handle to hold queryAll values +typedef struct { + char *msg; + int idx; + size_t len; +} queryHandleT; + + +// Helper to retrieve 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); +} + +STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) { + queryHandleT *query = (queryHandleT*)handle; + + query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value); +} + +// Helper to retrieve argument from connection +PUBLIC const char* getQueryAll(AFB_request * request, char *buffer, size_t len) { + queryHandleT query; + + query.msg= buffer; + query.len= len; + query.idx= 0; + + MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query); + return (query.msg); +} + + +// Sample Generic Ping Debug API +PUBLIC json_object* apiPingTest(AFB_session *session, AFB_request *request, void* handle) { + static pingcount = 0; + json_object *response; + char query [512]; + + // request all query key/value + getQueryAll (request, query, sizeof(query)); + + // check if we have some post data + if (request->post == NULL) request->post="NoData"; + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); + return (response); +} // Because of POST call multiple time requestApi we need to free POST handle here + STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { AFB_HttpPost *posthandle = *con_cls; @@ -39,92 +92,192 @@ STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_ } } +/*---------------------------------------------------------- + | timeout signalQuit + +--------------------------------------------------------- */ +STATIC void pluginError (int signum) { -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); + sigset_t sigset; + + // unlock timeout signal to allow a new signal to come + sigemptyset (&sigset); + sigaddset (&sigset, SIGALRM); + sigprocmask (SIG_UNBLOCK, &sigset, 0); + + fprintf (stderr, "Oops:%s Plugin Api Timeout timeout\n", configTime()); + longjmp (checkPluginCall, signum); } + // Check of apiurl is declare in this plugin and call it -STATIC json_object * callPluginApi (AFB_plugin *plugin, AFB_session *session, AFB_request *request) { + +STATIC json_object * callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_request *request) { json_object *response; - int idx; - + int idx, status; + // 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); - } + for (idx = 0; plugin->apis[idx].callback != NULL; idx++) { + if (!strcmp(plugin->apis[idx].name, request->api)) { + + // save context before calling the API + status = setjmp (checkPluginCall); + if (status != 0) { + response = jsonNewMessage(AFB_FATAL, "Plugin Call Fail prefix=%s api=%s info=%s", plugin->prefix, request->api, plugin->info); + } else { + if (signal (SIGALRM, pluginError) == SIG_ERR) { + fprintf (stderr, "%s ERR: main no Signal/timeout handler installed.", configTime()); + return NULL; + } + + if (signal (SIGSEGV, pluginError) == SIG_ERR) { + fprintf (stderr, "%s ERR: main no Signal/memory handler installed.", configTime()); + return NULL; + } + + if (signal (SIGFPE , pluginError) == SIG_ERR) { + fprintf (stderr, "%s ERR: main no Signal/memory handler installed.", configTime()); + return NULL; + } + + // protect plugin call with a timeout + alarm (session->config->apiTimeout); + + response = plugin->apis[idx].callback(session, request, plugin->apis[idx].handle); + if (response != NULL) json_object_object_add(response, "jtype", plugin->jtype); + + // cancel timeout and sleep before next aquisition + alarm (0); + signal(SIGALRM, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE , SIG_DFL); + } + 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; +PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char* url, const char *method + , const char *upload_data, size_t *upload_data_size, void **con_cls) { + + static int postcount = 0; // static counter to debug POST protocol + char *baseurl, *baseapi, *urlcpy1, *urlcpy2, *query; json_object *jsonResponse, *errMessage; struct MHD_Response *webResponse; const char *serialized, parsedurl; AFB_request request; - int idx, ret; + AFB_HttpPost *posthandle = *con_cls; + int idx, ret; - // Extract plugin urlpath from request - urlcpy=strdup (url); - baseurl = strsep(&urlcpy, "/"); + // Extract plugin urlpath from request and make two copy because strsep overload copy + urlcpy1 = urlcpy2 = strdup(url); + baseurl = strsep(&urlcpy2, "/"); if (baseurl == NULL) { errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s", url); goto ExitOnError; } - - baseapi = strsep(&urlcpy, "/"); + + baseapi = strsep(&urlcpy2, "/"); 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"; + // if post data may come in multiple calls + if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) { + const char *encoding, *param; + int contentlen = -1; + AFB_HttpPost *posthandle = *con_cls; + + // Let make sure we have the right encoding and a valid length + encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); + param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); + if (param) sscanf(param, "%i", &contentlen); + + // POST datas may come in multiple chunk. Even when it never happen on AFB, we still have to handle the case + if (strcasestr(encoding, JSON_CONTENT) == 0) { + errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT); + goto ExitOnError; + } + + if (contentlen > MAX_POST_SIZE) { + errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE); + goto ExitOnError; + } + + // In POST mode first libmicrohttp call only establishes POST handling. + if (posthandle == NULL) { + posthandle = malloc(sizeof (AFB_HttpPost)); // allocate application POST processor handle + posthandle->uid = postcount++; // build a UID for DEBUG + posthandle->len = 0; // effective length within POST handler + posthandle->data = malloc(contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string + *con_cls = posthandle; // attache POST handle to current HTTP session + + if (verbose) fprintf(stderr, "Create Post[%d] Size=%d\n", posthandle->uid, contentlen); + return MHD_YES; + } + + // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless + // return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with + // 'Internal application error, closing connection'. + if (*upload_data_size) { + if (verbose) fprintf(stderr, "Update Post[%d]\n", posthandle->uid); + + memcpy(&posthandle->data[posthandle->len], upload_data, *upload_data_size); + posthandle->len = posthandle->len + *upload_data_size; + *upload_data_size = 0; + return MHD_YES; + } + + // We should only start to process DATA after Libmicrohttpd call or application handler with *upload_data_size==0 + // At this level we're may verify that we got everything and process DATA + if (posthandle->len != contentlen) { + errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %s", posthandle->uid, contentlen, posthandle->len); + goto ExitOnError; + } + + // Before processing data, make sure buffer string is properly ended + posthandle->data[posthandle->len] = '\0'; + request.post = posthandle->data; + + if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", posthandle->uid, request.post); + } else { - request.post=NULL; + request.post = NULL; }; + + + // build request structure + memset(&request, 0, sizeof (request)); + request.connection = connection; + request.url = url; + request.plugin = baseurl; + request.api = baseapi; // 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; + for (idx = 0; session->plugins[idx] != NULL; idx++) { + if (!strcmp(session->plugins[idx]->prefix, baseurl)) { + jsonResponse = callPluginApi(session->plugins[idx], session, &request); + free(urlcpy1); + break; } } // No plugin was found if (session->plugins[idx] == NULL) { errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl); - free (urlcpy); + free(urlcpy1); 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; + errMessage = jsonNewMessage(AFB_FATAL, "No Plugin/API for %s/%s", baseurl, baseapi); + goto ExitOnError; } serialized = json_object_to_json_string(jsonResponse); @@ -144,51 +297,45 @@ ExitOnError: 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); -} // Loop on plugins. Check that they have the right type, prepare a JSON object with prefix + STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { int idx; - - for (idx=0; plugins[idx] != NULL; idx++) { + + for (idx = 0; plugins[idx] != NULL; idx++) { if (plugins[idx]->type != AFB_PLUGIN) { - fprintf (stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN, plugins[idx]->type); + fprintf(stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN, plugins[idx]->type); } else { // some sanity controls - if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)){ + if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)) { if (plugins[idx]->prefix == NULL) plugins[idx]->prefix = "No URL prefix for APIs"; if (plugins[idx]->info == NULL) plugins[idx]->info = "No Info describing plugin APIs"; - fprintf (stderr, "ERROR: plugin[%d] invalid prefix=%s info=%s", idx,plugins[idx]->prefix, plugins[idx]->info); + fprintf(stderr, "ERROR: plugin[%d] invalid prefix=%s info=%s", idx, plugins[idx]->prefix, plugins[idx]->info); return NULL; } - - if (verbose) fprintf (stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info); - + + if (verbose) fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info); + // Prepare Plugin name to be added into each API response - plugins[idx]->jtype = json_object_new_string (plugins[idx]->prefix); - json_object_get (plugins[idx]->jtype); // increase reference count to make it permanent - + plugins[idx]->jtype = json_object_new_string(plugins[idx]->prefix); + json_object_get(plugins[idx]->jtype); // increase reference count to make it permanent + // compute urlprefix lenght - plugins[idx]->prefixlen = strlen (plugins[idx]->prefix); - } + plugins[idx]->prefixlen = strlen(plugins[idx]->prefix); + } } return (plugins); } -void initPlugins (AFB_session *session) { - static AFB_plugin *plugins[10]; +void initPlugins(AFB_session *session) { + static AFB_plugin * plugins[10]; - plugins[0]= afsvRegister (session), - plugins[1]= dbusRegister (session), - plugins[2]= alsaRegister (session), - plugins[3]= NULL; + plugins[0] = afsvRegister(session), + plugins[1] = dbusRegister(session), + plugins[2] = alsaRegister(session), + plugins[3] = NULL; // complete plugins and save them within current sessions - session->plugins= RegisterPlugins (plugins); + session->plugins = RegisterPlugins(plugins); } \ No newline at end of file -- cgit 1.2.3-korg