aqhome: more work on http server.

This commit is contained in:
Martin Preuss
2023-08-08 23:49:28 +02:00
parent 3378908c93
commit aafecfa704
50 changed files with 2988 additions and 497 deletions

View File

@@ -50,6 +50,9 @@
httpservice_conf.h
httpservice_http.h
httprequest.h
urlhandler.h
content.h
content_files.h
</headers>
@@ -57,6 +60,9 @@
endpoint_http_p.h
httpservice_p.h
httprequest_p.h
urlhandler_p.h
content_p.h
content_files_p.h
</headers>
@@ -68,6 +74,9 @@
httpservice_conf.c
httpservice_http.c
httprequest.c
urlhandler.c
content.c
content_files.c
</sources>

112
aqhome/http/content.c Normal file
View File

@@ -0,0 +1,112 @@
/****************************************************************************
* 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/content_p.h"
#include <gwenhywfar/debug.h>
GWEN_INHERIT_FUNCTIONS(AQH_HTTP_CONTENT)
GWEN_TREE2_FUNCTIONS(AQH_HTTP_CONTENT, AQH_HttpContent)
AQH_HTTP_CONTENT *AQH_HttpContent_new(const char *name)
{
AQH_HTTP_CONTENT *cp;
GWEN_NEW_OBJECT(AQH_HTTP_CONTENT, cp);
GWEN_INHERIT_INIT(AQH_HTTP_CONTENT, cp);
GWEN_TREE2_INIT(AQH_HTTP_CONTENT, cp, AQH_HttpContent);
cp->name=name?strdup(name):NULL;
return cp;
}
void AQH_HttpContent_free(AQH_HTTP_CONTENT *cp)
{
if (cp) {
GWEN_TREE2_FINI(AQH_HTTP_CONTENT, cp, AQH_HttpContent);
GWEN_INHERIT_FINI(AQH_HTTP_CONTENT, cp);
GWEN_FREE_OBJECT(cp);
}
}
const char *AQH_HttpContent_GetName(const AQH_HTTP_CONTENT *cp)
{
return cp?cp->name:NULL;
}
int AQH_HttpContent_AddOpeningContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer)
{
return (cp && cp->addOpeningContentFn)?(cp->addOpeningContentFn(cp, mode, buffer)):0;
}
int AQH_HttpContent_AddClosingContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer)
{
return (cp && cp->addClosingContentFn)?(cp->addClosingContentFn(cp, mode, buffer)):0;
}
void AQH_HttpContent_SetAddOpeningContentFn(AQH_HTTP_CONTENT *cp, AQH_HTTP_CONTENT_ADD_OPENING_CONTENT_FN f)
{
if (cp)
cp->addOpeningContentFn=f;
}
void AQH_HttpContent_SetAddClosingContentFn(AQH_HTTP_CONTENT *cp, AQH_HTTP_CONTENT_ADD_CLOSING_CONTENT_FN f)
{
if (cp)
cp->addClosingContentFn=f;
}
AQH_HTTP_CONTENT *AQH_HttpContent_Tree2_FindChildByName(AQH_HTTP_CONTENT *parent, const char *name)
{
if (parent) {
AQH_HTTP_CONTENT *c;
c=AQH_HttpContent_Tree2_GetFirstChild(parent);
while(c) {
const char *s;
s=AQH_HttpContent_GetName(c);
if (s && *s && strcasecmp(s, name)==0)
return c;
c=AQH_HttpContent_Tree2_GetNext(c);
}
}
return NULL;
}

63
aqhome/http/content.h Normal file
View File

@@ -0,0 +1,63 @@
/****************************************************************************
* 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 AQHOME_HTTP_CONTENT_H
#define AQHOME_HTTP_CONTENT_H
#include <aqhome/api.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/tree2.h>
#define AQH_HTTP_CONTENT_MODE_DESKTOP 0
#define AQH_HTTP_CONTENT_MODE_MOBILE 1
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AQH_HTTP_CONTENT AQH_HTTP_CONTENT;
GWEN_INHERIT_FUNCTION_LIB_DEFS(AQH_HTTP_CONTENT, AQHOME_API)
GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_HTTP_CONTENT, AQH_HttpContent, AQHOME_API)
typedef int (*AQH_HTTP_CONTENT_ADD_OPENING_CONTENT_FN)(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
typedef int (*AQH_HTTP_CONTENT_ADD_CLOSING_CONTENT_FN)(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
AQHOME_API AQH_HTTP_CONTENT *AQH_HttpContent_new(const char *name);
AQHOME_API void AQH_HttpContent_free(AQH_HTTP_CONTENT *cp);
AQHOME_API const char *AQH_HttpContent_GetName(const AQH_HTTP_CONTENT *cp);
AQHOME_API int AQH_HttpContent_AddOpeningContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
AQHOME_API int AQH_HttpContent_AddClosingContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
AQHOME_API void AQH_HttpContent_SetAddOpeningContentFn(AQH_HTTP_CONTENT *cp, AQH_HTTP_CONTENT_ADD_OPENING_CONTENT_FN f);
AQHOME_API void AQH_HttpContent_SetAddClosingContentFn(AQH_HTTP_CONTENT *cp, AQH_HTTP_CONTENT_ADD_CLOSING_CONTENT_FN f);
AQHOME_API AQH_HTTP_CONTENT *AQH_HttpContent_Tree2_FindChildByName(AQH_HTTP_CONTENT *parent, const char *name);
#ifdef __cplusplus
}
#endif
#endif

140
aqhome/http/content_files.c Normal file
View File

@@ -0,0 +1,140 @@
/****************************************************************************
* 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/content_files_p.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/syncio.h>
GWEN_INHERIT(AQH_HTTP_CONTENT, AQH_HTTP_CONTENT_FILES)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _freeData(void *bp, void *p);
static int _addOpeningContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
static int _addClosingContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQH_HTTP_CONTENT *AQH_HttpContentFiles_new(const char *name, const char *headerFilename, const char *footerFilename)
{
AQH_HTTP_CONTENT *cp;
AQH_HTTP_CONTENT_FILES *xcp;
cp=AQH_HttpContent_new(name);
GWEN_NEW_OBJECT(AQH_HTTP_CONTENT_FILES, xcp);
GWEN_INHERIT_SETDATA(AQH_HTTP_CONTENT, AQH_HTTP_CONTENT_FILES, cp, xcp, _freeData);
xcp->headerFilename=(headerFilename && *headerFilename)?strdup(headerFilename):NULL;
xcp->footerFilename=(footerFilename && *footerFilename)?strdup(footerFilename):NULL;
AQH_HttpContent_SetAddOpeningContentFn(cp, _addOpeningContent);
AQH_HttpContent_SetAddClosingContentFn(cp, _addClosingContent);
return cp;
}
void _freeData(void *bp, void *p)
{
AQH_HTTP_CONTENT_FILES *xcp;
xcp=(AQH_HTTP_CONTENT_FILES*) p;
free(xcp->footerData);
free(xcp->headerData);
free(xcp->footerFilename);
free(xcp->headerFilename);
GWEN_FREE_OBJECT(xcp);
}
int _addOpeningContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer)
{
if (cp) {
AQH_HTTP_CONTENT_FILES *xcp;
xcp=GWEN_INHERIT_GETDATA(AQH_HTTP_CONTENT, AQH_HTTP_CONTENT_FILES, cp);
if (xcp) {
if (xcp->headerData==NULL) {
if (xcp->headerFilename) {
int rv;
GWEN_BUFFER *fileBuffer;
fileBuffer=GWEN_Buffer_new(0, 256, 0, 1);
rv=GWEN_SyncIo_Helper_ReadFile(xcp->headerFilename, fileBuffer);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error reading header file \"%s\": %d", xcp->headerFilename, rv);
GWEN_Buffer_free(fileBuffer);
return rv;
}
xcp->headerData=strdup(GWEN_Buffer_GetStart(fileBuffer));
GWEN_Buffer_free(fileBuffer);
}
}
if (xcp->headerData)
GWEN_Buffer_AppendString(buffer, xcp->headerData);
}
}
return 0;
}
int _addClosingContent(AQH_HTTP_CONTENT *cp, int mode, GWEN_BUFFER *buffer)
{
if (cp) {
AQH_HTTP_CONTENT_FILES *xcp;
xcp=GWEN_INHERIT_GETDATA(AQH_HTTP_CONTENT, AQH_HTTP_CONTENT_FILES, cp);
if (xcp) {
if (xcp->footerData==NULL) {
if (xcp->footerFilename) {
int rv;
GWEN_BUFFER *fileBuffer;
fileBuffer=GWEN_Buffer_new(0, 256, 0, 1);
rv=GWEN_SyncIo_Helper_ReadFile(xcp->footerFilename, fileBuffer);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error reading footer file \"%s\": %d", xcp->footerFilename, rv);
GWEN_Buffer_free(fileBuffer);
return rv;
}
xcp->footerData=strdup(GWEN_Buffer_GetStart(fileBuffer));
GWEN_Buffer_free(fileBuffer);
}
if (xcp->footerData)
GWEN_Buffer_AppendString(buffer, xcp->footerData);
}
}
}
return 0;
}

View File

@@ -0,0 +1,34 @@
/****************************************************************************
* 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 AQHOME_HTTP_CONTENT_FILES_H
#define AQHOME_HTTP_CONTENT_FILES_H
#include <aqhome/api.h>
#include <aqhome/http/content.h>
#ifdef __cplusplus
extern "C" {
#endif
AQHOME_API AQH_HTTP_CONTENT *AQH_HttpContentFiles_new(const char *name, const char *headerFilename, const char *footerFilename);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,29 @@
/****************************************************************************
* 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 AQHOME_HTTP_CONTENT_FILES_P_H
#define AQHOME_HTTP_CONTENT_FILES_P_H
#include "aqhome/http/content_files.h"
typedef struct AQH_HTTP_CONTENT_FILES AQH_HTTP_CONTENT_FILES;
struct AQH_HTTP_CONTENT_FILES {
char *headerFilename;
char *footerFilename;
char *headerData;
char *footerData;
};
#endif

29
aqhome/http/content_p.h Normal file
View File

@@ -0,0 +1,29 @@
/****************************************************************************
* 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 AQHOME_HTTP_CONTENT_P_H
#define AQHOME_HTTP_CONTENT_P_H
#include "aqhome/http/content.h"
struct AQH_HTTP_CONTENT {
GWEN_INHERIT_ELEMENT(AQH_HTTP_CONTENT);
GWEN_TREE2_ELEMENT(AQH_HTTP_CONTENT);
char *name;
AQH_HTTP_CONTENT_ADD_OPENING_CONTENT_FN addOpeningContentFn;
AQH_HTTP_CONTENT_ADD_CLOSING_CONTENT_FN addClosingContentFn;
};
#endif

View File

@@ -70,6 +70,7 @@ void AQH_HttpEndpoint_Extend(GWEN_MSG_ENDPOINT *ep, uint32_t flags)
GWEN_NEW_OBJECT(AQH_ENDPOINT_HTTP, xep);
GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep, xep, _freeData);
xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
xep->flags=flags;
xep->addSocketsFn=GWEN_MsgEndpoint_SetAddSocketsFn(ep, _addSockets);
xep->checkSocketsFn=GWEN_MsgEndpoint_SetCheckSocketsFn(ep, _checkSockets);
@@ -325,7 +326,9 @@ int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferP
_finishMessageAndStartNext(ep);
return rv;
}
DBG_INFO(AQH_LOGDOMAIN, "Command line complete, advancing to header read mode");
DBG_INFO(AQH_LOGDOMAIN,
"Command line complete, advancing to header read mode (start: %d)",
GWEN_Buffer_GetPos(xep->currentReadBuffer));
xep->readMode=AQH_EndpointHttpd_ReadMode_Headers;
xep->currentHeaderPos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
}
@@ -368,7 +371,7 @@ int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPt
AQH_ENDPOINT_HTTP *xep;
int lineLength;
/* line complete, TODO: parse status/command line */
/* line complete, parse status/command line */
rv=-rv;
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
@@ -379,7 +382,7 @@ int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPt
int contentLength;
/* Empty line received, TODO: parse header */
DBG_INFO(AQH_LOGDOMAIN, "Empty header line received, end of header reached.");
DBG_INFO(AQH_LOGDOMAIN, "Empty header line received, end of header reached (header pos: %d).", xep->currentHeaderPos);
copyOfHeader=strdup(GWEN_Buffer_GetStart(xep->currentReadBuffer)+xep->currentHeaderPos);
xep->dbCurrentReadHeader=GWEN_DB_Group_new("header");
if (_parseHeader(copyOfHeader, xep->dbCurrentReadHeader)<0) {
@@ -498,10 +501,14 @@ void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep)
xep->dbCurrentReadCommand=NULL;
xep->dbCurrentReadHeader=NULL;
if (xep->flags & AQH_ENDPOINT_HTTP_FLAGS_PASSIVE)
if (xep->flags & AQH_ENDPOINT_HTTP_FLAGS_PASSIVE) {
DBG_INFO(AQH_LOGDOMAIN, "Passive connection");
xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
else
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Active connection");
xep->readMode=AQH_EndpointHttpd_ReadMode_Status;
}
}
@@ -571,7 +578,7 @@ int _parseHeader(char *bufferPtr, GWEN_DB_NODE *db)
p++;
pVarEnd=p;
if (*p!=':') {
DBG_INFO(AQH_LOGDOMAIN, "No separator after variable name in received header");
DBG_INFO(AQH_LOGDOMAIN, "No separator after variable name in received header (var: [%s])", pVarBegin);
return GWEN_ERROR_BAD_DATA;
}
*pVarEnd=0;

