/****************************************************************************
 * 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

//#define DISABLE_DEBUGLOG


#include "aqhome/http/httpservice_conf.h"
#include "aqhome/http/httpservice_p.h"

#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/directory.h>

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>



#define AQH_HTTP_SERVICE_FILE_CONFIG     "service.conf"
#define AQH_HTTP_SERVICE_DIR_MODS        "modules"
#define AQH_HTTP_SERVICE_DIR_USERS       "users"
#define AQH_HTTP_SERVICE_DIR_SESSIONS    "sessions"


#ifdef GWEN_INHERIT_REF
  GWEN_INHERIT_REF(AQH_SERVICE, AQH_HTTP_SERVICE)
#else
  extern uint32_t AQH_HTTP_SERVICE_ID;
#endif



/* ------------------------------------------------------------------------------------------------
 * forward declarations
 * ------------------------------------------------------------------------------------------------
 */

static void _calculateUserPerms(AQH_SERVICE *sv, AQH_USER *user);
static void _calculateModuleUserPerms(AQH_SERVICE *sv, AQH_MODULE_PERMS *p);
static AQH_MODULE *_ensureModule(AQH_SERVICE *sv, const char *modName);
static AQH_USER *_ensureUser(AQH_SERVICE *sv, const char *alias);
static AQH_SESSION *_ensureSession(AQH_SERVICE *sv, const char *sessionUid);
static GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileName);
static GWEN_BUFFER *_getModuleFilePath(const AQH_SERVICE *sv, const char *modName);
static GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias);
static GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid);
static GWEN_BUFFER *_getSessionFolder(const AQH_SERVICE *sv);
static int _checkUser(const AQH_USER *user, int ignoreMissingId);
static int _checkSession(const AQH_SESSION *session);
static int _checkModule(const AQH_MODULE *m, int ignoreMissingId);
static int _checkRoleList(const AQH_ROLE_LIST *roleList, int ignoreMissingId);
static int _writeDbFile(const char *fname, GWEN_DB_NODE *db);
static AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv);
static AQH_SESSION *_readSessionFromFile(const char *filename);



/* ------------------------------------------------------------------------------------------------
 * implementations
 * ------------------------------------------------------------------------------------------------
 */

int AQH_HttpService_LoadConfig(AQH_SERVICE *sv)
{
  AQH_HTTP_SERVICE *xsv;
  GWEN_BUFFER *nameBuf;
  int rv;
  GWEN_DB_NODE *db;

  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;
  }

  db=GWEN_DB_Group_new("service");

  nameBuf=_getConfigFilePath(sv, AQH_HTTP_SERVICE_FILE_CONFIG);
  rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(nameBuf);
    GWEN_DB_Group_free(db);
    return rv;
  }
  GWEN_Buffer_free(nameBuf);

  xsv->lastModuleId=GWEN_DB_GetIntValue(db, "lastModuleId", 0, 0);
  xsv->lastUserId=GWEN_DB_GetIntValue(db, "lastUserId", 0, 0);
  xsv->maxSessionAgeInSecs=GWEN_DB_GetIntValue(db, "maxSessionAgeInSecs", 0, AQH_HTTP_SERVICE_DEFAULT_MAXSESSIONAGE);

  GWEN_DB_Group_free(db);

  return 0;
}



int AQH_HttpService_SaveConfig(const AQH_SERVICE *sv)
{
  AQH_HTTP_SERVICE *xsv;
  int rv;
  GWEN_BUFFER *nameBuf;
  GWEN_DB_NODE *db;

  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;
  }

  db=GWEN_DB_Group_new("service");
  GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastModuleId", xsv->lastModuleId);
  GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastUserId", xsv->lastUserId);
  GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "maxSessionAgeInSecs", xsv->maxSessionAgeInSecs);

  nameBuf=_getConfigFilePath(sv, AQH_HTTP_SERVICE_FILE_CONFIG);
  rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
  GWEN_Buffer_free(nameBuf);
  GWEN_DB_Group_free(db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }
  return 0;
}



AQH_MODULE *AQH_HttpService_GetModule(AQH_SERVICE *sv, const char *modName)
{
  AQH_HTTP_SERVICE *xsv;
  AQH_MODULE *m;
  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 NULL;
  }

  rv=GWEN_Mutex_Lock(xsv->moduleMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on module mutex");
    return NULL;
  }

  m=_ensureModule(sv, modName);

  rv=GWEN_Mutex_Unlock(xsv->moduleMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on module mutex");
    return NULL;
  }

  if (m==NULL) {
    DBG_INFO(AQH_LOGDOMAIN, "here");
    return NULL;
  }

  return m;
}



AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias)
{
  AQH_HTTP_SERVICE *xsv;
  AQH_USER *user;
  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 NULL;
  }

  rv=GWEN_Mutex_Lock(xsv->userMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on user mutex");
    return NULL;
  }

  user=_ensureUser(sv, alias);

  rv=GWEN_Mutex_Unlock(xsv->userMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on user mutex");
    return NULL;
  }

  if (user==NULL) {
    DBG_INFO(AQH_LOGDOMAIN, "here");
    return NULL;
  }

  return user;
}



int AQH_HttpService_WriteUser(const AQH_SERVICE *sv, const AQH_USER *user)
{
  AQH_HTTP_SERVICE *xsv;
  int rv;

  xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv);
  if (xsv==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object");
    return GWEN_ERROR_INVALID;
  }

  rv=GWEN_Mutex_Lock(xsv->userMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on user mutex");
    return rv;
  }

  rv=AQH_HttpService_SaveUser(sv, user);
  if (rv<0) {
    GWEN_Mutex_Unlock(xsv->userMutex);
    DBG_ERROR(AQH_LOGDOMAIN, "Error saving user \"%s\" (%d)", AQH_User_GetAlias(user), rv);
    return rv;
  }

  rv=GWEN_Mutex_Unlock(xsv->userMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on user mutex");
    return rv;
  }

  return 0;
}




AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid)
{
  AQH_HTTP_SERVICE *xsv;
  AQH_SESSION *session;
  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 NULL;
  }

  rv=GWEN_Mutex_Lock(xsv->sessionMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on session mutex");
    return NULL;
  }

  session=_ensureSession(sv, sessionUid);

  rv=GWEN_Mutex_Unlock(xsv->sessionMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on session mutex");
    return NULL;
  }

  if (session==NULL) {
    DBG_INFO(AQH_LOGDOMAIN, "here");
    return NULL;
  }

  return session;
}



int AQH_HttpService_LockSessions(AQH_SERVICE *sv)
{
  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_GENERIC;
  }

  rv=GWEN_Mutex_Lock(xsv->sessionMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on session mutex");
    return rv;
  }

  return 0;
}



int AQH_HttpService_UnlockSessions(AQH_SERVICE *sv)
{
  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_GENERIC;
  }

  rv=GWEN_Mutex_Unlock(xsv->sessionMutex);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on session mutex");
    return rv;
  }

  return 0;
}



int AQH_HttpService_CleanupSessions(AQH_SERVICE *sv, int maxAgeInSecs)
{
  time_t tNow;
  AQH_SESSION_LIST *sessionList;
  int rv;

  rv=AQH_HttpService_LockSessions(sv);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "Error locking sessions (%d)", rv);
    return rv;
  }

  tNow=time(NULL);

  sessionList=AQH_Service_GetSessionList(sv);
  if (sessionList) {
    AQH_SESSION *session;

    session=AQH_Session_List_First(sessionList);
    while(session) {
      AQH_SESSION *next;
      const GWEN_TIMESTAMP *ts;

      next=AQH_Session_List_Next(session);

      ts=AQH_Session_GetTimestampLastAccess(session);
      if (ts==NULL)
        ts=AQH_Session_GetTimestampCreation(session);
      if (ts) {
        time_t diff;

        diff=tNow-GWEN_Timestamp_toTimeT(ts);
        if (((int)diff)>maxAgeInSecs) {
          DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" expired (%d secs)", AQH_Session_GetUid(session), (int) diff);
          rv=AQH_HttpService_DelSession(sv, session); /* frees session!! */
          if (rv<0) {
            DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
          }
        }
      }

      session=next;
    }
  }

  rv=AQH_HttpService_UnlockSessions(sv);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "Error unlocking sessions (%d)", rv);
    return rv;
  }
  return 0;
}


