aqhome: more work on http server.

This commit is contained in:
Martin Preuss
2023-08-08 23:49:28 +02:00
parent 3378908c93
commit aafecfa704
50 changed files with 2988 additions and 497 deletions

View File

@@ -38,7 +38,12 @@
aqhomestorage_p.h aqhomestorage_p.h
aqhomestorage.h aqhomestorage.h
init.h init.h
http.h init_http.h
fini.h
loop.h
loop_http.h
u_login.h
u_rooms.h
aqhomehttp.h aqhomehttp.h
aqhomehttp_p.h aqhomehttp_p.h
</headers> </headers>
@@ -48,7 +53,12 @@
aqhomestorage.c aqhomestorage.c
init.c init.c
http.c init_http.c
fini.c
loop.c
loop_http.c
u_login.c
u_rooms.c
main.c main.c
aqhomehttp.c aqhomehttp.c
</sources> </sources>

View File

@@ -32,10 +32,6 @@ GWEN_INHERIT(AQH_SERVICE, AQHOME_HTTP)
*/ */
static void GWENHYWFAR_CB _freeData(void *bp, void *p); 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_NEW_OBJECT(AQHOME_HTTP, xsv);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQHOME_HTTP, sv, xsv, _freeData); 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; xsv=(AQHOME_HTTP*) p;
AQH_Storage_free(xsv->storage);
xsv->storage=NULL; xsv->storage=NULL;
GWEN_MsgEndpoint_free(xsv->rootEndpoint); AQH_HttpContent_free(xsv->contentTree);
xsv->rootEndpoint=NULL; xsv->contentTree=NULL;
xsv->ipcdEndpoint=NULL;
xsv->mqttEndpoint=NULL;
xsv->httpdEndpoint=NULL;
GWEN_DB_Group_free(xsv->dbArgs);
xsv->dbArgs=NULL;
free(xsv->pidFile);
GWEN_FREE_OBJECT(xsv); 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) AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv)
{ {
if (sv) { if (sv) {
@@ -192,72 +82,43 @@ AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv)
void AqHomeHttpService_SetStorage(AQH_SERVICE *sv, AQH_STORAGE *sto)
int _handleUrl(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq)
{ {
if (sv) {
AQHOME_HTTP *xsv;
const GWEN_URL *url; xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
const char *sPath; if (xsv)
xsv->storage=sto;
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;
} }
} }
int _handleUrl_login(AQH_SERVICE *sv, AQH_HTTP_REQUEST *rq) AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv)
{ {
const char *protocol; if (sv) {
const char *cmd; AQHOME_HTTP *xsv;
AQH_SESSION *session;
GWEN_MSG *msgOut=NULL;
protocol=AQH_HttpRequest_GetProtocol(rq); xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
session=AQH_HttpRequest_GetSession(rq); if (xsv)
cmd=AQH_HttpRequest_GetCommand(rq); return xsv->contentTree;
if (cmd && strcasecmp(cmd, "GET")==0) {
GWEN_BUFFER *pageBuf;
const char *s;
int rv;
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;
} }
return NULL;
}
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); void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c)
AQH_HttpRequest_SetResponseMsg(rq, msgOut); {
return 0; if (sv) {
AQHOME_HTTP *xsv;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
if (xsv) {
AQH_HttpContent_free(xsv->contentTree);
xsv->contentTree=c;
} }
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);
}

View File

@@ -12,26 +12,42 @@
#include "aqhome/service/service.h" #include "aqhome/service/service.h"
#include "aqhome/data/storage.h" #include "aqhome/data/storage.h"
#include "aqhome/http/content.h"
#include <gwenhywfar/endpoint.h> #include <gwenhywfar/endpoint.h>
#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); 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); 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); AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv);
const char *AqHomeHttpService_GetPidFile(const AQH_SERVICE *sv); void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c);
void AqHomeHttpService_SetPidFile(AQH_SERVICE *sv, const char *s);

View File

@@ -15,17 +15,9 @@
typedef struct AQHOME_HTTP AQHOME_HTTP; typedef struct AQHOME_HTTP AQHOME_HTTP;
struct AQHOME_HTTP { struct AQHOME_HTTP {
GWEN_MSG_ENDPOINT *rootEndpoint; AQH_STORAGE *storage; /* do not release */
GWEN_MSG_ENDPOINT *ipcdEndpoint; AQH_HTTP_CONTENT *contentTree;
GWEN_MSG_ENDPOINT *mqttEndpoint;
GWEN_MSG_ENDPOINT *httpdEndpoint;
AQH_STORAGE *storage;
GWEN_DB_NODE *dbArgs;
char *pidFile;
}; };