View File

@@ -16,8 +16,7 @@
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid"
#include <ctype.h>
GWEN_TREE2_FUNCTIONS(AQH_HTTP_REQUEST, AQH_HttpRequest);
@@ -34,13 +33,13 @@ 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);
static int _unescapeUrlEncoded(const char *src, unsigned int srclen, char *buffer, unsigned int maxsize);
@@ -79,6 +78,7 @@ void AQH_HttpRequest_free(AQH_HTTP_REQUEST *rq)
free(rq->moduleName);
free(rq->responseText);
GWEN_Msg_free(rq->responseMsg);
GWEN_StringList_free(rq->urlPathMembers);
GWEN_Url_free(rq->url);
GWEN_DB_Group_free(rq->dbPostBody);
@@ -133,6 +133,30 @@ void AQH_HttpRequest_SetUrl(AQH_HTTP_REQUEST *rq, GWEN_URL *url)
const char *AQH_HttpRequest_GetUrlPath(const AQH_HTTP_REQUEST *rq)
{
return rq?(rq->urlPath):NULL;
}
GWEN_STRINGLIST *AQH_HttpRequest_GetUrlPathMembers(const AQH_HTTP_REQUEST *rq)
{
return rq?(rq->urlPathMembers):NULL;
}
void AQH_HttpRequest_SetUrlPathMembers(AQH_HTTP_REQUEST *rq, GWEN_STRINGLIST *sl)
{
if (rq) {
GWEN_StringList_free(rq->urlPathMembers);
rq->urlPathMembers=sl;
}
}
GWEN_DB_NODE *AQH_HttpRequest_GetDbCommand(const AQH_HTTP_REQUEST *rq)
{
return rq?(rq->dbCommand):NULL;
@@ -154,6 +178,13 @@ GWEN_DB_NODE *AQH_HttpRequest_GetDbCookies(const AQH_HTTP_REQUEST *rq)
GWEN_DB_NODE *AQH_HttpRequest_GetDbPostBody(const AQH_HTTP_REQUEST *rq)
{
return rq?(rq->dbPostBody):NULL;
}
const char *AQH_HttpRequest_GetSessionId(const AQH_HTTP_REQUEST *rq)
{
return rq?(rq->sessionId):NULL;
@@ -296,6 +327,27 @@ void AQH_HttpRequest_SetResponseMsg(AQH_HTTP_REQUEST *rq, GWEN_MSG *msg)
void AQH_HttpRequest_SetupUrlPathMembers(AQH_HTTP_REQUEST *rq)
{
const char *path;
path=AQH_HttpRequest_GetUrlPath(rq);
if (path && *path) {
GWEN_STRINGLIST *sl;
if (*path=='/') /* skip leading slash */
path++;
sl=GWEN_StringList_fromString(path, "/", 0);
if (sl==NULL) {
DBG_ERROR(NULL, "Empty url path member list");
}
else
AQH_HttpRequest_SetUrlPathMembers(rq, sl);
}
}
int _inspectReceivedMessage(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
{
int rv;
@@ -384,7 +436,12 @@ int _inspectMsgCommand(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
return GWEN_ERROR_GENERIC;
}
_setModuleNameFromUrl(rq);
s=GWEN_Url_GetPath(rq->url);
if (!(s && *s)) {
DBG_ERROR(AQH_LOGDOMAIN, "Empty url in request, assuming \"/\"");
s="/";
}
rq->urlPath=s;
return 0;
}
@@ -399,8 +456,10 @@ void _inspectMsgHeader(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
const char *s;
s=GWEN_DB_GetCharValue(rq->dbCookies, AQH_HTTP_REQUEST_SESSIONCOOKIE, 0, NULL);
if (s && *s)
if (s && *s) {
DBG_INFO(AQH_LOGDOMAIN, "Sessionid: %s", s);
AQH_HttpRequest_SetSessionId(rq, s);
}
}
}
@@ -419,7 +478,7 @@ int _inspectMsgBody(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
const char *s;
/* check whether we need to parse POST body */
s=GWEN_DB_GetCharValue(rq->dbCommand, "content-type", 0, NULL);
s=GWEN_DB_GetCharValue(rq->dbHeader, "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);
@@ -519,34 +578,6 @@ void _setCookieValue(GWEN_DB_NODE *dbCookies, const char *nameStart, int nameLen
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) {
@@ -569,6 +600,7 @@ int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
int valueLen;
s++;
contentLength--;
while((contentLength>0) && (*s<33)) {
s++;
contentLength--;
@@ -584,16 +616,18 @@ int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
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);
if (_unescapeUrlEncoded(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))>=0 &&
_unescapeUrlEncoded(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))>=0)
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=='&'))
if ((contentLength>0) && (*s=='&')) {
contentLength--;
s++;
}
}
} /* while */
return 0;
@@ -601,5 +635,92 @@ int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
int _unescapeUrlEncoded(const char *src, unsigned int srclen, char *buffer, unsigned int maxsize)
{
unsigned int size;
size=0;
while (*src && srclen>0) {
unsigned char x;
x=(unsigned char)*src;
if (
(x>='A' && x<='Z') ||
(x>='a' && x<='z') ||
(x>='0' && x<='9') ||
x==' ' ||
x=='.' ||
x==',' ||
x=='.' ||
x=='*' ||
x=='?' ||
x=='+'
) {
if (size<(maxsize-1)) {
buffer[size++]=(x=='+')?' ':x;
}
else {
DBG_ERROR(GWEN_LOGDOMAIN, "Buffer too small");
return GWEN_ERROR_BUFFER_OVERFLOW;
}
}
else {
if (*src=='%') {
unsigned char d1, d2;
unsigned char c;
if (srclen<3) {
DBG_ERROR(GWEN_LOGDOMAIN, "Incomplete escape sequence (EOLN met)");
return GWEN_ERROR_BAD_DATA;
}
/* skip '%' */
src++;
if (!(*src) || !isxdigit((int)*src)) {
DBG_ERROR(GWEN_LOGDOMAIN, "Incomplete escape sequence (no digits)");
return GWEN_ERROR_BAD_DATA;
}
/* read first digit */
d1=(unsigned char)(toupper(*src));
/* get second digit */
src++;
if (!(*src) || !isxdigit((int)*src)) {
DBG_ERROR(GWEN_LOGDOMAIN, "Incomplete escape sequence (only 1 digit)");
return GWEN_ERROR_BAD_DATA;
}
d2=(unsigned char)(toupper(*src));
/* compute character */
d1-='0';
if (d1>9)
d1-=7;
c=(d1<<4)&0xf0;
d2-='0';
if (d2>9)
d2-=7;
c+=(d2&0xf);
/* store character */
if (size<(maxsize-1))
buffer[size++]=(char)c;
else {
DBG_ERROR(GWEN_LOGDOMAIN, "Buffer too small");
return GWEN_ERROR_BUFFER_OVERFLOW;
}
srclen-=2;
}
else {
DBG_ERROR(GWEN_LOGDOMAIN, "Found non-alphanum characters in escaped string (\"%s\")", src);
return GWEN_ERROR_BAD_DATA;
}
}
srclen--;
src++;
} /* while */
buffer[size]=0;
return 0;
}

