/****************************************************************************
 * 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/endpoint_http_p.h"


#include <gwenhywfar/endpoint_tcpc.h>
#include <gwenhywfar/debug.h>

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



#define AQH_ENDPOINT_HTTP_NAME        "http"
#define AQH_ENDPOINT_HTTP_BUFFERSIZE  1024

#define AQH_ENDPOINT_HTTP_BOOKMARK_HEADER 0
#define AQH_ENDPOINT_HTTP_BOOKMARK_BODY   1



GWEN_INHERIT(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP)


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

static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static void _addSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET *writeSet, GWEN_UNUSED GWEN_SOCKETSET *xSet);
static void _checkSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET *writeSet, GWEN_SOCKETSET *xSet);
static int _writeCurrentMessage(GWEN_MSG_ENDPOINT *ep);
static int _readCurrentMessage(GWEN_MSG_ENDPOINT *ep);
static int _distributeBufferContent(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static int _distributeBufferInStatusMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static int _distributeBufferInBodyMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static int _distributeBufferAsLine(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen);
static void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep);
static void _abortMessage(GWEN_MSG_ENDPOINT *ep);
static int _parseHeader(char *bufferPtr, GWEN_DB_NODE *db);
static int _parseCommand(const char *buffer, GWEN_DB_NODE *db);



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

void AQH_HttpEndpoint_Extend(GWEN_MSG_ENDPOINT *ep, uint32_t flags)
{
  if (ep) {
    AQH_ENDPOINT_HTTP *xep;

    GWEN_NEW_OBJECT(AQH_ENDPOINT_HTTP, xep);
    GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep, xep, _freeData);
    xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
    xep->flags=flags;

    xep->addSocketsFn=GWEN_MsgEndpoint_SetAddSocketsFn(ep, _addSockets);
    xep->checkSocketsFn=GWEN_MsgEndpoint_SetCheckSocketsFn(ep, _checkSockets);
  }
}



void _freeData(void *bp, void *p)
{
  AQH_ENDPOINT_HTTP *xep;

  xep=(AQH_ENDPOINT_HTTP*) p;

  GWEN_DB_Group_free(xep->dbCurrentReadHeader);
  GWEN_DB_Group_free(xep->dbCurrentReadCommand);
  GWEN_Buffer_free(xep->currentReadBuffer);

  GWEN_FREE_OBJECT(xep);
}



void _addSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET *writeSet, GWEN_UNUSED GWEN_SOCKETSET *xSet)
{
  if (ep) {
    AQH_ENDPOINT_HTTP *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
    if (xep) {
      if (GWEN_MsgEndpoint_GetState(ep)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
        GWEN_SOCKET *sk;

        sk=GWEN_MsgEndpoint_GetSocket(ep);
        if (sk) {
          DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Adding socket %d to read set",
                    GWEN_MsgEndpoint_GetName(ep),
                    GWEN_Socket_GetSocketInt(sk));
          GWEN_SocketSet_AddSocket(readSet, sk);
          if (GWEN_MsgEndpoint_HaveMessageToSend(ep)) {
            DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Adding socket %d to write set",
                      GWEN_MsgEndpoint_GetName(ep),
                      GWEN_Socket_GetSocketInt(sk));
            GWEN_SocketSet_AddSocket(writeSet, sk);
          }
        } /* if socket */
      }
      else if (xep->addSocketsFn) {
        DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep));
        xep->addSocketsFn(ep, readSet, writeSet, xSet);
      }
    } /* if (xep) */
  } /* if (ep) */
}