View File

@@ -12,6 +12,7 @@
#include "aqhome/data/storage.h" #include "aqhome/data/storage.h"
#include "aqhome/service/session.h" #include "aqhome/service/session.h"
#include "aqhome/http/httpservice.h"
#include <gwenhywfar/endpoint.h> #include <gwenhywfar/endpoint.h>

View File

@@ -22,6 +22,11 @@
#define AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE 600 #define AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE 600
#define AQHOME_STORAGE_DEFAULT_MQTT_PORT 1883 #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_SITEHEADER "site-header.html"
#define AQHOME_STORAGE_SITEFOOTER "site-footer.html" #define AQHOME_STORAGE_SITEFOOTER "site-footer.html"
@@ -36,6 +41,7 @@ struct AQHOME_STORAGE {
GWEN_DB_NODE *dbArgs; GWEN_DB_NODE *dbArgs;
AQH_SERVICE *httpService;
AQH_STORAGE *storage; AQH_STORAGE *storage;
char *pidFile; char *pidFile;

View File

@@ -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 <config.h>
#endif
#include "./fini.h"
#include "./aqhomestorage_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <unistd.h>
/* ------------------------------------------------------------------------------------------------
* 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);
}

View File

@@ -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

View File

@@ -1,19 +1,3 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- copyright (c) 2023 by martin -->
<meta name="generator" content="FTE 1.1" />
<meta name="revised" content="martin,2023-07-23" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="author" content="martin" />
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>AqHome Storage Service</title>
</head>
<body>
</body> </body>

View File

@@ -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

View File

@@ -12,18 +12,24 @@
#include "./init.h" #include "./init.h"
#include "./init_http.h"
#include "./aqhomestorage_p.h" #include "./aqhomestorage_p.h"
#include "./aqhomehttp.h"
#include "aqhome/msg/endpoint_tty.h" #include "aqhome/msg/endpoint_tty.h"
#include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/mqtt/endpoint_mqttc.h" #include "aqhome/mqtt/endpoint_mqttc.h"
#include "aqhome/http/endpoint_http.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 <gwenhywfar/gwenhywfar.h> #include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h> #include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h> #include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h> #include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/endpoint_msgio.h> #include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/directory.h>
#ifdef HAVE_SYS_TYPES_H #ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> # include <sys/types.h>
@@ -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 int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
static void _setupIpc(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 _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 *_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 _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
static int _createPidFile(const char *pidFilename); 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); _setupIpc(aqh, dbArgs);
_setupMqtt(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; 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) int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
{ {
AQH_STORAGE *sto;
const char *stateFile; const char *stateFile;
stateFile=GWEN_DB_GetCharValue(dbArgs, "stateFile", 0, NULL); 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);
rv=AQH_Storage_Init(sto);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQH_Storage_free(sto);
return rv;
}
aqh->storage=sto; aqh->storage=sto;
}
else {
DBG_ERROR(NULL, "No state file given");
return GWEN_ERROR_GENERIC;
}
return 0; 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_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep,
GWEN_SOCKET *sk, GWEN_SOCKET *sk,
const GWEN_INETADDRESS *addr, 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) int _createPidFile(const char *pidFilename)
{ {
FILE *f; FILE *f;
@@ -360,7 +399,7 @@ int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
"httpAddress", /* name */ "httpAddress", /* name */
0, /* minnum */ 0, /* minnum */
1, /* maxnum */ 1, /* maxnum */
"ma", /* short option */ "ha", /* short option */
"httpaddress", /* long 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)"),
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 */ "httpPort", /* name */
0, /* minnum */ 0, /* minnum */
1, /* maxnum */ 1, /* maxnum */
"mp", /* short option */ "hp", /* short option */
"httpport", /* long option */ "httpport", /* long option */
I18S("Specify the port to listen on for HTTP connections"), I18S("Specify the port to listen on for HTTP connections"),
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_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */ GWEN_ArgsType_Char, /* type */

View File

@@ -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 <config.h>
#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 <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/directory.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* 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;
}

View File

@@ -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

View File