View File

@@ -19,6 +19,8 @@
#include <gwenhywfar/url.h>
#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid"
typedef struct AQH_HTTP_REQUEST AQH_HTTP_REQUEST;
GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_HTTP_REQUEST, AQH_HttpRequest, AQHOME_API);
@@ -37,9 +39,15 @@ 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 const char *AQH_HttpRequest_GetUrlPath(const AQH_HTTP_REQUEST *rq);
AQHOME_API GWEN_STRINGLIST *AQH_HttpRequest_GetUrlPathMembers(const AQH_HTTP_REQUEST *rq);
AQHOME_API void AQH_HttpRequest_SetUrlPathMembers(AQH_HTTP_REQUEST *rq, GWEN_STRINGLIST *sl);
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 GWEN_DB_NODE *AQH_HttpRequest_GetDbPostBody(const AQH_HTTP_REQUEST *rq);
AQHOME_API const char *AQH_HttpRequest_GetSessionId(const AQH_HTTP_REQUEST *rq);
@@ -68,6 +76,7 @@ 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);
AQHOME_API void AQH_HttpRequest_SetupUrlPathMembers(AQH_HTTP_REQUEST *rq);
#endif

View File

@@ -25,8 +25,11 @@ struct AQH_HTTP_REQUEST {
const char *command; /* don't free */
const char *protocol; /* don't free */
const char *urlPath; /* don't free */
GWEN_URL *url;
GWEN_STRINGLIST *urlPathMembers;
GWEN_DB_NODE *dbCommand; /* don't free */
GWEN_DB_NODE *dbHeader; /* don't free */
GWEN_DB_NODE *dbCookies; /* don't free */

View File

@@ -28,16 +28,34 @@ static void GWENHYWFAR_CB _freeData(void *bp, void *p);
void AQH_HttpService_Extend(AQH_SERVICE *sv)
AQH_SERVICE *AQH_HttpService_new(const char *configFolder, const char *sourceFolder)
{
AQH_SERVICE *sv;
sv=AQH_Service_new();
AQH_HttpService_Extend(sv, configFolder, sourceFolder);
return sv;
}
void AQH_HttpService_Extend(AQH_SERVICE *sv, const char *configFolder, const char *sourceFolder)
{
AQH_HTTP_SERVICE *xsv;
GWEN_NEW_OBJECT(AQH_HTTP_SERVICE, xsv);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv, xsv, _freeData);
AQH_HttpService_SetConfigFolder(sv, configFolder);
AQH_HttpService_SetSourceFolder(sv, sourceFolder);
xsv->moduleMutex=GWEN_Mutex_new();
xsv->userMutex=GWEN_Mutex_new();
xsv->sessionMutex=GWEN_Mutex_new();
xsv->urlHandlerList=AQH_HttpUrlHandler_List_new(sv);
}
@@ -48,6 +66,8 @@ void _freeData(void *bp, void *p)
xsv=(AQH_HTTP_SERVICE*) p;
AQH_HttpUrlHandler_List_free(xsv->urlHandlerList);
GWEN_Mutex_free(xsv->moduleMutex);
GWEN_Mutex_free(xsv->userMutex);
GWEN_Mutex_free(xsv->sessionMutex);
@@ -61,25 +81,6 @@ 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) {

View File

@@ -14,6 +14,7 @@
#include "aqhome/service/service.h"
#include "aqhome/http/httprequest.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/msg.h>
#include <gwenhywfar/db.h>
@@ -23,10 +24,9 @@
typedef int (*AQH_HTTP_SERVICE_HANDLEREQUEST_FN)(AQH_SERVICE *sv, AQH_HTTP_REQUEST *request);
AQHOME_API void AQH_HttpService_Extend(AQH_SERVICE *sv);
AQHOME_API AQH_SERVICE *AQH_HttpService_new(const char *configFolder, const char *sourceFolder);
AQHOME_API void AQH_HttpService_Extend(AQH_SERVICE *sv, const char *configFolder, const char *sourceFolder);
AQHOME_API const char *AQH_HttpService_GetConfigFolder(const AQH_SERVICE *sv);
@@ -51,9 +51,6 @@ 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

@@ -83,7 +83,7 @@ int AQH_HttpService_LoadConfig(AQH_SERVICE *sv)
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);
rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf);
@@ -206,6 +206,42 @@ AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias)
int AQH_HttpService_WriteUser(const AQH_SERVICE *sv, const AQH_USER *user)
{
AQH_HTTP_SERVICE *xsv;
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 GWEN_ERROR_INVALID;
}
rv=GWEN_Mutex_Lock(xsv->userMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on user mutex");
return rv;
}
rv=AQH_HttpService_SaveUser(sv, user);
if (rv<0) {
GWEN_Mutex_Unlock(xsv->userMutex);
DBG_ERROR(AQH_LOGDOMAIN, "Error saving user \"%s\" (%d)", AQH_User_GetAlias(user), rv);
return rv;
}
rv=GWEN_Mutex_Unlock(xsv->userMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on user mutex");
return rv;
}
return 0;
}
AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid)
{
AQH_HTTP_SERVICE *xsv;
@@ -255,7 +291,7 @@ AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modNam
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);
rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf);
@@ -368,7 +404,7 @@ AQH_USER *AQH_HttpService_LoadUser(const AQH_SERVICE *sv, const char *userAlias)
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);
rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf);
@@ -488,7 +524,7 @@ AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sess
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);
rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf);
@@ -679,8 +715,8 @@ GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileName)
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);
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
GWEN_Buffer_AppendString(nameBuf, fileName);
return nameBuf;
}
}
@@ -958,7 +994,7 @@ int _writeDbFile(const char *fname, GWEN_DB_NODE *db)
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);
rv=GWEN_DB_WriteFile(db, GWEN_Buffer_GetStart(tmpFilenameBuf), GWEN_DB_FLAGS_DEFAULT);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error writing db file \"%s\": %d", GWEN_Buffer_GetStart(tmpFilenameBuf), rv);
GWEN_Buffer_free(tmpFilenameBuf);