void AQH_HttpService_LoadAllSessions(AQH_SERVICE *sv)
{
  AQH_SESSION_LIST *sessionList;

  sessionList=_readAllSessionsIntoList(sv);
  if (sessionList) {
    AQH_SESSION *session;

    while( (session=AQH_Session_List_First(sessionList)) ) {
      const char *sessionUid;
      const char *userAlias;

      AQH_Session_List_Del(session);
      sessionUid=AQH_Session_GetUid(session);
      userAlias=AQH_Session_GetUserAlias(session);
      if (userAlias && *userAlias) {
        AQH_USER *user;

        user=AQH_HttpService_GetUser(sv, userAlias);
        if (user==NULL) {
          DBG_ERROR(AQH_LOGDOMAIN, "User \"%s\" for session \"%s\" not available", userAlias, sessionUid);
          AQH_Session_free(session);
        }
        else {
          DBG_INFO(AQH_LOGDOMAIN, "Adding session \"%s\" (user=%s)", sessionUid, userAlias);
          AQH_Session_SetUser(session, user);
          AQH_Service_AddSession(sv, session);
        }
      }
      else {
        DBG_ERROR(AQH_LOGDOMAIN, "Session has no user, not adding");
        AQH_Session_free(session);
      }
    } /* while */
    AQH_Session_List_free(sessionList);
  }
}



AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modName)
{
  GWEN_BUFFER *nameBuf;
  int rv;
  GWEN_DB_NODE *db;
  AQH_MODULE *m;

  db=GWEN_DB_Group_new("user");

  nameBuf=_getModuleFilePath(sv, modName);
  rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(nameBuf);
    GWEN_DB_Group_free(db);
    return NULL;
  }
  GWEN_Buffer_free(nameBuf);

  m=AQH_Module_fromDb(db);
  if (m==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error loading module \"%s\" from config group", modName);
    GWEN_DB_Group_free(db);
    return NULL;
  }
  GWEN_DB_Group_free(db);

  rv=_checkModule(m, 0);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "Invalid data for module \"%s\"", modName);
    AQH_Module_free(m);
    return NULL;
  }

  return m;
}



int AQH_HttpService_SaveModule(const AQH_SERVICE *sv, const AQH_MODULE *m)
{
  int rv;
  const char *modName;
  GWEN_BUFFER *nameBuf;
  GWEN_DB_NODE *db;

  rv=_checkModule(m, 0);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  db=GWEN_DB_Group_new("module");
  rv=AQH_Module_toDb(m, db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_DB_Group_free(db);
    return rv;
  }

  modName=AQH_Module_GetName(m);
  nameBuf=_getModuleFilePath(sv, modName);
  rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
  GWEN_Buffer_free(nameBuf);
  GWEN_DB_Group_free(db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int AQH_HttpService_AddModule(AQH_SERVICE *sv, AQH_MODULE *m)
{
  int rv;
  uint32_t id;
  AQH_ROLE_LIST *roleList;
  AQH_ROLE *r;

  rv=_checkModule(m, 1);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Not adding invalid module (%d)", rv);
    return rv;
  }

  /* assign module id */
  id=AQH_HttpService_GetNextModuleId(sv);
  AQH_Module_SetId(m, id);

  /* assign role ids */
  roleList=AQH_Module_GetRoleList(m);
  id=1;
  r=AQH_Role_List_First(roleList);
  while(r) {
    AQH_Role_SetId(r, id++);
    r=AQH_Role_List_Next(r);
  }

  rv=AQH_HttpService_SaveModule(sv, m);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error saving module module \"%s\" (%d)", AQH_Module_GetName(m), rv);
    return rv;
  }

  AQH_Service_AddModule(sv, m);
  return 0;
}



AQH_USER *AQH_HttpService_LoadUser(const AQH_SERVICE *sv, const char *userAlias)
{
  GWEN_BUFFER *nameBuf;
  int rv;
  GWEN_DB_NODE *db;
  AQH_USER *user;

  db=GWEN_DB_Group_new("user");

  nameBuf=_getUserFilePath(sv, userAlias);
  rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(nameBuf);
    GWEN_DB_Group_free(db);
    return NULL;
  }
  GWEN_Buffer_free(nameBuf);

  user=AQH_User_fromDb(db);
  if (user==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error loading user \"%s\" from config group", userAlias);
    GWEN_DB_Group_free(db);
    return NULL;
  }
  GWEN_DB_Group_free(db);

  rv=_checkUser(user, 0);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "Invalid data for user \"%s\"", userAlias);
    AQH_User_free(user);
    return NULL;
  }

  return user;
}