@@ -11,38 +11,14 @@
#endif #endif
#include "./http.h" #include "./loop.h"
#include "./loop_http.h"
#include "./aqhomestorage_p.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 <gwenhywfar/gwenhywfar.h> #include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/text.h> #include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h> #include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h> #include <gwenhywfar/endpoint.h>
#include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/syncio.h>
#include <gwenhywfar/url.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
@@ -51,9 +27,6 @@
* ------------------------------------------------------------------------------------------------ * ------------------------------------------------------------------------------------------------
*/ */
//#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
@@ -63,10 +36,21 @@
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* implementations * implementations
* ------------------------------------------------------------------------------------------------ * ------------------------------------------------------------------------------------------------
*/ */
void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs)
{
if (aqh) {
GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs);
AqHomeStorage_ReadAndHandleHttpMessages(aqh);
// AqHomeStorage_ReadAndHandleIpcMessages(aqh);
}
}

View File

@@ -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

View File

@@ -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 <config.h>
#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 <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/syncio.h>
#include <gwenhywfar/url.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* 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");
}
}

View File

@@ -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

View File

@@ -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 <config.h>
#endif
#include <aqhome/api.h>
#include <aqhome/aqhome.h>
#include "./aqhomestorage.h"
#include "./init.h"
#include "./fini.h"
#include "./loop.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/cgui.h>
#include <gwenhywfar/debug.h>
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#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) 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;
}
}

View File

@@ -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 <config.h>
#endif
#include "./u_login.h"
#include "aqhome/http/httpservice.h"
#include "aqhome/http/httpservice_http.h"
#include "aqhome/http/httpservice_conf.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/mdigest.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/timestamp.h>
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<sizeof(binbuf)) {
DBG_ERROR(NULL, "Error reading from /dev/urandom: %d", rv);
return NULL;
}
buf=GWEN_Buffer_new(0, 20, 0, 1);
rv=GWEN_Text_ToHexBuffer((const char*) binbuf, rv, buf, 0, 0, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error converting random bytes to hex (%d)", rv);
GWEN_Buffer_free(buf);
return NULL;
}
return buf;
}
GWEN_MSG *_createLoginResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *sessionId, 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");
_headerSetCookie(db, GWEN_DB_FLAGS_OVERWRITE_VARS, AQH_HTTP_REQUEST_SESSIONCOOKIE, sessionId);
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);
return msg;
}
void _headerSetCookie(GWEN_DB_NODE *db, uint32_t flags, const char *cookieName, const char *cookieValue)
{
if (cookieName && cookieValue) {
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 32, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "%s=%s", cookieName, cookieValue);
GWEN_DB_SetCharValue(db, flags, "Set-Cookie", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
}

View File

@@ -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_LOGIN_H
#define AQHOME_STORAGE_U_LOGIN_H
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/endpoint.h>
AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv);
#endif

View File

@@ -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 <config.h>
#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 <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/mdigest.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/timestamp.h>
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, "<h2>Rooms</h2>");
GWEN_Buffer_AppendString(pageBuf, "<table>");
GWEN_Buffer_AppendString(pageBuf, "<tr><th>Id</th><th>Name</th><th>Description</th></tr>");
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, "<tr><td>%lu</td><td>%s</td><td>%s</td></tr>", id, name?name:"", descr?descr:"");
r=AQH_Room_List_Next(r);
}
}
GWEN_Buffer_AppendString(pageBuf, "</table>");
GWEN_Buffer_AppendString(pageBuf, "<a href=\"/rooms/add\">Add Room</a><br>");
}
else {
GWEN_Buffer_AppendString(pageBuf, "<p>Internal error.</p>");
}
}
else {
GWEN_Buffer_AppendString(pageBuf, "<p>No permissions to see list of rooms.</p>");
}
}
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, "<p>No permissions to add a room.</p>");
}
}
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, "<p>Internal error.</p>");
}
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;
}
}

View File

@@ -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 <gwenhywfar/endpoint.h>
AQH_HTTP_URLHANDLER *AQH_RoomsHttpUrlHandler_new(AQH_SERVICE *sv);
#endif

15
aqhome-storage.sh Executable file
View File

@@ -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

View File

@@ -41,7 +41,7 @@
<default>0</default> <default>0</default>
<preset>0</preset> <preset>0</preset>
<access>public</access> <access>public</access>
<flags></flags> <flags>with_getbymember</flags>
</member> </member>
<member name="description" type="char_ptr" maxlen="256"> <member name="description" type="char_ptr" maxlen="256">

View File