View File

@@ -19,27 +19,34 @@
#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);
#define AQH_HTTP_SERVICE_ADMINUSER "admin"
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);
AQHOME_API int AQH_HttpService_LoadConfig(AQH_SERVICE *sv);
AQHOME_API int AQH_HttpService_SaveConfig(const AQH_SERVICE *sv);
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);
AQHOME_API AQH_MODULE *AQH_HttpService_GetModule(AQH_SERVICE *sv, const char *modName);
AQHOME_API AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias);
AQHOME_API int AQH_HttpService_WriteUser(const AQH_SERVICE *sv, const AQH_USER *user);
AQHOME_API AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid);
AQHOME_API AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modName);
AQHOME_API int AQH_HttpService_SaveModule(const AQH_SERVICE *sv, const AQH_MODULE *m);
AQHOME_API int AQH_HttpService_AddModule(AQH_SERVICE *sv, AQH_MODULE *m);
AQHOME_API AQH_USER *AQH_HttpService_LoadUser(const AQH_SERVICE *sv, const char *userAlias);
AQHOME_API int AQH_HttpService_SaveUser(const AQH_SERVICE *sv, const AQH_USER *user);
AQHOME_API int AQH_HttpService_AddUser(AQH_SERVICE *sv, AQH_USER *user);
AQHOME_API int AQH_HttpService_DelUser(AQH_SERVICE *sv, AQH_USER *user);
AQHOME_API AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sessionUid);
AQHOME_API int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session);
AQHOME_API int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session);
AQHOME_API int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session);
#endif

