diff --git a/apps/0BUILD b/apps/0BUILD index 3adc6a8..9a167cd 100644 --- a/apps/0BUILD +++ b/apps/0BUILD @@ -6,6 +6,7 @@ aqhomed aqhome-tool aqhome-mqttlog + aqhome-storage diff --git a/apps/aqhome-storage/0BUILD b/apps/aqhome-storage/0BUILD new file mode 100644 index 0000000..436a34c --- /dev/null +++ b/apps/aqhome-storage/0BUILD @@ -0,0 +1,72 @@ + + + + + + + + $(gwenhywfar_cflags) + -I$(topsrcdir) + -I$(topbuilddir) + -I$(builddir) + -I$(srcdir) + + + + --include=$(builddir) + --include=$(srcdir) + + + $(visibility_cflags) + + + + + + + + + + + + + + + + + + aqhomestorage_p.h + aqhomestorage.h + init.h + + + + $(local/typefiles) + + aqhomestorage.c + init.c + main.c + + + + aqhome + + + + $(gwenhywfar_libs) + + + + + + + + + + + + + + + + diff --git a/apps/aqhome-storage/aqhomestorage.c b/apps/aqhome-storage/aqhomestorage.c new file mode 100644 index 0000000..e207056 --- /dev/null +++ b/apps/aqhome-storage/aqhomestorage.c @@ -0,0 +1,119 @@ +/**************************************************************************** + * 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 "./aqhomestorage_p.h" + +#include +#include + + + + + + +AQHOME_STORAGE *AqHomeStorage_new() +{ + AQHOME_STORAGE *aqh; + + GWEN_NEW_OBJECT(AQHOME_STORAGE, aqh); + aqh->rootEndpoint=GWEN_MsgEndpoint_new("root", 0); + + return aqh; +} + + + +void AqHomeStorage_free(AQHOME_STORAGE *aqh) +{ + if (aqh) { + AQH_Storage_free(aqh->storage); + GWEN_MsgEndpoint_free(aqh->rootEndpoint); + GWEN_MsgEndpoint_free(aqh->rootEndpoint); + GWEN_DB_Group_free(aqh->dbArgs); + + aqh->storage=NULL; + aqh->rootEndpoint=NULL; + aqh->ipcdEndpoint=NULL; + aqh->mqttEndpoint=NULL; + aqh->httpdEndpoint=NULL; + aqh->dbArgs=NULL; + free(aqh->pidFile); + + GWEN_FREE_OBJECT(aqh); + } +} + + + +GWEN_MSG_ENDPOINT *AqHomeStorage_GetRootEndpoint(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->rootEndpoint):NULL; +} + + + +GWEN_MSG_ENDPOINT *AqHomeStorage_GetIpcdEndpoint(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->ipcdEndpoint):NULL; +} + + + +GWEN_MSG_ENDPOINT *AqHomeStorage_GetMqttEndpoint(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->mqttEndpoint):NULL; +} + + + +GWEN_MSG_ENDPOINT *AqHomeStorage_GetHttpdEndpoint(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->httpdEndpoint):NULL; +} + + + +GWEN_DB_NODE *AqHomeStorage_GetDbArgs(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->dbArgs):NULL; +} + + + +AQH_STORAGE *AqHomeStorage_GetStorage(const AQHOME_STORAGE *aqh) +{ + return aqh?(aqh->storage):NULL; +} + + + +const char *AqHomeStorage_GetPidFile(const AQHOME_STORAGE *aqh) +{ + return aqh?aqh->pidFile:NULL; +} + + + +void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s) +{ + if (aqh) { + free(aqh->pidFile); + aqh->pidFile=s?strdup(s):NULL; + } +} + + + + + + diff --git a/apps/aqhome-storage/aqhomestorage.h b/apps/aqhome-storage/aqhomestorage.h new file mode 100644 index 0000000..b4584ff --- /dev/null +++ b/apps/aqhome-storage/aqhomestorage.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * 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_H +#define AQHOME_STORAGE_H + + +#include "aqhome/data/storage.h" + +#include + + +typedef struct AQHOME_STORAGE AQHOME_STORAGE; + + +AQHOME_STORAGE *AqHomeStorage_new(); +void AqHomeStorage_free(AQHOME_STORAGE *aqh); + +GWEN_MSG_ENDPOINT *AqHomeStorage_GetRootEndpoint(const AQHOME_STORAGE *aqh); +GWEN_MSG_ENDPOINT *AqHomeStorage_GetIpcdEndpoint(const AQHOME_STORAGE *aqh); +GWEN_MSG_ENDPOINT *AqHomeStorage_GetMqttEndpoint(const AQHOME_STORAGE *aqh); +GWEN_MSG_ENDPOINT *AqHomeStorage_GetHttpdEndpoint(const AQHOME_STORAGE *aqh); + +GWEN_DB_NODE *AqHomeStorage_GetDbArgs(const AQHOME_STORAGE *aqh); + +AQH_STORAGE *AqHomeStorage_GetStorage(const AQHOME_STORAGE *aqh); + +const char *AqHomeStorage_GetPidFile(const AQHOME_STORAGE *aqh); +void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s); + + +#endif + diff --git a/apps/aqhome-storage/aqhomestorage_p.h b/apps/aqhome-storage/aqhomestorage_p.h new file mode 100644 index 0000000..704ec0f --- /dev/null +++ b/apps/aqhome-storage/aqhomestorage_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * 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_P_H +#define AQHOME_STORAGE_P_H + + +#include "./aqhomestorage.h" + + + +/* default values */ +#define AQHOME_STORAGE_DEFAULT_PIDFILE "/var/run/aqhome-storage.pid" +#define AQHOME_STORAGE_DEFAULT_IPC_PORT 45455 +#define AQHOME_STORAGE_DEFAULT_HTTP_PORT 45456 +#define AQHOME_STORAGE_DEFAULT_MQTT_CLIENTID "AQHOMESTORAGE" +#define AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE 600 +#define AQHOME_STORAGE_DEFAULT_MQTT_PORT 1883 + + + +struct AQHOME_STORAGE { + GWEN_MSG_ENDPOINT *rootEndpoint; + + GWEN_MSG_ENDPOINT *ipcdEndpoint; + GWEN_MSG_ENDPOINT *mqttEndpoint; + GWEN_MSG_ENDPOINT *httpdEndpoint; + + GWEN_DB_NODE *dbArgs; + + AQH_STORAGE *storage; + + char *pidFile; + +}; + +#endif + diff --git a/apps/aqhome-storage/init.c b/apps/aqhome-storage/init.c new file mode 100644 index 0000000..4aef7fd --- /dev/null +++ b/apps/aqhome-storage/init.c @@ -0,0 +1,452 @@ +/**************************************************************************** + * 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.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 +#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 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); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv) +{ + GWEN_DB_NODE *dbArgs; + int rv; + const char *s; + + dbArgs=GWEN_DB_Group_new("args"); + rv=_readArgs(argc, argv, dbArgs); + if (rv<0) { + DBG_ERROR(NULL, "Error reading args (%d)", rv); + return rv; + } + aqh->dbArgs=dbArgs; + + s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_STORAGE_DEFAULT_PIDFILE); + if (s && *s) { + AqHomeStorage_SetPidFile(aqh, s); + rv=_createPidFile(s); + if (rv<0) { + DBG_ERROR(NULL, "Error creating PID file (%d)", rv); + return rv; + } + } + +// _setupStorage(aqh, dbArgs); + + _setupIpc(aqh, dbArgs); + _setupMqtt(aqh, dbArgs); + _setupHttp(aqh, dbArgs); + + 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); + + sto=AQH_Storage_new(); + + aqh->storage=sto; + return 0; +} + + + +void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *tcpAddress; + int tcpPort; + + tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL); + tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, AQHOME_STORAGE_DEFAULT_IPC_PORT); + + if (tcpAddress && *tcpAddress && tcpPort) { + GWEN_MSG_ENDPOINT *ep; + + ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); + GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh); + + GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); + aqh->ipcdEndpoint=ep; + } +} + + + +void _setupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *mqttAddress; + int mqttPort; + const char *mqttClientId; + int mqttKeepAlive; + + mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL); + mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, AQHOME_STORAGE_DEFAULT_MQTT_PORT); + mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, AQHOME_STORAGE_DEFAULT_MQTT_CLIENTID); + mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE); + + if (mqttAddress && *mqttAddress && mqttPort) { + GWEN_MSG_ENDPOINT *ep; + int rv; + + ep=AQH_MqttClientEndpoint_new(mqttClientId, mqttAddress, mqttPort, NULL, 0); + AQH_MqttClientEndpoint_SetKeepAliveTime(ep, mqttKeepAlive); + + GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); + aqh->mqttEndpoint=ep; + + rv=AQH_MqttClientEndpoint_StartConnect(ep); + if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) { + DBG_ERROR(NULL, "Error connecting to MQTT server %s:%d (%d), will retry later", mqttAddress, mqttPort, rv); + } + } +} + + + +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, + GWEN_UNUSED void *data) +{ +/* AQHOME_STORAGE *aqh; + * + * aqh=(AQHOME_STORAGE*) data; + */ + DBG_INFO(NULL, "Incoming IPC connection"); + return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, 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 _createPidFile(const char *pidFilename) +{ + FILE *f; + int pidfd; + + if (remove(pidFilename)==0) { + DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)"); + } + +#ifdef HAVE_SYS_STAT_H + pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (pidfd < 0) { + DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); + return GWEN_ERROR_IO; + } + + f = fdopen(pidfd, "w"); +#else /* HAVE_STAT_H */ + f=fopen(pidFilename,"w+"); +#endif /* HAVE_STAT_H */ + + /* write pid */ +#ifdef HAVE_GETPID + fprintf(f,"%d\n",getpid()); +#else + fprintf(f,"-1\n"); +#endif + if (fclose(f)) { + DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); + return GWEN_ERROR_IO; + } + return 0; +} + + + + +int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) +{ + int rv; + const GWEN_ARGS args[]= { + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "cfgdir", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "D", /* short option */ + "cfgdir", /* long option */ + I18S("Specify the configuration folder"), + I18S("Specify the configuration folder") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "tcpAddress", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "t", /* short option */ + "tcpaddress", /* long option */ + I18S("Specify the TCP address to listen on (disabled if missing)"), + I18S("Specify the TCP address to listen on (disabled if missing)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "tcpPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "P", /* short option */ + "tcpport", /* long option */ + I18S("Specify the TCP port to listen on"), + I18S("Specify the TCP port to listen on") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "mqttAddress", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "ma", /* short option */ + "mqttaddress", /* long option */ + I18S("Specify the address of the MQTT server to connect to (disabled if missing)"), + I18S("Specify the address of the MQTT server to connect to (disabled if missing)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "mqttPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "mp", /* short option */ + "mqttport", /* long option */ + I18S("Specify the port of the MQTT server (default: 1883)"), + I18S("Specify the port of the MQTT server (default: 1883)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "mqttClientId", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + NULL, /* short option */ + "mqttclientid", /* long option */ + I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")"), + I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "mqttKeepAlive", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "mk", /* short option */ + "mqttkeepalive", /* long option */ + I18S("Specify keepalive time in seconds (defaults: 600)"), + I18S("Specify keepalive time in seconds (defaults: 600)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "httpAddress", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "ma", /* 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)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "httpPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "mp", /* 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 */ + "statefile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "S", /* short option */ + "statefile", /* long option */ + I18S("File where rooms, devices and values etc. are stored"), + I18S("File where rooms, devices and values etc. are stored") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "pidfile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "p", /* short option */ + "pidfile", /* long option */ + I18S("Specify the PID file"), + I18S("Specify the PID file") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "timeout", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "T", /* short option */ + "timeout", /* long option */ + I18S("Specify timeout in second (default: no timeout)"), + I18S("Specify timeout in second (default: no timeout)") + }, + { + GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ + GWEN_ArgsType_Int, /* type */ + "help", /* name */ + 0, /* minnum */ + 0, /* maxnum */ + "h", /* short option */ + "help", + I18S("Show this help screen."), + I18S("Show this help screen.") + } + }; + + rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs); + if (rv==GWEN_ARGS_RESULT_ERROR) { + fprintf(stderr, "ERROR: Could not parse arguments main\n"); + return GWEN_ERROR_INVALID; + } + else if (rv==GWEN_ARGS_RESULT_HELP) { + GWEN_BUFFER *ubuf; + + ubuf=GWEN_Buffer_new(0, 1024, 0, 1); + GWEN_Buffer_AppendArgs(ubuf, + I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"), + AQHOME_VERSION_STRING, + argv[0]); + if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { + fprintf(stderr, "ERROR: Could not create help string\n"); + return 1; + } + GWEN_Buffer_AppendString(ubuf, "\n"); + + fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf)); + GWEN_Buffer_free(ubuf); + return GWEN_ERROR_CLOSE; + } + return 0; +} + + + diff --git a/apps/aqhome-storage/init.h b/apps/aqhome-storage/init.h new file mode 100644 index 0000000..6627a7f --- /dev/null +++ b/apps/aqhome-storage/init.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_H +#define AQHOME_STORAGE_INIT_H + + +#include "./aqhomestorage.h" + + + +int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv); + + + +#endif + + diff --git a/apps/aqhome-storage/main.c b/apps/aqhome-storage/main.c new file mode 100644 index 0000000..b20f0fb --- /dev/null +++ b/apps/aqhome-storage/main.c @@ -0,0 +1,12 @@ + + + + + +int main(int argc, char **argv) +{ + return 1; +} + + + diff --git a/aqhome/data/0BUILD b/aqhome/data/0BUILD index 4c7218f..ed915d8 100644 --- a/aqhome/data/0BUILD +++ b/aqhome/data/0BUILD @@ -73,6 +73,8 @@ storage_p.h + storage_readxml.h + storage_writexml.h @@ -80,6 +82,8 @@ $(local/typefiles) storage.c + storage_readxml.c + storage_writexml.c diff --git a/aqhome/data/storage.c b/aqhome/data/storage.c index 8fb712c..c7d12f7 100644 --- a/aqhome/data/storage.c +++ b/aqhome/data/storage.c @@ -11,8 +11,12 @@ #endif #include "aqhome/data/storage_p.h" +#include "aqhome/data/storage_readxml.h" +#include "aqhome/data/storage_writexml.h" #include +#include +#include @@ -25,7 +29,6 @@ - /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ @@ -37,6 +40,7 @@ AQH_STORAGE *AQH_Storage_new(void) AQH_STORAGE *sto; GWEN_NEW_OBJECT(AQH_STORAGE, sto); + sto->roomList=AQH_Room_List_new(); sto->deviceList=AQH_Device_List_new(); sto->mqttTopicList=AQH_MqttTopic_List_new(); sto->valueList=AQH_Value_List_new(); @@ -52,6 +56,8 @@ void AQH_Storage_free(AQH_STORAGE *sto) AQH_Value_List_free(sto->valueList); AQH_MqttTopic_List_free(sto->mqttTopicList); AQH_Device_List_free(sto->deviceList); + AQH_Room_List_free(sto->roomList); + free(sto->stateFile); GWEN_FREE_OBJECT(sto); } @@ -59,6 +65,23 @@ void AQH_Storage_free(AQH_STORAGE *sto) +const char *AQH_Storage_GetStateFile(const AQH_STORAGE *sto) +{ + return sto?(sto->stateFile):NULL; +} + + + +void AQH_Storage_SetStateFile(AQH_STORAGE *sto, const char *s) +{ + if (sto) { + free(sto->stateFile); + sto->stateFile=s?strdup(s):NULL; + } +} + + + void AQH_Storage_AddDevice(AQH_STORAGE *sto, AQH_DEVICE *dev) { if (sto && dev) { @@ -154,5 +177,43 @@ void AQH_Storage_HandleMqttPublish(AQH_STORAGE *sto, const char *topic, const ch +int AQH_Storage_Init(AQH_STORAGE *sto) +{ + int rv; + + rv=GWEN_Directory_GetPath(sto->stateFile, + GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_NAMEMUSTEXIST); + if (rv==0) { + rv=AQH_Storage_ReadStateFile(sto, sto->stateFile); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + else { + DBG_WARN(AQH_LOGDOMAIN, "State file \"%s\" not available, will try to create it later", sto->stateFile); + } + + return 0; +} + + + +int AQH_Storage_WriteState(AQH_STORAGE *sto) +{ + int rv; + + rv=AQH_Storage_WriteStateFile(sto, sto->stateFile); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + + return 0; +} + + + + diff --git a/aqhome/data/storage.h b/aqhome/data/storage.h index b4e461b..b2a0cdb 100644 --- a/aqhome/data/storage.h +++ b/aqhome/data/storage.h @@ -59,6 +59,13 @@ AQHOME_API void AQH_Storage_AddValue(AQH_STORAGE *sto, AQH_VALUE *value); AQHOME_API AQH_VALUE_LIST *AQH_Storage_GetValueList(const AQH_STORAGE *sto); AQHOME_API AQH_VALUE *AQH_Storage_GetValueById(const AQH_STORAGE *sto, uint64_t id); +AQHOME_API const char *AQH_Storage_GetStateFile(const AQH_STORAGE *sto); +AQHOME_API void AQH_Storage_SetStateFile(AQH_STORAGE *sto, const char *s); + + +AQHOME_API int AQH_Storage_Init(AQH_STORAGE *sto); +AQHOME_API int AQH_Storage_WriteState(AQH_STORAGE *sto); + AQHOME_API void AQH_Storage_HandleMqttPublish(AQH_STORAGE *sto, const char *topic, const char *value); diff --git a/aqhome/data/storage_p.h b/aqhome/data/storage_p.h index c10e234..082262a 100644 --- a/aqhome/data/storage_p.h +++ b/aqhome/data/storage_p.h @@ -13,15 +13,34 @@ #include "aqhome/data/storage.h" +#define AQH_STORAGE_XML_ELEMENTNAME_LASTIDS "lastIds" + +#define AQH_STORAGE_XML_ELEMENTNAME_ROOMS "rooms" +#define AQH_STORAGE_XML_ELEMENTNAME_ROOM "room" + +#define AQH_STORAGE_XML_ELEMENTNAME_DEVICES "devices" +#define AQH_STORAGE_XML_ELEMENTNAME_DEVICE "device" + +#define AQH_STORAGE_XML_ELEMENTNAME_TOPICS "topics" +#define AQH_STORAGE_XML_ELEMENTNAME_TOPIC "topic" + +#define AQH_STORAGE_XML_ELEMENTNAME_VALUES "values" +#define AQH_STORAGE_XML_ELEMENTNAME_VALUE "value" + + struct AQH_STORAGE { + AQH_ROOM_LIST *roomList; AQH_DEVICE_LIST *deviceList; AQH_MQTT_TOPIC_LIST *mqttTopicList; AQH_VALUE_LIST *valueList; + uint64_t lastRoomId; uint64_t lastDeviceId; uint64_t lastTopicId; uint64_t lastValueId; + + char *stateFile; }; diff --git a/aqhome/data/storage_readxml.c b/aqhome/data/storage_readxml.c new file mode 100644 index 0000000..0767351 --- /dev/null +++ b/aqhome/data/storage_readxml.c @@ -0,0 +1,184 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (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/data/storage_readxml.h" +#include "aqhome/data/storage_p.h" + +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + + +static void _readLastIdsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _readRoomsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _readDevicesFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _readTopicsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _readValuesFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AQH_Storage_ReadStateFile(AQH_STORAGE *sto, const char *sFilename) +{ + GWEN_XMLNODE *rootNode; + int rv; + + rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL); + rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error reading XML file \"%s\": %d", sFilename, rv); + GWEN_XMLNode_free(rootNode); + return rv; + } + + _readLastIdsFromXml(sto, rootNode); + _readRoomsFromXml(sto, rootNode); + _readDevicesFromXml(sto, rootNode); + _readTopicsFromXml(sto, rootNode); + _readValuesFromXml(sto, rootNode); + + return 0; +} + + + +void _readLastIdsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nLastIds; + + nLastIds=GWEN_XMLNode_FindFirstTag(rootNode, AQH_STORAGE_XML_ELEMENTNAME_LASTIDS, NULL, NULL); + if (nLastIds) { + sto->lastRoomId=GWEN_XMLNode_GetIntValue(nLastIds, "lastRoomId", 0); + sto->lastDeviceId=GWEN_XMLNode_GetIntValue(nLastIds, "lastDeviceId", 0); + sto->lastTopicId=GWEN_XMLNode_GetIntValue(nLastIds, "lastTopicId", 0); + sto->lastValueId=GWEN_XMLNode_GetIntValue(nLastIds, "lastValueId", 0); + } + else { + sto->lastRoomId=0; + sto->lastDeviceId=0; + sto->lastTopicId=0; + sto->lastValueId=0; + } +} + + + +void _readRoomsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nRooms; + + nRooms=GWEN_XMLNode_FindFirstTag(rootNode, AQH_STORAGE_XML_ELEMENTNAME_ROOMS, NULL, NULL); + if (nRooms) { + GWEN_XMLNODE *nRoom; + + nRoom=GWEN_XMLNode_FindFirstTag(nRooms, AQH_STORAGE_XML_ELEMENTNAME_ROOM, NULL, NULL); + while(nRoom) { + AQH_ROOM *r; + + r=AQH_Room_fromXml(nRoom); + if (r) { + AQH_Room_List_Add(r, sto->roomList); + } + nRoom=GWEN_XMLNode_FindNextTag(nRoom, AQH_STORAGE_XML_ELEMENTNAME_ROOM, NULL, NULL); + } + } +} + + + +void _readDevicesFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nDevices; + + nDevices=GWEN_XMLNode_FindFirstTag(rootNode, AQH_STORAGE_XML_ELEMENTNAME_DEVICES, NULL, NULL); + if (nDevices) { + GWEN_XMLNODE *nDevice; + + nDevice=GWEN_XMLNode_FindFirstTag(nDevices, AQH_STORAGE_XML_ELEMENTNAME_DEVICE, NULL, NULL); + while(nDevice) { + AQH_DEVICE *device; + + device=AQH_Device_fromXml(nDevice); + if (device) { + AQH_Device_List_Add(device, sto->deviceList); + } + nDevice=GWEN_XMLNode_FindNextTag(nDevice, AQH_STORAGE_XML_ELEMENTNAME_DEVICE, NULL, NULL); + } + } +} + + + +void _readTopicsFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nTopics; + + nTopics=GWEN_XMLNode_FindFirstTag(rootNode, AQH_STORAGE_XML_ELEMENTNAME_TOPICS, NULL, NULL); + if (nTopics) { + GWEN_XMLNODE *nTopic; + + nTopic=GWEN_XMLNode_FindFirstTag(nTopics, AQH_STORAGE_XML_ELEMENTNAME_TOPIC, NULL, NULL); + while(nTopic) { + AQH_MQTT_TOPIC *topic; + + topic=AQH_MqttTopic_fromXml(nTopic); + if (topic) { + AQH_MqttTopic_List_Add(topic, sto->mqttTopicList); + } + nTopic=GWEN_XMLNode_FindNextTag(nTopic, AQH_STORAGE_XML_ELEMENTNAME_TOPIC, NULL, NULL); + } + } +} + + + +void _readValuesFromXml(AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nValues; + + nValues=GWEN_XMLNode_FindFirstTag(rootNode, AQH_STORAGE_XML_ELEMENTNAME_VALUES, NULL, NULL); + if (nValues) { + GWEN_XMLNODE *nValue; + + nValue=GWEN_XMLNode_FindFirstTag(nValues, AQH_STORAGE_XML_ELEMENTNAME_VALUE, NULL, NULL); + while(nValue) { + AQH_VALUE *value; + + value=AQH_Value_fromXml(nValue); + if (value) { + AQH_Value_List_Add(value, sto->roomList); + } + nValue=GWEN_XMLNode_FindNextTag(nValue, AQH_STORAGE_XML_ELEMENTNAME_VALUE, NULL, NULL); + } + } +} + + + + + + + + + + diff --git a/aqhome/data/storage_readxml.h b/aqhome/data/storage_readxml.h new file mode 100644 index 0000000..62f0fa4 --- /dev/null +++ b/aqhome/data/storage_readxml.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (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_STORAGE_READXML_H +#define AQH_STORAGE_READXML_H + + +#include "aqhome/data/storage.h" + + + +int AQH_Storage_ReadStateFile(AQH_STORAGE *sto, const char *sFilename); + + + + +#endif + + diff --git a/aqhome/data/storage_writexml.c b/aqhome/data/storage_writexml.c new file mode 100644 index 0000000..dac9aed --- /dev/null +++ b/aqhome/data/storage_writexml.c @@ -0,0 +1,190 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (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/data/storage_writexml.h" +#include "aqhome/data/storage_p.h" + +#include +#include +#include + +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + + +static void _writeLastIdsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _writeRoomsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _writeDevicesToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _writeTopicsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); +static void _writeValuesToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AQH_Storage_WriteStateFile(const AQH_STORAGE *sto, const char *sFilename) +{ + GWEN_XMLNODE *rootNode; + int rv; + GWEN_BUFFER *nbuf; + + rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "root"); + _writeLastIdsToXml(sto, rootNode); + _writeRoomsToXml(sto, rootNode); + _writeDevicesToXml(sto, rootNode); + _writeTopicsToXml(sto, rootNode); + _writeValuesToXml(sto, rootNode); + + nbuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(nbuf, sFilename); + GWEN_Buffer_AppendString(nbuf, ".tmp"); + unlink(GWEN_Buffer_GetStart(nbuf)); + rv=GWEN_XMLNode_WriteFile(rootNode, + GWEN_Buffer_GetStart(nbuf), + GWEN_XML_FLAGS_SIMPLE | GWEN_XML_FLAGS_HANDLE_HEADERS | GWEN_XML_FLAGS_INDENT); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error writing XML file \"%s\": %d", GWEN_Buffer_GetStart(nbuf), rv); + GWEN_Buffer_free(nbuf); + GWEN_XMLNode_free(rootNode); + return rv; + } + if (rename(GWEN_Buffer_GetStart(nbuf), sFilename)<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error renaming \"%s\"->\"%s\": %d (%s)", + GWEN_Buffer_GetStart(nbuf), sFilename, errno, strerror(errno)); + GWEN_Buffer_free(nbuf); + GWEN_XMLNode_free(rootNode); + return rv; + } + GWEN_XMLNode_free(rootNode); + return 0; +} + + + +void _writeLastIdsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nLastIds; + + nLastIds=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_LASTIDS); + GWEN_XMLNode_SetIntValue(nLastIds, "lastRoomId", sto->lastRoomId); + GWEN_XMLNode_SetIntValue(nLastIds, "lastDeviceId", sto->lastDeviceId); + GWEN_XMLNode_SetIntValue(nLastIds, "lastTopicId", sto->lastTopicId); + GWEN_XMLNode_SetIntValue(nLastIds, "lastValueId", sto->lastValueId); + + GWEN_XMLNode_AddChild(rootNode, nLastIds); +} + + + +void _writeRoomsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nElems; + AQH_ROOM *elem; + + nElems=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_ROOMS); + + elem=AQH_Room_List_First(sto->roomList); + while(elem) { + GWEN_XMLNODE *nElem; + + nElem=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_ROOM); + AQH_Room_toXml(elem, nElem); + GWEN_XMLNode_AddChild(nElems, nElem); + elem=AQH_Room_List_Next(elem); + } + GWEN_XMLNode_AddChild(rootNode, nElems); +} + + + +void _writeDevicesToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nElems; + AQH_DEVICE *elem; + + nElems=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_DEVICES); + + elem=AQH_Device_List_First(sto->roomList); + while(elem) { + GWEN_XMLNODE *nElem; + + nElem=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_DEVICE); + AQH_Device_toXml(elem, nElem); + GWEN_XMLNode_AddChild(nElems, nElem); + elem=AQH_Device_List_Next(elem); + } + GWEN_XMLNode_AddChild(rootNode, nElems); +} + + + +void _writeTopicsToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nElems; + AQH_MQTT_TOPIC *elem; + + nElems=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_TOPICS); + + elem=AQH_MqttTopic_List_First(sto->mqttTopicList); + while(elem) { + GWEN_XMLNODE *nElem; + + nElem=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_TOPIC); + AQH_MqttTopic_toXml(elem, nElem); + GWEN_XMLNode_AddChild(nElems, nElem); + elem=AQH_MqttTopic_List_Next(elem); + } + GWEN_XMLNode_AddChild(rootNode, nElems); +} + + + +void _writeValuesToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) +{ + GWEN_XMLNODE *nElems; + AQH_VALUE *elem; + + nElems=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_VALUES); + + elem=AQH_Value_List_First(sto->valueList); + while(elem) { + GWEN_XMLNODE *nElem; + + nElem=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_VALUE); + AQH_Value_toXml(elem, nElem); + GWEN_XMLNode_AddChild(nElems, nElem); + elem=AQH_Value_List_Next(elem); + } + GWEN_XMLNode_AddChild(rootNode, nElems); +} + + + + + + + + + + diff --git a/aqhome/data/storage_writexml.h b/aqhome/data/storage_writexml.h new file mode 100644 index 0000000..246f865 --- /dev/null +++ b/aqhome/data/storage_writexml.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (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_STORAGE_WRITEXML_H +#define AQH_STORAGE_WRITEXML_H + + +#include "aqhome/data/storage.h" + + + +int AQH_Storage_WriteStateFile(const AQH_STORAGE *sto, const char *sFilename); + + + + +#endif + + diff --git a/aqhome/http/0BUILD b/aqhome/http/0BUILD index a12c16e..f8acc58 100644 --- a/aqhome/http/0BUILD +++ b/aqhome/http/0BUILD @@ -46,11 +46,13 @@ endpoint_http.h + urlhandler.h endpoint_http_p.h + urlhandler_p.h @@ -58,6 +60,7 @@ $(local/typefiles) endpoint_http.c + urlhandler.c diff --git a/aqhome/http/urlhandler.c b/aqhome/http/urlhandler.c new file mode 100644 index 0000000..0e226d1 --- /dev/null +++ b/aqhome/http/urlhandler.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * 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 +#include +#include + + + + +GWEN_INHERIT_FUNCTIONS(AQH_URLHANDLER) +GWEN_LIST_FUNCTIONS(AQH_URLHANDLER, AQH_UrlHandler) + + + + + +AQH_URLHANDLER *AQH_UrlHandler_new(void) +{ + AQH_URLHANDLER *uh; + + GWEN_NEW_OBJECT(AQH_URLHANDLER, uh); + GWEN_INHERIT_INIT(AQH_URLHANDLER, uh); + uh->urlPatternList=GWEN_StringList_new(); + + return uh; +} + + + +void AQH_UrlHandler_free(AQH_URLHANDLER *uh) +{ + if (uh) { + GWEN_INHERIT_FINI(AQH_URLHANDLER, uh); + GWEN_StringList_free(uh->urlPatternList); + GWEN_FREE_OBJECT(uh); + } +} + + + +void AQH_UrlHandler_AddUrlPattern(AQH_URLHANDLER *uh, const char *s) +{ + if (uh && s && *s) + GWEN_StringList_AppendString(uh->urlPatternList, s, 0, 1); +} + + + +int AQH_UrlHandler_UrlMatches(const AQH_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; +} + + + +GWEN_MSG *AQH_UrlHandler_HandleMessage(AQH_URLHANDLER *uh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived) +{ + if (uh && uh->handleFn) + return uh->handleFn(uh, ep, msgReceived); + return NULL; +} + + + + + + + + + diff --git a/aqhome/http/urlhandler.h b/aqhome/http/urlhandler.h new file mode 100644 index 0000000..4386ca4 --- /dev/null +++ b/aqhome/http/urlhandler.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * 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_URLHANDLER_H +#define AQHOME_URLHANDLER_H + +#include + +#include +#include +#include +#include + + + +typedef struct AQH_URLHANDLER AQH_URLHANDLER; +GWEN_INHERIT_FUNCTION_LIB_DEFS(AQH_URLHANDLER, AQHOME_API) +GWEN_LIST_FUNCTION_LIB_DEFS(AQH_URLHANDLER, AQH_UrlHandler, AQHOME_API) + + + +typedef GWEN_MSG*(*AQH_URLHANDLER_HANDLE_FN)(AQH_URLHANDLER *uh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived); + + +AQHOME_API AQH_URLHANDLER *AQH_UrlHandler_new(void); +AQHOME_API void AQH_UrlHandler_free(AQH_URLHANDLER *uh); + +AQHOME_API void AQH_UrlHandler_AddUrlPattern(AQH_URLHANDLER *uh, const char *s); +AQHOME_API int AQH_UrlHandler_UrlMatches(const AQH_URLHANDLER *uh, const char *s); + +AQHOME_API GWEN_MSG *AQH_UrlHandler_HandleMessage(AQH_URLHANDLER *uh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived); + + + + + + +#endif diff --git a/aqhome/http/urlhandler_p.h b/aqhome/http/urlhandler_p.h new file mode 100644 index 0000000..6177e7d --- /dev/null +++ b/aqhome/http/urlhandler_p.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOME_URLHANDLER_P_H +#define AQHOME_URLHANDLER_P_H + + +#include "aqhome/http/urlhandler.h" + + +struct AQH_URLHANDLER { + GWEN_INHERIT_ELEMENT(AQH_URLHANDLER); + GWEN_LIST_ELEMENT(AQH_URLHANDLER); + + GWEN_STRINGLIST *urlPatternList; + AQH_URLHANDLER_HANDLE_FN handleFn; +}; + + + + +#endif