@@ -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) void AQH_Storage_AddDevice(AQH_STORAGE *sto, AQH_DEVICE *dev)
{ {
if (sto && dev) { if (sto && dev) {

View File

@@ -46,6 +46,11 @@ extern "C" {
AQHOME_API AQH_STORAGE *AQH_Storage_new(void); AQHOME_API AQH_STORAGE *AQH_Storage_new(void);
AQHOME_API void AQH_Storage_free(AQH_STORAGE *sto); 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 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_LIST *AQH_Storage_GetDeviceList(const AQH_STORAGE *sto);
AQHOME_API AQH_DEVICE *AQH_Storage_GetDeviceById(const AQH_STORAGE *sto, uint64_t id); AQHOME_API AQH_DEVICE *AQH_Storage_GetDeviceById(const AQH_STORAGE *sto, uint64_t id);

View File

@@ -50,6 +50,9 @@
httpservice_conf.h httpservice_conf.h
httpservice_http.h httpservice_http.h
httprequest.h httprequest.h
urlhandler.h
content.h
content_files.h
</headers> </headers>
@@ -57,6 +60,9 @@
endpoint_http_p.h endpoint_http_p.h
httpservice_p.h httpservice_p.h
httprequest_p.h httprequest_p.h
urlhandler_p.h
content_p.h
content_files_p.h
</headers> </headers>
@@ -68,6 +74,9 @@
httpservice_conf.c httpservice_conf.c
httpservice_http.c httpservice_http.c
httprequest.c httprequest.c
urlhandler.c
content.c
content_files.c
</sources> </sources>

112
aqhome/http/content.c Normal file
View File

@@ -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 <config.h>
#endif
#include "aqhome/http/content_p.h"
#include <gwenhywfar/debug.h>
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;
}

63
aqhome/http/content.h Normal file
View File

@@ -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 <aqhome/api.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/tree2.h>
#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

140
aqhome/http/content_files.c Normal file
View File

@@ -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 <config.h>
#endif
#include "aqhome/http/content_files_p.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/syncio.h>
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;
}

View File

@@ -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 <aqhome/api.h>
#include <aqhome/http/content.h>
#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

View File

@@ -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

29
aqhome/http/content_p.h Normal file
View File

@@ -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

View File

@@ -70,6 +70,7 @@ void AQH_HttpEndpoint_Extend(GWEN_MSG_ENDPOINT *ep, uint32_t flags)
GWEN_NEW_OBJECT(AQH_ENDPOINT_HTTP, xep); GWEN_NEW_OBJECT(AQH_ENDPOINT_HTTP, xep);
GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep, xep, _freeData); GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep, xep, _freeData);
xep->readMode=AQH_EndpointHttpd_ReadMode_Command; xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
xep->flags=flags;
xep->addSocketsFn=GWEN_MsgEndpoint_SetAddSocketsFn(ep, _addSockets); xep->addSocketsFn=GWEN_MsgEndpoint_SetAddSocketsFn(ep, _addSockets);
xep->checkSocketsFn=GWEN_MsgEndpoint_SetCheckSocketsFn(ep, _checkSockets); xep->checkSocketsFn=GWEN_MsgEndpoint_SetCheckSocketsFn(ep, _checkSockets);
@@ -325,7 +326,9 @@ int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferP
_finishMessageAndStartNext(ep); _finishMessageAndStartNext(ep);
return rv; 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->readMode=AQH_EndpointHttpd_ReadMode_Headers;
xep->currentHeaderPos=GWEN_Buffer_GetPos(xep->currentReadBuffer); 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; AQH_ENDPOINT_HTTP *xep;
int lineLength; int lineLength;
/* line complete, TODO: parse status/command line */ /* line complete, parse status/command line */
rv=-rv; rv=-rv;
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep); 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; int contentLength;
/* Empty line received, TODO: parse header */ /* 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); copyOfHeader=strdup(GWEN_Buffer_GetStart(xep->currentReadBuffer)+xep->currentHeaderPos);
xep->dbCurrentReadHeader=GWEN_DB_Group_new("header"); xep->dbCurrentReadHeader=GWEN_DB_Group_new("header");
if (_parseHeader(copyOfHeader, xep->dbCurrentReadHeader)<0) { if (_parseHeader(copyOfHeader, xep->dbCurrentReadHeader)<0) {
@@ -498,10 +501,14 @@ void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep)
xep->dbCurrentReadCommand=NULL; xep->dbCurrentReadCommand=NULL;
xep->dbCurrentReadHeader=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; xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
else }
else {
DBG_INFO(AQH_LOGDOMAIN, "Active connection");
xep->readMode=AQH_EndpointHttpd_ReadMode_Status; xep->readMode=AQH_EndpointHttpd_ReadMode_Status;
}
} }
@@ -571,7 +578,7 @@ int _parseHeader(char *bufferPtr, GWEN_DB_NODE *db)
p++; p++;
pVarEnd=p; pVarEnd=p;
if (*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; return GWEN_ERROR_BAD_DATA;
} }
*pVarEnd=0; *pVarEnd=0;

View File

@@ -16,8 +16,7 @@
#include <gwenhywfar/debug.h> #include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h> #include <gwenhywfar/text.h>
#include <ctype.h>
#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid"
GWEN_TREE2_FUNCTIONS(AQH_HTTP_REQUEST, AQH_HttpRequest); 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 int _inspectMsgCommand(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg);
static void _inspectMsgHeader(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 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 GWEN_DB_NODE *_extractCookies(GWEN_DB_NODE *dbHeader);
static void _setCookieValue(GWEN_DB_NODE *dbCookies, static void _setCookieValue(GWEN_DB_NODE *dbCookies,
const char *nameStart, int nameLen, const char *nameStart, int nameLen,
const char *valueStart, int valueLen, const char *valueStart, int valueLen,
uint32_t dbFlags); uint32_t dbFlags);
static int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody); 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->moduleName);
free(rq->responseText); free(rq->responseText);
GWEN_Msg_free(rq->responseMsg); GWEN_Msg_free(rq->responseMsg);
GWEN_StringList_free(rq->urlPathMembers);
GWEN_Url_free(rq->url); GWEN_Url_free(rq->url);
GWEN_DB_Group_free(rq->dbPostBody); 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) GWEN_DB_NODE *AQH_HttpRequest_GetDbCommand(const AQH_HTTP_REQUEST *rq)
{ {
return rq?(rq->dbCommand):NULL; 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) const char *AQH_HttpRequest_GetSessionId(const AQH_HTTP_REQUEST *rq)
{ {
return rq?(rq->sessionId):NULL; 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 _inspectReceivedMessage(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
{ {
int rv; int rv;
@@ -384,7 +436,12 @@ int _inspectMsgCommand(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
return GWEN_ERROR_GENERIC; 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; return 0;
} }
@@ -399,9 +456,11 @@ void _inspectMsgHeader(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
const char *s; const char *s;
s=GWEN_DB_GetCharValue(rq->dbCookies, AQH_HTTP_REQUEST_SESSIONCOOKIE, 0, NULL); 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); AQH_HttpRequest_SetSessionId(rq, s);
} }
}
} }
@@ -419,7 +478,7 @@ int _inspectMsgBody(AQH_HTTP_REQUEST *rq, const GWEN_MSG *msg)
const char *s; const char *s;
/* check whether we need to parse POST body */ /* 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) { if (s && strcasecmp(s, "application/x-www-form-urlencoded")==0) {
rq->dbPostBody=GWEN_DB_Group_new("post"); rq->dbPostBody=GWEN_DB_Group_new("post");
rv=_parsePostBody((const char*) (rq->recvdBodyPtr), rq->recvdBodySize, rq->dbPostBody); 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) int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
{ {
while(contentLength>0) { while(contentLength>0) {
@@ -569,6 +600,7 @@ int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
int valueLen; int valueLen;
s++; s++;
contentLength--;
while((contentLength>0) && (*s<33)) { while((contentLength>0) && (*s<33)) {
s++; s++;
contentLength--; contentLength--;
@@ -584,22 +616,111 @@ int _parsePostBody(const char *s, int contentLength, GWEN_DB_NODE *dbBody)
char sNameBuf[32]; char sNameBuf[32];
char sValueBuf[64]; char sValueBuf[64];
if (GWEN_Text_UnescapeN(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))!=NULL && if (_unescapeUrlEncoded(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))>=0 &&
GWEN_Text_UnescapeN(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))!=NULL) _unescapeUrlEncoded(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))>=0)
GWEN_DB_SetCharValue(dbBody, 0, sNameBuf, sValueBuf); GWEN_DB_SetCharValue(dbBody, 0, sNameBuf, sValueBuf);
else { else {
DBG_ERROR(NULL, "Either name or value invalid in body, aborting"); DBG_ERROR(NULL, "Either name or value invalid in body, aborting");
return GWEN_ERROR_BAD_DATA; return GWEN_ERROR_BAD_DATA;
} }
} }
if ((contentLength>0) && (*s=='&')) if ((contentLength>0) && (*s=='&')) {
contentLength--;
s++; s++;
} }
}
} /* while */ } /* while */
return 0; return 0;
} }
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;
}