View File

@@ -14,6 +14,7 @@
#include "aqhome/http/httpservice_http.h"
#include "aqhome/http/httpservice_conf.h"
#include "aqhome/http/httpservice_p.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
@@ -37,7 +38,7 @@
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);
static AQH_HTTP_URLHANDLER *_findMatchingUrlHandler(AQH_SERVICE *sv, const char *path);
@@ -52,10 +53,12 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
AQH_HTTP_SERVICE *xsv;
AQH_HTTP_REQUEST *rq;
const char *s;
AQH_MODULE *m=NULL;
const char *cmd;
const char *protocol;
AQH_SESSION *session=NULL;
GWEN_MSG *msgResponse;
int rv;
AQH_HTTP_URLHANDLER *uh;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv==NULL) {
@@ -63,17 +66,16 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
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");
return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal server error", "HTTP/1.1", NULL);
}
cmd=AQH_HttpRequest_GetCommand(rq);
protocol=AQH_HttpRequest_GetProtocol(rq);
#if 0
s=AQH_HttpRequest_GetModuleName(rq);
if (s && *s) {
m=AQH_HttpService_GetModule(sv, s);
@@ -83,6 +85,7 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
else
AQH_HttpRequest_SetModule(rq, m);
}
#endif
s=AQH_HttpRequest_GetSessionId(rq);
if (s && *s) {
@@ -91,17 +94,25 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" not found", s);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Found session \"%s\"", s);
AQH_HttpRequest_SetSession(rq, session);
}
}
_setRequestPerms(sv, rq);
s=AQH_HttpRequest_GetUrlPath(rq);
DBG_INFO(AQH_LOGDOMAIN, "Received \"%s\" request for url \"%s\"", cmd?cmd:"<no cmd>", s);
uh=_findMatchingUrlHandler(sv, s);
if (uh==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "No matching handler found for url \"%s\"", s);
AQH_HttpRequest_free(rq);
return AQH_HttpService_CreateResponseMsg(sv, 404, "Not found", protocol?protocol:"http/1.1", NULL);
}
rv=xsv->handleRequestFn(sv, rq);
rv=AQH_HttpUrlHandler_HandleMessage(uh, 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");
return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal server error", protocol?protocol:"http/1.1", NULL);
}
msgResponse=AQH_HttpRequest_TakeResponseMsg(rq);
AQH_HttpRequest_free(rq);
@@ -110,6 +121,68 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
void AQH_HttpService_AddUrlHandler(AQH_SERVICE *sv, AQH_HTTP_URLHANDLER *urlHandler)
{
AQH_HTTP_SERVICE *xsv;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
}
else {
AQH_HttpUrlHandler_List_Add(urlHandler, xsv->urlHandlerList);
}
}
void AQH_HttpService_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) {
DBG_INFO(AQH_LOGDOMAIN, "Presetting module's guest perms");
AQH_HttpRequest_SetModulePerms(rq, AQH_Module_GetGuestPerms(m));
if (session) {
AQH_USER * user;
user=AQH_Session_GetUser(session);
if (user) {
AQH_MODULE_PERMS_LIST *permsList;
permsList=AQH_User_GetModulePermList(user);
if (permsList) {
AQH_MODULE_PERMS *userPerms;
userPerms=AQH_ModulePerms_List_GetByModuleId(AQH_User_GetModulePermList(user), AQH_Module_GetId(m));
if (userPerms) {
DBG_INFO(AQH_LOGDOMAIN, "Using user perms for module");
AQH_HttpRequest_SetModulePerms(rq, AQH_ModulePerms_GetPerms(userPerms));
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No module perms list in user, using module's guest perms");
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Session but no user, SNH!");
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No session, using module's guest perms");
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No module, no perms");
}
}
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:"");
@@ -163,14 +236,40 @@ void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeader, GWEN_BUF
GWEN_MSG *AQH_HttpService_CreateResponseMsg(AQH_SERVICE *sv, int code, const char *text, const char *protocol)
GWEN_MSG *AQH_HttpService_CreateResponseMsg(AQH_SERVICE *sv, int code, const char *text, const char *protocol, const char *page)
{
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);
_writeDefaultResponseHeaderToBuffer(sv, (page && *page)?strlen(page):0, buf);
if (page && *page)
GWEN_Buffer_AppendString(buf, page);
msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
GWEN_Buffer_free(buf);
return msg;
}
GWEN_MSG *AQH_HttpService_CreateRedirectingResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *newPage)
{
GWEN_BUFFER *buf;
GWEN_MSG *msg;
GWEN_DB_NODE *db;
buf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_HttpService_AddStatusLine(sv, 303, "OK, redirecting to content", protocol, buf);
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_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Location", newPage);
GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", 0);
AQH_HttpService_AddHeader(sv, db, buf);
GWEN_DB_Group_free(db);
msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
GWEN_Buffer_free(buf);
@@ -201,6 +300,39 @@ int AQH_HttpService_AddFile(AQH_SERVICE *sv, GWEN_UNUSED AQH_SESSION *session, c
void AQH_HttpService_SetupModuleAndPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq, const char *modulName)
{
AQH_SESSION *session;
AQH_MODULE *m;
AQH_HttpRequest_SetModuleName(rq, modulName);
m=AQH_HttpService_GetModule(sv, modulName);
if (m==NULL) {
DBG_ERROR(NULL, "Module \"%s\" not found", modulName);
AQH_HttpRequest_SetModulePerms(rq, 0);
}
else {
AQH_HttpRequest_SetModule(rq, m);
}
session=AQH_HttpRequest_GetSession(rq);
if (session) {
AQH_USER *u;
u=AQH_Session_GetUser(session);
if (u) {
if (strcasecmp(AQH_User_GetAlias(u), AQH_HTTP_SERVICE_ADMINUSER)==0) {
DBG_ERROR(NULL, "special admin user, full access granted");
AQH_HttpRequest_SetModulePerms(rq, 0xffffffff);
return;
}
}
}
AQH_HttpService_SetRequestPerms(sv, rq);
}
GWEN_BUFFER *_getPageFilePath(const AQH_SERVICE *sv, const char *langFolder, const char *fileName)
{
const char *sourceFolder;
@@ -220,7 +352,7 @@ GWEN_BUFFER *_getPageFilePath(const AQH_SERVICE *sv, const char *langFolder, con
GWEN_Buffer_AppendString(nameBuf, langFolder);
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
}
GWEN_Text_EscapeToBuffer(fileName, nameBuf);
GWEN_Text_EscapeToBufferTolerant(fileName, nameBuf);
return nameBuf;
}
}
@@ -245,37 +377,28 @@ void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWE
void _setRequestPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq)
AQH_HTTP_URLHANDLER *_findMatchingUrlHandler(AQH_SERVICE *sv, const char *path)
{
AQH_MODULE *m;
AQH_SESSION *session;
AQH_HTTP_SERVICE *xsv;
AQH_HTTP_URLHANDLER *uh;
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");
}
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
return NULL;
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No module");
uh=AQH_HttpUrlHandler_List_First(xsv->urlHandlerList);
while(uh) {
if (AQH_HttpUrlHandler_UrlMatches(uh, path)) {
DBG_INFO(AQH_LOGDOMAIN, "Found matching url handler for \"%s\"", path);
return uh;
}
uh=AQH_HttpUrlHandler_List_Next(uh);
}
DBG_INFO(AQH_LOGDOMAIN, "No matching url handler for \"%s\"", path);
return NULL;
}

