aqhome: more work on http service.

This commit is contained in:
Martin Preuss
2023-08-07 14:08:52 +02:00
parent 6269431467
commit 1efcd09f0c
20 changed files with 2375 additions and 227 deletions

View File

@@ -47,12 +47,16 @@
<headers dist="true" install="$(pkgincludedir)/http" >
endpoint_http.h
httpservice.h
httpservice_conf.h
httpservice_http.h
httprequest.h
</headers>
<headers dist="true" >
endpoint_http_p.h
httpservice_p.h
httprequest_p.h
</headers>
@@ -61,6 +65,9 @@
endpoint_http.c
httpservice.c
httpservice_conf.c
httpservice_http.c
httprequest.c
</sources>

605
aqhome/http/httprequest.c Normal file
View File

@@ -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 <config.h>
#endif
#include "./httprequest_p.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#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;
}

76
aqhome/http/httprequest.h Normal file
View File

@@ -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 <gwenhywfar/tree2.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/url.h>
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

View File

@@ -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

View File

@@ -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:"<no type>");
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;
}

View File

@@ -13,33 +13,45 @@
#include "aqhome/service/service.h"
#include "aqhome/http/httprequest.h"
#include <gwenhywfar/msg.h>
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/url.h>
#include <gwenhywfar/mutex.h>
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

View File

@@ -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 <config.h>
#endif
#include "aqhome/http/httpservice_conf.h"
#include "aqhome/http/httpservice_p.h"
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#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; i<j; i++) {
uint8_t roleId;
roleId=AQH_ModulePerms_GetRoleArrayAt(p, i);
if (roleId>0) {
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;
}

View File

@@ -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 <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/url.h>
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

View File

@@ -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 <config.h>
#endif
#include "aqhome/http/httpservice_http.h"
#include "aqhome/http/httpservice_conf.h"
#include "aqhome/http/httpservice_p.h"
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#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");
}
}

View File

@@ -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 <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/url.h>
#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

View File

@@ -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 <config.h>
#endif
#include "aqhome/http/httpservice_login.h"
#include "aqhome/http/httpservice_p.h"
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
/*
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)
{
}

View File

@@ -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 <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/url.h>
GWEN_MSG *AQH_HttpService_HandleLoginUrl(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, const GWEN_URL *url);
#endif

View File

@@ -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;
};