diff --git a/aqhome/http/0BUILD b/aqhome/http/0BUILD
index a185b6f..e998d7c 100644
--- a/aqhome/http/0BUILD
+++ b/aqhome/http/0BUILD
@@ -47,12 +47,16 @@
endpoint_http.h
httpservice.h
+ httpservice_conf.h
+ httpservice_http.h
+ httprequest.h
endpoint_http_p.h
httpservice_p.h
+ httprequest_p.h
@@ -61,6 +65,9 @@
endpoint_http.c
httpservice.c
+ httpservice_conf.c
+ httpservice_http.c
+ httprequest.c
diff --git a/aqhome/http/httprequest.c b/aqhome/http/httprequest.c
new file mode 100644
index 0000000..bedec70
--- /dev/null
+++ b/aqhome/http/httprequest.c
@@ -0,0 +1,605 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "./httprequest_p.h"
+
+#include
+#include
+
+
+#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid"
+
+
+GWEN_TREE2_FUNCTIONS(AQH_HTTP_REQUEST, AQH_HttpRequest);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static int _getParsedDbInfo(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
+static int _inspectReceivedMessage(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
+static int _inspectMsgCommand(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
+static void _inspectMsgHeader(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
+static int _inspectMsgBody(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
+static void _setModuleNameFromUrl(AQH_HTTP_REQUEST *rq);
+static GWEN_DB_NODE *_extractCookies(GWEN_DB_NODE *dbHeader);
+static void _setCookieValue(GWEN_DB_NODE *dbCookies,
+ const char *nameStart, int nameLen,
+ const char *valueStart, int valueLen,
+ uint32_t dbFlags);
+static int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+AQH_HTTP_REQUEST *AQH_HttpRequest_new(GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *receivedMsg)
+{
+ AQH_HTTP_REQUEST *rq;
+ int rv;
+
+ GWEN_NEW_OBJECT(AQH_HTTP_REQUEST, rq);
+ GWEN_TREE2_INIT(AQH_HTTP_REQUEST, rq, AQH_HttpRequest);
+
+ rq->endpoint=endpoint;
+ rq->receivedMsg=receivedMsg;
+
+ rv=_inspectReceivedMessage(rq, receivedMsg);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ AQH_HttpRequest_free(rq);
+ return NULL;
+ }
+ return rq;
+}
+
+
+
+void AQH_HttpRequest_free(AQH_HTTP_REQUEST *rq)
+{
+ if (rq) {
+ GWEN_TREE2_FINI(AQH_HTTP_REQUEST, rq, AQH_HttpRequest);
+ free(rq->sessionId);
+ free(rq->moduleName);
+ free(rq->responseText);
+ GWEN_Msg_free(rq->responseMsg);
+ GWEN_Url_free(rq->url);
+ GWEN_DB_Group_free(rq->dbPostBody);
+
+ GWEN_FREE_OBJECT(rq);
+ }
+}
+
+
+
+GWEN_MSG_ENDPOINT *AQH_HttpRequest_GetEndpoint(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->endpoint):NULL;
+}
+
+
+
+const GWEN_MSG *AQH_HttpRequest_GetReceivedMsg(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->receivedMsg):NULL;
+}
+
+
+
+const char *AQH_HttpRequest_GetCommand(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->command):NULL;
+}
+
+
+
+const char *AQH_HttpRequest_GetProtocol(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->protocol):NULL;
+}
+
+
+
+GWEN_URL *AQH_HttpRequest_GetUrl(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->url):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetUrl(AQH_HTTP_REQUEST *rq, GWEN_URL *url)
+{
+ if (rq) {
+ GWEN_Url_free(rq->url);
+ rq->url=url;
+ }
+}
+
+
+
+GWEN_DB_NODE *AQH_HttpRequest_GetDbCommand(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->dbCommand):NULL;
+}
+
+
+
+GWEN_DB_NODE *AQH_HttpRequest_GetDbHeader(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->dbHeader):NULL;
+}
+
+
+
+GWEN_DB_NODE *AQH_HttpRequest_GetDbCookies(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->dbCookies):NULL;
+}
+
+
+
+const char *AQH_HttpRequest_GetSessionId(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->sessionId):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetSessionId(AQH_HTTP_REQUEST *rq, const char *s)
+{
+ if (rq) {
+ free(rq->sessionId);
+ rq->sessionId=s?strdup(s):NULL;
+ }
+}
+
+
+
+const char *AQH_HttpRequest_GetModuleName(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->moduleName):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetModuleName(AQH_HTTP_REQUEST *rq, const char *s)
+{
+ if (rq) {
+ free(rq->moduleName);
+ rq->moduleName=s?strdup(s):NULL;
+ }
+}
+
+
+
+AQH_SESSION *AQH_HttpRequest_GetSession(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->session):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetSession(AQH_HTTP_REQUEST *rq, AQH_SESSION *session)
+{
+ if (rq)
+ rq->session=session;
+}
+
+
+
+AQH_MODULE *AQH_HttpRequest_GetModule(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->module):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetModule(AQH_HTTP_REQUEST *rq, AQH_MODULE *m)
+{
+ if (rq)
+ rq->module=m;
+}
+
+
+
+uint32_t AQH_HttpRequest_GetModulePerms(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->modulePerms):0;
+}
+
+
+
+void AQH_HttpRequest_SetModulePerms(AQH_HTTP_REQUEST *rq, uint32_t i)
+{
+ if (rq)
+ rq->modulePerms=i;
+}
+
+
+
+int AQH_HttpRequest_GetResponseCode(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->responseCode):0;
+}
+
+
+
+void AQH_HttpRequest_SetResponseCode(AQH_HTTP_REQUEST *rq, int i)
+{
+ if (rq)
+ rq->responseCode=i;
+}
+
+
+
+const char *AQH_HttpRequest_GetResponseText(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->responseText):NULL;
+}
+
+
+
+void AQH_HttpRequest_SetResponseText(AQH_HTTP_REQUEST *rq, const char *s)
+{
+ if (rq) {
+ free(rq->responseText);
+ rq->responseText=s?strdup(s):NULL;
+ }
+}
+
+
+
+GWEN_MSG *AQH_HttpRequest_GetResponseMsg(const AQH_HTTP_REQUEST *rq)
+{
+ return rq?(rq->responseMsg):NULL;
+}
+
+
+
+GWEN_MSG *AQH_HttpRequest_TakeResponseMsg(AQH_HTTP_REQUEST *rq)
+{
+ if (rq) {
+ GWEN_MSG *msg;
+
+ msg=rq->responseMsg;
+ rq->responseMsg=NULL;
+ return msg;
+ }
+ return NULL;
+}
+
+
+
+void AQH_HttpRequest_SetResponseMsg(AQH_HTTP_REQUEST *rq, GWEN_MSG *msg)
+{
+ if (rq && msg) {
+ GWEN_Msg_free(rq->responseMsg);
+ rq->responseMsg=msg;
+ }
+}
+
+
+
+int _inspectReceivedMessage(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
+{
+ int rv;
+
+ rv=_getParsedDbInfo(rq, msg);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ rv=_inspectMsgCommand(rq, msg);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ _inspectMsgHeader(rq, msg);
+
+ rv=_inspectMsgBody(rq, msg);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+
+
+int _getParsedDbInfo(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
+{
+ GWEN_DB_NODE *dbParsedInfo;
+ GWEN_DB_NODE *db;
+
+ dbParsedInfo=GWEN_Msg_GetDbParsedInfo(msg);
+ if (dbParsedInfo==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "No parsed info db, msg probably not generated by HTTP endpoint module");
+ return GWEN_ERROR_GENERIC;
+ }
+
+ db=GWEN_DB_GetGroup(dbParsedInfo, GWEN_PATH_FLAGS_PATHMUSTEXIST, "command");
+ if (db==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "No parsed command db, msg probably not generated by HTTP endpoint module");
+ return GWEN_ERROR_GENERIC;
+ }
+ rq->dbCommand=db;
+
+ db=GWEN_DB_GetGroup(dbParsedInfo, GWEN_PATH_FLAGS_PATHMUSTEXIST, "header");
+ if (db==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "No parsed header db, msg probably not generated by HTTP endpoint module or no header recvd, ignoring");
+ }
+ rq->dbHeader=db;
+
+ return 0;
+}
+
+
+
+int _inspectMsgCommand(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
+{
+ const char *s;
+
+ s=GWEN_DB_GetCharValue(rq->dbCommand, "command", 0, NULL);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No command in request");
+ return GWEN_ERROR_GENERIC;
+ }
+ rq->command=s;
+
+ s=GWEN_DB_GetCharValue(rq->dbCommand, "protocol", 0, NULL);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No protocol in request");
+ return GWEN_ERROR_GENERIC;
+ }
+ rq->protocol=s;
+
+ s=GWEN_DB_GetCharValue(rq->dbCommand, "url", 0, NULL);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No url in request");
+ return GWEN_ERROR_GENERIC;
+ }
+
+ rq->url=GWEN_Url_fromCommandString(s);
+ if (rq->url==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid URL [%s]", s);
+ return GWEN_ERROR_GENERIC;
+ }
+
+ _setModuleNameFromUrl(rq);
+
+ return 0;
+}
+
+
+
+void _inspectMsgHeader(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
+{
+ if (rq->dbHeader)
+ rq->dbCookies=_extractCookies(rq->dbHeader);
+ if (rq->dbCookies) {
+ const char *s;
+
+ s=GWEN_DB_GetCharValue(rq->dbCookies, AQH_HTTP_REQUEST_SESSIONCOOKIE, 0, NULL);
+ if (s && *s)
+ AQH_HttpRequest_SetSessionId(rq, s);
+ }
+
+}
+
+
+
+int _inspectMsgBody(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
+{
+ int rv;
+
+ rq->recvdBodySize=GWEN_Msg_GetParsedPayloadSize(msg);
+ if (rq->recvdBodySize>0)
+ rq->recvdBodyPtr=GWEN_Msg_GetConstBuffer(msg)+GWEN_Msg_GetParsedPayloadOffset(msg);
+
+ if (rq->dbHeader && rq->recvdBodySize>0 && strcasecmp(rq->command, "POST")==0) {
+ const char *s;
+
+ /* check whether we need to parse POST body */
+ s=GWEN_DB_GetCharValue(rq->dbCommand, "content-type", 0, NULL);
+ if (s && strcasecmp(s, "application/x-www-form-urlencoded")==0) {
+ rq->dbPostBody=GWEN_DB_Group_new("post");
+ rv=_parsePostBody((const char*) (rq->recvdBodyPtr), rq->recvdBodySize, rq->dbPostBody);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+GWEN_DB_NODE *_extractCookies(GWEN_DB_NODE *dbHeader)
+{
+ int i;
+ GWEN_DB_NODE *dbCookies;
+
+ dbCookies=GWEN_DB_GetGroup(dbHeader, 0, "cookies");
+ for (i=0; ; i++) {
+ const char *sCookie;
+
+ sCookie=GWEN_DB_GetCharValue(dbHeader, "Cookie", i, NULL);
+ if (sCookie && *sCookie) {
+ const char *s;
+
+ s=sCookie;
+ while(*s) {
+ while(*s && *s<33)
+ s++;
+
+ if (*s) {
+ const char *nameStart;
+ int nameLen;
+
+ nameStart=s++;
+ while(*s && *s!='=')
+ s++;
+ if (*s=='=') {
+ nameLen=s-nameStart;
+ s++;
+ while(*s && *s<33)
+ s++;
+ if (*s) {
+ const char *valueStart;
+ int valueLen;
+
+ valueStart=s;
+ while(*s && *s!=';')
+ s++;
+ valueLen=s-valueStart;
+
+ _setCookieValue(dbCookies, nameStart, nameLen, valueStart, valueLen, 0);
+
+ if (*s)
+ s++;
+ }
+ }
+ }
+ } /* while(*s) */
+ } /* if (sCookie && *sCookie) */
+ else
+ break;
+ } /* for */
+ return dbCookies;
+}
+
+
+
+void _setCookieValue(GWEN_DB_NODE *dbCookies, const char *nameStart, int nameLen, const char *valueStart, int valueLen, uint32_t dbFlags)
+{
+ if (nameLen && valueLen) {
+ int i;
+ char *sName;
+ char *sValue;
+
+ for (i=nameLen-1; i>0; i--) {
+ if (nameStart[i]>32)
+ break;
+ }
+ sName=GWEN_Text_strndup(nameStart, i+1);
+
+ for (i=valueLen-1; i>0; i--) {
+ if (valueStart[i]>32)
+ break;
+ }
+ sValue=GWEN_Text_strndup(valueStart, i+1);
+
+ DBG_INFO(AQH_LOGDOMAIN, "Received Cookie: [%s]=[%s]", sName, sValue);
+ GWEN_DB_SetCharValue(dbCookies, dbFlags, sName, sValue);
+ free(sValue);
+ free(sName);
+ }
+}
+
+
+
+void _setModuleNameFromUrl(AQH_HTTP_REQUEST *rq)
+{
+ if (rq->url) {
+ const char *sPath;
+
+ sPath=GWEN_Url_GetPath(rq->url);
+ if (sPath && *sPath) {
+ if (*sPath=='/')
+ sPath++;
+ while(*sPath && *sPath<33)
+ sPath++;
+ if (*sPath) {
+ const char *s;
+
+ s=sPath;
+ while(*s && *s!='/')
+ s++;
+ if (s>sPath) {
+ free(rq->moduleName);
+ rq->moduleName=GWEN_Text_strndup(sPath, s-sPath);
+ }
+ }
+ }
+ }
+}
+
+
+
+int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
+{
+ while(contentLength>0) {
+ const char *sNameStart;
+ int nameLen;
+
+ while((contentLength>0) && (*s<33)) {
+ contentLength--;
+ s++;
+ }
+ sNameStart=s;
+ while((contentLength>0) && (*s!='=') && (*s!='&')) {
+ contentLength--;
+ s++;
+ }
+ nameLen=s-sNameStart;
+
+ if ((contentLength>0) && (*s=='=')) {
+ const char *sValueStart;
+ int valueLen;
+
+ s++;
+ while((contentLength>0) && (*s<33)) {
+ s++;
+ contentLength--;
+ }
+ sValueStart=s;
+ while((contentLength>0) && (*s!='&')) {
+ contentLength--;
+ s++;
+ }
+ valueLen=s-sValueStart;
+
+ if (nameLen && valueLen) {
+ char sNameBuf[32];
+ char sValueBuf[64];
+
+ if (GWEN_Text_UnescapeN(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))!=NULL &&
+ GWEN_Text_UnescapeN(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))!=NULL)
+ GWEN_DB_SetCharValue(dbBody, 0, sNameBuf, sValueBuf);
+ else {
+ DBG_ERROR(NULL, "Either name or value invalid in body, aborting");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ }
+ if ((contentLength>0) && (*s=='&'))
+ s++;
+ }
+ } /* while */
+ return 0;
+}
+
+
+
+
+
diff --git a/aqhome/http/httprequest.h b/aqhome/http/httprequest.h
new file mode 100644
index 0000000..208f564
--- /dev/null
+++ b/aqhome/http/httprequest.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifndef AQH_HTTP_REQUEST_H
+#define AQH_HTTP_REQUEST_H
+
+
+#include "aqhome/service/session.h"
+#include "aqhome/service/module.h"
+
+#include
+#include
+#include
+#include
+
+
+
+typedef struct AQH_HTTP_REQUEST AQH_HTTP_REQUEST;
+GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_HTTP_REQUEST, AQH_HttpRequest, AQHOME_API);
+
+AQHOME_API AQH_HTTP_REQUEST *AQH_HttpRequest_new(GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *receivedMsg);
+AQHOME_API void AQH_HttpRequest_free(AQH_HTTP_REQUEST *rq);
+
+AQHOME_API GWEN_MSG_ENDPOINT *AQH_HttpRequest_GetEndpoint(const AQH_HTTP_REQUEST *rq);
+
+AQHOME_API const GWEN_MSG *AQH_HttpRequest_GetReceivedMsg(const AQH_HTTP_REQUEST *rq);
+
+AQHOME_API const char *AQH_HttpRequest_GetCommand(const AQH_HTTP_REQUEST *rq);
+
+AQHOME_API const char *AQH_HttpRequest_GetProtocol(const AQH_HTTP_REQUEST *rq);
+
+AQHOME_API GWEN_URL *AQH_HttpRequest_GetUrl(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetUrl(AQH_HTTP_REQUEST *rq, GWEN_URL *url);
+
+AQHOME_API GWEN_DB_NODE *AQH_HttpRequest_GetDbCommand(const AQH_HTTP_REQUEST *rq);
+AQHOME_API GWEN_DB_NODE *AQH_HttpRequest_GetDbHeader(const AQH_HTTP_REQUEST *rq);
+AQHOME_API GWEN_DB_NODE *AQH_HttpRequest_GetDbCookies(const AQH_HTTP_REQUEST *rq);
+
+
+AQHOME_API const char *AQH_HttpRequest_GetSessionId(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetSessionId(AQH_HTTP_REQUEST *rq, const char *s);
+
+AQHOME_API const char *AQH_HttpRequest_GetModuleName(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetModuleName(AQH_HTTP_REQUEST *rq, const char *s);
+
+AQHOME_API AQH_SESSION *AQH_HttpRequest_GetSession(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetSession(AQH_HTTP_REQUEST *rq, AQH_SESSION *session);
+
+AQHOME_API AQH_MODULE *AQH_HttpRequest_GetModule(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetModule(AQH_HTTP_REQUEST *rq, AQH_MODULE *m);
+
+AQHOME_API uint32_t AQH_HttpRequest_GetModulePerms(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetModulePerms(AQH_HTTP_REQUEST *rq, uint32_t i);
+
+
+AQHOME_API int AQH_HttpRequest_GetResponseCode(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetResponseCode(AQH_HTTP_REQUEST *rq, int i);
+
+AQHOME_API const char *AQH_HttpRequest_GetResponseText(const AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetResponseText(AQH_HTTP_REQUEST *rq, const char *s);
+
+AQHOME_API GWEN_MSG *AQH_HttpRequest_GetResponseMsg(const AQH_HTTP_REQUEST *rq);
+AQHOME_API GWEN_MSG *AQH_HttpRequest_TakeResponseMsg(AQH_HTTP_REQUEST *rq);
+AQHOME_API void AQH_HttpRequest_SetResponseMsg(AQH_HTTP_REQUEST *rq, GWEN_MSG *msg);
+
+
+
+#endif
+
+
+
diff --git a/aqhome/http/httprequest_p.h b/aqhome/http/httprequest_p.h
new file mode 100644
index 0000000..6d73f59
--- /dev/null
+++ b/aqhome/http/httprequest_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifndef AQH_HTTP_REQUEST_P_H
+#define AQH_HTTP_REQUEST_P_H
+
+
+#include "aqhome/http/httprequest.h"
+
+
+
+typedef struct AQH_HTTP_REQUEST AQH_HTTP_REQUEST;
+struct AQH_HTTP_REQUEST {
+ GWEN_TREE2_ELEMENT(AQH_HTTP_REQUEST)
+
+ GWEN_MSG_ENDPOINT *endpoint;
+
+ /* received stuff */
+ const GWEN_MSG *receivedMsg; /* don't free */
+
+ const char *command; /* don't free */
+ const char *protocol; /* don't free */
+ GWEN_URL *url;
+
+ GWEN_DB_NODE *dbCommand; /* don't free */
+ GWEN_DB_NODE *dbHeader; /* don't free */
+ GWEN_DB_NODE *dbCookies; /* don't free */
+
+ uint32_t recvdBodySize;
+ const uint8_t *recvdBodyPtr;
+
+ /* derived stuff */
+ char *sessionId;
+ char *moduleName;
+
+ AQH_SESSION *session; /* don't free */
+ AQH_MODULE *module; /* don't free */
+ uint32_t modulePerms;
+
+ GWEN_DB_NODE *dbPostBody;
+
+
+ /* response stuff */
+ int responseCode;
+ char *responseText;
+ GWEN_MSG *responseMsg;
+};
+
+
+
+#endif
+
+
+
diff --git a/aqhome/http/httpservice.c b/aqhome/http/httpservice.c
index 30e9c9a..9c7d738 100644
--- a/aqhome/http/httpservice.c
+++ b/aqhome/http/httpservice.c
@@ -25,9 +25,6 @@ GWEN_INHERIT(AQH_SERVICE, AQH_HTTP_SERVICE)
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
-static int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody);
-
-
@@ -37,6 +34,10 @@ void AQH_HttpService_Extend(AQH_SERVICE *sv)
GWEN_NEW_OBJECT(AQH_HTTP_SERVICE, xsv);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv, xsv, _freeData);
+
+ xsv->moduleMutex=GWEN_Mutex_new();
+ xsv->userMutex=GWEN_Mutex_new();
+ xsv->sessionMutex=GWEN_Mutex_new();
}
@@ -46,6 +47,12 @@ void _freeData(void *bp, void *p)
AQH_HTTP_SERVICE *xsv;
xsv=(AQH_HTTP_SERVICE*) p;
+
+ GWEN_Mutex_free(xsv->moduleMutex);
+ GWEN_Mutex_free(xsv->userMutex);
+ GWEN_Mutex_free(xsv->sessionMutex);
+
+ free(xsv->configFolder);
free(xsv->sourceFolder);
free(xsv->siteHeader);
free(xsv->siteFooter);
@@ -54,6 +61,54 @@ void _freeData(void *bp, void *p)
+AQH_HTTP_SERVICE_HANDLEREQUEST_FN AQH_HttpService_SetHandleRequestFn(AQH_SERVICE *sv, AQH_HTTP_SERVICE_HANDLEREQUEST_FN fn)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv) {
+ AQH_HTTP_SERVICE_HANDLEREQUEST_FN oldFn;
+
+ oldFn=xsv->handleRequestFn;
+ xsv->handleRequestFn=fn;
+ return oldFn;
+ }
+ }
+ return NULL;
+}
+
+
+
+const char *AQH_HttpService_GetConfigFolder(const AQH_SERVICE *sv)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv)
+ return xsv->configFolder;
+ }
+ return NULL;
+}
+
+
+
+void AQH_HttpService_SetConfigFolder(AQH_SERVICE *sv, const char *s)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv) {
+ free(xsv->configFolder);
+ xsv->configFolder=s?strdup(s):NULL;
+ }
+ }
+}
+
+
+
const char *AQH_HttpService_GetSourceFolder(const AQH_SERVICE *sv)
{
if (sv) {
@@ -83,6 +138,64 @@ void AQH_HttpService_SetSourceFolder(AQH_SERVICE *sv, const char *s)
+uint32_t AQH_HttpService_GetNextModuleId(AQH_SERVICE *sv)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv) {
+ return ++(xsv->lastModuleId);
+ }
+ }
+ return 0;
+}
+
+
+
+uint32_t AQH_HttpService_GetNextUserId(AQH_SERVICE *sv)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv) {
+ return ++(xsv->lastUserId);
+ }
+ }
+ return 0;
+}
+
+
+
+int AQH_HttpService_GetMaxSessionAgeInSecs(const AQH_SERVICE *sv)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv) {
+ return xsv->maxSessionAgeInSecs;
+ }
+ }
+ return 0;
+}
+
+
+
+void AQH_HttpService_SetMaxSessionAgeInSecs(AQH_SERVICE *sv, int i)
+{
+ if (sv) {
+ AQH_HTTP_SERVICE *xsv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv)
+ xsv->maxSessionAgeInSecs=i;
+ }
+}
+
+
+
const char *AQH_HttpService_GetSiteHeader(const AQH_SERVICE *sv)
{
if (sv) {
@@ -141,183 +254,3 @@ void AQH_HttpService_SetSiteFooter(AQH_SERVICE *sv, const char *s)
-int AQH_HttpService_AddFile(AQH_SERVICE *sv, const char *fname, GWEN_BUFFER *buf)
-{
- if (fname && *fname) {
- AQH_HTTP_SERVICE *xsv;
-
- xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
- if (xsv) {
- int rv;
- GWEN_BUFFER *fnbuf;
-
- fnbuf=GWEN_Buffer_new(0, 256, 0, 1);
- if (xsv->sourceFolder) {
- GWEN_Buffer_AppendString(fnbuf, xsv->sourceFolder);
- GWEN_Buffer_AppendString(fnbuf, GWEN_DIR_SEPARATOR_S);
- }
- GWEN_Buffer_AppendString(fnbuf, fname);
-
- rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fnbuf), buf);
- if (rv<0) {
- DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"%s\": %d", GWEN_Buffer_GetStart(fnbuf), rv);
- GWEN_Buffer_free(fnbuf);
- return rv;
- }
- GWEN_Buffer_free(fnbuf);
- }
- else {
- DBG_ERROR(AQH_LOGDOMAIN, "No http service object");
- }
- }
- return 0;
-}
-
-
-
-void AQH_HttpService_AddStatusLine(AQH_SERVICE *sv, int code, const char *msg, const char *proto, GWEN_BUFFER *buf)
-{
- GWEN_Buffer_AppendArgs(buf, "%s %03d %s \r\n", proto?proto:"HTTP/1.1", code, msg?msg:"");
-}
-
-
-
-void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeader, GWEN_BUFFER *buf)
-{
- GWEN_DB_NODE *dbVar;
-
- dbVar=GWEN_DB_GetFirstVar(dbHeader);
- while (dbVar) {
- GWEN_DB_NODE *dbVal;
-
- /* only handle first value */
- dbVal=GWEN_DB_GetFirstValue(dbVar);
- if (dbVal) {
- const char *sVar;
-
- sVar=GWEN_DB_VariableName(dbVar);
- if (sVar && *sVar) {
- GWEN_DB_NODE_TYPE vtype;
-
- vtype=GWEN_DB_GetValueType(dbVal);
- if (vtype==GWEN_DB_NodeType_ValueChar) {
- const char *sValue;
-
- sValue=GWEN_DB_GetCharValueFromNode(dbVal);
- if (sValue)
- GWEN_Buffer_AppendArgs(buf, "%s:%s\r\n", sVar, sValue);
- } /* if char */
- else if (vtype==GWEN_DB_NodeType_ValueInt) {
- int i;
-
- i=GWEN_DB_GetIntValueFromNode(dbVal);
- if (i!=-1 || strcasecmp(sVar, "Content-Length")==0)
- GWEN_Buffer_AppendArgs(buf, "%s:%d\r\n", sVar, i);
- } /* if int */
- else {
- DBG_INFO(AQH_LOGDOMAIN, "Variable type %d of var [%s] not supported, ignoring", vtype, sVar);
- }
- } /* if sVar */
- }
- dbVar=GWEN_DB_GetNextVar(dbVar);
- }
-
- /* finalize header */
- GWEN_Buffer_AppendString(buf, "\r\n");
-}
-
-
-
-int AQH_HttpService_ParsePostBody(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, GWEN_DB_NODE *dbBody)
-{
- GWEN_DB_NODE *dbParsedInfo;
- GWEN_DB_NODE *dbHeader;
- const char *contentType;
- int contentLength;
- const char *s;
-
- dbParsedInfo=GWEN_Msg_GetDbParsedInfo(msgReceived);
- if (dbParsedInfo==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "No parsed info in received message");
- return GWEN_ERROR_BAD_DATA;
- }
- dbHeader=GWEN_DB_GetGroup(dbParsedInfo, GWEN_PATH_FLAGS_PATHMUSTEXIST, "header");
- if (dbHeader==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "No http header group in received message");
- return GWEN_ERROR_BAD_DATA;
- }
-
- contentType=GWEN_DB_GetCharValue(dbHeader, "content-type", 0, NULL);
- if (!(contentType && *contentType && strcasecmp(contentType, "application/x-www-form-urlencoded")==0)) {
- DBG_ERROR(NULL, "Invalid or missing content type [%s]", contentType?contentType:"");
- return GWEN_ERROR_BAD_DATA;
- }
-
- contentLength=GWEN_Msg_GetParsedPayloadSize(msgReceived);
- if (contentLength<1) {
- DBG_ERROR(NULL, "Empty message body");
- return 0;
- }
-
- if (contentLength<1) {
- DBG_ERROR(NULL, "Empty message body");
- return 0;
- }
- s=(const char*)(GWEN_Msg_GetConstBuffer(msgReceived)+GWEN_Msg_GetParsedPayloadOffset(msgReceived));
- return _parsePostBody(s, contentLength, dbBody);
-}
-
-
-
-int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
-{
- while(contentLength>0) {
- const char *sNameStart;
- int nameLen;
-
- while((contentLength>0) && (*s<33)) {
- contentLength--;
- s++;
- }
- sNameStart=s;
- while((contentLength>0) && (*s!='=') && (*s!='&')) {
- contentLength--;
- s++;
- }
- nameLen=s-sNameStart;
- if ((contentLength>0) && (*s=='=')) {
- const char *sValueStart;
- int valueLen;
-
- s++;
- while((contentLength>0) && (*s<33)) {
- s++;
- contentLength--;
- }
- sValueStart=s;
- while((contentLength>0) && (*s!='&')) {
- contentLength--;
- s++;
- }
- valueLen=s-sValueStart;
- if (nameLen && valueLen) {
- char sNameBuf[32];
- char sValueBuf[64];
-
- if (GWEN_Text_UnescapeN(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))!=NULL &&
- GWEN_Text_UnescapeN(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))!=NULL)
- GWEN_DB_SetCharValue(dbBody, 0, sNameBuf, sValueBuf);
- else {
- DBG_ERROR(NULL, "Either name or value invalid in body, aborting");
- return GWEN_ERROR_BAD_DATA;
- }
- }
- if ((contentLength>0) && (*s=='&'))
- s++;
- }
- } /* while */
- return 0;
-}
-
-
-
diff --git a/aqhome/http/httpservice.h b/aqhome/http/httpservice.h
index 54730ba..5400161 100644
--- a/aqhome/http/httpservice.h
+++ b/aqhome/http/httpservice.h
@@ -13,33 +13,45 @@
#include "aqhome/service/service.h"
+#include "aqhome/http/httprequest.h"
+
+#include
#include
#include
-#include
+#include
+#include
-void AQH_HttpService_Extend(AQH_SERVICE *sv);
+typedef int (*AQH_HTTP_SERVICE_HANDLEREQUEST_FN)(AQH_SERVICE *sv, AQH_HTTP_REQUEST *request);
-const char *AQH_HttpService_GetSourceFolder(const AQH_SERVICE *sv);
-void AQH_HttpService_SetSourceFolder(AQH_SERVICE *sv, const char *s);
-
-const char *AQH_HttpService_GetSiteHeader(const AQH_SERVICE *sv);
-void AQH_HttpService_SetSiteHeader(AQH_SERVICE *sv, const char *s);
+AQHOME_API void AQH_HttpService_Extend(AQH_SERVICE *sv);
-const char *AQH_HttpService_GetSiteFooter(const AQH_SERVICE *sv);
-void AQH_HttpService_SetSiteFooter(AQH_SERVICE *sv, const char *s);
+AQHOME_API const char *AQH_HttpService_GetConfigFolder(const AQH_SERVICE *sv);
+AQHOME_API void AQH_HttpService_SetConfigFolder(AQH_SERVICE *sv, const char *s);
-int AQH_HttpService_AddFile(AQH_SERVICE *sv, const char *fname, GWEN_BUFFER *buf);
-void AQH_HttpService_AddStatusLine(AQH_SERVICE *sv, int code, const char *msg, const char *proto, GWEN_BUFFER *buf);
-void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeader, GWEN_BUFFER *buf);
+AQHOME_API const char *AQH_HttpService_GetSourceFolder(const AQH_SERVICE *sv);
+AQHOME_API void AQH_HttpService_SetSourceFolder(AQH_SERVICE *sv, const char *s);
-int AQH_HttpService_ParsePostBody(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, GWEN_DB_NODE *dbBody);
+AQHOME_API const char *AQH_HttpService_GetSiteHeader(const AQH_SERVICE *sv);
+AQHOME_API void AQH_HttpService_SetSiteHeader(AQH_SERVICE *sv, const char *s);
+AQHOME_API const char *AQH_HttpService_GetSiteFooter(const AQH_SERVICE *sv);
+AQHOME_API void AQH_HttpService_SetSiteFooter(AQH_SERVICE *sv, const char *s);
+
+
+AQHOME_API uint32_t AQH_HttpService_GetNextModuleId(AQH_SERVICE *sv);
+AQHOME_API uint32_t AQH_HttpService_GetNextUserId(AQH_SERVICE *sv);
+
+AQHOME_API int AQH_HttpService_GetMaxSessionAgeInSecs(const AQH_SERVICE *sv);
+AQHOME_API void AQH_HttpService_SetMaxSessionAgeInSecs(AQH_SERVICE *sv, int i);
+
+
+AQHOME_API AQH_HTTP_SERVICE_HANDLEREQUEST_FN AQH_HttpService_SetHandleRequestFn(AQH_SERVICE *sv, AQH_HTTP_SERVICE_HANDLEREQUEST_FN fn);
#endif
diff --git a/aqhome/http/httpservice_conf.c b/aqhome/http/httpservice_conf.c
new file mode 100644
index 0000000..0f0f70f
--- /dev/null
+++ b/aqhome/http/httpservice_conf.c
@@ -0,0 +1,982 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "aqhome/http/httpservice_conf.h"
+#include "aqhome/http/httpservice_p.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+
+#define AQH_HTTP_SERVICE_FILE_CONFIG "service.conf"
+#define AQH_HTTP_SERVICE_DIR_MODS "modules"
+#define AQH_HTTP_SERVICE_DIR_USERS "users"
+#define AQH_HTTP_SERVICE_DIR_SESSIONS "sessions"
+
+
+#ifdef GWEN_INHERIT_REF
+ GWEN_INHERIT_REF(AQH_SERVICE, AQH_HTTP_SERVICE)
+#else
+ extern uint32_t AQH_HTTP_SERVICE_ID;
+#endif
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _calculateUserPerms(AQH_SERVICE *sv, AQH_USER *user);
+static void _calculateModuleUserPerms(AQH_SERVICE *sv, AQH_MODULE_PERMS *p);
+static AQH_MODULE *_ensureModule(AQH_SERVICE *sv, const char *modName);
+static AQH_USER *_ensureUser(AQH_SERVICE *sv, const char *alias);
+static AQH_SESSION *_ensureSession(AQH_SERVICE *sv, const char *sessionUid);
+static GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileName);
+static GWEN_BUFFER *_getModuleFilePath(const AQH_SERVICE *sv, const char *modName);
+static GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias);
+static GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid);
+static int _checkUser(const AQH_USER *user, int ignoreMissingId);
+static int _checkSession(const AQH_SESSION *session);
+static int _checkModule(const AQH_MODULE *m, int ignoreMissingId);
+static int _checkRoleList(const AQH_ROLE_LIST *roleList, int ignoreMissingId);
+static int _writeDbFile(const char *fname, GWEN_DB_NODE *db);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+int AQH_HttpService_LoadConfig(AQH_SERVICE *sv)
+{
+ AQH_HTTP_SERVICE *xsv;
+ GWEN_BUFFER *nameBuf;
+ int rv;
+ GWEN_DB_NODE *db;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return GWEN_ERROR_INVALID;
+ }
+
+ db=GWEN_DB_Group_new("service");
+
+ nameBuf=_getConfigFilePath(sv, AQH_HTTP_SERVICE_FILE_CONFIG);
+ rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT | GWEN_DB_FLAGS_LOCKFILE);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ return rv;
+ }
+ GWEN_Buffer_free(nameBuf);
+
+ xsv->lastModuleId=GWEN_DB_GetIntValue(db, "lastModuleId", 0, 0);
+ xsv->lastUserId=GWEN_DB_GetIntValue(db, "lastUserId", 0, 0);
+ xsv->maxSessionAgeInSecs=GWEN_DB_GetIntValue(db, "maxSessionAgeInSecs", 0, AQH_HTTP_SERVICE_DEFAULT_MAXSESSIONAGE);
+
+ GWEN_DB_Group_free(db);
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_SaveConfig(const AQH_SERVICE *sv)
+{
+ AQH_HTTP_SERVICE *xsv;
+ int rv;
+ GWEN_BUFFER *nameBuf;
+ GWEN_DB_NODE *db;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return GWEN_ERROR_INVALID;
+ }
+
+ db=GWEN_DB_Group_new("service");
+ GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastModuleId", xsv->lastModuleId);
+ GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastUserId", xsv->lastUserId);
+ GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "maxSessionAgeInSecs", xsv->maxSessionAgeInSecs);
+
+ nameBuf=_getConfigFilePath(sv, AQH_HTTP_SERVICE_FILE_CONFIG);
+ rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+ return 0;
+}
+
+
+
+AQH_MODULE *AQH_HttpService_GetModule(AQH_SERVICE *sv, const char *modName)
+{
+ AQH_HTTP_SERVICE *xsv;
+ AQH_MODULE *m;
+ int rv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return NULL;
+ }
+
+ rv=GWEN_Mutex_Lock(xsv->moduleMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on module mutex");
+ return NULL;
+ }
+
+ m=_ensureModule(sv, modName);
+
+ rv=GWEN_Mutex_Unlock(xsv->moduleMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on module mutex");
+ return NULL;
+ }
+
+ if (m==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "here");
+ return NULL;
+ }
+
+ return m;
+}
+
+
+
+AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias)
+{
+ AQH_HTTP_SERVICE *xsv;
+ AQH_USER *user;
+ int rv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return NULL;
+ }
+
+ rv=GWEN_Mutex_Lock(xsv->userMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on user mutex");
+ return NULL;
+ }
+
+ user=_ensureUser(sv, alias);
+
+ rv=GWEN_Mutex_Unlock(xsv->userMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on user mutex");
+ return NULL;
+ }
+
+ if (user==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "here");
+ return NULL;
+ }
+
+ return user;
+}
+
+
+
+AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid)
+{
+ AQH_HTTP_SERVICE *xsv;
+ AQH_SESSION *session;
+ int rv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return NULL;
+ }
+
+ rv=GWEN_Mutex_Lock(xsv->sessionMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on session mutex");
+ return NULL;
+ }
+
+ session=_ensureSession(sv, sessionUid);
+
+ rv=GWEN_Mutex_Unlock(xsv->sessionMutex);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on session mutex");
+ return NULL;
+ }
+
+ if (session==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "here");
+ return NULL;
+ }
+
+ return session;
+}
+
+
+
+
+
+
+AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modName)
+{
+ GWEN_BUFFER *nameBuf;
+ int rv;
+ GWEN_DB_NODE *db;
+ AQH_MODULE *m;
+
+ db=GWEN_DB_Group_new("user");
+
+ nameBuf=_getModuleFilePath(sv, modName);
+ rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT | GWEN_DB_FLAGS_LOCKFILE);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_Buffer_free(nameBuf);
+
+ m=AQH_Module_fromDb(db);
+ if (m==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error loading module \"%s\" from config group", modName);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_DB_Group_free(db);
+
+ rv=_checkModule(m, 0);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "Invalid data for module \"%s\"", modName);
+ AQH_Module_free(m);
+ return NULL;
+ }
+
+ return m;
+}
+
+
+
+int AQH_HttpService_SaveModule(const AQH_SERVICE *sv, const AQH_MODULE *m)
+{
+ int rv;
+ const char *modName;
+ GWEN_BUFFER *nameBuf;
+ GWEN_DB_NODE *db;
+
+ rv=_checkModule(m, 0);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ db=GWEN_DB_Group_new("module");
+ rv=AQH_Module_toDb(m, db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_DB_Group_free(db);
+ return rv;
+ }
+
+ modName=AQH_Module_GetName(m);
+ nameBuf=_getModuleFilePath(sv, modName);
+ rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_AddModule(AQH_SERVICE *sv, AQH_MODULE *m)
+{
+ int rv;
+ uint32_t id;
+ AQH_ROLE_LIST *roleList;
+ AQH_ROLE *r;
+
+ rv=_checkModule(m, 1);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not adding invalid module (%d)", rv);
+ return rv;
+ }
+
+ /* assign module id */
+ id=AQH_HttpService_GetNextModuleId(sv);
+ AQH_Module_SetId(m, id);
+
+ /* assign role ids */
+ roleList=AQH_Module_GetRoleList(m);
+ id=1;
+ r=AQH_Role_List_First(roleList);
+ while(r) {
+ AQH_Role_SetId(r, id++);
+ r=AQH_Role_List_Next(r);
+ }
+
+ rv=AQH_HttpService_SaveModule(sv, m);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error saving module module \"%s\" (%d)", AQH_Module_GetName(m), rv);
+ return rv;
+ }
+
+ AQH_Service_AddModule(sv, m);
+ return 0;
+}
+
+
+
+AQH_USER *AQH_HttpService_LoadUser(const AQH_SERVICE *sv, const char *userAlias)
+{
+ GWEN_BUFFER *nameBuf;
+ int rv;
+ GWEN_DB_NODE *db;
+ AQH_USER *user;
+
+ db=GWEN_DB_Group_new("user");
+
+ nameBuf=_getUserFilePath(sv, userAlias);
+ rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT | GWEN_DB_FLAGS_LOCKFILE);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_Buffer_free(nameBuf);
+
+ user=AQH_User_fromDb(db);
+ if (user==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error loading user \"%s\" from config group", userAlias);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_DB_Group_free(db);
+
+ rv=_checkUser(user, 0);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "Invalid data for user \"%s\"", userAlias);
+ AQH_User_free(user);
+ return NULL;
+ }
+
+ return user;
+}
+
+
+
+int AQH_HttpService_SaveUser(const AQH_SERVICE *sv, const AQH_USER *user)
+{
+ int rv;
+ const char *sAlias;
+ GWEN_BUFFER *nameBuf;
+ GWEN_DB_NODE *db;
+
+ rv=_checkUser(user, 0);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ db=GWEN_DB_Group_new("user");
+ rv=AQH_User_toDb(user, db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_DB_Group_free(db);
+ return rv;
+ }
+
+ sAlias=AQH_User_GetAlias(user);
+ nameBuf=_getUserFilePath(sv, sAlias);
+ rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_AddUser(AQH_SERVICE *sv, AQH_USER *user)
+{
+ int rv;
+ uint32_t id;
+
+ rv=_checkUser(user, 0);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ /* assign user id */
+ id=AQH_HttpService_GetNextUserId(sv);
+ AQH_User_SetId(user, id);
+
+ rv=AQH_HttpService_SaveUser(sv, user);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ AQH_Service_AddUser(sv, user);
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_DelUser(AQH_SERVICE *sv, AQH_USER *user)
+{
+ const char *sAlias;
+ GWEN_BUFFER *nameBuf;
+ uint32_t userId;
+
+ sAlias=AQH_User_GetAlias(user);
+ userId=AQH_User_GetId(user);
+ nameBuf=_getUserFilePath(sv, sAlias);
+ unlink(GWEN_Buffer_GetStart(nameBuf));
+ GWEN_Buffer_free(nameBuf);
+ AQH_Service_DelUser(sv, userId);
+ return 0;
+}
+
+
+
+AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sessionUid)
+{
+ GWEN_BUFFER *nameBuf;
+ int rv;
+ GWEN_DB_NODE *db;
+ AQH_SESSION *session;
+
+ db=GWEN_DB_Group_new("user");
+
+ nameBuf=_getSessionFilePath(sv, sessionUid);
+ rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT | GWEN_DB_FLAGS_LOCKFILE);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_Buffer_free(nameBuf);
+
+ session=AQH_Session_fromDb(db);
+ if (session==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error loading session \"%s\" from config group", sessionUid);
+ GWEN_DB_Group_free(db);
+ return NULL;
+ }
+ GWEN_DB_Group_free(db);
+
+ rv=_checkSession(session);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "Invalid data for session \"%s\"", sessionUid);
+ AQH_Session_free(session);
+ return NULL;
+ }
+
+ return session;
+}
+
+
+
+int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session)
+{
+ int rv;
+ const char *sUid;
+ GWEN_BUFFER *nameBuf;
+ GWEN_DB_NODE *db;
+
+ rv=_checkSession(session);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ db=GWEN_DB_Group_new("user");
+ rv=AQH_Session_toDb(session, db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_DB_Group_free(db);
+ return rv;
+ }
+
+ sUid=AQH_Session_GetUid(session);
+ nameBuf=_getSessionFilePath(sv, sUid);
+ rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
+ GWEN_Buffer_free(nameBuf);
+ GWEN_DB_Group_free(db);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session)
+{
+ int rv;
+
+ rv=_checkSession(session);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ rv=AQH_HttpService_SaveSession(sv, session);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ return rv;
+ }
+
+ AQH_Service_AddSession(sv, session);
+
+ return 0;
+}
+
+
+
+int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session)
+{
+ const char *uid;
+ GWEN_BUFFER *nameBuf;
+
+ uid=AQH_Session_GetUid(session);
+ nameBuf=_getSessionFilePath(sv, uid);
+ unlink(GWEN_Buffer_GetStart(nameBuf));
+ GWEN_Buffer_free(nameBuf);
+ AQH_Service_DelSession(sv, uid);
+ return 0;
+}
+
+
+
+AQH_MODULE *_ensureModule(AQH_SERVICE *sv, const char *modName)
+{
+ AQH_MODULE *module;
+
+ module=AQH_Service_GetModuleByName(sv, modName);
+ if (module)
+ return module;
+
+ module=AQH_HttpService_LoadModule(sv, modName);
+ if (module==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Could not load module \"%s\"", modName);
+ return NULL;
+ }
+ AQH_Service_AddModule(sv, module);
+
+ return module;
+}
+
+
+
+AQH_USER *_ensureUser(AQH_SERVICE *sv, const char *alias)
+{
+ AQH_USER *user;
+
+ user=AQH_Service_GetUserByAlias(sv, alias);
+ if (user)
+ return user;
+
+ user=AQH_HttpService_LoadUser(sv, alias);
+ if (user==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Could not load user \"%s\"", alias);
+ return NULL;
+ }
+ _calculateUserPerms(sv, user);
+
+ AQH_Service_AddUser(sv, user);
+ return user;
+}
+
+
+
+AQH_SESSION *_ensureSession(AQH_SERVICE *sv, const char *sessionUid)
+{
+ AQH_SESSION *session;
+
+ session=AQH_Service_GetSessionByUid(sv, sessionUid);
+ if (session)
+ return session;
+
+ session=AQH_HttpService_LoadSession(sv, sessionUid);
+ if (session) {
+ AQH_USER *user;
+
+ user=AQH_HttpService_GetUser(sv, AQH_Session_GetUserAlias(session));
+ if (user==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "User \"%s\" for session \"%s\" not available", AQH_Session_GetUserAlias(session), sessionUid);
+ AQH_Session_free(session);
+ return NULL;
+ }
+ AQH_User_Attach(user);
+ AQH_Session_SetUser(session, user);
+ AQH_Service_AddSession(sv, session);
+ }
+
+ if (session==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Could not load session \"%s\"", sessionUid);
+ return NULL;
+ }
+
+ return session;
+}
+
+
+
+GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileName)
+{
+ const char *configFolder;
+
+ configFolder=AQH_HttpService_GetConfigFolder(sv);
+ if (!(configFolder && *configFolder)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
+ return NULL;
+ }
+ else {
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, configFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS GWEN_DIR_SEPARATOR_S);
+ GWEN_Text_EscapeToBuffer(fileName, nameBuf);
+ return nameBuf;
+ }
+}
+
+
+
+
+GWEN_BUFFER *_getModuleFilePath(const AQH_SERVICE *sv, const char *modName)
+{
+ const char *configFolder;
+
+ configFolder=AQH_HttpService_GetConfigFolder(sv);
+ if (!(configFolder && *configFolder)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
+ return NULL;
+ }
+ else {
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, configFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_MODS GWEN_DIR_SEPARATOR_S);
+ GWEN_Text_EscapeToBuffer(modName, nameBuf);
+ GWEN_Buffer_AppendString(nameBuf, ".conf");
+ return nameBuf;
+ }
+}
+
+
+
+
+GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias)
+{
+ const char *configFolder;
+
+ configFolder=AQH_HttpService_GetConfigFolder(sv);
+ if (!(configFolder && *configFolder)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
+ return NULL;
+ }
+ else {
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, configFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_USERS GWEN_DIR_SEPARATOR_S);
+ GWEN_Text_EscapeToBuffer(userAlias, nameBuf);
+ GWEN_Buffer_AppendString(nameBuf, ".conf");
+ return nameBuf;
+ }
+}
+
+
+
+GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid)
+{
+ const char *configFolder;
+
+ configFolder=AQH_HttpService_GetConfigFolder(sv);
+ if (!(configFolder && *configFolder)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
+ return NULL;
+ }
+ else {
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, configFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS GWEN_DIR_SEPARATOR_S);
+ GWEN_Text_EscapeToBuffer(sessionUid, nameBuf);
+ GWEN_Buffer_AppendString(nameBuf, ".conf");
+ return nameBuf;
+ }
+}
+
+
+
+int _checkUser(const AQH_USER *user, int ignoreMissingId)
+{
+ const char *s;
+
+ if (!ignoreMissingId) {
+ uint32_t id;
+
+ id=AQH_User_GetId(user);
+ if (id==0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no id");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ }
+
+ s=AQH_User_GetAlias(user);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no alias");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ s=AQH_User_GetHashedPassword(user);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no password");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ s=AQH_User_GetEmail(user);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no email");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ return 0;
+}
+
+
+
+int _checkSession(const AQH_SESSION *session)
+{
+ const char *s;
+
+ s=AQH_Session_GetUid(session);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no uid");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ s=AQH_Session_GetUserAlias(session);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no user alias");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ if (AQH_Session_GetTimestampCreation(session)==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no creation time");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ return 0;
+}
+
+
+
+int _checkModule(const AQH_MODULE *m, int ignoreMissingId)
+{
+ const char *s;
+ const AQH_ROLE_LIST *roleList;
+ int rv;
+
+ if (!ignoreMissingId) {
+ if (AQH_Module_GetId(m)==0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: missing id");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ }
+
+ s=AQH_Module_GetName(m);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: missing name");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ roleList=AQH_Module_GetRoleList(m);
+ if (roleList==NULL || AQH_Role_List_GetCount(roleList)<1) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: empty or missing role list");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ rv=_checkRoleList(roleList, ignoreMissingId);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid module %s: invalid role definition", AQH_Module_GetName(m));
+ return rv;
+ }
+
+ return 0;
+}
+
+
+
+int _checkRoleList(const AQH_ROLE_LIST *roleList, int ignoreMissingId)
+{
+ const AQH_ROLE *r;
+
+ r=AQH_Role_List_First(roleList);
+ while(r) {
+ const char *s;
+
+ if (!ignoreMissingId) {
+ if (AQH_Role_GetId(r)==0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid role: missing id");
+ return GWEN_ERROR_BAD_DATA;
+ }
+ }
+
+ s=AQH_Role_GetName(r);
+ if (!(s && *s)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Invalid role: missing name");
+ return GWEN_ERROR_BAD_DATA;
+ }
+
+ r=AQH_Role_List_Next(r);
+ }
+
+ return 0;
+}
+
+
+
+void _calculateUserPerms(AQH_SERVICE *sv, AQH_USER *user)
+{
+ AQH_MODULE_PERMS_LIST *permList;
+
+ permList=AQH_User_GetModulePermList(user);
+ if (permList) {
+ AQH_MODULE_PERMS *p;
+
+ p=AQH_ModulePerms_List_First(permList);
+ while(p) {
+ _calculateModuleUserPerms(sv, p);
+ p=AQH_ModulePerms_List_Next(p);
+ }
+ }
+ AQH_User_AddRuntimeFlags(user, AQH_USER_RTFLAGS_PERMSCALC);
+}
+
+
+
+void _calculateModuleUserPerms(AQH_SERVICE *sv, AQH_MODULE_PERMS *p)
+{
+ uint32_t perms=0;
+ uint32_t addPerms=0;
+ uint32_t delPerms=0;
+ const AQH_MODULE *m;
+
+ m=AQH_Service_GetModuleById(sv, AQH_ModulePerms_GetModuleId(p));
+ if (m==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Module \"%d\" not found, ignoring", AQH_ModulePerms_GetModuleId(p));
+ }
+ else {
+ const AQH_ROLE_LIST *roleList;
+
+ roleList=AQH_Module_GetRoleList(m);
+ if (roleList) {
+ int i, j;
+
+ j=AQH_ModulePerms_GetRoleArrayArraySize();
+ for(i=0; i0) {
+ AQH_ROLE *r;
+
+ r=AQH_Role_List_GetById(roleList, roleId);
+ if (r==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Role %d not found in module \"%s\", ignoring", roleId, AQH_Module_GetName(m));
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Module \"%s\": adding perms for role \"%s\"", AQH_Module_GetName(m), AQH_Role_GetName(r));
+ perms|=AQH_Role_GetPerms(r);
+ addPerms|=AQH_Role_GetExplAddPerms(r);
+ delPerms|=AQH_Role_GetExplDelPerms(r);
+ }
+ }
+ }
+ }
+ }
+ perms|=addPerms;
+ perms&=~delPerms;
+ AQH_ModulePerms_SetPerms(p, perms);
+}
+
+
+
+int _writeDbFile(const char *fname, GWEN_DB_NODE *db)
+{
+ GWEN_BUFFER *tmpFilenameBuf;
+ int rv;
+
+ tmpFilenameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(tmpFilenameBuf, fname);
+ GWEN_Buffer_AppendString(tmpFilenameBuf, ".tmp");
+ unlink(GWEN_Buffer_GetStart(tmpFilenameBuf));
+ rv=GWEN_DB_WriteFile(db, GWEN_Buffer_GetStart(tmpFilenameBuf), GWEN_DB_FLAGS_DEFAULT | GWEN_DB_FLAGS_LOCKFILE);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error writing db file \"%s\": %d", GWEN_Buffer_GetStart(tmpFilenameBuf), rv);
+ GWEN_Buffer_free(tmpFilenameBuf);
+ return rv;
+ }
+ if (rename(GWEN_Buffer_GetStart(tmpFilenameBuf), fname)<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error renaming tmp file to \"%s\": %d (%s)", fname, errno, strerror(errno));
+ GWEN_Buffer_free(tmpFilenameBuf);
+ return GWEN_ERROR_IO;
+ }
+ unlink(GWEN_Buffer_GetStart(tmpFilenameBuf));
+ GWEN_Buffer_free(tmpFilenameBuf);
+ return 0;
+}
+
+
+
+
+
+
+
diff --git a/aqhome/http/httpservice_conf.h b/aqhome/http/httpservice_conf.h
new file mode 100644
index 0000000..ecb9590
--- /dev/null
+++ b/aqhome/http/httpservice_conf.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifndef AQH_HTTP_SERVICE_CONF_H
+#define AQH_HTTP_SERVICE_CONF_H
+
+
+
+#include "aqhome/service/service.h"
+
+#include
+#include
+#include
+#include
+
+
+int AQH_HttpService_LoadConfig(AQH_SERVICE *sv);
+int AQH_HttpService_SaveConfig(const AQH_SERVICE *sv);
+
+AQH_MODULE *AQH_HttpService_GetModule(AQH_SERVICE *sv, const char *modName);
+AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias);
+AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid);
+
+
+AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modName);
+int AQH_HttpService_SaveModule(const AQH_SERVICE *sv, const AQH_MODULE *m);
+int AQH_HttpService_AddModule(AQH_SERVICE *sv, AQH_MODULE *m);
+
+AQH_USER *AQH_HttpService_LoadUser(const AQH_SERVICE *sv, const char *userAlias);
+int AQH_HttpService_SaveUser(const AQH_SERVICE *sv, const AQH_USER *user);
+int AQH_HttpService_AddUser(AQH_SERVICE *sv, AQH_USER *user);
+int AQH_HttpService_DelUser(AQH_SERVICE *sv, AQH_USER *user);
+
+AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sessionUid);
+int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session);
+int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session);
+int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session);
+
+
+#endif
+
+
+
diff --git a/aqhome/http/httpservice_http.c b/aqhome/http/httpservice_http.c
new file mode 100644
index 0000000..45de3b3
--- /dev/null
+++ b/aqhome/http/httpservice_http.c
@@ -0,0 +1,281 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "aqhome/http/httpservice_http.h"
+#include "aqhome/http/httpservice_conf.h"
+#include "aqhome/http/httpservice_p.h"
+
+#include
+#include
+#include
+#include
+
+
+
+#ifdef GWEN_INHERIT_REF
+ GWEN_INHERIT_REF(AQH_SERVICE, AQH_HTTP_SERVICE)
+#else
+ extern uint32_t AQH_HTTP_SERVICE_ID;
+#endif
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWEN_BUFFER *buf);
+static GWEN_BUFFER *_getPageFilePath(const AQH_SERVICE *sv, const char *langFolder, const char *fileName);
+static void _setRequestPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+
+GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *msgReceived)
+{
+ AQH_HTTP_SERVICE *xsv;
+ AQH_HTTP_REQUEST *rq;
+ const char *s;
+ AQH_MODULE *m=NULL;
+ AQH_SESSION *session=NULL;
+ GWEN_MSG *msgResponse;
+ int rv;
+
+ xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
+ if (xsv==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
+ return NULL;
+ }
+
+ if (xsv->handleRequestFn==NULL) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No request handler set");
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal server error", "HTTP/1.1");
+ }
+
+ rq=AQH_HttpRequest_new(endpoint, msgReceived);
+ if (rq==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Could not create valie request from incoming message");
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal server error", "HTTP/1.1");
+ }
+
+ s=AQH_HttpRequest_GetModuleName(rq);
+ if (s && *s) {
+ m=AQH_HttpService_GetModule(sv, s);
+ if (m==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Module \"%s\" not found", s);
+ }
+ else
+ AQH_HttpRequest_SetModule(rq, m);
+ }
+
+ s=AQH_HttpRequest_GetSessionId(rq);
+ if (s && *s) {
+ session=AQH_HttpService_GetSession(sv, s);
+ if (session==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" not found", s);
+ }
+ else {
+ AQH_HttpRequest_SetSession(rq, session);
+ }
+ }
+
+ _setRequestPerms(sv, rq);
+
+ rv=xsv->handleRequestFn(sv, rq);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling request");
+ AQH_HttpRequest_free(rq);
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal server error", "http/1.1");
+ }
+ msgResponse=AQH_HttpRequest_TakeResponseMsg(rq);
+ AQH_HttpRequest_free(rq);
+ return msgResponse;
+}
+
+
+
+void AQH_HttpService_AddStatusLine(AQH_SERVICE *sv, int code, const char *msg, const char *proto, GWEN_BUFFER *buf)
+{
+ GWEN_Buffer_AppendArgs(buf, "%s %03d %s \r\n", proto?proto:"HTTP/1.1", code, msg?msg:"");
+}
+
+
+
+void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeader, GWEN_BUFFER *buf)
+{
+ GWEN_DB_NODE *dbVar;
+
+ dbVar=GWEN_DB_GetFirstVar(dbHeader);
+ while (dbVar) {
+ GWEN_DB_NODE *dbVal;
+
+ /* only handle first value */
+ dbVal=GWEN_DB_GetFirstValue(dbVar);
+ if (dbVal) {
+ const char *sVar;
+
+ sVar=GWEN_DB_VariableName(dbVar);
+ if (sVar && *sVar) {
+ GWEN_DB_NODE_TYPE vtype;
+
+ vtype=GWEN_DB_GetValueType(dbVal);
+ if (vtype==GWEN_DB_NodeType_ValueChar) {
+ const char *sValue;
+
+ sValue=GWEN_DB_GetCharValueFromNode(dbVal);
+ if (sValue)
+ GWEN_Buffer_AppendArgs(buf, "%s: %s\r\n", sVar, sValue);
+ } /* if char */
+ else if (vtype==GWEN_DB_NodeType_ValueInt) {
+ int i;
+
+ i=GWEN_DB_GetIntValueFromNode(dbVal);
+ if (i!=-1 || strcasecmp(sVar, "Content-Length")==0)
+ GWEN_Buffer_AppendArgs(buf, "%s: %d\r\n", sVar, i);
+ } /* if int */
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Variable type %d of var [%s] not supported, ignoring", vtype, sVar);
+ }
+ } /* if sVar */
+ }
+ dbVar=GWEN_DB_GetNextVar(dbVar);
+ }
+
+ /* finalize header */
+ GWEN_Buffer_AppendString(buf, "\r\n");
+}
+
+
+
+GWEN_MSG *AQH_HttpService_CreateResponseMsg(AQH_SERVICE *sv, int code, const char *text, const char *protocol)
+{
+ GWEN_BUFFER *buf;
+ GWEN_MSG *msg;
+
+ buf=GWEN_Buffer_new(0, 256, 0, 1);
+ AQH_HttpService_AddStatusLine(sv, code, text, protocol, buf);
+ _writeDefaultResponseHeaderToBuffer(sv, 0, buf);
+
+ msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
+ GWEN_Buffer_free(buf);
+ return msg;
+}
+
+
+
+int AQH_HttpService_AddFile(AQH_SERVICE *sv, GWEN_UNUSED AQH_SESSION *session, const char *fname, GWEN_BUFFER *buf)
+{
+ if (fname && *fname) {
+ int rv;
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=_getPageFilePath(sv, NULL, fname);
+
+ rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(nameBuf), buf);
+ if (rv<0) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"%s\": %d", fname, rv);
+ GWEN_Buffer_free(nameBuf);
+ return rv;
+ }
+ GWEN_Buffer_free(nameBuf);
+ }
+
+ return 0;
+}
+
+
+
+GWEN_BUFFER *_getPageFilePath(const AQH_SERVICE *sv, const char *langFolder, const char *fileName)
+{
+ const char *sourceFolder;
+
+ sourceFolder=AQH_HttpService_GetSourceFolder(sv);
+ if (!(sourceFolder && *sourceFolder)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "No source folder given");
+ return NULL;
+ }
+ else {
+ GWEN_BUFFER *nameBuf;
+
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, sourceFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
+ if (langFolder && *langFolder) {
+ GWEN_Buffer_AppendString(nameBuf, langFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
+ }
+ GWEN_Text_EscapeToBuffer(fileName, nameBuf);
+ return nameBuf;
+ }
+}
+
+
+
+
+
+
+void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWEN_BUFFER *buf)
+{
+ GWEN_DB_NODE *db;
+
+ db=GWEN_DB_Group_new("header");
+ GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Connection", "Keep-Alive");
+ GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Type", "text/html; charset=utf-8");
+ GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", contentLength);
+
+ AQH_HttpService_AddHeader(sv, db, buf);
+ GWEN_DB_Group_free(db);
+}
+
+
+
+void _setRequestPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq)
+{
+ AQH_MODULE *m;
+ AQH_SESSION *session;
+
+ AQH_HttpRequest_SetModulePerms(rq, 0);
+ m=AQH_HttpRequest_GetModule(rq);
+ session=AQH_HttpRequest_GetSession(rq);
+ if (m) {
+ if (session) {
+ AQH_USER * user;
+
+ user=AQH_Session_GetUser(session);
+ if (user) {
+ AQH_MODULE_PERMS *userPerms;
+
+ userPerms=AQH_ModulePerms_List_GetByModuleId(AQH_User_GetModulePermList(user), AQH_Module_GetId(m));
+ if (userPerms)
+ AQH_HttpRequest_SetModulePerms(rq, AQH_ModulePerms_GetPerms(userPerms));
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Session but no user, SNH!");
+ }
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "No session");
+ }
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "No module");
+ }
+}
+
+
diff --git a/aqhome/http/httpservice_http.h b/aqhome/http/httpservice_http.h
new file mode 100644
index 0000000..1ae2448
--- /dev/null
+++ b/aqhome/http/httpservice_http.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifndef AQH_HTTP_SERVICE_HTTP_H
+#define AQH_HTTP_SERVICE_HTTP_H
+
+
+#include "aqhome/service/service.h"
+
+#include
+#include
+#include
+
+
+#define AQH_HTTP_SERVICE_SESSIONCOOKIE "sessionid"
+
+
+AQHOME_API GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *msgReceived);
+
+
+AQHOME_API void AQH_HttpService_AddStatusLine(AQH_SERVICE *sv, int code, const char *msg, const char *proto, GWEN_BUFFER *buf);
+AQHOME_API void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeader, GWEN_BUFFER *buf);
+
+AQHOME_API int AQH_HttpService_ParsePostBody(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, GWEN_DB_NODE *dbBody);
+
+
+AQHOME_API GWEN_MSG *AQH_HttpService_CreateResponseMsg(AQH_SERVICE *sv, int code, const char *text, const char *protocol);
+
+AQHOME_API int AQH_HttpService_AddFile(AQH_SERVICE *sv, AQH_SESSION *session, const char *fname, GWEN_BUFFER *buf);
+
+
+#endif
+
+
+
diff --git a/aqhome/http/httpservice_login.c b/aqhome/http/httpservice_login.c
new file mode 100644
index 0000000..59e848e
--- /dev/null
+++ b/aqhome/http/httpservice_login.c
@@ -0,0 +1,45 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "aqhome/http/httpservice_login.h"
+#include "aqhome/http/httpservice_p.h"
+
+#include
+#include
+#include
+#include
+
+
+
+/*
+ Login process:
+ - GET: send a login page
+ - POST:
+ - get USER and PASS
+ - look for user (maybe add virtual function to load a user?)
+ - check password
+ - if all okay:
+ - create session
+ - add header "set-cookie"
+ - redirect to main page
+ */
+
+GWEN_MSG *AQH_HttpService_HandleLoginUrl(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, const GWEN_URL *url)
+{
+
+}
+
+
+
+
+
diff --git a/aqhome/http/httpservice_login.h b/aqhome/http/httpservice_login.h
new file mode 100644
index 0000000..5c378f1
--- /dev/null
+++ b/aqhome/http/httpservice_login.h
@@ -0,0 +1,30 @@
+/****************************************************************************
+ * This file is part of the project AqHome.
+ * AqHome (c) by 2023 Martin Preuss, all rights reserved.
+ *
+ * The license for this file can be found in the file COPYING which you
+ * should have received along with this file.
+ ****************************************************************************/
+
+#ifndef AQH_HTTP_SERVICE_LOGIN_H
+#define AQH_HTTP_SERVICE_LOGIN_H
+
+
+
+#include "aqhome/http/httpservice.h"
+
+#include
+#include
+#include
+#include
+
+
+
+GWEN_MSG *AQH_HttpService_HandleLoginUrl(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, const GWEN_URL *url);
+
+
+
+#endif
+
+
+
diff --git a/aqhome/http/httpservice_p.h b/aqhome/http/httpservice_p.h
index 5b73a21..1d69e8a 100644
--- a/aqhome/http/httpservice_p.h
+++ b/aqhome/http/httpservice_p.h
@@ -13,14 +13,27 @@
#include "aqhome/http/httpservice.h"
+#define AQH_HTTP_SERVICE_DEFAULT_MAXSESSIONAGE 600 /* 10m */
+
typedef struct AQH_HTTP_SERVICE AQH_HTTP_SERVICE;
struct AQH_HTTP_SERVICE {
char *sourceFolder;
+ char *configFolder;
char *siteHeader;
char *siteFooter;
+ uint32_t lastModuleId;
+ uint32_t lastUserId;
+
+ int maxSessionAgeInSecs;
+
+ GWEN_MUTEX *moduleMutex;
+ GWEN_MUTEX *userMutex;
+ GWEN_MUTEX *sessionMutex;
+
+ AQH_HTTP_SERVICE_HANDLEREQUEST_FN handleRequestFn;
};
diff --git a/aqhome/service/module.t2d b/aqhome/service/module.t2d
index ff50d36..e78c385 100644
--- a/aqhome/service/module.t2d
+++ b/aqhome/service/module.t2d
@@ -64,6 +64,8 @@
NULL
public
own
+ none
+ none
diff --git a/aqhome/service/moduleperms.t2d b/aqhome/service/moduleperms.t2d
index 015d862..ed8d35b 100644
--- a/aqhome/service/moduleperms.t2d
+++ b/aqhome/service/moduleperms.t2d
@@ -38,13 +38,6 @@
with_getbymember
-
- 0
- 0
- public
-
-
-
0
0
@@ -59,13 +52,21 @@
-
+
0
0
public
+
+
+ 0
+ 0
+ public
+ volatile
+
+
diff --git a/aqhome/service/role.t2d b/aqhome/service/role.t2d
index 311916b..3e1d536 100644
--- a/aqhome/service/role.t2d
+++ b/aqhome/service/role.t2d
@@ -29,7 +29,7 @@
-
+
0
0
public
@@ -50,14 +50,14 @@
-
+
0
0
public
-
+
0
0
public
diff --git a/aqhome/service/service.c b/aqhome/service/service.c
index 0381ce0..116a1fe 100644
--- a/aqhome/service/service.c
+++ b/aqhome/service/service.c
@@ -149,9 +149,9 @@ AQH_SESSION_LIST *AQH_Service_GetSessionList(const AQH_SERVICE *sv)
-AQH_SESSION *AQH_Service_GetSessionById(const AQH_SERVICE *sv, uint32_t sessionId)
+AQH_SESSION *AQH_Service_GetSessionByUid(const AQH_SERVICE *sv, const char *sessionUid)
{
- return sv?AQH_Session_List_GetById(sv->sessionList, sessionId):NULL;
+ return sv?AQH_Session_List_GetByUid(sv->sessionList, sessionUid):NULL;
}
@@ -164,12 +164,12 @@ void AQH_Service_AddSession(AQH_SERVICE *sv, AQH_SESSION *session)
-void AQH_Service_DelSession(AQH_SERVICE *sv, uint32_t id)
+void AQH_Service_DelSession(AQH_SERVICE *sv, const char *sid)
{
- if (sv && id) {
+ if (sv && sid && *sid) {
AQH_SESSION *session;
- session=AQH_Session_List_GetById(sv->sessionList, id);
+ session=AQH_Session_List_GetByUid(sv->sessionList, sid);
if (session) {
AQH_Session_List_Del(session);
AQH_Session_free(session);
diff --git a/aqhome/service/service.h b/aqhome/service/service.h
index 1358825..e5363b8 100644
--- a/aqhome/service/service.h
+++ b/aqhome/service/service.h
@@ -44,9 +44,9 @@ AQHOME_API void AQH_Service_AddModule(AQH_SERVICE *sv, AQH_MODULE *m);
AQHOME_API void AQH_Service_DelModule(AQH_SERVICE *sv, uint32_t moduleId);
AQHOME_API AQH_SESSION_LIST *AQH_Service_GetSessionList(const AQH_SERVICE *sv);
-AQHOME_API AQH_SESSION *AQH_Service_GetSessionById(const AQH_SERVICE *sv, uint32_t sessionId);
+AQHOME_API AQH_SESSION *AQH_Service_GetSessionByUid(const AQH_SERVICE *sv, const char *sessionUid);
AQHOME_API void AQH_Service_AddSession(AQH_SERVICE *sv, AQH_SESSION *session);
-AQHOME_API void AQH_Service_DelSession(AQH_SERVICE *sv, uint32_t id);
+AQHOME_API void AQH_Service_DelSession(AQH_SERVICE *sv, const char *sid);
diff --git a/aqhome/service/session.t2d b/aqhome/service/session.t2d
index d0ccca1..002e7bb 100644
--- a/aqhome/service/session.t2d
+++ b/aqhome/service/session.t2d
@@ -14,6 +14,8 @@
with_db
with_list1
with_list2
+ nodup
+ nocopy
@@ -31,18 +33,11 @@
-
- 0
- 0
- public
- with_getbymember
-
-
0
0
public
-
+ with_getbymember
@@ -52,9 +47,9 @@
with_flags
-
- 0
- 0
+
+ NULL
+ NULL
public
@@ -87,8 +82,8 @@
NULL
public
assign
- assign
- volatile
+ none
+ volatile nodup nocopy
diff --git a/aqhome/service/user.t2d b/aqhome/service/user.t2d
index c7b9f5d..45c583c 100644
--- a/aqhome/service/user.t2d
+++ b/aqhome/service/user.t2d
@@ -14,6 +14,9 @@
with_db
with_list1
with_list2
+ with_refcount
+ nodup
+ nocopy
@@ -28,6 +31,14 @@
+
+
+
+
+
+
+
+
@@ -103,6 +114,14 @@
own
+
+
+ 0
+ 0
+ public
+ volatile with_flags
+
+