/**************************************************************************** * 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 "./u_static_p.h" #include "./aqhomehttp.h" #include "aqhome/http/httpservice.h" #include "aqhome/http/httpservice_http.h" #include "aqhome/http/httpservice_conf.h" #include #include #include GWEN_INHERIT(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC); /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static void GWENHYWFAR_CB _freeData(void *bp, void *p); static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static void _readFileList(AQH_HTTP_URLHANDLER *uh); static GWEN_BUFFER *_readFileIntoBuffer(AQH_HTTP_URLHANDLER *uh, const char *requestedFilename); static const char *_getContentTypeFromFilename(const char *filename); static GWEN_MSG *_createFileResponseMsg(AQH_HTTP_URLHANDLER *uh, const char *protocol, const char *contentType, const uint8_t *ptr, uint32_t len); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ AQH_HTTP_URLHANDLER *AQH_StaticHttpUrlHandler_new(AQH_SERVICE *sv, const char *folder) { AQH_HTTP_URLHANDLER *uh; AQH_URLHANDLER_STATIC *xuh; uh=AQH_HttpUrlHandler_new(sv); GWEN_NEW_OBJECT(AQH_URLHANDLER_STATIC, xuh); GWEN_INHERIT_SETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh, xuh, _freeData); AQH_HttpUrlHandler_SetFolder(uh, folder); AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl); _readFileList(uh); return uh; } void _freeData(void *bp, void *p) { AQH_URLHANDLER_STATIC *xuh; xuh=(AQH_URLHANDLER_STATIC*)p; GWEN_StringList_free(xuh->fileList); GWEN_FREE_OBJECT(xuh); } int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { const char *protocol; const char *cmd; GWEN_MSG *msgOut; AQH_HttpService_SetupModuleAndPerms(AQH_HttpUrlHandler_GetHttpService(uh), rq, "aqhome"); AQH_HttpRequest_SetupUrlPathMembers(rq); protocol=AQH_HttpRequest_GetProtocol(rq); cmd=AQH_HttpRequest_GetCommand(rq); if (cmd && *cmd) { if (strcasecmp(cmd, "GET")==0) msgOut=_handleGet(uh, rq); else { msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL); } AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } else { DBG_ERROR(AQH_LOGDOMAIN, "No command in request"); return GWEN_ERROR_INVALID; } } GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { AQH_URLHANDLER_STATIC *xuh; AQH_SERVICE *sv; const char *protocol; const GWEN_STRINGLIST *sl; xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh); if (xuh==NULL) { DBG_ERROR(NULL, "Not a AQH_URLHANDLER_STATIC object"); return NULL; } sv=AQH_HttpUrlHandler_GetHttpService(uh); protocol=AQH_HttpRequest_GetProtocol(rq); /* handle middle part (header - middle - footer) */ sl=AQH_HttpRequest_GetUrlPathMembers(rq); if (sl) { const char *s; s=GWEN_StringList_StringAt(sl, 1); if (!(s && *s)) { DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s); return AQH_HttpService_CreateResponseMsg(sv, 404, "Not found", protocol, NULL); } else { GWEN_BUFFER *fileBuf; const char *contentType; GWEN_MSG *msgOut; contentType=_getContentTypeFromFilename(s); DBG_INFO(NULL, "Reading file \"%s\" (%s)", s, contentType); fileBuf=_readFileIntoBuffer(uh, s); if (fileBuf==NULL) { DBG_INFO(NULL, "here"); return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); } msgOut=_createFileResponseMsg(uh, protocol, contentType, (const uint8_t*) GWEN_Buffer_GetStart(fileBuf), GWEN_Buffer_GetUsedBytes(fileBuf)); GWEN_Buffer_free(fileBuf); return msgOut; } } else { DBG_ERROR(NULL, "No list of url members"); return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); } } void _readFileList(AQH_HTTP_URLHANDLER *uh) { AQH_URLHANDLER_STATIC *xuh; const char *folder; xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh); GWEN_StringList_free(xuh->fileList); xuh->fileList=NULL; folder=AQH_HttpUrlHandler_GetFolder(uh); if (folder && *folder) { GWEN_STRINGLIST *fileList; fileList=AQH_HttpService_GetFolderFileList(folder, NULL, 1); if (fileList) { GWEN_StringList_RemoveString(fileList, "."); GWEN_StringList_RemoveString(fileList, ".."); GWEN_StringList_free(xuh->fileList); xuh->fileList=fileList; } else { DBG_INFO(NULL, "No file list (empty folder?)"); } } } GWEN_BUFFER *_readFileIntoBuffer(AQH_HTTP_URLHANDLER *uh, const char *requestedFilename) { AQH_URLHANDLER_STATIC *xuh; const char *folder; xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh); folder=AQH_HttpUrlHandler_GetFolder(uh); if (!(xuh->fileList && GWEN_StringList_Count(xuh->fileList)>0)) { DBG_INFO(NULL, "Reading file list for folder \"%s\"", folder); _readFileList(uh); } if (!(xuh->fileList && GWEN_StringList_Count(xuh->fileList)>0)) { DBG_INFO(NULL, "No file list (empty or missing/bad folder? Permissions?"); return NULL; } if (GWEN_StringList_HasString(xuh->fileList, requestedFilename)) { GWEN_BUFFER *nameBuf; GWEN_BUFFER *fileBuf; int rv; fileBuf=GWEN_Buffer_new(0, 1024, 0, 1); nameBuf=GWEN_Buffer_new(0, 256, 0, 1); GWEN_Buffer_AppendString(nameBuf, folder); GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S); GWEN_Buffer_AppendString(nameBuf, requestedFilename); DBG_INFO(NULL, "Reading file \"%s\"", GWEN_Buffer_GetStart(nameBuf)); rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(nameBuf), fileBuf); if (rv<0) { DBG_ERROR(NULL, "Error reading file \"%s\" (%d)", GWEN_Buffer_GetStart(nameBuf), rv); GWEN_Buffer_free(nameBuf); GWEN_Buffer_free(fileBuf); return NULL; } GWEN_Buffer_free(nameBuf); return fileBuf; } else { DBG_INFO(NULL, "File \"%s\" not found in folder \"%s\"", requestedFilename, folder); return NULL; } } GWEN_MSG *_createFileResponseMsg(AQH_HTTP_URLHANDLER *uh, const char *protocol, const char *contentType, const uint8_t *ptr, uint32_t len) { GWEN_BUFFER *buf; GWEN_MSG *msg; GWEN_DB_NODE *db; buf=GWEN_Buffer_new(0, len+256, 0, 1); AQH_HttpService_AddStatusLine(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", 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", contentType); GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", len); AQH_HttpService_AddHeader(AQH_HttpUrlHandler_GetHttpService(uh), db, buf); GWEN_DB_Group_free(db); GWEN_Buffer_AppendBytes(buf, (const char *) ptr, len); msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf)); GWEN_Buffer_free(buf); return msg; } const char *_getContentTypeFromFilename(const char *filename) { if (filename) { const char *s; s=strrchr(filename, '.'); if (s) { if (strcasecmp(s, ".png")==0) return "image/png"; else if (strcasecmp(s, ".jpg")==0) return "image/jpg"; } } return "application/x-binary"; }