/**************************************************************************** * 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 #endif #include "./mservice_p.h" #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * defs and enums * ------------------------------------------------------------------------------------------------ */ #define AQH_MOD_SERVICE_HEADERFILE "header.html" #define AQH_MOD_SERVICE_FOOTERFILE "footer.html" #define GBAS GWEN_Buffer_AppendString #define GBAA GWEN_Buffer_AppendArgs /* ------------------------------------------------------------------------------------------------ * global vars * ------------------------------------------------------------------------------------------------ */ GWEN_INHERIT(AQCGI_MODULE, AQH_MOD_SERVICE) /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static void GWENHYWFAR_CB _freeData(void *bp, void *p); static void _calcUserModPerms(AQCGI_MODULE *m, const AQCGI_USER *user); static uint32_t _calcRolePerms(const AQCGI_MODULE *m, const AQCGI_MODULE_PERMS *modPerms); /* ------------------------------------------------------------------------------------------------ * code * ------------------------------------------------------------------------------------------------ */ void AQH_ModService_Extend(AQCGI_MODULE *m, AQCGI_SERVICE *sv, const char *baseFolder) { AQH_MOD_SERVICE *xm; GWEN_NEW_OBJECT(AQH_MOD_SERVICE, xm); GWEN_INHERIT_SETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m, xm, _freeData); xm->service=sv; xm->baseFolder=(baseFolder && *baseFolder)?strdup(baseFolder):NULL; } void _freeData(GWEN_UNUSED void *bp, void *p) { AQH_MOD_SERVICE *xm; xm=(AQH_MOD_SERVICE*) p; free(xm->baseFolder); GWEN_FREE_OBJECT(xm); } AQCGI_SERVICE *AQH_ModService_GetService(const AQCGI_MODULE *m) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { return xm->service; } } return NULL; } const char *AQH_ModService_GetBaseFolder(const AQCGI_MODULE *m) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { return xm->baseFolder; } } return NULL; } uint32_t AQH_ModService_GetUserPerms(const AQCGI_MODULE *m) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { return xm->userPerms; } } return 0; } void AQH_ModService_SetHandleRequestFn(AQCGI_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { xm->handleRequestFn=fn; } } } void AQH_ModService_SetLoadSubModuleFn(AQCGI_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { xm->loadSubModuleFn=fn; } } } void AQH_ModService_SetAddHeaderFn(AQCGI_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { xm->addHeaderFn=fn; } } } void AQH_ModService_SetAddFooterFn(AQCGI_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { xm->addFooterFn=fn; } } } void AQH_ModService_AddHeader(AQCGI_MODULE *m, const char *lang, GWEN_BUFFER *dbuf) { if (m && dbuf) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { if (xm->addHeaderFn) xm->addHeaderFn(m, lang, dbuf); else { AQCGI_MODULE *mParent; mParent=AQCGI_Module_Tree2_GetParent(m); if (mParent) AQH_ModService_AddHeader(mParent, lang, dbuf); AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_HEADERFILE, dbuf); } } } } void AQH_ModService_AddFooter(AQCGI_MODULE *m, const char *lang, GWEN_BUFFER *dbuf) { if (m && dbuf) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { if (xm->addFooterFn) xm->addFooterFn(m, lang, dbuf); else { AQCGI_MODULE *mParent; AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_FOOTERFILE, dbuf); mParent=AQCGI_Module_Tree2_GetParent(m); if (mParent) AQH_ModService_AddFooter(mParent, lang, dbuf); } } } } int AQH_ModService_RespondWithFile(AQCGI_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename, GWEN_BUFFER *dbuf) { int rv; rv=AQH_ModService_ReadStaticFile(m, lang, sFilename, dbuf); if (rv<0) { AQCGI_Request_SetResponseCode(rq, 500); AQCGI_Request_SetResponseText(rq, "Internal Error"); return GWEN_ERROR_INTERNAL; } AQCGI_Request_SetResponseCode(rq, 200); AQCGI_Request_SetResponseText(rq, "Ok"); return 0; } int AQH_ModService_RespondWithVarFile(AQCGI_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename, GWEN_DB_NODE *dbVars, GWEN_BUFFER *dbuf) { int rv; GWEN_BUFFER *fbuf; fbuf=GWEN_Buffer_new(0, 256, 0, 1); rv=AQH_ModService_ReadStaticFile(m, lang, sFilename, fbuf); if (rv<0) { AQCGI_Request_SetResponseCode(rq, 500); AQCGI_Request_SetResponseText(rq, "Internal Error"); GWEN_Buffer_free(fbuf); return GWEN_ERROR_INTERNAL; } else { GWEN_DB_ReplaceVars(dbVars, GWEN_Buffer_GetStart(fbuf), dbuf); AQCGI_Request_SetResponseCode(rq, 200); AQCGI_Request_SetResponseText(rq, "Ok"); GWEN_Buffer_free(fbuf); return 0; } } int AQH_ModService_HandleRequest(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, const char *sLastPathElem) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm && xm->handleRequestFn) return xm->handleRequestFn(m, rq, session, sLastPathElem); } return GWEN_ERROR_NOT_IMPLEMENTED; } AQCGI_MODULE *AQH_ModService_LoadSubModule(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, const char *sModuleName) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm && xm->loadSubModuleFn) { AQCGI_MODULE *mReturn; mReturn=xm->loadSubModuleFn(m, rq, session, sModuleName); if (mReturn) AQH_ModService_CalcSessionModPerms(mReturn, session); return mReturn; } } return NULL; } int AQH_ModService_ReadStaticFile(AQCGI_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf) { if (m && filename && dbuf) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { GWEN_BUFFER *fbuf; int rv; fbuf=GWEN_Buffer_new(0, 256, 0, 1); GWEN_Buffer_AppendString(fbuf, xm->baseFolder); GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S); GWEN_Buffer_AppendString(fbuf, (lang && *lang)?lang:"en"); GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S); GWEN_Buffer_AppendString(fbuf, filename); DBG_ERROR(NULL, "Reading file \"%s\"", GWEN_Buffer_GetStart(fbuf)); rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fbuf), dbuf); if (rv<0) { DBG_ERROR(NULL, "Read(%s): %d", GWEN_Buffer_GetStart(fbuf), rv); GWEN_Buffer_free(fbuf); return rv; } GWEN_Buffer_free(fbuf); return 0; } } DBG_ERROR(NULL, "Any arg is missing (or is not a AQH_MOD_SERVICE object)"); return GWEN_ERROR_INTERNAL; } AQCGI_SESSION *AQH_ModService_ReadSession(AQCGI_MODULE *m, AQCGI_REQUEST *rq) { AQCGI_SERVICE *sv; GWEN_DB_NODE *db; const char *s; sv=AQH_ModService_GetService(m); db=AQCGI_Request_GetDbRequestHeader(rq); s=GWEN_DB_GetCharValue(db, "cookies/session", 0, NULL); if (s && *s) { AQCGI_SESSION *session; session=AQCGI_Service_LoadSession(sv, s); if (session==NULL) { DBG_ERROR(NULL, "Session \"%s\" not found", s); return NULL; } else { const char *sUserName; GWEN_BUFFER *tbuf; sUserName=AQCGI_Session_GetUserAlias(session); if (sUserName && *sUserName) { AQCGI_USER *user; user=AQCGI_Service_LoadUser(sv, sUserName); if (user==NULL) { DBG_ERROR(NULL, "User \"%s\" not found", sUserName); AQCGI_Session_free(session); return NULL; } else { DBG_ERROR(NULL, "User is \"%s\"", sUserName); } AQCGI_Session_SetUser(session, user); } /* renew session cookie */ tbuf=GWEN_Buffer_new(0, 256, 0, 1); GWEN_Buffer_AppendArgs(tbuf, "Set-Cookie: session=%s; max-age=86400", AQCGI_Session_GetUid(session)); AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf)); DBG_ERROR(NULL, "Renew session cookie"); GWEN_Buffer_free(tbuf); return session; } } else { DBG_ERROR(NULL, "No session cookie"); } return NULL; } void AQH_ModService_CalcSessionModPerms(AQCGI_MODULE *m, const AQCGI_SESSION *session) { const AQCGI_USER *user; user=session?AQCGI_Session_GetUser(session):NULL; _calcUserModPerms(m, user); } void _calcUserModPerms(AQCGI_MODULE *m, const AQCGI_USER *user) { if (m) { AQH_MOD_SERVICE *xm; xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_SERVICE, m); if (xm) { uint32_t perms=0; if (user) { if (AQCGI_User_GetFlags(user) & AQCGI_USER_FLAGS_ADMIN) perms=0xffffffff; else { const char *sModName; const AQCGI_MODULE_PERMS_LIST *modPermsList; AQCGI_MODULE_PERMS *modPerms; sModName=AQCGI_Module_GetName(m); modPermsList=AQCGI_User_GetModulePermList(user); modPerms=(sModName && modPermsList)?AQCGI_ModulePerms_List_GetByModuleId(modPermsList, sModName):NULL; if (modPerms) perms=_calcRolePerms(m, modPerms); else perms=AQCGI_Module_GetGuestPerms(m); } } /* if (user) */ else perms=AQCGI_Module_GetGuestPerms(m); xm->userPerms=perms; } } /* if (m) */ } uint32_t _calcRolePerms(const AQCGI_MODULE *m, const AQCGI_MODULE_PERMS *modPerms) { uint32_t perms=0; const AQCGI_ROLE_LIST *roleList; roleList=AQCGI_Module_GetRoleList(m); if (roleList) { int roleArraySize; int i; uint32_t explAddPerms=0; uint32_t explDelPerms=0; roleArraySize=AQCGI_ModulePerms_GetRoleArrayArraySize(); for (i=0; i", GWEN_Buffer_GetStart(tbuf)); else GBAA(dbuf, "", s?s:""); GBAA(dbuf, "", GWEN_Buffer_GetStart(tbuf), s?s:""); GWEN_Buffer_Crop(tbuf, 0, pos); } permDef=AQCGI_PermDef_List_Next(permDef); } GWEN_Buffer_free(tbuf); } } uint32_t AQH_ModService_ReadPermsFromForm(GWEN_DB_NODE *dbPost, const AQCGI_PERMDEF_LIST *permDefList, const char *sPrefix) { uint32_t result=0; if (permDefList) { const AQCGI_PERMDEF *permDef; GWEN_BUFFER *tbuf; uint32_t pos; tbuf=GWEN_Buffer_new(0, 256, 0, 1); if (sPrefix && *sPrefix) GBAA(tbuf, "%s:", sPrefix); pos=GWEN_Buffer_GetPos(tbuf); permDef=AQCGI_PermDef_List_First(permDefList); while(permDef) { const char *id; id=AQCGI_PermDef_GetId(permDef); if (id && *id) { uint32_t mask; const char *s; GBAS(tbuf, id); mask=AQCGI_PermDef_GetMask(permDef); s=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(tbuf), 0, NULL); if (s && *s) result|=mask; GWEN_Buffer_Crop(tbuf, 0, pos); } permDef=AQCGI_PermDef_List_Next(permDef); } GWEN_Buffer_free(tbuf); } return result; } AQCGI_MODULE_LIST *AQH_ModService_LoadRawModules(AQCGI_MODULE *m) { AQCGI_SERVICE *sv; GWEN_STRINGLIST *slModuleNames; sv=AQH_ModService_GetService(m); slModuleNames=AQCGI_Service_ListModules(sv); if (slModuleNames) { AQCGI_MODULE_LIST *modList; GWEN_STRINGLISTENTRY *se; modList=AQCGI_Module_List_new(); se=GWEN_StringList_FirstEntry(slModuleNames); while(se) { const char *sModName; sModName=GWEN_StringListEntry_Data(se); if (sModName && *sModName) { AQCGI_MODULE *currentMod; currentMod=AQCGI_Service_LoadModule(sv, sModName); if (currentMod) AQCGI_Module_List_Add(currentMod, modList); } se=GWEN_StringListEntry_Next(se); } GWEN_StringList_free(slModuleNames); if (AQCGI_Module_List_GetCount(modList)) return modList; AQCGI_Module_List_free(modList); } return NULL; } AQCGI_USER_LIST *AQH_ModService_LoadRawUsers(AQCGI_MODULE *m) { AQCGI_SERVICE *sv; GWEN_STRINGLIST *slUserNames; sv=AQH_ModService_GetService(m); slUserNames=AQCGI_Service_ListUsers(sv); if (slUserNames) { AQCGI_USER_LIST *userList; GWEN_STRINGLISTENTRY *se; userList=AQCGI_User_List_new(); se=GWEN_StringList_FirstEntry(slUserNames); while(se) { const char *sModName; sModName=GWEN_StringListEntry_Data(se); if (sModName && *sModName) { AQCGI_USER *u; u=AQCGI_Service_LoadUser(sv, sModName); if (u) AQCGI_User_List_Add(u, userList); } se=GWEN_StringListEntry_Next(se); } GWEN_StringList_free(slUserNames); if (AQCGI_User_List_GetCount(userList)) return userList; AQCGI_User_List_free(userList); } return NULL; } void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf) { while (*src) { unsigned char x; x=(unsigned char)*src; if (!( (x>='A' && x<='Z') || (x>='a' && x<='z') || (x>='0' && x<='9') || NULL!=strchr("-_", x) )) { unsigned char c; GWEN_Buffer_AppendByte(buf, '%'); c=(((unsigned char)(*src))>>4)&0xf; if (c>9) c+=7; c+='0'; GWEN_Buffer_AppendByte(buf, c); c=((unsigned char)(*src))&0xf; if (c>9) c+=7; c+='0'; GWEN_Buffer_AppendByte(buf, c); } else GWEN_Buffer_AppendByte(buf, *src); src++; } /* while */ } void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf) { while (*src) { int charHandled=0; if (*src=='%') { if (strlen(src)>2) { unsigned char d1, d2; unsigned char c; if (isxdigit((int)src[1]) && isxdigit((int)src[2])) { /* skip '%' */ src++; /* read first digit */ d1=(unsigned char)(toupper(*src)); /* get second digit */ src++; 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 */ GWEN_Buffer_AppendByte(buf, (char)c); charHandled=1; } } } if (!charHandled) GWEN_Buffer_AppendByte(buf, *src); src++; } /* while */ } int AQH_ModService_FileIsCurrent(const char *sPath, int seconds) { struct stat sb; time_t t1; if (lstat(sPath, &sb)==-1) { DBG_ERROR(NULL, "Error on lstat(%s): %s (%d)", sPath, strerror(errno), errno); return 0; } t1=time(0); if ((t1-sb.st_mtime)<(time_t) seconds) { DBG_DEBUG(NULL, "File %s is current", sPath); return 1; } return 0; } int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf) { GWEN_BUFFER *ibuf; int rv; ibuf=GWEN_Buffer_new(0, 1024, 0, 1); /* read file */ rv=GWEN_SyncIo_Helper_ReadFile(sFilename, ibuf); if (rv<0) { DBG_ERROR(NULL, "Error reading \"%s\" (%d)", sFilename, rv); return rv; } else { GWEN_Buffer_Reset(dbuf); GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf)); if (sMimeType && *sMimeType) { GWEN_BUFFER *tbuf; tbuf=GWEN_Buffer_new(0, 256, 0, 1); GBAA(tbuf, "Content-type: %s", sMimeType); AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf)); GWEN_Buffer_free(tbuf); } AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE); } GWEN_Buffer_free(ibuf); return 0; } int AQH_ModService_AddRequestUrl(const AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf) { GWEN_DB_NODE *dbRequest; dbRequest=AQCGI_Request_GetDbRequestHeader(rq); if (dbRequest) { const char *s; GWEN_Buffer_AppendArgs(dbuf, "%s%s", GWEN_DB_GetCharValue(dbRequest, "SCRIPT_NAME", 0, ""), GWEN_DB_GetCharValue(dbRequest, "PATH_INFO", 0, "")); s=GWEN_DB_GetCharValue(dbRequest, "QUERY_STRING", 0, NULL); if (s && *s) GWEN_Buffer_AppendArgs(dbuf, "?%s", s); return 1; } return 0; } void AQH_ModService_Redirect(AQCGI_REQUEST *rq, const char *destLocation) { GWEN_BUFFER *dbuf; dbuf=GWEN_Buffer_new(0, 128, 0, 1); AQCGI_Request_SetResponseCode(rq, 303); AQCGI_Request_SetResponseText(rq, "See other"); GWEN_Buffer_AppendArgs(dbuf, "Location: %s", destLocation); AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(dbuf)); GWEN_Buffer_free(dbuf); } void AQH_ModService_RedirectWithReferer(AQCGI_REQUEST *rq, const char *destLocation) { GWEN_BUFFER *dbuf; GWEN_BUFFER *ubuf; dbuf=GWEN_Buffer_new(0, 128, 0, 1); GWEN_Buffer_AppendArgs(dbuf, "Location: %s", destLocation); ubuf=GWEN_Buffer_new(0, 128, 0, 1); if (AQH_ModService_AddRequestUrl(rq, ubuf)) { DBG_ERROR(NULL, "Adding header: [%s]", GWEN_Buffer_GetStart(ubuf)); GWEN_Buffer_AppendString(dbuf, "?src="); GWEN_Text_EscapeToBuffer(GWEN_Buffer_GetStart(ubuf), dbuf); } GWEN_Buffer_free(ubuf); AQCGI_Request_SetResponseCode(rq, 303); AQCGI_Request_SetResponseText(rq, "See other"); AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(dbuf)); GWEN_Buffer_free(dbuf); } void AQH_ModService_DenyRequest(AQCGI_MODULE *m, AQCGI_REQUEST *rq, const AQCGI_SESSION *session, GWEN_BUFFER *dbuf) { if (session && AQCGI_Session_GetUser(session)) { /* no permissions */ AQCGI_Request_SetResponseCode(rq, 403); AQCGI_Request_SetResponseText(rq, "Forbidden"); if (dbuf) GWEN_Buffer_AppendString(dbuf, "

Error

No permissions for this request.

"); } else { /* need login */ AQH_ModService_RedirectWithReferer(rq, AQCGI_Service_GetLoginUrl(AQH_ModService_GetService(m))); } }