View File

@@ -11,6 +11,8 @@
#include "aqhome/service/service.h"
#include "aqhome/http/httprequest.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
@@ -22,6 +24,23 @@
AQHOME_API GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *msgReceived);
AQHOME_API void AQH_HttpService_AddUrlHandler(AQH_SERVICE *sv, AQH_HTTP_URLHANDLER *urlHandler);
/**
* Set modulname in request, get and set matching module and determine the calling users permissions in regard
* to the selected module.
*/
AQHOME_API void AQH_HttpService_SetupModuleAndPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq, const char *modulName);
/**
* Set permissions for this request according to session, user and module.
*
* The members session and module must have been set before calling this function.
*/
AQHOME_API void AQH_HttpService_SetRequestPerms(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq);
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);
@@ -29,7 +48,12 @@ AQHOME_API void AQH_HttpService_AddHeader(AQH_SERVICE *sv, GWEN_DB_NODE *dbHeade
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 GWEN_MSG *AQH_HttpService_CreateResponseMsg(AQH_SERVICE *sv,
int code, const char *text, const char *protocol,
const char *page);
AQHOME_API GWEN_MSG *AQH_HttpService_CreateRedirectingResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *newPage);
AQHOME_API int AQH_HttpService_AddFile(AQH_SERVICE *sv, AQH_SESSION *session, const char *fname, GWEN_BUFFER *buf);