int AQH_HttpService_SaveUser(const AQH_SERVICE *sv, const AQH_USER *user)
{
  int rv;
  const char *sAlias;
  GWEN_BUFFER *nameBuf;
  GWEN_DB_NODE *db;

  rv=_checkUser(user, 0);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  db=GWEN_DB_Group_new("user");
  rv=AQH_User_toDb(user, db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_DB_Group_free(db);
    return rv;
  }

  sAlias=AQH_User_GetAlias(user);
  nameBuf=_getUserFilePath(sv, sAlias);
  rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
  GWEN_Buffer_free(nameBuf);
  GWEN_DB_Group_free(db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int AQH_HttpService_AddUser(AQH_SERVICE *sv, AQH_USER *user)
{
  int rv;
  uint32_t id;

  rv=_checkUser(user, 0);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* assign user id */
  id=AQH_HttpService_GetNextUserId(sv);
  AQH_User_SetId(user, id);

  rv=AQH_HttpService_SaveUser(sv, user);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  AQH_Service_AddUser(sv, user);

  return 0;
}



int AQH_HttpService_DelUser(AQH_SERVICE *sv, AQH_USER *user)
{
  const char *sAlias;
  GWEN_BUFFER *nameBuf;
  uint32_t userId;

  sAlias=AQH_User_GetAlias(user);
  userId=AQH_User_GetId(user);
  nameBuf=_getUserFilePath(sv, sAlias);
  unlink(GWEN_Buffer_GetStart(nameBuf));
  GWEN_Buffer_free(nameBuf);
  AQH_Service_DelUser(sv, userId);
  return 0;
}



AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sessionUid)
{
  GWEN_BUFFER *nameBuf;
  int rv;
  AQH_SESSION *session;

  nameBuf=_getSessionFilePath(sv, sessionUid);
  if (nameBuf==NULL) {
    DBG_INFO(AQH_LOGDOMAIN, "here");
    return NULL;
  }
  session=_readSessionFromFile(GWEN_Buffer_GetStart(nameBuf));
  if (session==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error loading session \"%s\" from config group", sessionUid);
    GWEN_Buffer_free(nameBuf);
    return NULL;
  }
  GWEN_Buffer_free(nameBuf);

  rv=_checkSession(session);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "Invalid data for session \"%s\"", sessionUid);
    AQH_Session_free(session);
    return NULL;
  }

  return session;
}



int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session)
{
  int rv;
  const char *sUid;
  GWEN_BUFFER *nameBuf;
  GWEN_DB_NODE *db;

  rv=_checkSession(session);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  db=GWEN_DB_Group_new("user");
  rv=AQH_Session_toDb(session, db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_DB_Group_free(db);
    return rv;
  }

  sUid=AQH_Session_GetUid(session);
  nameBuf=_getSessionFilePath(sv, sUid);
  rv=_writeDbFile(GWEN_Buffer_GetStart(nameBuf), db);
  GWEN_Buffer_free(nameBuf);
  GWEN_DB_Group_free(db);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
  int rv;

  rv=_checkSession(session);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  rv=AQH_HttpService_SaveSession(sv, session);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  AQH_Service_AddSession(sv, session);

  return 0;
}



int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
  const char *uid;
  GWEN_BUFFER *nameBuf;

  uid=AQH_Session_GetUid(session);
  nameBuf=_getSessionFilePath(sv, uid);
  unlink(GWEN_Buffer_GetStart(nameBuf));
  GWEN_Buffer_free(nameBuf);
  AQH_Service_DelSession(sv, uid);
  return 0;
}



GWEN_STRINGLIST *AQH_HttpService_GetFolderFileList(const char *folder, const char *mask, int senseCase)
{
  GWEN_STRINGLIST *sl;
  int rv;

  sl=GWEN_StringList_new();
  GWEN_StringList_SetSenseCase(sl, senseCase);

  rv=GWEN_Directory_GetFileEntries(folder, sl, mask);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_StringList_free(sl);
    return NULL;
  }
  if (GWEN_StringList_Count(sl)<1) {
    DBG_INFO(AQH_LOGDOMAIN, "Empty string list");
    GWEN_StringList_free(sl);
    return NULL;
  }

  return sl;
}