View File

@@ -19,6 +19,8 @@
#include <gwenhywfar/url.h> #include <gwenhywfar/url.h>
#define AQH_HTTP_REQUEST_SESSIONCOOKIE "sessionid"
typedef struct AQH_HTTP_REQUEST AQH_HTTP_REQUEST; typedef struct AQH_HTTP_REQUEST AQH_HTTP_REQUEST;
GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_HTTP_REQUEST, AQH_HttpRequest, AQHOME_API); 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 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 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_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_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_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); 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 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_SetResponseMsg(AQH_HTTP_REQUEST *rq, GWEN_MSG *msg);
AQHOME_API void AQH_HttpRequest_SetupUrlPathMembers(AQH_HTTP_REQUEST *rq);
#endif #endif

View File

@@ -25,8 +25,11 @@ struct AQH_HTTP_REQUEST {
const char *command; /* don't free */ const char *command; /* don't free */
const char *protocol; /* don't free */ const char *protocol; /* don't free */
const char *urlPath; /* don't free */
GWEN_URL *url; GWEN_URL *url;
GWEN_STRINGLIST *urlPathMembers;
GWEN_DB_NODE *dbCommand; /* don't free */ GWEN_DB_NODE *dbCommand; /* don't free */
GWEN_DB_NODE *dbHeader; /* don't free */ GWEN_DB_NODE *dbHeader; /* don't free */
GWEN_DB_NODE *dbCookies; /* don't free */ GWEN_DB_NODE *dbCookies; /* don't free */

View File

@@ -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; AQH_HTTP_SERVICE *xsv;
GWEN_NEW_OBJECT(AQH_HTTP_SERVICE, xsv); GWEN_NEW_OBJECT(AQH_HTTP_SERVICE, xsv);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv, xsv, _freeData); 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->moduleMutex=GWEN_Mutex_new();
xsv->userMutex=GWEN_Mutex_new(); xsv->userMutex=GWEN_Mutex_new();
xsv->sessionMutex=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; xsv=(AQH_HTTP_SERVICE*) p;
AQH_HttpUrlHandler_List_free(xsv->urlHandlerList);
GWEN_Mutex_free(xsv->moduleMutex); GWEN_Mutex_free(xsv->moduleMutex);
GWEN_Mutex_free(xsv->userMutex); GWEN_Mutex_free(xsv->userMutex);
GWEN_Mutex_free(xsv->sessionMutex); 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) const char *AQH_HttpService_GetConfigFolder(const AQH_SERVICE *sv)
{ {
if (sv) { if (sv) {

View File

@@ -14,6 +14,7 @@
#include "aqhome/service/service.h" #include "aqhome/service/service.h"
#include "aqhome/http/httprequest.h" #include "aqhome/http/httprequest.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/msg.h> #include <gwenhywfar/msg.h>
#include <gwenhywfar/db.h> #include <gwenhywfar/db.h>
@@ -23,10 +24,9 @@
typedef int (*AQH_HTTP_SERVICE_HANDLEREQUEST_FN)(AQH_SERVICE *sv, AQH_HTTP_REQUEST *request);
AQHOME_API AQH_SERVICE *AQH_HttpService_new(const char *configFolder, const char *sourceFolder);
AQHOME_API void AQH_HttpService_Extend(AQH_SERVICE *sv); 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); 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 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 #endif

View File

@@ -83,7 +83,7 @@ int AQH_HttpService_LoadConfig(AQH_SERVICE *sv)
db=GWEN_DB_Group_new("service"); db=GWEN_DB_Group_new("service");
nameBuf=_getConfigFilePath(sv, AQH_HTTP_SERVICE_FILE_CONFIG); 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) { if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf); 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_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid)
{ {
AQH_HTTP_SERVICE *xsv; 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"); db=GWEN_DB_Group_new("user");
nameBuf=_getModuleFilePath(sv, modName); 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) { if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf); 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"); db=GWEN_DB_Group_new("user");
nameBuf=_getUserFilePath(sv, userAlias); 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) { if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf); 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"); db=GWEN_DB_Group_new("user");
nameBuf=_getSessionFilePath(sv, sessionUid); 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) { if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
GWEN_Buffer_free(nameBuf); 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); nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendString(nameBuf, configFolder); GWEN_Buffer_AppendString(nameBuf, configFolder);
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS GWEN_DIR_SEPARATOR_S); GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
GWEN_Text_EscapeToBuffer(fileName, nameBuf); GWEN_Buffer_AppendString(nameBuf, fileName);
return nameBuf; return nameBuf;
} }
} }
@@ -958,7 +994,7 @@ int _writeDbFile(const char *fname, GWEN_DB_NODE *db)
GWEN_Buffer_AppendString(tmpFilenameBuf, fname); GWEN_Buffer_AppendString(tmpFilenameBuf, fname);
GWEN_Buffer_AppendString(tmpFilenameBuf, ".tmp"); GWEN_Buffer_AppendString(tmpFilenameBuf, ".tmp");
unlink(GWEN_Buffer_GetStart(tmpFilenameBuf)); 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) { if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error writing db file \"%s\": %d", GWEN_Buffer_GetStart(tmpFilenameBuf), rv); DBG_ERROR(AQH_LOGDOMAIN, "Error writing db file \"%s\": %d", GWEN_Buffer_GetStart(tmpFilenameBuf), rv);
GWEN_Buffer_free(tmpFilenameBuf); GWEN_Buffer_free(tmpFilenameBuf);

