diff --git a/.gitignore b/.gitignore
index fb3350f..9201dfd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ aqhome.log
aqhomed.vg
aqhome-mqttlog.vg
aqhome-storage.vg
+aqhome-storage.pid
diff --git a/apps/aqhome-storage/0BUILD b/apps/aqhome-storage/0BUILD
index 8194cf6..4dc0482 100644
--- a/apps/aqhome-storage/0BUILD
+++ b/apps/aqhome-storage/0BUILD
@@ -45,6 +45,8 @@
cleanup.h
u_login.h
u_rooms.h
+ u_static.h
+ u_static_p.h
aqhomehttp.h
aqhomehttp_p.h
@@ -61,6 +63,7 @@
cleanup.c
u_login.c
u_rooms.c
+ u_static.c
main.c
aqhomehttp.c
diff --git a/apps/aqhome-storage/aqhomestorage_p.h b/apps/aqhome-storage/aqhomestorage_p.h
index 7eef60e..76a2f2f 100644
--- a/apps/aqhome-storage/aqhomestorage_p.h
+++ b/apps/aqhome-storage/aqhomestorage_p.h
@@ -32,6 +32,7 @@
#define AQHOME_STORAGE_SITEHEADER "site-header.html"
#define AQHOME_STORAGE_SITEFOOTER "site-footer.html"
+#define AQHOME_STORAGE_STATIC_RELFOLDER "static"
struct AQHOME_STORAGE {
diff --git a/apps/aqhome-storage/init_http.c b/apps/aqhome-storage/init_http.c
index 3b59ad6..5f38309 100644
--- a/apps/aqhome-storage/init_http.c
+++ b/apps/aqhome-storage/init_http.c
@@ -16,6 +16,7 @@
#include "./aqhomehttp.h"
#include "./u_login.h"
#include "./u_rooms.h"
+#include "./u_static.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/ipc/endpoint_ipc.h"
@@ -72,6 +73,7 @@ static GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk,
static int _createUrlHandler_login(AQHOME_STORAGE *aqh);
static int _createUrlHandler_rooms(AQHOME_STORAGE *aqh);
+static int _createUrlHandler_static(AQHOME_STORAGE *aqh);
@@ -116,6 +118,12 @@ int AqHomeStorage_SetupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
return rv;
}
+ rv=_createUrlHandler_static(aqh);
+ if (rv<0) {
+ DBG_INFO(NULL, "here (%d)", rv);
+ return rv;
+ }
+
}
return 0;
}
@@ -241,4 +249,24 @@ int _createUrlHandler_rooms(AQHOME_STORAGE *aqh)
+int _createUrlHandler_static(AQHOME_STORAGE *aqh)
+{
+ AQH_HTTP_URLHANDLER *uh;
+ GWEN_BUFFER *nameBuf;
+ const char *sourceFolder;
+
+ sourceFolder=AQH_HttpService_GetSourceFolder(aqh->httpService);
+ nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(nameBuf, sourceFolder);
+ GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_STATIC_RELFOLDER GWEN_DIR_SEPARATOR_S "pics");
+
+ uh=AQH_StaticHttpUrlHandler_new(aqh->httpService, GWEN_Buffer_GetStart(nameBuf));
+ GWEN_Buffer_free(nameBuf);
+ AQH_HttpUrlHandler_AddUrlPattern(uh, "/pics/*");
+ AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
+ return 0;
+}
+
+
+
diff --git a/apps/aqhome-storage/u_rooms.c b/apps/aqhome-storage/u_rooms.c
index b261e8a..3a1e0b9 100644
--- a/apps/aqhome-storage/u_rooms.c
+++ b/apps/aqhome-storage/u_rooms.c
@@ -42,9 +42,13 @@ static GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
static void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);
static void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);
+static void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf);
static GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
+static GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id);
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
+static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
static GWEN_MSG *_addRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
+static GWEN_MSG *_editRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id);
@@ -125,6 +129,8 @@ GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
_handleGetList(uh, rq, pageBuf);
else if (strcasecmp(s, "add")==0)
_handleGetAdd(uh, rq, pageBuf);
+ else if (strcasecmp(s, "edit")==0)
+ _handleGetEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0), pageBuf);
else {
DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
GWEN_Buffer_free(pageBuf);
@@ -170,6 +176,8 @@ GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
if (s && *s) {
if (strcasecmp(s, "add")==0)
return _handlePostAdd(uh, rq);
+ else if (strcasecmp(s, "edit")==0)
+ return _handlePostEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0));
else {
DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
@@ -207,7 +215,7 @@ void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *
"
%s
"
""
""
- " | Id | %s | %s |
"
+ " | Id | %s | %s | |
"
"",
I18N("Rooms"),
I18N("Name"),
@@ -226,10 +234,15 @@ void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *
id=(long unsigned int) AQH_Room_GetId(r);
name=AQH_Room_GetName(r);
descr=AQH_Room_GetDescription(r);
- GWEN_Buffer_AppendArgs(pageBuf, "| %lu | %s | %s |
", id, name?name:"", descr?descr:"");
-
- r=AQH_Room_List_Next(r);
- }
+ GWEN_Buffer_AppendArgs(pageBuf,
+ "| %lu | %s | %s | "
+ ""
+ " | "
+ "
",
+ id, name?name:"", descr?descr:"", id);
+
+ r=AQH_Room_List_Next(r);
+ }
}
GWEN_Buffer_AppendString(pageBuf, "");
GWEN_Buffer_AppendArgs(pageBuf, "
%s
", I18N("Add Room"));
@@ -256,7 +269,61 @@ void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *p
}
else {
DBG_INFO(NULL, "No permissions to add a room.");
- GWEN_Buffer_AppendString(pageBuf, "No permissions to add a room.
");
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("No permissions to add a room."));
+ }
+}
+
+
+
+void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf)
+{
+ uint32_t perms;
+
+ DBG_ERROR(NULL, "EDIT");
+ perms=AQH_HttpRequest_GetModulePerms(rq);
+ if (perms & AQHOME_HTTP_PERMS_EDIT_ROOM) {
+ AQH_SERVICE *sv;
+ AQH_STORAGE *sto;
+
+ sv=AQH_HttpUrlHandler_GetHttpService(uh);
+ sto=AqHomeHttpService_GetStorage(sv);
+ if (sto) {
+ if (id>0) {
+ const AQH_ROOM *r;
+
+ r=AQH_Storage_GetRoomById(sto, id);
+ if (r) {
+ GWEN_DB_NODE *db;
+ int rv;
+
+ db=GWEN_DB_Group_new("room");
+ rv=AQH_Room_toDb(r, db);
+ if (rv>=0)
+ _writeEditPage(uh, rq, NULL, pageBuf);
+ else {
+ DBG_ERROR(NULL, "Error writing room %d to db", id);
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("Internal error."));
+ }
+ GWEN_DB_Group_free(db);
+ }
+ else {
+ DBG_ERROR(NULL, "Room %d not found", id);
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("Room not found."));
+ }
+ }
+ else {
+ DBG_ERROR(NULL, "Missing room id");
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("Missing or invalid room id."));
+ }
+ }
+ else {
+ DBG_ERROR(NULL, "No storage");
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("Internal error."));
+ }
+ }
+ else {
+ DBG_INFO(NULL, "No permissions to edit a room.");
+ GWEN_Buffer_AppendArgs(pageBuf, "%s
", I18N("No permissions to edit a room."));
}
}
@@ -297,6 +364,44 @@ int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *d
+int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
+{
+ unsigned long int id;
+ const char *name=NULL;
+ const char *descr=NULL;
+
+ if (dbValues) {
+ id=GWEN_DB_GetIntValue(dbValues, "id", 0, 0);
+ name=GWEN_DB_GetCharValue(dbValues, "name", 0, NULL);
+ descr=GWEN_DB_GetCharValue(dbValues, "description", 0, NULL);
+ }
+ GWEN_Buffer_AppendArgs(pageBuf,
+ "%s
\n"
+ "",
+ I18N("Edit a Room"),
+ id,
+ I18N("Room Name"),
+ name?name:"",
+ I18N("Description"),
+ descr?descr:"",
+ I18N("Submit"));
+ return 0;
+}
+
+
+
GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
const char *protocol;
@@ -325,6 +430,34 @@ GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
+GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id)
+{
+ const char *protocol;
+ AQH_SERVICE *sv;
+ uint32_t perms;
+
+ protocol=AQH_HttpRequest_GetProtocol(rq);
+ sv=AQH_HttpUrlHandler_GetHttpService(uh);
+ perms=AQH_HttpRequest_GetModulePerms(rq);
+ if (perms & AQHOME_HTTP_PERMS_ADD_ROOM) {
+ AQH_STORAGE *sto;
+
+ sto=AqHomeHttpService_GetStorage(sv);
+ if (sto) {
+ return _editRoomCreateResponse(uh, rq, id);
+ }
+ else {
+ DBG_ERROR(NULL, "No storage");
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL);
+ }
+ }
+ else {
+ return AQH_HttpService_CreateResponseMsg(sv, 403, "Forbidden", protocol, NULL);
+ }
+}
+
+
+
GWEN_MSG *_addRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
AQH_SERVICE *sv;
@@ -382,4 +515,62 @@ GWEN_MSG *_addRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
+GWEN_MSG *_editRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id)
+{
+ AQH_SERVICE *sv;
+ const char *protocol;
+ GWEN_DB_NODE *db;
+ const char *roomName;
+ const char *roomDescr;
+ AQH_STORAGE *sto;
+
+ sv=AQH_HttpUrlHandler_GetHttpService(uh);
+ protocol=AQH_HttpRequest_GetProtocol(rq);
+ db=AQH_HttpRequest_GetDbPostBody(rq);
+
+ roomName=GWEN_DB_GetCharValue(db, "name", 0, NULL);
+ roomDescr=GWEN_DB_GetCharValue(db, "description", 0, NULL);
+
+ if (!(roomName && *roomName)) {
+ DBG_INFO(NULL, "Missing room name");
+ return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Missing room name"), 1, db, _writeAddPage);
+ }
+
+ sto=AqHomeHttpService_GetStorage(sv);
+ if (sto) {
+ AQH_ROOM *r;
+ int rv;
+
+ rv=AqHomeHttpService_LockStorage(sv);
+ if (rv<0) {
+ DBG_ERROR(NULL, "Error locking storage");
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL);
+ }
+
+ r=AQH_Storage_GetRoomById(sto, id);
+ if (r==NULL) {
+ AqHomeHttpService_UnlockStorage(sv);
+ DBG_ERROR(NULL, "Room %d not found", id);
+ return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Room not found"), 1, db, _writeEditPage);
+ }
+
+ /* edit room */
+ AQH_Room_SetName(r, roomName);
+ if (roomDescr && *roomDescr)
+ AQH_Room_SetDescription(r, roomDescr);
+ AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
+
+ rv=AqHomeHttpService_UnlockStorage(sv);
+ if (rv<0) {
+ DBG_ERROR(NULL, "Error unlocking storage");
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL);
+ }
+ return AQH_HttpService_CreateRedirectingResponseMsg(sv, protocol, "/rooms/list");
+ }
+ else
+ return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL);
+}
+
+
+
diff --git a/apps/aqhome-storage/u_static.c b/apps/aqhome-storage/u_static.c
new file mode 100644
index 0000000..d7af540
--- /dev/null
+++ b/apps/aqhome-storage/u_static.c
@@ -0,0 +1,291 @@
+/****************************************************************************
+ * 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";
+}
+
+
+
+
diff --git a/apps/aqhome-storage/u_static.h b/apps/aqhome-storage/u_static.h
new file mode 100644
index 0000000..c692387
--- /dev/null
+++ b/apps/aqhome-storage/u_static.h
@@ -0,0 +1,24 @@
+/****************************************************************************
+ * 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_STORAGE_U_STATIC_H
+#define AQHOME_STORAGE_U_STATIC_H
+
+
+#include "aqhome/http/urlhandler.h"
+
+#include
+
+
+AQH_HTTP_URLHANDLER *AQH_StaticHttpUrlHandler_new(AQH_SERVICE *sv, const char *folder);
+
+
+
+#endif
+
+
diff --git a/apps/aqhome-storage/u_static_p.h b/apps/aqhome-storage/u_static_p.h
new file mode 100644
index 0000000..1439dd1
--- /dev/null
+++ b/apps/aqhome-storage/u_static_p.h
@@ -0,0 +1,27 @@
+/****************************************************************************
+ * 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_STORAGE_U_STATIC_P_H
+#define AQHOME_STORAGE_U_STATIC_P_H
+
+
+#include "./u_static.h"
+
+#include
+
+
+typedef struct AQH_URLHANDLER_STATIC AQH_URLHANDLER_STATIC;
+struct AQH_URLHANDLER_STATIC {
+ GWEN_STRINGLIST *fileList;
+};
+
+
+
+#endif
+
+
diff --git a/aqhome/http/httpservice_conf.c b/aqhome/http/httpservice_conf.c
index 94b0fb9..15da098 100644
--- a/aqhome/http/httpservice_conf.c
+++ b/aqhome/http/httpservice_conf.c
@@ -63,7 +63,6 @@ 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);
-static GWEN_STRINGLIST *_getConfFileList(const char *folder, const char *mask);
static AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv);
static AQH_SESSION *_readSessionFromFile(const char *filename);
@@ -761,6 +760,31 @@ int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session)
+GWEN_STRINGLIST *AQH_HttpService_GetFolderFileList(const char *folder, const char *mask, int senseCase)
+{
+ GWEN_STRINGLIST *sl;
+ int rv;
+
+ sl=GWEN_StringList_new();
+ GWEN_StringList_SetSenseCase(sl, senseCase);
+
+ rv=GWEN_Directory_GetFileEntries(folder, sl, mask);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ GWEN_StringList_free(sl);
+ return NULL;
+ }
+ if (GWEN_StringList_Count(sl)<1) {
+ DBG_INFO(AQH_LOGDOMAIN, "Empty string list");
+ GWEN_StringList_free(sl);
+ return NULL;
+ }
+
+ return sl;
+}
+
+
+
AQH_MODULE *_ensureModule(AQH_SERVICE *sv, const char *modName)
{
AQH_MODULE *module;
@@ -1159,29 +1183,6 @@ GWEN_BUFFER *_getSessionFolder(const AQH_SERVICE *sv)
-GWEN_STRINGLIST *_getConfFileList(const char *folder, const char *mask)
-{
- GWEN_STRINGLIST *sl;
- int rv;
-
- sl=GWEN_StringList_new();
- rv=GWEN_Directory_GetFileEntries(folder, sl, mask);
- if (rv<0) {
- DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
- GWEN_StringList_free(sl);
- return NULL;
- }
- if (GWEN_StringList_Count(sl)<1) {
- DBG_INFO(AQH_LOGDOMAIN, "Empty string list");
- GWEN_StringList_free(sl);
- return NULL;
- }
-
- return sl;
-}
-
-
-
AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv)
{
GWEN_BUFFER *folderBuf;
@@ -1190,7 +1191,7 @@ AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv)
if (folderBuf) {
GWEN_STRINGLIST *fileList;
- fileList=_getConfFileList(GWEN_Buffer_GetStart(folderBuf), "*.conf");
+ fileList=AQH_HttpService_GetFolderFileList(GWEN_Buffer_GetStart(folderBuf), "*.conf", 1);
if (fileList) {
AQH_SESSION_LIST *sessionList;
uint32_t pos;
diff --git a/aqhome/http/httpservice_conf.h b/aqhome/http/httpservice_conf.h
index 3104eb0..5924dbf 100644
--- a/aqhome/http/httpservice_conf.h
+++ b/aqhome/http/httpservice_conf.h
@@ -52,6 +52,8 @@ AQHOME_API int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session)
AQHOME_API void AQH_HttpService_LoadAllSessions(AQH_SERVICE *sv);
+AQHOME_API GWEN_STRINGLIST *AQH_HttpService_GetFolderFileList(const char *folder, const char *mask, int senseCase);
+
#endif
diff --git a/aqhome/http/urlhandler.c b/aqhome/http/urlhandler.c
index 3484eaa..4e131e8 100644
--- a/aqhome/http/urlhandler.c
+++ b/aqhome/http/urlhandler.c
@@ -53,6 +53,8 @@ void AQH_HttpUrlHandler_free(AQH_HTTP_URLHANDLER *uh)
if (uh) {
GWEN_LIST_FINI(AQH_HTTP_URLHANDLER, uh);
GWEN_INHERIT_FINI(AQH_HTTP_URLHANDLER, uh);
+
+ free(uh->folder);
GWEN_StringList_free(uh->urlPatternList);
GWEN_FREE_OBJECT(uh);
}
@@ -90,6 +92,23 @@ void AQH_HttpUrlHandler_AddUrlPattern(AQH_HTTP_URLHANDLER *uh, const char *s)
+const char *AQH_HttpUrlHandler_GetFolder(const AQH_HTTP_URLHANDLER *uh)
+{
+ return uh?uh->folder:NULL;
+}
+
+
+
+void AQH_HttpUrlHandler_SetFolder(AQH_HTTP_URLHANDLER *uh, const char *s)
+{
+ if (uh) {
+ free(uh->folder);
+ uh->folder=s?strdup(s):NULL;
+ }
+}
+
+
+
int AQH_HttpUrlHandler_UrlMatches(const AQH_HTTP_URLHANDLER *uh, const char *s)
{
if (uh && s && *s) {
diff --git a/aqhome/http/urlhandler.h b/aqhome/http/urlhandler.h
index 1946555..80ededc 100644
--- a/aqhome/http/urlhandler.h
+++ b/aqhome/http/urlhandler.h
@@ -39,6 +39,9 @@ AQHOME_API int AQH_HttpUrlHandler_UrlMatches(const AQH_HTTP_URLHANDLER *uh, cons
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 const char *AQH_HttpUrlHandler_GetFolder(const AQH_HTTP_URLHANDLER *uh);
+AQHOME_API void AQH_HttpUrlHandler_SetFolder(AQH_HTTP_URLHANDLER *uh, const char *s);
+
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);
diff --git a/aqhome/http/urlhandler_p.h b/aqhome/http/urlhandler_p.h
index c2c0f07..edc3390 100644
--- a/aqhome/http/urlhandler_p.h
+++ b/aqhome/http/urlhandler_p.h
@@ -21,6 +21,8 @@ struct AQH_HTTP_URLHANDLER {
AQH_SERVICE *httpService;
GWEN_STRINGLIST *urlPatternList;
AQH_HTTP_CONTENT *httpContentProvider;
+ char *folder;
+
AQH_HTTP_URLHANDLER_HANDLE_FN handleFn;
};