AQH_MODULE *_ensureModule(AQH_SERVICE *sv, const char *modName)
{
  AQH_MODULE *module;

  module=AQH_Service_GetModuleByName(sv, modName);
  if (module)
    return module;

  module=AQH_HttpService_LoadModule(sv, modName);
  if (module==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Could not load module \"%s\"", modName);
    return NULL;
  }
  AQH_Service_AddModule(sv, module);

  return module;
}



AQH_USER *_ensureUser(AQH_SERVICE *sv, const char *alias)
{
  AQH_USER *user;

  user=AQH_Service_GetUserByAlias(sv, alias);
  if (user)
    return user;

  user=AQH_HttpService_LoadUser(sv, alias);
  if (user==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Could not load user \"%s\"", alias);
    return NULL;
  }
  _calculateUserPerms(sv, user);

  AQH_Service_AddUser(sv, user);
  return user;
}



AQH_SESSION *_ensureSession(AQH_SERVICE *sv, const char *sessionUid)
{
  AQH_SESSION *session;

  session=AQH_Service_GetSessionByUid(sv, sessionUid);
  if (session)
    return session;

  session=AQH_HttpService_LoadSession(sv, sessionUid);
  if (session) {
    AQH_USER *user;

    user=AQH_HttpService_GetUser(sv, AQH_Session_GetUserAlias(session));
    if (user==NULL) {
      DBG_ERROR(AQH_LOGDOMAIN, "User \"%s\" for session \"%s\" not available", AQH_Session_GetUserAlias(session), sessionUid);
      AQH_Session_free(session);
      return NULL;
    }
    AQH_Session_SetUser(session, user);
    AQH_Service_AddSession(sv, session);
  }

  if (session==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Could not load session \"%s\"", sessionUid);
    return NULL;
  }

  return session;
}



GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileName)
{
  const char *configFolder;

  configFolder=AQH_HttpService_GetConfigFolder(sv);
  if (!(configFolder && *configFolder)) {
    DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
    return NULL;
  }
  else {
    GWEN_BUFFER *nameBuf;
  
    nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendString(nameBuf, configFolder);
    GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
    GWEN_Buffer_AppendString(nameBuf, fileName);
    return nameBuf;
  }
}




GWEN_BUFFER *_getModuleFilePath(const AQH_SERVICE *sv, const char *modName)
{
  const char *configFolder;

  configFolder=AQH_HttpService_GetConfigFolder(sv);
  if (!(configFolder && *configFolder)) {
    DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
    return NULL;
  }
  else {
    GWEN_BUFFER *nameBuf;
  
    nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendString(nameBuf, configFolder);
    GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_MODS GWEN_DIR_SEPARATOR_S);
    GWEN_Text_EscapeToBuffer(modName, nameBuf);
    GWEN_Buffer_AppendString(nameBuf, ".conf");
    return nameBuf;
  }
}




GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias)
{
  const char *configFolder;

  configFolder=AQH_HttpService_GetConfigFolder(sv);
  if (!(configFolder && *configFolder)) {
    DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
    return NULL;
  }
  else {
    GWEN_BUFFER *nameBuf;
  
    nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendString(nameBuf, configFolder);
    GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_USERS GWEN_DIR_SEPARATOR_S);
    GWEN_Text_EscapeToBuffer(userAlias, nameBuf);
    GWEN_Buffer_AppendString(nameBuf, ".conf");
    return nameBuf;
  }
}



GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid)
{
  GWEN_BUFFER *nameBuf;

  nameBuf=_getSessionFolder(sv);
  if (nameBuf) {
    GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
    GWEN_Text_EscapeToBuffer(sessionUid, nameBuf);
    GWEN_Buffer_AppendString(nameBuf, ".conf");
    return nameBuf;
  }
  return NULL;
}



int _checkUser(const AQH_USER *user, int ignoreMissingId)
{
  const char *s;

  if (!ignoreMissingId) {
    uint32_t id;

    id=AQH_User_GetId(user);
    if (id==0) {
      DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no id");
      return GWEN_ERROR_BAD_DATA;
    }
  }

  s=AQH_User_GetAlias(user);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no alias");
    return GWEN_ERROR_BAD_DATA;
  }
  s=AQH_User_GetHashedPassword(user);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no password");
    return GWEN_ERROR_BAD_DATA;
  }
  s=AQH_User_GetEmail(user);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid user: no email");
    return GWEN_ERROR_BAD_DATA;
  }

  return 0;
}