void _checkSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET *writeSet, GWEN_SOCKETSET *xSet)
{
  if (ep) {
    AQH_ENDPOINT_HTTP *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
    if (xep) {
      int rv;

      if (GWEN_MsgEndpoint_GetState(ep)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
        GWEN_SOCKET *sk;
      
        sk=GWEN_MsgEndpoint_GetSocket(ep);
        if (sk) {
          if (GWEN_SocketSet_HasSocket(writeSet, sk)) {
            DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Has socket in write set", GWEN_MsgEndpoint_GetName(ep));
            rv=_writeCurrentMessage(ep);
            if (rv<0 && rv!=GWEN_ERROR_TIMEOUT) {
              DBG_INFO(AQH_LOGDOMAIN,
                       "Endpoint %s: Error writing current message (%d), disconnecting",
                       GWEN_MsgEndpoint_GetName(ep),
                       rv);
              _abortMessage(ep);
              GWEN_MsgEndpoint_Disconnect(ep);
              return;
            }
          }

          if (GWEN_SocketSet_HasSocket(readSet, sk)) {
            DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Has socket in read set", GWEN_MsgEndpoint_GetName(ep));
            rv=_readCurrentMessage(ep);
            if (rv<0 && rv!=GWEN_ERROR_TIMEOUT) {
              DBG_INFO(AQH_LOGDOMAIN,
                       "Endpoint %s: Error reading current message (%d), disconnecting",
                       GWEN_MsgEndpoint_GetName(ep),
                       rv);
              _abortMessage(ep);
              GWEN_MsgEndpoint_Disconnect(ep);
              return;
            }
          }
        }
      } /* if connected */
      else if (xep->checkSocketsFn) {
        DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep));
        xep->checkSocketsFn(ep, readSet, writeSet, xSet);
      }
    }
  }
}



int _writeCurrentMessage(GWEN_MSG_ENDPOINT *ep)
{
  GWEN_MSG *msg;

  DBG_DEBUG(AQH_LOGDOMAIN, "Writing to endpoint %s", GWEN_MsgEndpoint_GetName(ep));
  msg=GWEN_MsgEndpoint_GetFirstSendMessage(ep);
  if (msg) {
    uint8_t pos;
    int remaining;
    int rv;

    pos=GWEN_Msg_GetCurrentPos(msg);
    remaining=GWEN_Msg_GetRemainingBytes(msg);
    if (remaining>0) {
      const uint8_t *buf;

      /* start new message */
      buf=GWEN_Msg_GetBuffer(msg)+pos;
      rv=GWEN_MsgEndpoint_WriteToSocket(ep, buf, remaining);
      if (rv<0) {
        if (rv==GWEN_ERROR_TIMEOUT)
          return rv;
        DBG_ERROR(AQH_LOGDOMAIN, "Error on write() (%d)", rv);
        return rv;
      }
      GWEN_Msg_IncCurrentPos(msg, rv);
      if (rv==remaining) {
        DBG_DEBUG(AQH_LOGDOMAIN, "Message completely sent");
        /* end current message */
        GWEN_Msg_List_Del(msg);
        GWEN_Msg_free(msg);
      }
    }
  }
  else {
    DBG_DEBUG(AQH_LOGDOMAIN, "Nothing to send");
  }
  return 0;
}




