327 lines
10 KiB
C
327 lines
10 KiB
C
/****************************************************************************
|
|
* This file is part of the project AqHome.
|
|
* AqHome (c) by 2025 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 "./mroot_p.h"
|
|
|
|
#include "aqhome-cgi/service/module.h"
|
|
#include "aqhome-cgi/modules/mdevices.h"
|
|
#include "aqhome-cgi/modules/common/madmin.h"
|
|
|
|
#include <gwenhywfar/debug.h>
|
|
#include <gwenhywfar/timestamp.h>
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* defs and enums
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* global vars
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* forward declarations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
|
|
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
|
|
static int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
|
|
static int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
|
|
static int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
|
|
static AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* code
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder)
|
|
{
|
|
AQH_MODULE *m;
|
|
|
|
m=AQH_Module_new();
|
|
AQH_ModService_Extend(m, sv, baseFolder);
|
|
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
|
|
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
|
|
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
|
|
sv=AQH_ModService_GetService(m);
|
|
if (strcasecmp(sModuleName, "devices")==0) {
|
|
AQH_MODULE *mSub;
|
|
|
|
mSub=AQH_Service_LoadModule(sv, sModuleName);
|
|
if (mSub) {
|
|
const char *s;
|
|
GWEN_BUFFER *nbuf;
|
|
|
|
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
s=AQH_ModService_GetBaseFolder(m);
|
|
GWEN_Buffer_AppendArgs(nbuf, "%s/devices", s?s:".");
|
|
|
|
AQH_ModDevices_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
|
|
AQH_Module_Tree2_AddChild(m, mSub);
|
|
GWEN_Buffer_free(nbuf);
|
|
return mSub;
|
|
}
|
|
}
|
|
else if (strcasecmp(sModuleName, "admin")==0) {
|
|
AQH_MODULE *mSub;
|
|
|
|
mSub=AQH_Service_LoadModule(sv, sModuleName);
|
|
if (mSub) {
|
|
const char *s;
|
|
GWEN_BUFFER *nbuf;
|
|
|
|
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
s=AQH_ModService_GetBaseFolder(m);
|
|
GWEN_Buffer_AppendArgs(nbuf, "%s/devices", s?s:".");
|
|
|
|
AQH_ModAdmin_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
|
|
AQH_Module_Tree2_AddChild(m, mSub);
|
|
GWEN_Buffer_free(nbuf);
|
|
return mSub;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
|
|
{
|
|
GWEN_BUFFER *dbuf;
|
|
|
|
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
AQH_ModService_AddHeader(m, "en", dbuf);
|
|
|
|
if (strcasecmp(sLastPathElem, "login")==0)
|
|
_handleRqLogin(m, rq, dbuf);
|
|
else if (strcasecmp(sLastPathElem, "signup")==0) {
|
|
AQCGI_Request_SetResponseCode(rq, 501);
|
|
AQCGI_Request_SetResponseText(rq, "Not Implemented");
|
|
}
|
|
else if (strcasecmp(sLastPathElem, "confirm")==0) {
|
|
AQCGI_Request_SetResponseCode(rq, 501);
|
|
AQCGI_Request_SetResponseText(rq, "Not Implemented");
|
|
}
|
|
else if (strcasecmp(sLastPathElem, "index.html")==0)
|
|
_handleRqIndex(m, rq, dbuf);
|
|
else {
|
|
AQCGI_Request_SetResponseCode(rq, 404);
|
|
AQCGI_Request_SetResponseText(rq, "Not Found");
|
|
}
|
|
|
|
AQH_ModService_AddFooter(m, "en", dbuf);
|
|
AQCGI_Request_SetBufferResponseBody(rq, dbuf);
|
|
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
|
|
|
|
return AQCGI_SendResponse(rq);
|
|
}
|
|
|
|
|
|
|
|
int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
|
|
{
|
|
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
|
|
return AQH_ModService_RespondWithFile(m, rq, "en", "index.html", dbuf);
|
|
else {
|
|
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
|
|
AQCGI_Request_SetResponseCode(rq, 405);
|
|
AQCGI_Request_SetResponseText(rq, "Method Not Allowed");
|
|
return GWEN_ERROR_GENERIC;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
|
|
{
|
|
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
|
|
AQH_ModService_RespondWithFile(m, rq, "en", "login.html", dbuf);
|
|
else if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_POST)
|
|
_handleRqLoginPost(m, rq, dbuf);
|
|
else {
|
|
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
|
|
AQCGI_Request_SetResponseCode(rq, 405);
|
|
AQCGI_Request_SetResponseText(rq, "Method Not Allowed");
|
|
return GWEN_ERROR_GENERIC;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
AQH_USER *user;
|
|
AQH_SESSION *session;
|
|
GWEN_BUFFER *tbuf;
|
|
GWEN_TIMESTAMP *ts;
|
|
int rv;
|
|
|
|
DBG_ERROR(NULL, "Handling request");
|
|
sv=AQH_ModService_GetService(m);
|
|
user=_getAndCheckUser(m, rq, dbuf);
|
|
if (user==NULL) {
|
|
DBG_INFO(NULL, "here");
|
|
AQCGI_Request_SetResponseCode(rq, 500);
|
|
AQCGI_Request_SetResponseText(rq, "Internal Error");
|
|
return GWEN_ERROR_GENERIC;
|
|
}
|
|
|
|
ts=GWEN_Timestamp_NowInLocalTime();
|
|
AQH_User_SetTimestampLastLogin(user, ts);
|
|
DBG_ERROR(NULL, "Saving user");
|
|
rv=AQH_Service_SaveUser(sv, user);
|
|
if (rv<0) {
|
|
DBG_ERROR(NULL, "Error saving user \"%s\"", AQH_User_GetAlias(user));
|
|
AQCGI_Request_SetResponseCode(rq, 500);
|
|
AQCGI_Request_SetResponseText(rq, "Internal Error");
|
|
AQH_User_free(user);
|
|
return rv;
|
|
}
|
|
|
|
/* generate session */
|
|
DBG_ERROR(NULL, "Generating session");
|
|
tbuf=GWEN_Buffer_new(0, 64, 0, 1);
|
|
AQCGI_GenerateSessionId(tbuf);
|
|
session=AQH_Session_new();
|
|
AQH_Session_SetTimestampCreation(session, ts);
|
|
AQH_Session_SetTimestampLastAccess(session, ts);
|
|
AQH_Session_SetUid(session, GWEN_Buffer_GetStart(tbuf));
|
|
GWEN_Buffer_free(tbuf);
|
|
AQH_Session_SetUserAlias(session, AQH_User_GetAlias(user));
|
|
rv=AQH_Service_AddSession(sv, session);
|
|
if (rv<0) {
|
|
DBG_ERROR(NULL, "Error adding session for user \"%s\" (%d)", AQH_User_GetAlias(user), rv);
|
|
AQCGI_Request_SetResponseCode(rq, 500);
|
|
AQCGI_Request_SetResponseText(rq, "Internal Error");
|
|
AQH_Session_free(session);
|
|
AQH_User_free(user);
|
|
return GWEN_ERROR_INTERNAL;
|
|
}
|
|
|
|
/* add Set-Cookie header */
|
|
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
GWEN_Buffer_AppendArgs(tbuf, "Set-Cookie: session=%s; max-age=3600", AQH_Session_GetUid(session));
|
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
|
|
GWEN_Buffer_free(tbuf);
|
|
|
|
/* finish */
|
|
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
|
|
AQCGI_Request_SetResponseCode(rq, 303);
|
|
AQCGI_Request_SetResponseText(rq, "See other");
|
|
AQH_Session_free(session);
|
|
AQH_User_free(user);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
|
|
{
|
|
GWEN_DB_NODE *dbPost;
|
|
|
|
dbPost=AQCGI_Request_GetDbPostBody(rq);
|
|
if (dbPost) {
|
|
AQH_SERVICE *sv;
|
|
const char *sUserName;
|
|
const char *sPasswd;
|
|
AQH_USER *user;
|
|
const char *hashedPaswd;
|
|
GWEN_BUFFER *buf;
|
|
|
|
sv=AQH_ModService_GetService(m);
|
|
sUserName=GWEN_DB_GetCharValue(dbPost, "userid", 0, NULL);
|
|
sPasswd=GWEN_DB_GetCharValue(dbPost, "password", 0, NULL);
|
|
if (!(sUserName && *sUserName && sPasswd && *sPasswd)) {
|
|
DBG_ERROR(NULL, "Either user name or password missing");
|
|
AQCGI_Request_SetResponseCode(rq, 400);
|
|
AQCGI_Request_SetResponseText(rq, "Bad Request");
|
|
return NULL;
|
|
}
|
|
|
|
DBG_ERROR(NULL, "Loading user \"%s\" (%p)", sUserName, sv);
|
|
user=AQH_Service_LoadUser(sv, sUserName);
|
|
if (user==NULL) {
|
|
DBG_ERROR(NULL, "User \"%s\" not found", sUserName);
|
|
AQCGI_Request_SetResponseCode(rq, 403);
|
|
AQCGI_Request_SetResponseText(rq, "Forbidden");
|
|
return NULL;
|
|
}
|
|
DBG_ERROR(NULL, "Loaded user \"%s\"", sUserName);
|
|
|
|
if (AQH_User_GetState(user)!=AQH_UserState_Active) {
|
|
DBG_ERROR(NULL, "User \"%s\" not active", sUserName);
|
|
AQCGI_Request_SetResponseCode(rq, 403);
|
|
AQCGI_Request_SetResponseText(rq, "Forbidden");
|
|
AQH_User_free(user);
|
|
return NULL;
|
|
}
|
|
|
|
hashedPaswd=AQH_User_GetHashedPassword(user);
|
|
if (!(hashedPaswd && *hashedPaswd)) {
|
|
DBG_ERROR(NULL, "User \"%s\" has no hashed password", sUserName);
|
|
AQCGI_Request_SetResponseCode(rq, 403);
|
|
AQCGI_Request_SetResponseText(rq, "Forbidden");
|
|
AQH_User_free(user);
|
|
return NULL;
|
|
}
|
|
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
AQCGI_HashMd256ToBuffer(sPasswd, buf);
|
|
DBG_ERROR(NULL, "Password: [%s]", sPasswd); // TODO: Remove!!!
|
|
DBG_ERROR(NULL, "Hashed password: [%s]", GWEN_Buffer_GetStart(buf));
|
|
if (strcasecmp(GWEN_Buffer_GetStart(buf), hashedPaswd)!=0) {
|
|
DBG_ERROR(NULL, "Bad password for user \"%s\"", sUserName);
|
|
AQCGI_Request_SetResponseCode(rq, 403);
|
|
AQCGI_Request_SetResponseText(rq, "Forbidden");
|
|
GWEN_Buffer_free(buf);
|
|
AQH_User_free(user);
|
|
return NULL;
|
|
}
|
|
GWEN_Buffer_free(buf);
|
|
|
|
DBG_ERROR(NULL, "User \"%s\" accepted", sUserName);
|
|
return user;
|
|
}
|
|
else {
|
|
DBG_ERROR(NULL, "No POST data");
|
|
AQCGI_SendResponseWithStatus(rq, 400, "Bad Request");
|
|
AQCGI_Request_SetResponseCode(rq, 400);
|
|
AQCGI_Request_SetResponseText(rq, "Bad Request");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|