/*

$Id: bufsock,v 1.13 2001/02/25 12:03:37 joseph Exp $

*/

#include <stdlib.h>
#include "memleak.h"
#include <string.h>

#include "sys/errno.h"

#include "bufsock.h"
#include "errlist.h"
#include "log.h"
#include "msgs.h"
#include "leakcheck.h"
#include "popstar.h"

#define bufsock_BUFSIZE 1500

struct bufsock_data {
  xsock s;   /* internet socket */
  char *buf; /* beginning of buffer */
  char *dp;  /* pointer to start of unused data */
  int count; /* amount of data in buffer */
};

/* Store handles to buffers rather than free'ing them, to reduce effects of strange memory leak */
static bufsock save_bufsock = NULL;
static char *  save_bufsockbuf = NULL;

#ifdef NOT_USED
xsock bufsock_get_socket(bufsock b)
{
  return b->s;
}
#endif

bufsock bufsock_create(xsock s)
{
  bufsock b;

  if (save_bufsock == NULL)
  {
    b = malloc(sizeof(struct bufsock_data));
    if (!b)
    {
      return 0;
    }
  }
  else
  {
    b = save_bufsock;
    save_bufsock = NULL;
  }

  b->s = s;

  if (save_bufsockbuf == NULL)
  {
    b->dp = b->buf = malloc(bufsock_BUFSIZE);
    if (!b->buf)
    {
      save_bufsock = b;
      return 0;
    }
  }
  else
  {
    b->dp = b->buf = save_bufsockbuf;
    save_bufsockbuf = NULL;
  }

  b->count = 0;
  return b;
}

void bufsock_free(bufsock b)
{
  if (save_bufsockbuf == NULL)
  {
    save_bufsockbuf = b->buf;
  }
  else
  {
    free(b->buf);
  }
  b->buf = 0;

  if (save_bufsock == NULL)
  {
    save_bufsock = b;
  }
  else
  {
    free(b);
  }
}


/* this checks through the data we have in memory, looking for a
 * line break. ("\r\n")
 * If found, removes data from buf, returns start of line. */

static char *bufsock_find_line(bufsock b, char *from, int *length)
{
  int left = b->count;
  char *lookfrom = from; /* ptr to start of section not yet searched */
  char *ptr;

  while ( 1 )
  {
    if ( left < 2 )
      return NULL; /* less than two characters to be checked */

    ptr = memchr( lookfrom, '\r', left );

    if ( !ptr )
      return NULL; /* no more \r's to check */

    if ( ptr[1] == '\n' )
    {
      int len;
      /* this is a line break */
      *ptr = 0; /* replace \r with NULL */
      ptr += 2; /* beginning of next line */
      len = ptr - from;
      b->count -= len;
      b->dp = ptr;
      if ( length )
        *length = len - 2;
//      xsyslogf(log_NAME, log_DebugInfo, ">>>> %s", from);
      return from;
    }

    ptr++;  /* character after the \r we found */
    left -= ptr - lookfrom;
    lookfrom = ptr;
  }
}



const char *bufsock_read_line(bufsock b, bool *wasline, int *length)
{
  leak_check();

  if (b->count)
  {
    const char *found = bufsock_find_line(b, b->dp, length);

    if (found)
    {
      if (wasline)
        *wasline = true;
      return found;
    }
    else
    {
      memmove(b->buf, b->dp, b->count);
      b->dp = b->buf;
    }
  }
  else
    b->dp = b->buf;

  while (b->count < bufsock_BUFSIZE)
  {
    int res = xsock_read(b->s, b->buf + b->count, bufsock_BUFSIZE - b->count);

    if (res < 0)
    {
      char str[32];
      sprintf(str, "%d", errno);
      xsyslog_logmessage(log_NAME,
        msgs_lookup2("ReadErr", errlist_str(errno), str),
        log_SocketError);
      leak_check();
      return 0;
    }
    else if (!res)
    {
      char str[32];
      sprintf(str, "%d", errno);
      xsyslog_logmessage(log_NAME,
        msgs_lookup2("EofErr", errlist_str(errno), str),
        log_ServerError);
      leak_check();
      return 0;
    }
    else
    {
      const char *found;

      b->count += res;
      found = bufsock_find_line(b, b->buf, length);

      if (found)
      {
        if (wasline)
          *wasline = true;
        leak_check();
        return found;
      }
    }
  }

  /* If we get here, buffer is full without finding a line */
  if ( length )
    *length = b->count;

  if (wasline)
    *wasline = false;
  b->count = 0;
  leak_check();
  return b->dp = b->buf;
}