int _readCurrentMessage(GWEN_MSG_ENDPOINT *ep)
{
  int rv;
  uint8_t buffer[AQH_ENDPOINT_HTTP_BUFFERSIZE];

  DBG_DEBUG(AQH_LOGDOMAIN, "Reading from endpoint %s", GWEN_MsgEndpoint_GetName(ep));
  rv=GWEN_MsgEndpoint_ReadFromSocket(ep, buffer, sizeof(buffer));
  if (rv<0 && rv!=GWEN_ERROR_TIMEOUT) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }
  else if (rv==0) {
    DBG_INFO(AQH_LOGDOMAIN, "EOF met on read()");
    return GWEN_ERROR_IO;
  }

  rv=_distributeBufferContent(ep, buffer, rv);
  if (rv<0) {
    DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int _distributeBufferContent(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  if (ep) {
    AQH_ENDPOINT_HTTP *xep;

    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
    if (xep) {
      int rv;
      if (xep->currentReadBuffer==NULL)
	xep->currentReadBuffer=GWEN_Buffer_new(0, 256, 0, 1);

      while(bufferLen) {
	switch(xep->readMode) {
	case AQH_EndpointHttpd_ReadMode_Command:
	  rv=_distributeBufferInCommandMode(ep, bufferPtr, bufferLen);
	  break;
	case AQH_EndpointHttpd_ReadMode_Status:
	  rv=_distributeBufferInStatusMode(ep, bufferPtr, bufferLen);
	  break;
	case AQH_EndpointHttpd_ReadMode_Headers:
	  rv=_distributeBufferInHeaderMode(ep, bufferPtr, bufferLen);
	  break;
	case AQH_EndpointHttpd_ReadMode_Body:
	  rv=_distributeBufferInBodyMode(ep, bufferPtr, bufferLen);
	  break;
	default:
	  DBG_ERROR(AQH_LOGDOMAIN, "Invalid read mode %d", xep->readMode);
	  return GWEN_ERROR_GENERIC;
	}
	if (rv<0) {
          DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
	  return rv;
	}
	else if (rv==0) {
	  DBG_INFO(AQH_LOGDOMAIN, "No bytes used? SNH!");
	  return GWEN_ERROR_INTERNAL;
	}
	bufferPtr+=rv;
	bufferLen-=rv;
      } /* while */
      return 0;
    } /* if (xep) */
  } /* if (ep) */
  return GWEN_ERROR_GENERIC;
}



int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  int rv;

  rv=_distributeBufferAsLine(ep, bufferPtr, bufferLen);
  if (rv<0) {
    AQH_ENDPOINT_HTTP *xep;
    const char *s;

    /* line complete */
    DBG_DEBUG(AQH_LOGDOMAIN, "Command line complete");
    rv=-rv;
    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);

    xep->dbCurrentReadCommand=GWEN_DB_Group_new("command");

    if (_parseCommand(GWEN_Buffer_GetStart(xep->currentReadBuffer), xep->dbCurrentReadCommand)<0) {
      DBG_INFO(AQH_LOGDOMAIN, "Error parsing command line [%s]", GWEN_Buffer_GetStart(xep->currentReadBuffer));
      return GWEN_ERROR_BAD_DATA;
    }
    DBG_DEBUG(AQH_LOGDOMAIN, "Command line received: %s", GWEN_Buffer_GetStart(xep->currentReadBuffer));
    s=GWEN_DB_GetCharValue(xep->dbCurrentReadCommand, "protocol", 0, "HTTP/0.9");
    if (s && *s && strcasecmp(s, "HTTP/0.9")==0) {
      DBG_INFO(AQH_LOGDOMAIN, "HTTP 0.9, no header, message finished");
      _finishMessageAndStartNext(ep);
      return rv;
    }
    DBG_DEBUG(AQH_LOGDOMAIN,
             "Command line complete, advancing to header read mode (start: %d)",
             GWEN_Buffer_GetPos(xep->currentReadBuffer));
    xep->readMode=AQH_EndpointHttpd_ReadMode_Headers;
    xep->currentHeaderPos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
  }
  else {
    DBG_DEBUG(AQH_LOGDOMAIN, "Line not yet finished (%d)", rv);
  }

  return rv;
}



int _distributeBufferInStatusMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  int rv;

  rv=_distributeBufferAsLine(ep, bufferPtr, bufferLen);
  if (rv<0) {
    AQH_ENDPOINT_HTTP *xep;

    /* line complete, TODO: parse status/command line */
    rv=-rv;
    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
    DBG_DEBUG(AQH_LOGDOMAIN, "Line complete, advancing to header read mode");
    xep->readMode=AQH_EndpointHttpd_ReadMode_Headers;
    xep->currentBodyPos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
  }

  return rv;
}



int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  int rv;

  rv=_distributeBufferAsLine(ep, bufferPtr, bufferLen);
  if (rv<0) {
    AQH_ENDPOINT_HTTP *xep;
    int lineLength;

    /* line complete, parse status/command line */
    rv=-rv;
    xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);

    lineLength=GWEN_Buffer_GetPos(xep->currentReadBuffer)-xep->lastLineStartPos;
    xep->lastLineStartPos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
    if (lineLength==2) {
      char *copyOfHeader;
      int contentLength;

      /* Empty line received, TODO: parse header */
      DBG_DEBUG(AQH_LOGDOMAIN, "Empty header line received, end of header reached (header pos: %d).", xep->currentHeaderPos);
      copyOfHeader=strdup(GWEN_Buffer_GetStart(xep->currentReadBuffer)+xep->currentHeaderPos);
      xep->dbCurrentReadHeader=GWEN_DB_Group_new("header");
      if (_parseHeader(copyOfHeader, xep->dbCurrentReadHeader)<0) {
	DBG_INFO(AQH_LOGDOMAIN, "Error parsing HTTP header");
	free(copyOfHeader);
	return GWEN_ERROR_BAD_DATA;
      }
      free(copyOfHeader);
      contentLength=GWEN_DB_GetIntValue(xep->dbCurrentReadHeader, "Content-Length", 0, -1);
      if (contentLength==0 || contentLength==-1) {
        DBG_DEBUG(AQH_LOGDOMAIN, "Message has no body, done");
        _finishMessageAndStartNext(ep);
      }
      else {
	xep->currentBodyPos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
        xep->currentBodySize=contentLength;
        xep->remainingBodySize=contentLength;
	xep->readMode=AQH_EndpointHttpd_ReadMode_Body;
      }
    }
  }

  return rv;
}