View File

@@ -20,7 +20,7 @@
GWEN_MSG *AQH_HttpService_HandleLoginUrl(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, const GWEN_URL *url);
AQHOME_API GWEN_MSG *AQH_HttpService_HandleLoginUrl(AQH_SERVICE *sv, const GWEN_MSG *msgReceived, const GWEN_URL *url);

View File

@@ -33,7 +33,7 @@ struct AQH_HTTP_SERVICE {
GWEN_MUTEX *userMutex;
GWEN_MUTEX *sessionMutex;
AQH_HTTP_SERVICE_HANDLEREQUEST_FN handleRequestFn;
AQH_HTTP_URLHANDLER_LIST *urlHandlerList;
};

239
aqhome/http/urlhandler.c Normal file
View File

@@ -0,0 +1,239 @@
/****************************************************************************
* 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 "./urlhandler_p.h"
#include "aqhome/http/httpservice_http.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/misc.h>
#include <gwenhywfar/text.h>
GWEN_INHERIT_FUNCTIONS(AQH_HTTP_URLHANDLER)
GWEN_LIST_FUNCTIONS(AQH_HTTP_URLHANDLER, AQH_HttpUrlHandler)
static int _addContentHeaders(AQH_HTTP_CONTENT *c, int m, GWEN_BUFFER *dbuf);
static int _addContentFooters(AQH_HTTP_CONTENT *c, int m, GWEN_BUFFER *dbuf);
AQH_HTTP_URLHANDLER *AQH_HttpUrlHandler_new(AQH_SERVICE *sv)
{
AQH_HTTP_URLHANDLER *uh;
GWEN_NEW_OBJECT(AQH_HTTP_URLHANDLER, uh);
GWEN_INHERIT_INIT(AQH_HTTP_URLHANDLER, uh);
GWEN_LIST_INIT(AQH_HTTP_URLHANDLER, uh);
uh->urlPatternList=GWEN_StringList_new();
uh->httpService=sv;
return uh;
}
void AQH_HttpUrlHandler_free(AQH_HTTP_URLHANDLER *uh)
{
if (uh) {
GWEN_LIST_FINI(AQH_HTTP_URLHANDLER, uh);
GWEN_INHERIT_FINI(AQH_HTTP_URLHANDLER, uh);
GWEN_StringList_free(uh->urlPatternList);
GWEN_FREE_OBJECT(uh);
}
}
AQH_SERVICE *AQH_HttpUrlHandler_GetHttpService(const AQH_HTTP_URLHANDLER *uh)
{
return uh?uh->httpService:NULL;
}
AQH_HTTP_CONTENT *AQH_HttpUrlHandler_GetContentProvider(const AQH_HTTP_URLHANDLER *uh)
{
return uh?uh->httpContentProvider:NULL;
}
void AQH_HttpUrlHandler_SetContentProvider(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_CONTENT *cp)
{
if (uh)
uh->httpContentProvider=cp;
}
void AQH_HttpUrlHandler_AddUrlPattern(AQH_HTTP_URLHANDLER *uh, const char *s)
{
if (uh && s && *s)
GWEN_StringList_AppendString(uh->urlPatternList, s, 0, 1);
}
int AQH_HttpUrlHandler_UrlMatches(const AQH_HTTP_URLHANDLER *uh, const char *s)
{
if (uh && s && *s) {
GWEN_STRINGLISTENTRY *se;
se=GWEN_StringList_FirstEntry(uh->urlPatternList);
while(se) {
const char *pattern;
pattern=GWEN_StringListEntry_Data(se);
if (GWEN_Text_ComparePattern(s, pattern, 0)!=-1)
return 1;
se=GWEN_StringListEntry_Next(se);
}
}
return 0;
}
void AQH_HttpUrlHandler_SetHandleFn(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_URLHANDLER_HANDLE_FN fn)
{
if (uh)
uh->handleFn=fn;
}
int AQH_HttpUrlHandler_HandleMessage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
if (uh && uh->handleFn)
return uh->handleFn(uh, rq);
return 0;
}
int AQH_HttpUrlHandler_AddContentHeaders(AQH_HTTP_URLHANDLER *uh, int m, GWEN_BUFFER *dbuf)
{
return _addContentHeaders(uh->httpContentProvider, m, dbuf);
}
int AQH_HttpUrlHandler_AddContentFooters(AQH_HTTP_URLHANDLER *uh, int m, GWEN_BUFFER *dbuf)
{
return _addContentFooters(uh->httpContentProvider, m, dbuf);
}
GWEN_MSG *AQH_HttpUrlHandler_CreatePageMessage(AQH_HTTP_URLHANDLER *uh,
AQH_HTTP_REQUEST *rq,
const char *statusTextColor, const char *statusMsg,
int withHeadersAndFooters,
AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb)
{
GWEN_BUFFER *pageBuf;
int rv;
GWEN_MSG *msgOut=NULL;
const char *protocol;
protocol=AQH_HttpRequest_GetProtocol(rq);
pageBuf=GWEN_Buffer_new(0, 256, 0, 1);
if (withHeadersAndFooters) {
rv=AQH_HttpUrlHandler_AddContentHeaders(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers");
GWEN_Buffer_free(pageBuf);
return AQH_HttpService_CreateResponseMsg(uh->httpService, 500, "Internal Error", protocol, NULL);
}
}
if (statusMsg)
GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"%s\">%s</font></p>", statusTextColor?statusTextColor:"black", statusMsg);
if (cb) {
rv=cb(uh, rq, pageBuf);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers");
GWEN_Buffer_free(pageBuf);
return AQH_HttpService_CreateResponseMsg(uh->httpService, 500, "Internal Error", protocol, NULL);
}
}
if (withHeadersAndFooters) {
rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers");
GWEN_Buffer_free(pageBuf);
return AQH_HttpService_CreateResponseMsg(uh->httpService, 500, "Internal Error", protocol, NULL);
}
}
msgOut=AQH_HttpService_CreateResponseMsg(uh->httpService, 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf));
GWEN_Buffer_free(pageBuf);
return msgOut;
}
int _addContentHeaders(AQH_HTTP_CONTENT *c, int m, GWEN_BUFFER *dbuf)
{
AQH_HTTP_CONTENT *parent;
int rv;
parent=AQH_HttpContent_Tree2_GetParent(c);
if (parent) {
rv=_addContentHeaders(parent, m, dbuf);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "Error adding content headers for \"%s\"", AQH_HttpContent_GetName(parent));
return rv;
}
}
rv=AQH_HttpContent_AddOpeningContent(c, m, dbuf);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "Error adding content headers for \"%s\"", AQH_HttpContent_GetName(c));
return rv;
}
return 0;
}
int _addContentFooters(AQH_HTTP_CONTENT *c, int m, GWEN_BUFFER *dbuf)
{
AQH_HTTP_CONTENT *parent;
int rv;
rv=AQH_HttpContent_AddClosingContent(c, m, dbuf);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "Error adding content footers for \"%s\"", AQH_HttpContent_GetName(c));
return rv;
}
parent=AQH_HttpContent_Tree2_GetParent(c);
if (parent) {
rv=_addContentFooters(parent, m, dbuf);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "Error adding content footers for \"%s\"", AQH_HttpContent_GetName(parent));
return rv;
}
}
return 0;
}

65
aqhome/http/urlhandler.h Normal file
View File

@@ -0,0 +1,65 @@
/****************************************************************************
* 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_URLHANDLER_H
#define AQH_HTTP_URLHANDLER_H
#include <aqhome/api.h>
#include <aqhome/http/httprequest.h>
#include <aqhome/http/content.h>
#include <aqhome/service/service.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/stringlist.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
typedef struct AQH_HTTP_URLHANDLER AQH_HTTP_URLHANDLER;
GWEN_INHERIT_FUNCTION_LIB_DEFS(AQH_HTTP_URLHANDLER, AQHOME_API)
GWEN_LIST_FUNCTION_LIB_DEFS(AQH_HTTP_URLHANDLER, AQH_HttpUrlHandler, AQHOME_API)
typedef int (*AQH_HTTP_URLHANDLER_HANDLE_FN)(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
AQHOME_API AQH_HTTP_URLHANDLER *AQH_HttpUrlHandler_new(AQH_SERVICE *sv);
AQHOME_API void AQH_HttpUrlHandler_free(AQH_HTTP_URLHANDLER *uh);
AQHOME_API AQH_SERVICE *AQH_HttpUrlHandler_GetHttpService(const AQH_HTTP_URLHANDLER *uh);
AQHOME_API void AQH_HttpUrlHandler_AddUrlPattern(AQH_HTTP_URLHANDLER *uh, const char *s);
AQHOME_API int AQH_HttpUrlHandler_UrlMatches(const AQH_HTTP_URLHANDLER *uh, const char *s);
AQHOME_API AQH_HTTP_CONTENT *AQH_HttpUrlHandler_GetContentProvider(const AQH_HTTP_URLHANDLER *uh);
AQHOME_API void AQH_HttpUrlHandler_SetContentProvider(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_CONTENT *cp);
AQHOME_API void AQH_HttpUrlHandler_SetHandleFn(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_URLHANDLER_HANDLE_FN fn);
AQHOME_API int AQH_HttpUrlHandler_AddContentHeaders(AQH_HTTP_URLHANDLER *uh, int m, GWEN_BUFFER *dbuf);
AQHOME_API int AQH_HttpUrlHandler_AddContentFooters(AQH_HTTP_URLHANDLER *uh, int m, GWEN_BUFFER *dbuf);
AQHOME_API int AQH_HttpUrlHandler_HandleMessage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
typedef int (*AQH_HTTP_URLHANDLER_WRITEPAGE_CB)(AQH_HTTP_URLHANDLER *uh,
AQH_HTTP_REQUEST *rq,
GWEN_BUFFER *pageBuf);
AQHOME_API GWEN_MSG *AQH_HttpUrlHandler_CreatePageMessage(AQH_HTTP_URLHANDLER *uh,
AQH_HTTP_REQUEST *rq,
const char *statusTextColor, const char *statusMsg,
int withHeadersAndFooters,
AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb);
#endif

View File

@@ -0,0 +1,32 @@
/****************************************************************************
* 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_URLHANDLER_P_H
#define AQH_HTTP_URLHANDLER_P_H
#include "aqhome/http/urlhandler.h"
typedef struct AQH_HTTP_URLHANDLER AQH_HTTP_URLHANDLER;
struct AQH_HTTP_URLHANDLER {
GWEN_INHERIT_ELEMENT(AQH_HTTP_URLHANDLER);
GWEN_LIST_ELEMENT(AQH_HTTP_URLHANDLER);
AQH_SERVICE *httpService;
GWEN_STRINGLIST *urlPatternList;
AQH_HTTP_CONTENT *httpContentProvider;
AQH_HTTP_URLHANDLER_HANDLE_FN handleFn;
};
#endif