From aafecfa7046d40bed290c79208daf54761f4859d Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Tue, 8 Aug 2023 23:49:28 +0200 Subject: [PATCH] aqhome: more work on http server. --- apps/aqhome-storage/0BUILD | 14 +- apps/aqhome-storage/aqhomehttp.c | 233 ++----------- apps/aqhome-storage/aqhomehttp.h | 40 ++- apps/aqhome-storage/aqhomehttp_p.h | 12 +- apps/aqhome-storage/aqhomestorage.h | 1 + apps/aqhome-storage/aqhomestorage_p.h | 6 + apps/aqhome-storage/fini.c | 84 +++++ apps/aqhome-storage/fini.h | 23 ++ apps/aqhome-storage/html/site_footer.html | 16 - apps/aqhome-storage/http.h | 30 -- apps/aqhome-storage/init.c | 154 ++++++--- apps/aqhome-storage/init_http.c | 244 ++++++++++++++ apps/aqhome-storage/init_http.h | 23 ++ apps/aqhome-storage/{http.c => loop.c} | 46 +-- apps/aqhome-storage/loop.h | 24 ++ apps/aqhome-storage/loop_http.c | 117 +++++++ apps/aqhome-storage/loop_http.h | 21 ++ apps/aqhome-storage/main.c | 179 +++++++++- apps/aqhome-storage/u_login.c | 388 ++++++++++++++++++++++ apps/aqhome-storage/u_login.h | 24 ++ apps/aqhome-storage/u_rooms.c | 344 +++++++++++++++++++ apps/aqhome-storage/u_rooms.h | 24 ++ aqhome-storage.sh | 15 + aqhome/data/room.t2d | 2 +- aqhome/data/storage.c | 34 ++ aqhome/data/storage.h | 5 + aqhome/http/0BUILD | 9 + aqhome/http/content.c | 112 +++++++ aqhome/http/content.h | 63 ++++ aqhome/http/content_files.c | 140 ++++++++ aqhome/http/content_files.h | 34 ++ aqhome/http/content_files_p.h | 29 ++ aqhome/http/content_p.h | 29 ++ aqhome/http/endpoint_http.c | 19 +- aqhome/http/httprequest.c | 197 ++++++++--- aqhome/http/httprequest.h | 9 + aqhome/http/httprequest_p.h | 3 + aqhome/http/httpservice.c | 41 +-- aqhome/http/httpservice.h | 9 +- aqhome/http/httpservice_conf.c | 50 ++- aqhome/http/httpservice_conf.h | 39 ++- aqhome/http/httpservice_http.c | 205 +++++++++--- aqhome/http/httpservice_http.h | 26 +- aqhome/http/httpservice_login.h | 2 +- aqhome/http/httpservice_p.h | 2 +- aqhome/http/urlhandler.c | 239 +++++++++++++ aqhome/http/urlhandler.h | 65 ++++ aqhome/http/urlhandler_p.h | 32 ++ aqhome/service/module.t2d | 2 +- aqhome/service/user.t2d | 26 ++ 50 files changed, 2988 insertions(+), 497 deletions(-) create mode 100644 apps/aqhome-storage/fini.c create mode 100644 apps/aqhome-storage/fini.h delete mode 100644 apps/aqhome-storage/http.h create mode 100644 apps/aqhome-storage/init_http.c create mode 100644 apps/aqhome-storage/init_http.h rename apps/aqhome-storage/{http.c => loop.c} (65%) create mode 100644 apps/aqhome-storage/loop.h create mode 100644 apps/aqhome-storage/loop_http.c create mode 100644 apps/aqhome-storage/loop_http.h create mode 100644 apps/aqhome-storage/u_login.c create mode 100644 apps/aqhome-storage/u_login.h create mode 100644 apps/aqhome-storage/u_rooms.c create mode 100644 apps/aqhome-storage/u_rooms.h create mode 100755 aqhome-storage.sh create mode 100644 aqhome/http/content.c create mode 100644 aqhome/http/content.h create mode 100644 aqhome/http/content_files.c create mode 100644 aqhome/http/content_files.h create mode 100644 aqhome/http/content_files_p.h create mode 100644 aqhome/http/content_p.h create mode 100644 aqhome/http/urlhandler.c create mode 100644 aqhome/http/urlhandler.h create mode 100644 aqhome/http/urlhandler_p.h diff --git a/apps/aqhome-storage/0BUILD b/apps/aqhome-storage/0BUILD index dd8fe00..d7f0f5a 100644 --- a/apps/aqhome-storage/0BUILD +++ b/apps/aqhome-storage/0BUILD @@ -38,7 +38,12 @@ aqhomestorage_p.h aqhomestorage.h init.h - http.h + init_http.h + fini.h + loop.h + loop_http.h + u_login.h + u_rooms.h aqhomehttp.h aqhomehttp_p.h @@ -48,7 +53,12 @@ aqhomestorage.c init.c - http.c + init_http.c + fini.c + loop.c + loop_http.c + u_login.c + u_rooms.c main.c aqhomehttp.c diff --git a/apps/aqhome-storage/aqhomehttp.c b/apps/aqhome-storage/aqhomehttp.c index 3253b77..7812a33 100644 --- a/apps/aqhome-storage/aqhomehttp.c +++ b/apps/aqhome-storage/aqhomehttp.c @@ -32,10 +32,6 @@ GWEN_INHERIT(AQH_SERVICE, AQHOME_HTTP) */ static void GWENHYWFAR_CB _freeData(void *bp, void *p); -static int _handleUrl(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq); -static int _handleUrl_login(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq); -static GWEN_MSG *_createResponseMsg(AQH_SERVICE *sv, int code, const char *text, const char *protocol, const char *page); -static void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWEN_BUFFER *buf); @@ -51,7 +47,8 @@ void AqHomeHttpService_Extend(AQH_SERVICE *sv) GWEN_NEW_OBJECT(AQHOME_HTTP, xsv); GWEN_INHERIT_SETDATA(AQH_SERVICE, AQHOME_HTTP, sv, xsv, _freeData); - AQH_HttpService_SetHandleRequestFn(sv, _handleUrl); + xsv->contentTree=AQH_HttpContent_new("root"); + } @@ -62,122 +59,15 @@ void _freeData(void *bp, void *p) xsv=(AQHOME_HTTP*) p; - AQH_Storage_free(xsv->storage); xsv->storage=NULL; - GWEN_MsgEndpoint_free(xsv->rootEndpoint); - xsv->rootEndpoint=NULL; - xsv->ipcdEndpoint=NULL; - xsv->mqttEndpoint=NULL; - xsv->httpdEndpoint=NULL; - GWEN_DB_Group_free(xsv->dbArgs); - xsv->dbArgs=NULL; - free(xsv->pidFile); + AQH_HttpContent_free(xsv->contentTree); + xsv->contentTree=NULL; GWEN_FREE_OBJECT(xsv); } -GWEN_DB_NODE *AqHomeHttpService_GetDbArgs(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->dbArgs; - } - return NULL; -} - - - -const char *AqHomeHttpService_GetPidFile(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->pidFile; - } - return NULL; -} - - - -void AqHomeHttpService_SetPidFile(AQH_SERVICE *sv, const char *s) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) { - free(xsv->pidFile); - xsv->pidFile=s?strdup(s):NULL; - } - } -} - - - - -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetRootEndpoint(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->rootEndpoint; - } - return NULL; -} - - - -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetIpcdEndpoint(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->ipcdEndpoint; - } - return NULL; -} - - - -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetMqttEndpoint(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->mqttEndpoint; - } - return NULL; -} - - - -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetHttpdEndpoint(const AQH_SERVICE *sv) -{ - if (sv) { - AQHOME_HTTP *xsv; - - xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); - if (xsv) - return xsv->httpdEndpoint; - } - return NULL; -} - - - AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv) { if (sv) { @@ -192,72 +82,43 @@ AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv) - - - - - -int _handleUrl(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq) +void AqHomeHttpService_SetStorage(AQH_SERVICE *sv, AQH_STORAGE *sto) { + if (sv) { + AQHOME_HTTP *xsv; - const GWEN_URL *url; - const char *sPath; - - url=AQH_HttpRequest_GetUrl(rq); - - sPath=GWEN_Url_GetPath(url); - if (strcasecmp(sPath, "/login")==0) - return _handleUrl_login(sv, rq); - else { - DBG_ERROR(NULL, "Invalid URL [%s]", sPath); - return GWEN_ERROR_INVALID; + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) + xsv->storage=sto; } } -int _handleUrl_login(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq) +AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv) { - const char *protocol; - const char *cmd; - AQH_SESSION *session; - GWEN_MSG *msgOut=NULL; + if (sv) { + AQHOME_HTTP *xsv; - protocol=AQH_HttpRequest_GetProtocol(rq); - session=AQH_HttpRequest_GetSession(rq); - cmd=AQH_HttpRequest_GetCommand(rq); - if (cmd && strcasecmp(cmd, "GET")==0) { - GWEN_BUFFER *pageBuf; - const char *s; - int rv; + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) + return xsv->contentTree; + } + return NULL; +} - pageBuf=GWEN_Buffer_new(0, 256, 0, 1); - s=AQH_HttpService_GetSiteHeader(sv); - if (s) - GWEN_Buffer_AppendString(pageBuf, s); - rv=AQH_HttpService_AddFile(sv, session, "login.html", pageBuf); - if (rv<0) { - DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"login.html\""); - GWEN_Buffer_free(pageBuf); - msgOut=_createResponseMsg(sv, 500, "Internal Error", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + +void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c) +{ + if (sv) { + AQHOME_HTTP *xsv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) { + AQH_HttpContent_free(xsv->contentTree); + xsv->contentTree=c; } - - s=AQH_HttpService_GetSiteFooter(sv); - if (s) - GWEN_Buffer_AppendString(pageBuf, s); - - msgOut=_createResponseMsg(sv, 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf)); - GWEN_Buffer_free(pageBuf); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; - } - else { - msgOut=_createResponseMsg(sv, 501, "Not (yet) implemented", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; } } @@ -266,41 +127,5 @@ int _handleUrl_login(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq) -GWEN_MSG *_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, (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; -} - - - -void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWEN_BUFFER *buf) -{ - GWEN_DB_NODE *db; - - 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_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", contentLength); - - AQH_HttpService_AddHeader(sv, db, buf); - GWEN_DB_Group_free(db); -} - - - - - - diff --git a/apps/aqhome-storage/aqhomehttp.h b/apps/aqhome-storage/aqhomehttp.h index 726e020..f56d78f 100644 --- a/apps/aqhome-storage/aqhomehttp.h +++ b/apps/aqhome-storage/aqhomehttp.h @@ -12,26 +12,42 @@ #include "aqhome/service/service.h" #include "aqhome/data/storage.h" +#include "aqhome/http/content.h" #include +#define AQHOME_HTTP_PERMS_LIST_ROOMS 0x00000001 +#define AQHOME_HTTP_PERMS_LIST_DEVICES 0x00000002 +#define AQHOME_HTTP_PERMS_LIST_VALUES 0x00000004 +#define AQHOME_HTTP_PERMS_LIST_TOPICS 0x00000008 + +#define AQHOME_HTTP_PERMS_ADD_ROOM 0x00000100 +#define AQHOME_HTTP_PERMS_ADD_DEVICE 0x00000200 +#define AQHOME_HTTP_PERMS_ADD_VALUE 0x00000400 +#define AQHOME_HTTP_PERMS_ADD_TOPIC 0x00000800 + +#define AQHOME_HTTP_PERMS_DEL_ROOM 0x00010000 +#define AQHOME_HTTP_PERMS_DEL_DEVICE 0x00020000 +#define AQHOME_HTTP_PERMS_DEL_VALUE 0x00040000 +#define AQHOME_HTTP_PERMS_DEL_TOPIC 0x00080000 + +#define AQHOME_HTTP_PERMS_EDIT_ROOM 0x01000000 +#define AQHOME_HTTP_PERMS_EDIT_DEVICE 0x02000000 +#define AQHOME_HTTP_PERMS_EDIT_VALUE 0x04000000 +#define AQHOME_HTTP_PERMS_EDIT_TOPIC 0x08000000 + + + + + void AqHomeHttpService_Extend(AQH_SERVICE *sv); -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetRootEndpoint(const AQH_SERVICE *sv); -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetIpcdEndpoint(const AQH_SERVICE *sv); -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetMqttEndpoint(const AQH_SERVICE *sv); -GWEN_MSG_ENDPOINT *AqHomeHttpService_GetHttpdEndpoint(const AQH_SERVICE *sv); - AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv); +void AqHomeHttpService_SetStorage(AQH_SERVICE *sv, AQH_STORAGE *sto); -GWEN_DB_NODE *AqHomeHttpService_GetDbArgs(const AQH_SERVICE *sv); -const char *AqHomeHttpService_GetPidFile(const AQH_SERVICE *sv); -void AqHomeHttpService_SetPidFile(AQH_SERVICE *sv, const char *s); - - - - +AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv); +void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c); diff --git a/apps/aqhome-storage/aqhomehttp_p.h b/apps/aqhome-storage/aqhomehttp_p.h index e046244..96359c2 100644 --- a/apps/aqhome-storage/aqhomehttp_p.h +++ b/apps/aqhome-storage/aqhomehttp_p.h @@ -15,17 +15,9 @@ typedef struct AQHOME_HTTP AQHOME_HTTP; struct AQHOME_HTTP { - GWEN_MSG_ENDPOINT *rootEndpoint; + AQH_STORAGE *storage; /* do not release */ - GWEN_MSG_ENDPOINT *ipcdEndpoint; - GWEN_MSG_ENDPOINT *mqttEndpoint; - GWEN_MSG_ENDPOINT *httpdEndpoint; - - AQH_STORAGE *storage; - - GWEN_DB_NODE *dbArgs; - - char *pidFile; + AQH_HTTP_CONTENT *contentTree; }; diff --git a/apps/aqhome-storage/aqhomestorage.h b/apps/aqhome-storage/aqhomestorage.h index a9ba6ba..a68ed5c 100644 --- a/apps/aqhome-storage/aqhomestorage.h +++ b/apps/aqhome-storage/aqhomestorage.h @@ -12,6 +12,7 @@ #include "aqhome/data/storage.h" #include "aqhome/service/session.h" +#include "aqhome/http/httpservice.h" #include diff --git a/apps/aqhome-storage/aqhomestorage_p.h b/apps/aqhome-storage/aqhomestorage_p.h index 737525f..16801fa 100644 --- a/apps/aqhome-storage/aqhomestorage_p.h +++ b/apps/aqhome-storage/aqhomestorage_p.h @@ -22,6 +22,11 @@ #define AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE 600 #define AQHOME_STORAGE_DEFAULT_MQTT_PORT 1883 +#define AQHOME_STORAGE_DEFAULT_CONFIGDIR "/var/lib/aqhomestorage/config" +#define AQHOME_STORAGE_DEFAULT_HTTP_SOURCEDIR "/var/lib/aqhomestorage/html" + +#define AQHOME_STORAGE_DEFAULT_STATEFILE "/var/lib/aqhomestorage/config/statefile" + #define AQHOME_STORAGE_SITEHEADER "site-header.html" #define AQHOME_STORAGE_SITEFOOTER "site-footer.html" @@ -36,6 +41,7 @@ struct AQHOME_STORAGE { GWEN_DB_NODE *dbArgs; + AQH_SERVICE *httpService; AQH_STORAGE *storage; char *pidFile; diff --git a/apps/aqhome-storage/fini.c b/apps/aqhome-storage/fini.c new file mode 100644 index 0000000..1744b21 --- /dev/null +++ b/apps/aqhome-storage/fini.c @@ -0,0 +1,84 @@ +/**************************************************************************** + * 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 "./fini.h" +#include "./aqhomestorage_p.h" + +#include +#include +#include +#include + + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _disconnectTree(GWEN_MSG_ENDPOINT *ep); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeStorage_Fini(AQHOME_STORAGE *aqh) +{ + if (aqh) { + if (aqh->rootEndpoint) { + _disconnectTree(aqh->rootEndpoint); + GWEN_MsgEndpoint_Disconnect(aqh->rootEndpoint); + } + GWEN_MsgEndpoint_free(aqh->rootEndpoint); + aqh->rootEndpoint=NULL; + aqh->ipcdEndpoint=NULL; + aqh->httpdEndpoint=NULL; + aqh->mqttEndpoint=NULL; + + if (aqh->pidFile) + remove(aqh->pidFile); + } +} + + + +void _disconnectTree(GWEN_MSG_ENDPOINT *ep) +{ + GWEN_MSG_ENDPOINT *epChild; + + epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep); + while(epChild) { + _disconnectTree(epChild); + epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild); + } /* while */ + + GWEN_MsgEndpoint_Disconnect(ep); +} + + + + diff --git a/apps/aqhome-storage/fini.h b/apps/aqhome-storage/fini.h new file mode 100644 index 0000000..0fe7762 --- /dev/null +++ b/apps/aqhome-storage/fini.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * 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_FINI_H +#define AQHOME_STORAGE_FINI_H + + +#include "./aqhomestorage.h" + + + +void AqHomeStorage_Fini(AQHOME_STORAGE *aqh); + + + +#endif + + diff --git a/apps/aqhome-storage/html/site_footer.html b/apps/aqhome-storage/html/site_footer.html index 635e1f2..b47732e 100644 --- a/apps/aqhome-storage/html/site_footer.html +++ b/apps/aqhome-storage/html/site_footer.html @@ -1,19 +1,3 @@ - - - - - - - - - - - - -AqHome Storage Service - - - diff --git a/apps/aqhome-storage/http.h b/apps/aqhome-storage/http.h deleted file mode 100644 index 757ff24..0000000 --- a/apps/aqhome-storage/http.h +++ /dev/null @@ -1,30 +0,0 @@ -/**************************************************************************** - * 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_HTTP_H -#define AQHOME_STORAGE_HTTP_H - - -#include "./aqhomestorage.h" - - - -int AqHomeStorage_AddFile(AQHOME_STORAGE *aqh, AQH_SESSION *session, const char *fname, GWEN_BUFFER *buf); -int AqHomeStorage_AddSiteHeader(AQHOME_STORAGE *aqh, AQH_SESSION *session, GWEN_BUFFER *buf); -int AqHomeStorage_AddSiteFooter(AQHOME_STORAGE *aqh, AQH_SESSION *session, GWEN_BUFFER *buf); - -void AqHomeStorage_AddStatusLine(AQHOME_STORAGE *aqh, int code, const char *msg, const char *proto, GWEN_BUFFER *buf); -void AqHomeStorage_AddHeader(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbHeader, GWEN_BUFFER *buf); - -int AqHomeStorage_ParsePostBody(AQHOME_STORAGE *aqh, const GWEN_MSG *msgReceived, GWEN_DB_NODE *dbBody); - - - -#endif - - diff --git a/apps/aqhome-storage/init.c b/apps/aqhome-storage/init.c index 4aef7fd..fcd6fa0 100644 --- a/apps/aqhome-storage/init.c +++ b/apps/aqhome-storage/init.c @@ -12,18 +12,24 @@ #include "./init.h" +#include "./init_http.h" #include "./aqhomestorage_p.h" +#include "./aqhomehttp.h" #include "aqhome/msg/endpoint_tty.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/mqtt/endpoint_mqttc.h" #include "aqhome/http/endpoint_http.h" +#include "aqhome/http/httpservice_conf.h" +#include "aqhome/http/httpservice_http.h" +#include "aqhome/http/httpservice.h" #include #include #include #include #include +#include #ifdef HAVE_SYS_TYPES_H # include @@ -57,14 +63,14 @@ * ------------------------------------------------------------------------------------------------ */ +static int _setupFolders(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); + static int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static void _setupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); -static void _setupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); -static GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); static int _createPidFile(const char *pidFilename); @@ -100,27 +106,103 @@ int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv) } } -// _setupStorage(aqh, dbArgs); + rv=_setupFolders(aqh, dbArgs); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=_setupStorage(aqh, dbArgs); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } _setupIpc(aqh, dbArgs); _setupMqtt(aqh, dbArgs); - _setupHttp(aqh, dbArgs); + rv=AqHomeStorage_SetupHttp(aqh, dbArgs); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } return 0; } +int _setupFolders(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *s; + GWEN_BUFFER *nameBuf; + int pos; + int rv; + + s=GWEN_DB_GetCharValue(dbArgs, "cfgdir", 0, AQHOME_STORAGE_DEFAULT_CONFIGDIR); + if (!(s && *s)) { + DBG_ERROR(NULL, "Missing configuration folder"); + return GWEN_ERROR_GENERIC; + } + + nameBuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(nameBuf, s); + pos=GWEN_Buffer_GetPos(nameBuf); + rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT); + if (rv<0) { + DBG_ERROR(NULL, "Error accessing configuration folder \"%s\"", GWEN_Buffer_GetStart(nameBuf)); + GWEN_Buffer_free(nameBuf); + return GWEN_ERROR_GENERIC; + } + GWEN_Buffer_Crop(nameBuf, 0, pos); + + GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S "modules"); + rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT); + if (rv<0) { + DBG_ERROR(NULL, "Error accessing modules folder \"%s\"", GWEN_Buffer_GetStart(nameBuf)); + GWEN_Buffer_free(nameBuf); + return GWEN_ERROR_GENERIC; + } + GWEN_Buffer_Crop(nameBuf, 0, pos); + + GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S "users"); + rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT); + if (rv<0) { + DBG_ERROR(NULL, "Error accessing configuration folder \"%s\"", GWEN_Buffer_GetStart(nameBuf)); + GWEN_Buffer_free(nameBuf); + return GWEN_ERROR_GENERIC; + } + + GWEN_Buffer_free(nameBuf); + return 0; +} + + + + int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) { - AQH_STORAGE *sto; const char *stateFile; stateFile=GWEN_DB_GetCharValue(dbArgs, "stateFile", 0, NULL); + if (stateFile && *stateFile) { + AQH_STORAGE *sto; + int rv; - sto=AQH_Storage_new(); + sto=AQH_Storage_new(); + AQH_Storage_SetStateFile(sto, stateFile); - aqh->storage=sto; + rv=AQH_Storage_Init(sto); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + AQH_Storage_free(sto); + return rv; + } + aqh->storage=sto; + } + else { + DBG_ERROR(NULL, "No state file given"); + return GWEN_ERROR_GENERIC; + } return 0; } @@ -178,27 +260,6 @@ void _setupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) -void _setupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) -{ - const char *tcpAddress; - int tcpPort; - - tcpAddress=GWEN_DB_GetCharValue(dbArgs, "httpAddress", 0, NULL); - tcpPort=GWEN_DB_GetIntValue(dbArgs, "httpPort", 0, AQHOME_STORAGE_DEFAULT_HTTP_PORT); - - if (tcpAddress && *tcpAddress && tcpPort) { - GWEN_MSG_ENDPOINT *ep; - - ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); - GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptHttpFn, aqh); - - GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); - aqh->httpdEndpoint=ep; - } -} - - - GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, @@ -214,28 +275,6 @@ GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, -GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, - GWEN_SOCKET *sk, - const GWEN_INETADDRESS *addr, - GWEN_UNUSED void *data) -{ - GWEN_MSG_ENDPOINT *epIncoming; - - /* AQHOME_STORAGE *aqh; - * - * aqh=(AQHOME_STORAGE*) data; - */ - - DBG_INFO(NULL, "Incoming HTTP connection"); - epIncoming=GWEN_MsgEndpoint_new("http", 0); - GWEN_MsgEndpoint_SetSocket(epIncoming, sk); - GWEN_MsgIoEndpoint_Extend(epIncoming); - AQH_HttpEndpoint_Extend(epIncoming, AQH_ENDPOINT_HTTP_FLAGS_PASSIVE); - return epIncoming; -} - - - int _createPidFile(const char *pidFilename) { FILE *f; @@ -360,7 +399,7 @@ int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) "httpAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ - "ma", /* short option */ + "ha", /* short option */ "httpaddress", /* long option */ I18S("Specify the address to bind the http service to (disabled if missing)"), I18S("Specify the address to bind the http service to (disabled if missing)") @@ -371,11 +410,22 @@ int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) "httpPort", /* name */ 0, /* minnum */ 1, /* maxnum */ - "mp", /* short option */ + "hp", /* short option */ "httpport", /* long option */ I18S("Specify the port to listen on for HTTP connections"), I18S("Specify the port to listen on for HTTP connections") }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "sourcefolder", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + NULL, /* short option */ + "sourcefolder", /* long option */ + I18S("Folder where static HTML source files are stored"), + I18S("Folder where static HTML source files are stored") + }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ diff --git a/apps/aqhome-storage/init_http.c b/apps/aqhome-storage/init_http.c new file mode 100644 index 0000000..d85375f --- /dev/null +++ b/apps/aqhome-storage/init_http.c @@ -0,0 +1,244 @@ +/**************************************************************************** + * 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 "./init_http.h" +#include "./aqhomestorage_p.h" +#include "./aqhomehttp.h" +#include "./u_login.h" +#include "./u_rooms.h" + +#include "aqhome/msg/endpoint_tty.h" +#include "aqhome/ipc/endpoint_ipc.h" +#include "aqhome/mqtt/endpoint_mqttc.h" +#include "aqhome/http/endpoint_http.h" +#include "aqhome/http/httpservice_conf.h" +#include "aqhome/http/httpservice_http.h" +#include "aqhome/http/httpservice.h" +#include "aqhome/http/content_files.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +//#define I18N(msg) msg +#define I18S(msg) msg + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _setupEndpoint(AQHOME_STORAGE *aqh, const char *tcpAddress, int tcpPort); +static int _setupHttpService(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); +static int _createContentRoot(AQHOME_STORAGE *aqh); +static GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); + +static int _createUrlHandler_login(AQHOME_STORAGE *aqh); +static int _createUrlHandler_rooms(AQHOME_STORAGE *aqh); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AqHomeStorage_SetupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *tcpAddress; + int tcpPort; + + tcpAddress=GWEN_DB_GetCharValue(dbArgs, "httpAddress", 0, NULL); + tcpPort=GWEN_DB_GetIntValue(dbArgs, "httpPort", 0, AQHOME_STORAGE_DEFAULT_HTTP_PORT); + + if (tcpAddress && *tcpAddress && tcpPort) { + int rv; + + _setupEndpoint(aqh, tcpAddress, tcpPort); + rv=_setupHttpService(aqh, dbArgs); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=_createContentRoot(aqh); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=_createUrlHandler_login(aqh); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=_createUrlHandler_rooms(aqh); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + } + return 0; +} + + + +void _setupEndpoint(AQHOME_STORAGE *aqh, const char *tcpAddress, int tcpPort) +{ + GWEN_MSG_ENDPOINT *ep; + + ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); + GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptHttpFn, aqh); + + GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); + aqh->httpdEndpoint=ep; +} + + + +int _setupHttpService(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *configFolder; + const char *sourceFolder; + int rv; + + configFolder=GWEN_DB_GetCharValue(dbArgs, "cfgdir", 0, AQHOME_STORAGE_DEFAULT_CONFIGDIR); + sourceFolder=GWEN_DB_GetCharValue(dbArgs, "sourceFolder", 0, AQHOME_STORAGE_DEFAULT_HTTP_SOURCEDIR); + aqh->httpService=AQH_HttpService_new(configFolder, sourceFolder); + AqHomeHttpService_Extend(aqh->httpService); + AqHomeHttpService_SetStorage(aqh->httpService, aqh->storage); + rv=AQH_HttpService_LoadConfig(aqh->httpService); + if (rv<0) { + DBG_ERROR(NULL, "Error loading config for HTTP service (%d)", rv); + return GWEN_ERROR_GENERIC; + } + + return 0; +} + + + +GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, + GWEN_SOCKET *sk, + const GWEN_INETADDRESS *addr, + GWEN_UNUSED void *data) +{ + GWEN_MSG_ENDPOINT *epIncoming; + + /* AQHOME_STORAGE *aqh; + * + * aqh=(AQHOME_STORAGE*) data; + */ + + DBG_INFO(NULL, "Incoming HTTP connection"); + epIncoming=GWEN_MsgEndpoint_new("http", 0); + GWEN_MsgEndpoint_SetSocket(epIncoming, sk); + GWEN_MsgIoEndpoint_Extend(epIncoming); + AQH_HttpEndpoint_Extend(epIncoming, AQH_ENDPOINT_HTTP_FLAGS_PASSIVE); + return epIncoming; +} + + + +int _createContentRoot(AQHOME_STORAGE *aqh) +{ + const char *sourceFolder; + GWEN_BUFFER *nbuf1; + GWEN_BUFFER *nbuf2; + AQH_HTTP_CONTENT *c; + + sourceFolder=AQH_HttpService_GetSourceFolder(aqh->httpService); + + nbuf1=GWEN_Buffer_new(0, 256, 0, 1); + nbuf2=GWEN_Buffer_new(0, 256, 0, 1); + + GWEN_Buffer_AppendString(nbuf1, sourceFolder); + GWEN_Buffer_AppendString(nbuf1, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_SITEHEADER); + + GWEN_Buffer_AppendString(nbuf2, sourceFolder); + GWEN_Buffer_AppendString(nbuf2, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_SITEFOOTER); + + c=AQH_HttpContentFiles_new("root", GWEN_Buffer_GetStart(nbuf1), GWEN_Buffer_GetStart(nbuf2)); + if (c==NULL) { + DBG_INFO(NULL, + "Unable to create content for root (header=[%s], footer=[%s])", + GWEN_Buffer_GetStart(nbuf1), GWEN_Buffer_GetStart(nbuf2)); + GWEN_Buffer_free(nbuf2); + GWEN_Buffer_free(nbuf1); + return GWEN_ERROR_GENERIC; + } + AqHomeHttpService_SetContentTree(aqh->httpService, c); + + GWEN_Buffer_free(nbuf2); + GWEN_Buffer_free(nbuf1); + return 0; +} + + + +int _createUrlHandler_login(AQHOME_STORAGE *aqh) +{ + AQH_HTTP_URLHANDLER *uh; + + uh=AQH_LoginHttpUrlHandler_new(aqh->httpService); + AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService)); + AQH_HttpUrlHandler_AddUrlPattern(uh, "/login"); + AQH_HttpService_AddUrlHandler(aqh->httpService, uh); + return 0; +} + + + +int _createUrlHandler_rooms(AQHOME_STORAGE *aqh) +{ + AQH_HTTP_URLHANDLER *uh; + + uh=AQH_RoomsHttpUrlHandler_new(aqh->httpService); + AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService)); + AQH_HttpUrlHandler_AddUrlPattern(uh, "/rooms/*"); + AQH_HttpService_AddUrlHandler(aqh->httpService, uh); + return 0; +} + + + + diff --git a/apps/aqhome-storage/init_http.h b/apps/aqhome-storage/init_http.h new file mode 100644 index 0000000..28338cf --- /dev/null +++ b/apps/aqhome-storage/init_http.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * 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_INIT_HTTP_H +#define AQHOME_STORAGE_INIT_HTTP_H + + +#include "./aqhomestorage.h" + + + +int AqHomeStorage_SetupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); + + + +#endif + + diff --git a/apps/aqhome-storage/http.c b/apps/aqhome-storage/loop.c similarity index 65% rename from apps/aqhome-storage/http.c rename to apps/aqhome-storage/loop.c index a885eca..44c1e41 100644 --- a/apps/aqhome-storage/http.c +++ b/apps/aqhome-storage/loop.c @@ -11,38 +11,14 @@ #endif -#include "./http.h" +#include "./loop.h" +#include "./loop_http.h" #include "./aqhomestorage_p.h" -#include "aqhome/msg/endpoint_tty.h" -#include "aqhome/ipc/endpoint_ipc.h" -#include "aqhome/mqtt/endpoint_mqttc.h" -#include "aqhome/http/endpoint_http.h" -#include "aqhome/http/httpservice_http.h" - #include -#include +#include #include -#include -#include -#include -#include - -#ifdef HAVE_SYS_TYPES_H -# include -#endif - -#ifdef HAVE_SYS_STAT_H -# include -#endif - -#include -#include -#include -#include -#include -#include - +#include @@ -51,9 +27,6 @@ * ------------------------------------------------------------------------------------------------ */ -//#define I18N(msg) msg -#define I18S(msg) msg - /* ------------------------------------------------------------------------------------------------ @@ -63,10 +36,21 @@ + /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ +void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs) +{ + if (aqh) { + GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs); + AqHomeStorage_ReadAndHandleHttpMessages(aqh); +// AqHomeStorage_ReadAndHandleIpcMessages(aqh); + + } +} + diff --git a/apps/aqhome-storage/loop.h b/apps/aqhome-storage/loop.h new file mode 100644 index 0000000..9bca8e9 --- /dev/null +++ b/apps/aqhome-storage/loop.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_LOOP_H +#define AQHOME_STORAGE_LOOP_H + + +#include "./aqhomestorage.h" + + +void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs); + + +#endif + + + + + diff --git a/apps/aqhome-storage/loop_http.c b/apps/aqhome-storage/loop_http.c new file mode 100644 index 0000000..e4381f7 --- /dev/null +++ b/apps/aqhome-storage/loop_http.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * 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 "./loop_http.h" +#include "./aqhomestorage_p.h" + +#include "aqhome/msg/endpoint_tty.h" +#include "aqhome/ipc/endpoint_ipc.h" +#include "aqhome/mqtt/endpoint_mqttc.h" +#include "aqhome/http/endpoint_http.h" +#include "aqhome/http/httpservice_http.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include +#include +#include +#include + + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +//#define I18N(msg) msg +#define I18S(msg) msg + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _handleHttpEndpoint(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep); +static void _handleHttpMsg(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeStorage_ReadAndHandleHttpMessages(AQHOME_STORAGE *aqh) +{ + if (aqh->httpdEndpoint) { + GWEN_MSG_ENDPOINT *ep; + + ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->httpdEndpoint); + while(ep) { + _handleHttpEndpoint(aqh, ep); + ep=GWEN_MsgEndpoint_Tree2_GetNext(ep); + } + } +} + + + +void _handleHttpEndpoint(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep) +{ + GWEN_MSG *msg; + + while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) { + _handleHttpMsg(aqh, ep, msg); + GWEN_Msg_free(msg); + } +} + + + +void _handleHttpMsg(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived) +{ + GWEN_MSG *msgResponse; + + msgResponse=AQH_HttpService_HandleHttpRequest(aqh->httpService, ep, msgReceived); + if (msgResponse) { + DBG_INFO(NULL, "Sending response message"); + GWEN_MsgEndpoint_AddSendMessage(ep, msgResponse); + } + else { + DBG_INFO(NULL, "No response for message"); + } +} + + + + diff --git a/apps/aqhome-storage/loop_http.h b/apps/aqhome-storage/loop_http.h new file mode 100644 index 0000000..6374d9b --- /dev/null +++ b/apps/aqhome-storage/loop_http.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * 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_HTTP_H +#define AQHOME_STORAGE_HTTP_H + + +#include "./aqhomestorage.h" + + +void AqHomeStorage_ReadAndHandleHttpMessages(AQHOME_STORAGE *aqh); + + +#endif + + diff --git a/apps/aqhome-storage/main.c b/apps/aqhome-storage/main.c index b20f0fb..4f274c6 100644 --- a/apps/aqhome-storage/main.c +++ b/apps/aqhome-storage/main.c @@ -1,12 +1,189 @@ +/**************************************************************************** + * 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 +#include + +#include "./aqhomestorage.h" +#include "./init.h" +#include "./fini.h" +#include "./loop.h" + +#include +#include +#include +#include + +#ifdef HAVE_SIGNAL_H +# include +#endif + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +#ifdef HAVE_SIGNAL_H +static int _setSignalHandlers(void); +static int _setupSigAction(struct sigaction *sa, int sig); +static void _signalHandler(int s); +#endif + + + +/* ------------------------------------------------------------------------------------------------ + * static vars + * ------------------------------------------------------------------------------------------------ + */ + +#ifdef HAVE_SIGNAL_H +static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT; +#endif + +static int stopService=0; + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ int main(int argc, char **argv) { - return 1; + int rv; + AQHOME_STORAGE *aqh; + GWEN_GUI *gui; + + rv=GWEN_Init(); + if (rv) { + fprintf(stderr, "ERROR: Unable to init Gwen.\n"); + return 2; + } + + GWEN_Logger_Open(0, "aqhome-storage", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User); + //GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning); + GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info); + + rv=_setSignalHandlers(); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=AQH_Init(); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + gui=GWEN_Gui_CGui_new(); + GWEN_Gui_SetGui(gui); + + aqh=AqHomeStorage_new(); + rv=AqHomeStorage_Init(aqh, argc, argv); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + while(!stopService) { + DBG_DEBUG(NULL, "Next loop"); + AqHomeStorage_Loop(aqh, 2000); + } + + AqHomeStorage_Fini(aqh); + AqHomeStorage_free(aqh); + + GWEN_Gui_SetGui(NULL); + GWEN_Gui_free(gui); + + return 0; } +int _setSignalHandlers(void) +{ +#ifdef HAVE_SIGNAL_H + int rv; + + rv=_setupSigAction(&saINT, SIGINT); + if (rv) + return rv; + + rv=_setupSigAction(&saTERM, SIGTERM); + if (rv) + return rv; + + rv=_setupSigAction(&saHUP, SIGHUP); + if (rv) + return rv; + +# ifdef SIGTSTP + rv=_setupSigAction(&saTSTP, SIGTSTP); + if (rv) + return rv; +# endif + +# ifdef SIGCONT + rv=_setupSigAction(&saCONT, SIGCONT); + if (rv) + return rv; +# endif +#endif + return 0; +} + + + +int _setupSigAction(struct sigaction *sa, int sig) +{ + sa->sa_handler=_signalHandler; + sigemptyset(&sa->sa_mask); + sa->sa_flags=0; + if (sigaction(sig, sa, 0)) { + DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig); + return GWEN_ERROR_IO; + } + + return 0; +} + + + +void _signalHandler(int s) +{ + switch(s) { + case SIGINT: + case SIGTERM: + case SIGHUP: + DBG_WARN(0, "Received signal %d, stopping service in next loop.",s); + stopService=1; + break; + default: + DBG_WARN(0, "Unknown signal %d",s); + break; + } +} + + diff --git a/apps/aqhome-storage/u_login.c b/apps/aqhome-storage/u_login.c new file mode 100644 index 0000000..ccd58c0 --- /dev/null +++ b/apps/aqhome-storage/u_login.c @@ -0,0 +1,388 @@ +/**************************************************************************** + * 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_login.h" +#include "aqhome/http/httpservice.h" +#include "aqhome/http/httpservice_http.h" +#include "aqhome/http/httpservice_conf.h" + +#include +#include +#include +#include +#include +#include +#include + + + +static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); + +static int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _verifyPass(AQH_USER *u, const char *userName, const char *password); +static AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u); +static GWEN_BUFFER *_generateSessionUid(void); +static void _headerSetCookie(GWEN_DB_NODE *db, uint32_t flags, const char *cookieName, const char *cookieValue); +static GWEN_MSG *_createLoginResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *sessionId, const char *newPage); + + + + + +AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv) +{ + AQH_HTTP_URLHANDLER *uh; + + uh=AQH_HttpUrlHandler_new(sv); + AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl); + + return uh; +} + + + +int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + const char *protocol; + const char *cmd; + GWEN_MSG *msgOut=NULL; + + protocol=AQH_HttpRequest_GetProtocol(rq); + cmd=AQH_HttpRequest_GetCommand(rq); + if (cmd && *cmd) { + int rv; + + if (strcasecmp(cmd, "GET")==0) { + rv=_handleGet(uh, rq); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + else if (strcasecmp(cmd, "POST")==0) { + rv=_handlePost(uh, rq); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + 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; + } +} + + + +int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + 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); + 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); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + rv=AQH_HttpService_AddFile(AQH_HttpUrlHandler_GetHttpService(uh), NULL, "login.html", pageBuf); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"login.html\""); + GWEN_Buffer_free(pageBuf); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers"); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + GWEN_Buffer_free(pageBuf); + return rv; + } + + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf)); + GWEN_Buffer_free(pageBuf); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + + return 0; +} + + + +int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + GWEN_DB_NODE *db; + int rv; + + DBG_ERROR(NULL, "Login POST:"); + db=AQH_HttpRequest_GetDbPostBody(rq); + GWEN_DB_Dump(db, 2); + + rv=_loginUser(uh, rq); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + return 0; +} + + + +int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + AQH_SERVICE *sv; + GWEN_MSG *msgOut=NULL; + const char *protocol; + GWEN_DB_NODE *db; + const char *userName; + const char *password; + AQH_USER *u; + AQH_SESSION *session; + int i; + int rv; + + sv=AQH_HttpUrlHandler_GetHttpService(uh); + db=AQH_HttpRequest_GetDbPostBody(rq); + protocol=AQH_HttpRequest_GetProtocol(rq); + + userName=GWEN_DB_GetCharValue(db, "username", 0, NULL); + password=GWEN_DB_GetCharValue(db, "password", 0, NULL); + if (!(userName && *userName && password && *password)) { + msgOut=AQH_HttpService_CreateResponseMsg(sv, 401, "Unauthorized", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + u=AQH_HttpService_GetUser(sv, userName); + if (u==NULL) { + DBG_INFO(NULL, "User \"%s\" not found", userName); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + i=AQH_User_GetState(u); + if (i==AQH_UserState_Suspended) { + DBG_INFO(NULL, "User \"%s\" suspended", userName); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + rv=_verifyPass(u, userName, password); + if (rv<0) { + if (rv==GWEN_ERROR_VERIFY) + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, "error"); + else + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + session=_generateSessionForUser(sv, u); + if (session==NULL) { + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + DBG_ERROR(NULL, "Session generated"); + AQH_User_SetTimestampLastLogin(u, AQH_Session_GetTimestampCreation(session)); + rv=AQH_HttpService_WriteUser(sv, u); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + msgOut=_createLoginResponseMsg(sv, protocol, AQH_Session_GetUid(session), "/rooms/list"); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + + return 0; +} + + + +int _verifyPass(AQH_USER *u, const char *userName, const char *password) +{ + GWEN_MDIGEST *md; + int rv; + const char *storedPasswordHash; + char buffer[70]; + + storedPasswordHash=AQH_User_GetHashedPassword(u); + if (!(storedPasswordHash && *storedPasswordHash)) { + DBG_ERROR(NULL, "No password hash stored with user \"%s\"", userName); + return GWEN_ERROR_INTERNAL; + } + + md=GWEN_MDigest_Sha256_new(); + rv=GWEN_MDigest_Begin(md); + if (rv<0) { + DBG_ERROR(NULL, "Error digesting given password [begin] (%d)", rv); + GWEN_MDigest_free(md); + return rv; + } + + rv=GWEN_MDigest_Update(md, (const uint8_t*) password, strlen(password)); + if (rv<0) { + DBG_ERROR(NULL, "Error digesting given password [update] (%d)", rv); + GWEN_MDigest_free(md); + return rv; + } + + rv=GWEN_MDigest_End(md); + if (rv<0) { + DBG_ERROR(NULL, "Error digesting given password [end] (%d)", rv); + GWEN_MDigest_free(md); + return rv; + } + + DBG_ERROR(NULL, "Digest needs %d bytes", (GWEN_MDigest_GetDigestSize(md)*2)+1); + if (NULL==GWEN_Text_ToHex((const char*) GWEN_MDigest_GetDigestPtr(md), GWEN_MDigest_GetDigestSize(md), buffer, sizeof(buffer)-1)) { + DBG_ERROR(NULL, "Buffer too small (need %d)", (GWEN_MDigest_GetDigestSize(md)*2)+1); + GWEN_MDigest_free(md); + return GWEN_ERROR_INTERNAL; + } + GWEN_MDigest_free(md); + + if (strcasecmp(buffer, storedPasswordHash)!=0) { + DBG_ERROR(NULL, "Bad password for user \"%s\"", userName); + return GWEN_ERROR_VERIFY; + } + + DBG_ERROR(NULL, "Valid password for user \"%s\"", userName); + return 0; +} + + + +AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u) +{ + AQH_SESSION *session; + GWEN_BUFFER *buf; + GWEN_TIMESTAMP *ts; + int rv; + + buf=_generateSessionUid(); + if (buf==NULL) { + DBG_INFO(NULL, "here"); + return NULL; + } + DBG_INFO(NULL, "New session id: [%s]", GWEN_Buffer_GetStart(buf)); + + session=AQH_Session_new(); + AQH_Session_SetUid(session, GWEN_Buffer_GetStart(buf)); + + ts=GWEN_Timestamp_NowInGmTime(); + AQH_Session_SetTimestampCreation(session, ts); + GWEN_Timestamp_free(ts); + + AQH_Session_SetUserAlias(session, AQH_User_GetAlias(u)); + AQH_Session_SetUser(session, u); + + rv=AQH_HttpService_AddSession(sv, session); + if (rv<0) { + DBG_INFO(NULL, "Error adding session \"%s\" (%d)", AQH_Session_GetUid(session), rv); + AQH_Session_free(session); + return NULL; + } + + return session; +} + + + +GWEN_BUFFER *_generateSessionUid(void) +{ + uint8_t binbuf[8]; + GWEN_BUFFER *buf; + int rv; + + rv=GWEN_SyncIo_Helper_PartiallyReadFile("/dev/urandom", binbuf, sizeof(binbuf)); + if (rv + + +AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv); + + + +#endif + + diff --git a/apps/aqhome-storage/u_rooms.c b/apps/aqhome-storage/u_rooms.c new file mode 100644 index 0000000..1401d13 --- /dev/null +++ b/apps/aqhome-storage/u_rooms.c @@ -0,0 +1,344 @@ +/**************************************************************************** + * 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_rooms.h" +#include "./aqhomehttp.h" +#include "aqhome/http/httpservice.h" +#include "aqhome/http/httpservice_http.h" +#include "aqhome/http/httpservice_conf.h" + +#include +#include +#include +#include +#include +#include +#include + + + +static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _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 int _handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf); + + + + +AQH_HTTP_URLHANDLER *AQH_RoomsHttpUrlHandler_new(AQH_SERVICE *sv) +{ + AQH_HTTP_URLHANDLER *uh; + + uh=AQH_HttpUrlHandler_new(sv); + AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl); + + return uh; +} + + + +int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + const char *protocol; + const char *cmd; + GWEN_MSG *msgOut=NULL; + + 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) { + int rv; + + if (strcasecmp(cmd, "GET")==0) { + rv=_handleGet(uh, rq); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + else if (strcasecmp(cmd, "POST")==0) { + rv=_handlePost(uh, rq); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + 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; + } +} + + + +int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + GWEN_BUFFER *pageBuf; + int rv; + GWEN_MSG *msgOut=NULL; + const char *protocol; + const GWEN_STRINGLIST *sl; + + protocol=AQH_HttpRequest_GetProtocol(rq); + pageBuf=GWEN_Buffer_new(0, 256, 0, 1); + 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); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + /* 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)) + s="list"; + + if (strcasecmp(s, "list")==0) + _handleGetList(uh, rq, pageBuf); + else if (strcasecmp(s, "add")==0) + _handleGetAdd(uh, rq, pageBuf); + else { + DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + GWEN_Buffer_free(pageBuf); + return 0; + } + } + + rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers"); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + GWEN_Buffer_free(pageBuf); + return rv; + } + + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf)); + GWEN_Buffer_free(pageBuf); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + + return 0; +} + + + +int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + GWEN_DB_NODE *db; + const GWEN_STRINGLIST *sl; + const char *protocol; + GWEN_MSG *msgOut=NULL; + int rv; + + DBG_ERROR(NULL, "POST:"); + db=AQH_HttpRequest_GetDbPostBody(rq); + GWEN_DB_Dump(db, 2); + + protocol=AQH_HttpRequest_GetProtocol(rq); + sl=AQH_HttpRequest_GetUrlPathMembers(rq); + if (sl) { + const char *s; + + s=GWEN_StringList_StringAt(sl, 1); + if (s && *s) { + if (strcasecmp(s, "add")==0) + rv=_handlePostAdd(uh, rq); + else { + DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + return rv; + } + else { + DBG_ERROR(NULL, "Invalid url (2nd member missing)"); + msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + } + // TODO + + return 0; +} + + + +void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf) +{ + AQH_SERVICE *sv; + uint32_t perms; + + DBG_ERROR(NULL, "LIST"); + perms=AQH_HttpRequest_GetModulePerms(rq); + if (perms & AQHOME_HTTP_PERMS_LIST_ROOMS) { + AQH_STORAGE *sto; + + sv=AQH_HttpUrlHandler_GetHttpService(uh); + sto=AqHomeHttpService_GetStorage(sv); + if (sto) { + const AQH_ROOM_LIST *rl; + + GWEN_Buffer_AppendString(pageBuf, "

