aqhome: more work on http service.
This commit is contained in:
@@ -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
605
aqhome/http/httprequest.c
Normal 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
76
aqhome/http/httprequest.h
Normal 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
|
||||
|
||||
|
||||
|
||||
59
aqhome/http/httprequest_p.h
Normal file
59
aqhome/http/httprequest_p.h
Normal 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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
982
aqhome/http/httpservice_conf.c
Normal file
982
aqhome/http/httpservice_conf.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
48
aqhome/http/httpservice_conf.h
Normal file
48
aqhome/http/httpservice_conf.h
Normal 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
|
||||
|
||||
|
||||
|
||||
281
aqhome/http/httpservice_http.c
Normal file
281
aqhome/http/httpservice_http.c
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
aqhome/http/httpservice_http.h
Normal file
40
aqhome/http/httpservice_http.h
Normal 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
|
||||
|
||||
|
||||
|
||||
45
aqhome/http/httpservice_login.c
Normal file
45
aqhome/http/httpservice_login.c
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
30
aqhome/http/httpservice_login.h
Normal file
30
aqhome/http/httpservice_login.h
Normal 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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user