int _checkSession(const AQH_SESSION *session)
{
  const char *s;

  s=AQH_Session_GetUid(session);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no uid");
    return GWEN_ERROR_BAD_DATA;
  }

  s=AQH_Session_GetUserAlias(session);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no user alias");
    return GWEN_ERROR_BAD_DATA;
  }

  if (AQH_Session_GetTimestampCreation(session)==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid session: no creation time");
    return GWEN_ERROR_BAD_DATA;
  }

  return 0;
}



int _checkModule(const AQH_MODULE *m, int ignoreMissingId)
{
  const char *s;
  const AQH_ROLE_LIST *roleList;
  int rv;

  if (!ignoreMissingId) {
    if (AQH_Module_GetId(m)==0) {
      DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: missing id");
      return GWEN_ERROR_BAD_DATA;
    }
  }

  s=AQH_Module_GetName(m);
  if (!(s && *s)) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: missing name");
    return GWEN_ERROR_BAD_DATA;
  }

  roleList=AQH_Module_GetRoleList(m);
  if (roleList==NULL || AQH_Role_List_GetCount(roleList)<1) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid module: empty or missing role list");
    return GWEN_ERROR_BAD_DATA;
  }
  rv=_checkRoleList(roleList, ignoreMissingId);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Invalid module %s: invalid role definition", AQH_Module_GetName(m));
    return rv;
  }

  return 0;
}



int _checkRoleList(const AQH_ROLE_LIST *roleList, int ignoreMissingId)
{
  const AQH_ROLE *r;

  r=AQH_Role_List_First(roleList);
  while(r) {
    const char *s;

    if (!ignoreMissingId) {
      if (AQH_Role_GetId(r)==0) {
	DBG_ERROR(AQH_LOGDOMAIN, "Invalid role: missing id");
	return GWEN_ERROR_BAD_DATA;
      }
    }

    s=AQH_Role_GetName(r);
    if (!(s && *s)) {
      DBG_ERROR(AQH_LOGDOMAIN, "Invalid role: missing name");
      return GWEN_ERROR_BAD_DATA;
    }

    r=AQH_Role_List_Next(r);
  }

  return 0;
}



void _calculateUserPerms(AQH_SERVICE *sv, AQH_USER *user)
{
  AQH_MODULE_PERMS_LIST *permList;

  permList=AQH_User_GetModulePermList(user);
  if (permList) {
    AQH_MODULE_PERMS *p;

    p=AQH_ModulePerms_List_First(permList);
    while(p) {
      _calculateModuleUserPerms(sv, p);
      p=AQH_ModulePerms_List_Next(p);
    }
  }
  AQH_User_AddRuntimeFlags(user, AQH_USER_RTFLAGS_PERMSCALC);
}



void _calculateModuleUserPerms(AQH_SERVICE *sv, AQH_MODULE_PERMS *p)
{
  uint32_t perms=0;
  uint32_t addPerms=0;
  uint32_t delPerms=0;
  const AQH_MODULE *m;
  
  m=AQH_Service_GetModuleById(sv, AQH_ModulePerms_GetModuleId(p));
  if (m==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Module \"%d\" not found, ignoring", AQH_ModulePerms_GetModuleId(p));
  }
  else {
    const AQH_ROLE_LIST *roleList;
  
    roleList=AQH_Module_GetRoleList(m);
    if (roleList) {
      int i, j;
  
      j=AQH_ModulePerms_GetRoleArrayArraySize();
      for(i=0; i<j; i++) {
	uint8_t roleId;
  
	roleId=AQH_ModulePerms_GetRoleArrayAt(p, i);
	if (roleId>0) {
	  AQH_ROLE *r;
  
	  r=AQH_Role_List_GetById(roleList, roleId);
	  if (r==NULL) {
	    DBG_ERROR(AQH_LOGDOMAIN, "Role %d not found in module \"%s\", ignoring", roleId, AQH_Module_GetName(m));
	  }
	  else {
	    DBG_INFO(AQH_LOGDOMAIN, "Module \"%s\": adding perms for role \"%s\"", AQH_Module_GetName(m), AQH_Role_GetName(r));
	    perms|=AQH_Role_GetPerms(r);
	    addPerms|=AQH_Role_GetExplAddPerms(r);
	    delPerms|=AQH_Role_GetExplDelPerms(r);
	  }
	}
      }
    }
  }
  perms|=addPerms;
  perms&=~delPerms;
  AQH_ModulePerms_SetPerms(p, perms);
}