Rooms

"); + GWEN_Buffer_AppendString(pageBuf, ""); + GWEN_Buffer_AppendString(pageBuf, ""); + rl=AQH_Storage_GetRoomList(sto); + if (rl) { + const AQH_ROOM *r; + + r=AQH_Room_List_First(rl); + while(r) { + long unsigned int id; + const char *name; + const char *descr; + + id=(long unsigned int) AQH_Room_GetId(r); + name=AQH_Room_GetName(r); + descr=AQH_Room_GetDescription(r); + GWEN_Buffer_AppendArgs(pageBuf, "", id, name?name:"", descr?descr:""); + + r=AQH_Room_List_Next(r); + } + } + GWEN_Buffer_AppendString(pageBuf, "
IdNameDescription
%lu%s%s
"); + GWEN_Buffer_AppendString(pageBuf, "Add Room
"); + } + else { + GWEN_Buffer_AppendString(pageBuf, "

Internal error.

"); + } + } + else { + GWEN_Buffer_AppendString(pageBuf, "

No permissions to see list of rooms.

"); + } +} + + + +void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf) +{ + uint32_t perms; + + DBG_ERROR(NULL, "ADD"); + perms=AQH_HttpRequest_GetModulePerms(rq); + if (perms & AQHOME_HTTP_PERMS_ADD_ROOM) { + _writeAddPage(uh, rq, pageBuf); + } + else { + DBG_INFO(NULL, "No permissions to add a room."); + GWEN_Buffer_AppendString(pageBuf, "

