/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "aqhome/http/httpservice_p.h" #include #include #include #include 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:""); return GWEN_ERROR_BAD_DATA; } contentLength=GWEN_Msg_GetParsedPayloadSize(msgReceived); if (contentLength<1) { DBG_ERROR(NULL, "Empty message body"); return 0; } return contentLength; }