View File

@@ -19,27 +19,34 @@
#include <gwenhywfar/url.h> #include <gwenhywfar/url.h>
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); #define AQH_HTTP_SERVICE_ADMINUSER "admin"
AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias);
AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid);
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); AQHOME_API int AQH_HttpService_LoadConfig(AQH_SERVICE *sv);
int AQH_HttpService_SaveUser(const AQH_SERVICE *sv, const AQH_USER *user); AQHOME_API int AQH_HttpService_SaveConfig(const AQH_SERVICE *sv);
int AQH_HttpService_AddUser(AQH_SERVICE *sv, AQH_USER *user);
int AQH_HttpService_DelUser(AQH_SERVICE *sv, AQH_USER *user);
AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sessionUid); AQHOME_API AQH_MODULE *AQH_HttpService_GetModule(AQH_SERVICE *sv, const char *modName);
int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session); AQHOME_API AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias);
int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session); AQHOME_API int AQH_HttpService_WriteUser(const AQH_SERVICE *sv, const AQH_USER *user);
int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session);
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 #endif

View File

@@ -14,6 +14,7 @@
#include "aqhome/http/httpservice_http.h" #include "aqhome/http/httpservice_http.h"
#include "aqhome/http/httpservice_conf.h" #include "aqhome/http/httpservice_conf.h"
#include "aqhome/http/httpservice_p.h" #include "aqhome/http/httpservice_p.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/db.h> #include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h> #include <gwenhywfar/buffer.h>
@@ -37,7 +38,7 @@
static void _writeDefaultResponseHeaderToBuffer(AQH_SERVICE *sv, int contentLength, GWEN_BUFFER *buf); 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 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_SERVICE *xsv;
AQH_HTTP_REQUEST *rq; AQH_HTTP_REQUEST *rq;
const char *s; const char *s;
AQH_MODULE *m=NULL; const char *cmd;
const char *protocol;
AQH_SESSION *session=NULL; AQH_SESSION *session=NULL;
GWEN_MSG *msgResponse; GWEN_MSG *msgResponse;
int rv; int rv;
AQH_HTTP_URLHANDLER *uh;
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv); xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
if (xsv==NULL) { if (xsv==NULL) {
@@ -63,17 +66,16 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
return NULL; 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); rq=AQH_HttpRequest_new(endpoint, msgReceived);
if (rq==NULL) { if (rq==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Could not create valie request from incoming message"); 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); s=AQH_HttpRequest_GetModuleName(rq);
if (s && *s) { if (s && *s) {
m=AQH_HttpService_GetModule(sv, s); m=AQH_HttpService_GetModule(sv, s);
@@ -83,6 +85,7 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *
else else
AQH_HttpRequest_SetModule(rq, m); AQH_HttpRequest_SetModule(rq, m);
} }
#endif
s=AQH_HttpRequest_GetSessionId(rq); s=AQH_HttpRequest_GetSessionId(rq);
if (s && *s) { 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); DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" not found", s);
} }
else { else {
DBG_INFO(AQH_LOGDOMAIN, "Found session \"%s\"", s);
AQH_HttpRequest_SetSession(rq, session); 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:"<no 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) { if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling request"); DBG_INFO(AQH_LOGDOMAIN, "Error handling request");
AQH_HttpRequest_free(rq); 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); msgResponse=AQH_HttpRequest_TakeResponseMsg(rq);
AQH_HttpRequest_free(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) 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:""); 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_BUFFER *buf;
GWEN_MSG *msg; GWEN_MSG *msg;
buf=GWEN_Buffer_new(0, 256, 0, 1); buf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_HttpService_AddStatusLine(sv, code, text, protocol, buf); 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)); msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
GWEN_Buffer_free(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) GWEN_BUFFER *_getPageFilePath(const AQH_SERVICE *sv, const char *langFolder, const char *fileName)
{ {
const char *sourceFolder; 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, langFolder);
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S); GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
} }
GWEN_Text_EscapeToBuffer(fileName, nameBuf); GWEN_Text_EscapeToBufferTolerant(fileName, nameBuf);
return 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_HTTP_SERVICE *xsv;
AQH_SESSION *session; AQH_HTTP_URLHANDLER *uh;
AQH_HttpRequest_SetModulePerms(rq, 0); xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
m=AQH_HttpRequest_GetModule(rq); if (xsv==NULL) {
session=AQH_HttpRequest_GetSession(rq); DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
if (m) { return NULL;
if (session) { }
AQH_USER * user;
user=AQH_Session_GetUser(session); uh=AQH_HttpUrlHandler_List_First(xsv->urlHandlerList);
if (user) { while(uh) {
AQH_MODULE_PERMS *userPerms; if (AQH_HttpUrlHandler_UrlMatches(uh, path)) {
DBG_INFO(AQH_LOGDOMAIN, "Found matching url handler for \"%s\"", path);
userPerms=AQH_ModulePerms_List_GetByModuleId(AQH_User_GetModulePermList(user), AQH_Module_GetId(m)); return uh;
if (userPerms)
AQH_HttpRequest_SetModulePerms(rq, AQH_ModulePerms_GetPerms(userPerms));
} }
else { uh=AQH_HttpUrlHandler_List_Next(uh);
DBG_INFO(AQH_LOGDOMAIN, "Session but no user, SNH!");
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No session");
}
}
else {
DBG_INFO(AQH_LOGDOMAIN, "No module");
} }
DBG_INFO(AQH_LOGDOMAIN, "No matching url handler for \"%s\"", path);
return NULL;
} }

