Files
aqhomecontrol/aqhome/http/httpservice.c

329 lines
7.5 KiB
C

/****************************************************************************
* 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_p.h"
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
GWEN_INHERIT(AQH_SERVICE, AQH_HTTP_SERVICE)
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static int _checkHeaderGetBodySize(const GWEN_MSG *msgReceived);
void AQH_HttpService_Extend(AQH_SERVICE *sv)
{
AQH_HTTP_SERVICE *xsv;
GWEN_NEW_OBJECT(AQH_HTTP_SERVICE, xsv);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv, xsv, _freeData);
}
void _freeData(void *bp, void *p)
{
AQH_HTTP_SERVICE *xsv;
xsv=(AQH_HTTP_SERVICE*) p;
free(xsv->sourceFolder);
free(xsv->siteHeader);
free(xsv->siteFooter);
GWEN_FREE_OBJECT(xsv);
}
const char *AQH_HttpService_GetSourceFolder(const AQH_SERVICE *sv)
{
if (sv) {
AQH_HTTP_SERVICE *xsv;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv)
return xsv->sourceFolder;
}
return NULL;
}
void AQH_HttpService_SetSourceFolder(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->sourceFolder);
xsv->sourceFolder=s?strdup(s):NULL;
}
}
}
const char *AQH_HttpService_GetSiteHeader(const AQH_SERVICE *sv)
{
if (sv) {
AQH_HTTP_SERVICE *xsv;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv)
return xsv->siteHeader;
}
return NULL;
}
void AQH_HttpService_SetSiteHeader(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->siteHeader);
xsv->siteHeader=s?strdup(s):NULL;
}
}
}
const char *AQH_HttpService_GetSiteFooter(const AQH_SERVICE *sv)
{
if (sv) {
AQH_HTTP_SERVICE *xsv;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv)
return xsv->siteFooter;
}
return NULL;
}
void AQH_HttpService_SetSiteFooter(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->siteFooter);
xsv->siteFooter=s?strdup(s):NULL;
}
}
}
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)
{
int contentLength;
const char *s;
contentLength=_checkHeaderGetBodySize(msgReceived);
if (contentLength<1) {
DBG_ERROR(NULL, "Empty message body");
return 0;
}
s=(const char*)(GWEN_Msg_GetConstBuffer(msgReceived)+GWEN_Msg_GetParsedPayloadOffset(msgReceived));
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;
}
int _checkHeaderGetBodySize(const GWEN_MSG *msgReceived)
{
GWEN_DB_NODE *dbParsedInfo;
GWEN_DB_NODE *dbHeader;
const char *contentType;
int contentLength;
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;
}
return contentLength;
}