No permissions to add a room.

"); + } +} + + + +int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf) +{ + int rv; + int pos; + + pos=GWEN_Buffer_GetPos(pageBuf); + rv=AQH_HttpService_AddFile(AQH_HttpUrlHandler_GetHttpService(uh), NULL, "roomadd.html", pageBuf); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"roomadd.html\""); + GWEN_Buffer_Crop(pageBuf, 0, pos); + GWEN_Buffer_AppendString(pageBuf, "

Internal error.

"); + } + return 0; +} + + + +int _handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + const char *protocol; + AQH_SERVICE *sv; + uint32_t perms; + GWEN_MSG *msgOut=NULL; + + protocol=AQH_HttpRequest_GetProtocol(rq); + sv=AQH_HttpUrlHandler_GetHttpService(uh); + perms=AQH_HttpRequest_GetModulePerms(rq); + if (perms & AQHOME_HTTP_PERMS_ADD_ROOM) { + GWEN_DB_NODE *db; + const char *roomName; + const char *roomDescr; + AQH_STORAGE *sto; + + db=AQH_HttpRequest_GetDbPostBody(rq); + + roomName=GWEN_DB_GetCharValue(db, "roomname", 0, NULL); + roomDescr=GWEN_DB_GetCharValue(db, "descr", 0, NULL); + + if (!(roomName && *roomName)) { + msgOut=AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", "Missing room name", 1, _writeAddPage); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + + sto=AqHomeHttpService_GetStorage(sv); + if (sto) { + AQH_ROOM *r; + + r=AQH_Room_new(); + AQH_Room_SetName(r, roomName); + if (roomDescr && *roomDescr) + AQH_Room_SetDescription(r, roomDescr); + if (AQH_Storage_GetRoomByName(sto, roomName)!=NULL) { + msgOut=AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", "Room already exists", 1, _writeAddPage); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + AQH_Storage_AddRoom(sto, r); + msgOut=AQH_HttpService_CreateRedirectingResponseMsg(sv, protocol, "/rooms/list"); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + else { + DBG_ERROR(NULL, "No storage"); + msgOut=AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } + } + else { + msgOut=AQH_HttpService_CreateResponseMsg(sv, 403, "Forbidden", protocol, NULL); + AQH_HttpRequest_SetResponseMsg(rq, msgOut); + return 0; + } +} + + + + diff --git a/apps/aqhome-storage/u_rooms.h b/apps/aqhome-storage/u_rooms.h new file mode 100644 index 0000000..511e290 --- /dev/null +++ b/apps/aqhome-storage/u_rooms.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_ROOMS_H +#define AQHOME_STORAGE_U_ROOMS_H + + +#include "aqhome/http/urlhandler.h" + +#include + + +AQH_HTTP_URLHANDLER *AQH_RoomsHttpUrlHandler_new(AQH_SERVICE *sv); + + + +#endif + + diff --git a/aqhome-storage.sh b/aqhome-storage.sh new file mode 100755 index 0000000..b6312bb --- /dev/null +++ b/aqhome-storage.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +export AQHOME_LOGLEVEL=info +export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" + +# 0-build/apps/aqhome-storage/aqhome-storage $* + +0-build/apps/aqhome-storage/aqhome-storage \ + --sourcefolder=apps/aqhome-storage/test/html \ + -D apps/aqhome-storage/test/config \ + --statefile=apps/aqhome-storage/test/config/state \ + -ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMESTORAGETEST \ + -ha 127.0.0.1 -hp 1884 \ + -p ./aqhome-storage.pid + diff --git a/aqhome/data/room.t2d b/aqhome/data/room.t2d index 3effc18..c08dad5 100644 --- a/aqhome/data/room.t2d +++ b/aqhome/data/room.t2d @@ -41,7 +41,7 @@ 0 0 public - + with_getbymember diff --git a/aqhome/data/storage.c b/aqhome/data/storage.c index c7d12f7..ee79688 100644 --- a/aqhome/data/storage.c +++ b/aqhome/data/storage.c @@ -82,6 +82,40 @@ void AQH_Storage_SetStateFile(AQH_STORAGE *sto, const char *s) +void AQH_Storage_AddRoom(AQH_STORAGE *sto, AQH_ROOM *r) +{ + if (sto && r) { + uint64_t id; + + id=++(sto->lastRoomId); + AQH_Room_SetId(r, id); + AQH_Room_List_Add(r, sto->roomList); + } +} + + + +AQH_ROOM_LIST *AQH_Storage_GetRoomList(const AQH_STORAGE *sto) +{ + return sto?sto->roomList:NULL; +} + + + +AQH_ROOM *AQH_Storage_GetRoomById(const AQH_STORAGE *sto, uint64_t id) +{ + return sto?AQH_Room_List_GetById(sto->roomList, id):NULL; +} + + + +AQH_ROOM *AQH_Storage_GetRoomByName(const AQH_STORAGE *sto, const char *s) +{ + return sto?AQH_Room_List_GetByName(sto->roomList, s):NULL; +} + + + void AQH_Storage_AddDevice(AQH_STORAGE *sto, AQH_DEVICE *dev) { if (sto && dev) { diff --git a/aqhome/data/storage.h b/aqhome/data/storage.h index b2a0cdb..587c8f9 100644 --- a/aqhome/data/storage.h +++ b/aqhome/data/storage.h @@ -46,6 +46,11 @@ extern "C" { AQHOME_API AQH_STORAGE *AQH_Storage_new(void); AQHOME_API void AQH_Storage_free(AQH_STORAGE *sto); +AQHOME_API void AQH_Storage_AddRoom(AQH_STORAGE *sto, AQH_ROOM *r); +AQHOME_API AQH_ROOM_LIST *AQH_Storage_GetRoomList(const AQH_STORAGE *sto); +AQHOME_API AQH_ROOM *AQH_Storage_GetRoomById(const AQH_STORAGE *sto, uint64_t id); +AQHOME_API AQH_ROOM *AQH_Storage_GetRoomByName(const AQH_STORAGE *sto, const char *s); + AQHOME_API void AQH_Storage_AddDevice(AQH_STORAGE *sto, AQH_DEVICE *dev); AQHOME_API AQH_DEVICE_LIST *AQH_Storage_GetDeviceList(const AQH_STORAGE *sto); AQHOME_API AQH_DEVICE *AQH_Storage_GetDeviceById(const AQH_STORAGE *sto, uint64_t id); diff --git a/aqhome/http/0BUILD b/aqhome/http/0BUILD index e998d7c..8695dd0 100644 --- a/aqhome/http/0BUILD +++ b/aqhome/http/0BUILD @@ -50,6 +50,9 @@ httpservice_conf.h httpservice_http.h httprequest.h + urlhandler.h + content.h + content_files.h @@ -57,6 +60,9 @@ endpoint_http_p.h httpservice_p.h httprequest_p.h + urlhandler_p.h + content_p.h + content_files_p.h @@ -68,6 +74,9 @@ httpservice_conf.c httpservice_http.c httprequest.c + urlhandler.c + content.c + content_files.c diff --git a/aqhome/http/content.c b/aqhome/http/content.c new file mode 100644 index 0000000..6ed3e6b --- /dev/null +++ b/aqhome/http/content.c @@ -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 +#endif + + +#include "aqhome/http/content_p.h" + +#include + + + + +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; +} + + + + diff --git a/aqhome/http/content.h b/aqhome/http/content.h new file mode 100644 index 0000000..d6adfa9 --- /dev/null +++ b/aqhome/http/content.h @@ -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 + +#include +#include +#include + + +#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 + diff --git a/aqhome/http/content_files.c b/aqhome/http/content_files.c new file mode 100644 index 0000000..c0643fd --- /dev/null +++ b/aqhome/http/content_files.c @@ -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 +#endif + +#include "aqhome/http/content_files_p.h" + +#include +#include + + + +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; +} + + + diff --git a/aqhome/http/content_files.h b/aqhome/http/content_files.h new file mode 100644 index 0000000..aeef760 --- /dev/null +++ b/aqhome/http/content_files.h @@ -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 +#include + + + +#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 diff --git a/aqhome/http/content_files_p.h b/aqhome/http/content_files_p.h new file mode 100644 index 0000000..9c3487d --- /dev/null +++ b/aqhome/http/content_files_p.h @@ -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 + diff --git a/aqhome/http/content_p.h b/aqhome/http/content_p.h new file mode 100644 index 0000000..1553449 --- /dev/null +++ b/aqhome/http/content_p.h @@ -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 + diff --git a/aqhome/http/endpoint_http.c b/aqhome/http/endpoint_http.c index 5d86302..9ba4ad8 100644 --- a/aqhome/http/endpoint_http.c +++ b/aqhome/http/endpoint_http.c @@ -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; diff --git a/aqhome/http/httprequest.c b/aqhome/http/httprequest.c index bedec70..d891aa9 100644 --- a/aqhome/http/httprequest.c +++ b/aqhome/http/httprequest.c @@ -16,8 +16,7 @@ #include #include - -#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid" +#include 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; +} + + diff --git a/aqhome/http/httprequest.h b/aqhome/http/httprequest.h index 208f564..cf98821 100644 --- a/aqhome/http/httprequest.h +++ b/aqhome/http/httprequest.h @@ -19,6 +19,8 @@ #include +#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 diff --git a/aqhome/http/httprequest_p.h b/aqhome/http/httprequest_p.h index 6d73f59..3adde4f 100644 --- a/aqhome/http/httprequest_p.h +++ b/aqhome/http/httprequest_p.h @@ -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 */ diff --git a/aqhome/http/httpservice.c b/aqhome/http/httpservice.c index 9c7d738..7f02336 100644 --- a/aqhome/http/httpservice.c +++ b/aqhome/http/httpservice.c @@ -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) { diff --git a/aqhome/http/httpservice.h b/aqhome/http/httpservice.h index 5400161..c3e1da4 100644 --- a/aqhome/http/httpservice.h +++ b/aqhome/http/httpservice.h @@ -14,6 +14,7 @@ #include "aqhome/service/service.h" #include "aqhome/http/httprequest.h" +#include "aqhome/http/urlhandler.h" #include #include @@ -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 diff --git a/aqhome/http/httpservice_conf.c b/aqhome/http/httpservice_conf.c index 0f0f70f..8cd00c9 100644 --- a/aqhome/http/httpservice_conf.c +++ b/aqhome/http/httpservice_conf.c @@ -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); diff --git a/aqhome/http/httpservice_conf.h b/aqhome/http/httpservice_conf.h index ecb9590..243ab98 100644 --- a/aqhome/http/httpservice_conf.h +++ b/aqhome/http/httpservice_conf.h @@ -19,27 +19,34 @@ #include -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 diff --git a/aqhome/http/httpservice_http.c b/aqhome/http/httpservice_http.c index 45de3b3..fda88dc 100644 --- a/aqhome/http/httpservice_http.c +++ b/aqhome/http/httpservice_http.c @@ -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 #include @@ -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:"", 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; } + diff --git a/aqhome/http/httpservice_http.h b/aqhome/http/httpservice_http.h index 1ae2448..b8c0892 100644 --- a/aqhome/http/httpservice_http.h +++ b/aqhome/http/httpservice_http.h @@ -11,6 +11,8 @@ #include "aqhome/service/service.h" +#include "aqhome/http/httprequest.h" +#include "aqhome/http/urlhandler.h" #include #include @@ -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); diff --git a/aqhome/http/httpservice_login.h b/aqhome/http/httpservice_login.h index 5c378f1..186c725 100644 --- a/aqhome/http/httpservice_login.h +++ b/aqhome/http/httpservice_login.h @@ -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); diff --git a/aqhome/http/httpservice_p.h b/aqhome/http/httpservice_p.h index 1d69e8a..0426c50 100644 --- a/aqhome/http/httpservice_p.h +++ b/aqhome/http/httpservice_p.h @@ -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; }; diff --git a/aqhome/http/urlhandler.c b/aqhome/http/urlhandler.c new file mode 100644 index 0000000..0f78f7d --- /dev/null +++ b/aqhome/http/urlhandler.c @@ -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 +#endif + + +#include "./urlhandler_p.h" +#include "aqhome/http/httpservice_http.h" + +#include +#include +#include + + + +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, "

%s

", 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; +} + + + diff --git a/aqhome/http/urlhandler.h b/aqhome/http/urlhandler.h new file mode 100644 index 0000000..5cce3ad --- /dev/null +++ b/aqhome/http/urlhandler.h @@ -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 +#include +#include +#include + +#include +#include +#include +#include + + +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 + + diff --git a/aqhome/http/urlhandler_p.h b/aqhome/http/urlhandler_p.h new file mode 100644 index 0000000..c2c0f07 --- /dev/null +++ b/aqhome/http/urlhandler_p.h @@ -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 + + + diff --git a/aqhome/service/module.t2d b/aqhome/service/module.t2d index e78c385..d4e1927 100644 --- a/aqhome/service/module.t2d +++ b/aqhome/service/module.t2d @@ -59,7 +59,7 @@
- + NULL NULL public diff --git a/aqhome/service/user.t2d b/aqhome/service/user.t2d index 45c583c..9bff515 100644 --- a/aqhome/service/user.t2d +++ b/aqhome/service/user.t2d @@ -39,6 +39,25 @@ + + + + + User suspended + + + + Waiting for email confirmation + + + + Waiting for approval by admin + + + + + + @@ -56,6 +75,13 @@ with_flags + + 0 + 0 + public + + + 0 0