View File

@@ -11,6 +11,8 @@
#include "aqhome/service/service.h" #include "aqhome/service/service.h"
#include "aqhome/http/httprequest.h"
#include "aqhome/http/urlhandler.h"
#include <gwenhywfar/endpoint.h> #include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h> #include <gwenhywfar/msg.h>
@@ -22,6 +24,23 @@
AQHOME_API GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT *endpoint, const GWEN_MSG *msgReceived); 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_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); 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 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); AQHOME_API int AQH_HttpService_AddFile(AQH_SERVICE *sv, AQH_SESSION *session, const char *fname, GWEN_BUFFER *buf);

View File

@@ -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);

View File

@@ -33,7 +33,7 @@ struct AQH_HTTP_SERVICE {
GWEN_MUTEX *userMutex; GWEN_MUTEX *userMutex;
GWEN_MUTEX *sessionMutex; GWEN_MUTEX *sessionMutex;
AQH_HTTP_SERVICE_HANDLEREQUEST_FN handleRequestFn; AQH_HTTP_URLHANDLER_LIST *urlHandlerList;
}; };

239
aqhome/http/urlhandler.c Normal file
View File

@@ -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 <config.h>
#endif
#include "./urlhandler_p.h"
#include "aqhome/http/httpservice_http.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/misc.h>
#include <gwenhywfar/text.h>
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, "<p><font color=\"%s\">%s</font></p>", 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;
}