int _distributeBufferInBodyMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  AQH_ENDPOINT_HTTP *xep;

  xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
  if (xep->remainingBodySize>0) {
    int len;

    len=bufferLen;
    if (len>xep->remainingBodySize)
      len=xep->remainingBodySize;
    if (len) {
      GWEN_Buffer_AppendBytes(xep->currentReadBuffer, (const char*) bufferPtr, len);
      xep->remainingBodySize-=len;
      if (xep->remainingBodySize==0) {
	DBG_DEBUG(AQH_LOGDOMAIN, "Body completely received");
	_finishMessageAndStartNext(ep);
      }
      return len;
    }
    else {
      DBG_ERROR(AQH_LOGDOMAIN, "No bytes left to read, SNH!");
      return GWEN_ERROR_INTERNAL;
    }
  }
  else {
    DBG_ERROR(AQH_LOGDOMAIN, "No body size, aborting");
    return GWEN_ERROR_GENERIC;
  }
}



/* return negative number of bytes handled when LF encountered (i.e. line done), positive value otherwise */
int _distributeBufferAsLine(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, int bufferLen)
{
  AQH_ENDPOINT_HTTP *xep;
  const char *s;
  int i;

  xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);

  s=(const char*) bufferPtr;
  i=0;
  while(i<bufferLen && *s!=10) {   /* scan until LF is encountered */
    s++;
    i++;
  }
  i++;
  GWEN_Buffer_AppendBytes(xep->currentReadBuffer, (const char*) bufferPtr, i);
  if (*s==10) {
    DBG_DEBUG(AQH_LOGDOMAIN, "Received full line (added %d bytes from %d)", i, bufferLen);
    i=-i;
  }
  return i;
}



void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep)
{
  AQH_ENDPOINT_HTTP *xep;
  GWEN_MSG *msg;
  GWEN_DB_NODE *dbParsedData;
  
  DBG_DEBUG(AQH_LOGDOMAIN, "Message completely received.");
  xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);

  msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(xep->currentReadBuffer), GWEN_Buffer_GetUsedBytes(xep->currentReadBuffer));
  if (xep->currentBodySize) {
      GWEN_Msg_SetParsedPayloadOffset(msg, xep->currentBodyPos);
      GWEN_Msg_SetParsedPayloadSize(msg, xep->currentBodySize);
  }
  dbParsedData=GWEN_DB_Group_new("parsedData");
  if (xep->dbCurrentReadCommand)
    GWEN_DB_AddGroup(dbParsedData, xep->dbCurrentReadCommand);
  if (xep->dbCurrentReadHeader)
    GWEN_DB_AddGroup(dbParsedData, xep->dbCurrentReadHeader);
  GWEN_Msg_SetDbParsedInfo(msg, dbParsedData);

  GWEN_MsgEndpoint_AddReceivedMessage(ep, msg);

  GWEN_Buffer_Reset(xep->currentReadBuffer);
  xep->currentHeaderPos=0;
  xep->currentBodyPos=0;
  xep->currentBodySize=0;
  xep->remainingBodySize=0;
  xep->lastLineStartPos=0;
  xep->dbCurrentReadCommand=NULL;
  xep->dbCurrentReadHeader=NULL;

  if (xep->flags & AQH_ENDPOINT_HTTP_FLAGS_PASSIVE) {
    DBG_DEBUG(AQH_LOGDOMAIN, "Passive connection");
    xep->readMode=AQH_EndpointHttpd_ReadMode_Command;
  }
  else {
    DBG_DEBUG(AQH_LOGDOMAIN, "Active connection");
    xep->readMode=AQH_EndpointHttpd_ReadMode_Status;
  }
}