int _writeDbFile(const char *fname, GWEN_DB_NODE *db)
{
  GWEN_BUFFER *tmpFilenameBuf;
  int rv;

  tmpFilenameBuf=GWEN_Buffer_new(0, 256, 0, 1);
  GWEN_Buffer_AppendString(tmpFilenameBuf, fname);
  GWEN_Buffer_AppendString(tmpFilenameBuf, ".tmp");
  unlink(GWEN_Buffer_GetStart(tmpFilenameBuf));
  rv=GWEN_DB_WriteFile(db, GWEN_Buffer_GetStart(tmpFilenameBuf), GWEN_DB_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error writing db file \"%s\": %d", GWEN_Buffer_GetStart(tmpFilenameBuf), rv);
    GWEN_Buffer_free(tmpFilenameBuf);
    return rv;
  }
  if (rename(GWEN_Buffer_GetStart(tmpFilenameBuf), fname)<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error renaming tmp file to \"%s\": %d (%s)", fname, errno, strerror(errno));
    GWEN_Buffer_free(tmpFilenameBuf);
    return GWEN_ERROR_IO;
  }
  unlink(GWEN_Buffer_GetStart(tmpFilenameBuf));
  GWEN_Buffer_free(tmpFilenameBuf);
  return 0;
}



GWEN_BUFFER *_getSessionFolder(const AQH_SERVICE *sv)
{
  const char *configFolder;

  configFolder=AQH_HttpService_GetConfigFolder(sv);
  if (!(configFolder && *configFolder)) {
    DBG_ERROR(AQH_LOGDOMAIN, "No config folder given");
    return NULL;
  }
  else {
    GWEN_BUFFER *nameBuf;
  
    nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendString(nameBuf, configFolder);
    GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS);
    return nameBuf;
  }
}



AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv)
{
  GWEN_BUFFER *folderBuf;

  folderBuf=_getSessionFolder(sv);
  if (folderBuf) {
    GWEN_STRINGLIST *fileList;

    fileList=AQH_HttpService_GetFolderFileList(GWEN_Buffer_GetStart(folderBuf), "*.conf", 1);
    if (fileList) {
      AQH_SESSION_LIST *sessionList;
      uint32_t pos;
      GWEN_STRINGLISTENTRY *se;

      sessionList=AQH_Session_List_new();
      GWEN_Buffer_AppendString(folderBuf, GWEN_DIR_SEPARATOR_S);
      pos=GWEN_Buffer_GetPos(folderBuf);
      se=GWEN_StringList_FirstEntry(fileList);
      while(se) {
        const char *s;

        s=GWEN_StringListEntry_Data(se);
        if (s && *s) {
          AQH_SESSION *session;

          GWEN_Buffer_AppendString(folderBuf, s);
          session=_readSessionFromFile(GWEN_Buffer_GetStart(folderBuf));
          if (session==NULL) {
            DBG_INFO(AQH_LOGDOMAIN, "here");
          }
          else {
            AQH_Session_List_Add(session, sessionList);
          }
          GWEN_Buffer_Crop(folderBuf, 0, pos);
        }
        se=GWEN_StringListEntry_Next(se);
      }
      if (AQH_Session_List_GetCount(sessionList)<1) {
        DBG_INFO(NULL, "Empty session list");
        AQH_Session_List_free(sessionList);
        GWEN_StringList_free(fileList);
        GWEN_Buffer_free(folderBuf);
        return NULL;
      }
      GWEN_StringList_free(fileList);
      GWEN_Buffer_free(folderBuf);
      return sessionList;
    }
    GWEN_Buffer_free(folderBuf);
  }
  return NULL;
}



AQH_SESSION *_readSessionFromFile(const char *filename)
{
  int rv;
  GWEN_DB_NODE *db;
  AQH_SESSION *session;

  db=GWEN_DB_Group_new("session");

  rv=GWEN_DB_ReadFile(db, filename, GWEN_DB_FLAGS_DEFAULT);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    GWEN_DB_Group_free(db);
    return NULL;
  }

  session=AQH_Session_fromDb(db);
  if (session==NULL) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error loading session from file \"%s\"", filename);
    GWEN_DB_Group_free(db);
    return NULL;
  }
  GWEN_DB_Group_free(db);
  return session;
}







