/*
  aim_rxqueue.c

  This file contains the management routines for the receive
  (incoming packet) queue.  The actual packet handlers are in
  aim_rxhandlers.c.

 */

#include "aim.h"

/*
  This is a modified read() to make SURE we get the number
  of bytes we are told to, otherwise block.
 */
int Read(int fd, u_char *buf, int len)
{
  int i = 0;
  int j = 0;

  while ((i < len) && (!(i < 0)))
    {
      j = read(fd, &(buf[i]), len-i);
      if ( (j < 0) && (errno != EAGAIN))
	return -errno; /* fail */
      else
	i += j; /* success, continue */
    }
#if 0
  printf("\nRead Block: (%d/%04x)\n", len, len);
  printf("\t");
  for (j = 0; j < len; j++)
    {
      if (j % 8 == 0)
	printf("\n\t");
      if (buf[j] >= ' ' && buf[j] < 127)
	 printf("%c=%02x ",buf[j], buf[j]);
      else
	 printf("0x%02x ", buf[j]);
    }
  printf("\n\n");
#endif
  return i;
}

/*
  struct command_struct *
                         get_generic(
                                     struct connection_info struct *,
				     struct command_struct * 
				     )
  
  Grab as many command sequences as we can off the socket, and enqueue
  each command in the incoming event queue in a seperate struct.

*/
int aim_get_command(void)
{
  int i, readgood, j, isav, err;
  int s;
  fd_set fds;
  struct timeval tv;
  char generic[6]; 
  struct command_rx_struct *workingStruct = NULL;
  struct command_rx_struct *workingPtr = NULL;
  struct aim_conn_t *conn = NULL;
#if debug > 0
  printf("Reading generic/unknown response...");
#endif
  
  
  /* dont wait at all (ie, never call this unless something is there) */
  tv.tv_sec = 0; 
  tv.tv_usec = 0;
  conn = aim_select(&tv);

  if (conn==NULL)
    return 0;  /* nothing waiting */

  s = conn->fd;

  FD_ZERO(&fds);
  FD_SET(s, &fds);
  tv.tv_sec = 0;  /* wait, but only for 10us */
  tv.tv_usec = 10;
  
  generic[0] = 0x00;  

  readgood = 0;
  i = 0;
  j = 0;
  /* read first 6 bytes (the FLAP header only) off the socket */
  while ( (select(s+1, &fds, NULL, NULL, &tv) == 1) && (i < 6))
    {
      if ((err = Read(s, &(generic[i]), 1)) < 0)
	{
	  /* error is probably not recoverable...(must be a pessimistic day) */
	  aim_conn_close(conn);
	  return err;
   	}

      if (readgood == 0)
	{
	  if (generic[i] == 0x2a)
	  {
	    readgood = 1;
#if debug > 1
	    printf("%x ", generic[i]);
	    fflush(stdout);
#endif
	    i++;
	  }
	  else
	    {
#if debug > 1
	      printf("skipping 0x%d ", generic[i]);
	      fflush(stdout);
#endif
	      j++;
	    }
	}
      else
	{
#if debug > 1
	  printf("%x ", generic[i]);
#endif
	  i++;
	}
      FD_ZERO(&fds);
      FD_SET(s, &fds);
      tv.tv_sec= 2;
      tv.tv_usec= 2;
    }

  if (generic[0] != 0x2a)
    {
      /* this really shouldn't happen, since the main loop
	 select() should protect us from entering this function
	 without data waiting  */
      printf("Bad incoming data!");
      return -1;
    }

  isav = i;

  /* allocate a new struct */
  workingStruct = (struct command_rx_struct *) malloc(sizeof(struct command_rx_struct));
  workingStruct->lock = 1;  /* lock the struct */

  /* store type -- byte 2 */
  workingStruct->type = (char) generic[1];

  /* store seqnum -- bytes 3 and 4 */
  workingStruct->seqnum = ( (( (unsigned int) generic[2]) & 0xFF) << 8);
  workingStruct->seqnum += ( (unsigned int) generic[3]) & 0xFF;

  /* store commandlen -- bytes 5 and 6 */
  workingStruct->commandlen = ( (( (unsigned int) generic[4]) & 0xFF ) << 8);
  workingStruct->commandlen += ( (unsigned int) generic[5]) & 0xFF;

  /* malloc for data portion */
  workingStruct->data = (char *) malloc(workingStruct->commandlen);

  /* read the data portion of the packet */
  i = Read(s, workingStruct->data, workingStruct->commandlen);
  if (i < 0)
    {
      aim_conn_close(conn);
      return i;
    }

#if debug > 0
  printf(" done. (%db+%db read, %db skipped)\n", isav, i, j);
#endif

  workingStruct->conn = conn;

  workingStruct->next = NULL;  /* this will always be at the bottom */
  workingStruct->lock = 0; /* unlock */

  /* enqueue this packet */
  if (aim_queue_incoming == NULL)
    aim_queue_incoming = workingStruct;
  else
    {
      workingPtr = aim_queue_incoming;
      while (workingPtr->next != NULL)
	workingPtr = workingPtr->next;
      workingPtr->next = workingStruct;
    }

  return 0;  
}

/*
  purge_rxqueue()

  This is just what it sounds.  It purges the receive (rx) queue of
  all handled commands.  This is normally called from inside 
  aim_rxdispatch() after it's processed all the commands in the queue.
  
 */
struct command_rx_struct *aim_purge_rxqueue(struct command_rx_struct *queue)
{
  int i = 0;
  struct command_rx_struct *workingPtr = NULL;
  struct command_rx_struct *workingPtr2 = NULL;

  workingPtr = queue;
  if (queue == NULL)
    {
      return queue;
    }
  else if (queue->next == NULL)
    {
      if (queue->handled == 1)
	{
	  workingPtr2 = queue;
	  queue = NULL;
	  free(workingPtr2->data);
	  free(workingPtr2);
	}
      return queue;
    }
  else
    {
      for (i = 0; workingPtr != NULL; i++)
	{
	  if (workingPtr->next->handled == 1)
	    {
	      /* save struct */
	      workingPtr2 = workingPtr->next;
	      /* dequeue */
	      workingPtr->next = workingPtr2->next;
	      /* free */
	      free(workingPtr2->data);
	      free(workingPtr2);
	    }

	  workingPtr = workingPtr->next;  
	}
    }

  return queue;
}