void _abortMessage(GWEN_MSG_ENDPOINT *ep)
{
  AQH_ENDPOINT_HTTP *xep;
  
  DBG_DEBUG(AQH_LOGDOMAIN, "Aborting message (if any).");
  xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);

  if (xep->currentReadBuffer)
    GWEN_Buffer_Reset(xep->currentReadBuffer);
  xep->currentHeaderPos=0;
  xep->currentBodyPos=0;
  xep->currentBodySize=0;
  xep->remainingBodySize=0;
  xep->lastLineStartPos=0;
  if (xep->dbCurrentReadCommand)
    GWEN_DB_Group_free(xep->dbCurrentReadCommand);
  xep->dbCurrentReadCommand=NULL;
  if (xep->dbCurrentReadHeader)
    GWEN_DB_Group_free(xep->dbCurrentReadHeader);
  xep->dbCurrentReadHeader=NULL;

  xep->readMode=AQH_EndpointHttpd_ReadMode_Aborted;
}



int _parseHeader(char *bufferPtr, GWEN_DB_NODE *db)
{
  char *p;

  /* resolve line continuations */
  p=bufferPtr;
  while (*p) {
    p=strchr(p, 10);
    if (p) {
      if (p[1]==32 || p[1]==9)
        /* found a continuation */
        *p=32;
      p++;
    }
  }

  /* parse every line */
  p=bufferPtr;
  while (p && *p) {
    char *pNext;
    char *pVarBegin;
    char *pVarEnd;

    /* find end of line */
    pNext=strchr(p, 13);
    if (pNext) {
      *pNext=0;
      pNext++;
    }
    /* skip LF */
    if (*p==10)
      p++;
    while (*p && (*p==32 || *p==9))
      p++;
    if (*p) {
      pVarBegin=p;
      while (*p && *p!=':' && *p>32 && *p<127)
        p++;
      pVarEnd=p;
      if (*p!=':') {
        DBG_INFO(AQH_LOGDOMAIN, "No separator after variable name in received header (var: [%s])", pVarBegin);
	return GWEN_ERROR_BAD_DATA;
      }
      *pVarEnd=0;
      p++;

      while (*p && (*p==32 || *p==9))
        p++;
      if (*p) {
        DBG_DEBUG(AQH_LOGDOMAIN, "Setting header variable: [%s] = [%s]", pVarBegin, p);
        GWEN_DB_SetCharValue(db, GWEN_PATH_FLAGS_CREATE_VAR, pVarBegin, p);
      }
    }
    p=pNext;
  }

  return 0;
}



int _parseCommand(const char *buffer, GWEN_DB_NODE *db)
{
  char *tmp;
  char *p;
  char *s;

  tmp=strdup(buffer);
  /* get end of line (marked by CR/LF) */
  s=strchr(tmp, 13);
  if (s)
    *s=0;
  s=tmp;

  /* read command */
  p=strchr(s, ' ');
  if (!p) {
    DBG_ERROR(AQH_LOGDOMAIN, "Bad format of HTTP request (%s)", buffer);
    free(tmp);
    return GWEN_ERROR_BAD_DATA;
  }
  *p=0;
  p++;

  GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "command", s);
  s=p;

  /* read URL */
  p=strchr(s, ' ');
  if (!p) {
    DBG_ERROR(AQH_LOGDOMAIN, "Bad format of HTTP request (%s)", buffer);
    free(tmp);
    return GWEN_ERROR_BAD_DATA;
  }
  *p=0;
  p++;

  GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "url", s);
  s=p;

  if (*s==0) {
    /* no protocol information follows, so we assume HTTP/0.9 */
    DBG_ERROR(AQH_LOGDOMAIN, "Bad request (not in HTTP>=1.0)");
    free(tmp);
    return GWEN_ERROR_BAD_DATA;
  }
  else {
    GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "protocol", s);
  }

  free(tmp);
  return 0;
}