65
aqhome/http/urlhandler.h Normal file
View File

@@ -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 <aqhome/api.h>
#include <aqhome/http/httprequest.h>
#include <aqhome/http/content.h>
#include <aqhome/service/service.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/stringlist.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
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

View File

@@ -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

View File

@@ -59,7 +59,7 @@
<flags></flags> <flags></flags>
</member> </member>
<member name="roleList" type="AQH_ROLE_LIST"> <member name="roleList" type="AQH_ROLE_LIST" elementName="role" >
<default>NULL</default> <default>NULL</default>
<preset>NULL</preset> <preset>NULL</preset>
<access>public</access> <access>public</access>

View File

@@ -39,6 +39,25 @@
</defines> </defines>
<enums>
<enum id="AQH_USER_STATE" prefix="AQH_UserState_">
<item name="suspended">
<descr>User suspended</descr>
</item>
<item name="waitForConfirmation">
<descr>Waiting for email confirmation</descr>
</item>
<item name="waitForApproval">
<descr>Waiting for approval by admin</descr>
</item>
</enum>
</enums>
<members> <members>
@@ -56,6 +75,13 @@
<flags>with_flags</flags> <flags>with_flags</flags>
</member> </member>
<member name="state" type="int" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="name" type="char_ptr" maxlen="16"> <member name="name" type="char_ptr" maxlen="16">
<default>0</default> <default>0</default>
<preset>0</preset> <preset>0</preset>