/* gtd_rman.c - Request managment
 *
 * Copyright (C) 1994, 1995 Free Software Foundation
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can either send email to this
 * program's author (see below) or write to:
 * 
 *              The Free Software Foundation, Inc.
 *              675 Mass Ave.
 *              Cambridge, MA 02139, USA. 
 * 
 * Please send bug reports, etc. to zappo@gnu.ai.mit.edu.
 * 
 * Description:
 * 
 *  Manages lists of request types.  For convenience, all requests are
 * converted into the most advanced talk message type, and linked
 * together.  The version of the original, is, of course, stored.
 * 
 * History:
 * $Log: gtd_rman.c,v $
 * Revision 1.4  1995/03/25  04:24:10  zappo
 * Updated copyright
 *
 * Revision 1.3  1995/03/04  14:48:26  zappo
 * Added use of syslog to report errors when daemon not run on tty
 *
 * Revision 1.2  1995/03/03  02:51:43  zappo
 * Extra extend parameter was added to alloc object.
 *
 * Revision 1.1  1995/02/01  03:50:49  zappo
 * Initial revision
 *
 * zappo   9/17/94         Created
 *
 * Tokens: ::Header:: gtalkd.h
 */
#include "etalklib.h"
#include "gtalkd.h"

static struct RequestObject *Q_first = NULL;
static struct RequestObject *Q_last  = NULL;

/* give every control a unique id number when being stowed away
 * for types LEAVE_INVITE and ANNOUNCE
 */
static long my_ids = 0;


/*
 * Function: GCM_alloc
 *
 *   Allocates and initializes a new request object, and links it to
 * the local list of such objects.  It then returns the object.
 *
 * Returns:     struct RequestObject * - the new message
 * Parameters:  Ctxt   - Context
 *              dev    - Pointer to device
 *              source - Pointer to source request
 *              extend - Pointer to extensions
 * History:
 * zappo   9/17/94    Created
 * zappo   3/2/95     Added extend perameter
 */
struct RequestObject *GCM_alloc(Ctxt, dev, source, extend)
     struct DaemonContext *Ctxt;
     struct InputDevice   *dev;
     union ctl_msg        *source;
     char                 *extend;
{
  struct RequestObject *new;
  struct timezone       timezone;
  enum DaemonType       type;

  new = (struct RequestObject *)malloc(sizeof(struct RequestObject));

  if(!new)
    {
      if(verbose)
	fprintf(stderr, "GCM_malloc: malloc error in GCM_alloc!\n");
      else
	syslog(LOG_ERR, "GCM_malloc: malloc error in GCM_alloc!\n");
      return NULL;
    }

  /* stamp it for deletion later
   */
  gettimeofday(&new->stamp, &timezone);

  /* grab who sent it just in case there is some structureal error.
   */
  new->sender = UDP_byaddr(dev, &dev->lraddr);

  switch(Ctxt->type)
    {
    case OTALKD:
      new->request.vers = 0;
      new->request.type = source->otalk.type;
      new->request.answer = 0;
      new->request.id_num = source->otalk.id_num;
      new->request.addr = source->otalk.addr;
      new->request.ctl_addr = source->otalk.ctl_addr;
      new->retto = UDP_byaddr(dev, (struct sockaddr_in *)&
			      source->otalk.ctl_addr);
      strncpy(new->request.l_name, source->otalk.l_name, ONAME_SIZE);
      strncpy(new->request.r_name, source->otalk.r_name, ONAME_SIZE);
      strncpy(new->request.r_tty, source->otalk.r_tty, OTTY_SIZE);
      break;
    case NTALKD:
    case GTALKD:
      new->request = source->gtalk;
      new->retto = UDP_byaddr(dev, (struct sockaddr_in *)&
			      source->gtalk.ctl_addr);
      break;
    default:
      if(verbose)
	fprintf(stderr, "Error in request type %d...", type);
    }

  /* give these new id's which will be used when sending back the
   * response to these messages.
   */
  if((new->request.type == LEAVE_INVITE) ||
     (new->request.type == ANNOUNCE))
    new->request.id_num = ++my_ids;

  /* Now stuff info about the extension characters into the object. */
  if(extend[0])
    {
      new->extend = strdup(extend);
      if(!new->extend)
	{
	  if(verbose)
	    fprintf(stderr, "GCM_malloc: malloc error in GCM_alloc!\n");
	  else
	    syslog(LOG_ERR, "GCM_malloc: malloc error in GCM_alloc!\n");
	  /* Add log message */
	  exit(0);
	}
    }
  else
    new->extend = NULL;

  new->prev = Q_last;
  new->next = NULL;

  if(!Q_first)
    Q_first    = new;
  else
    Q_last->next = new;
  Q_last       = new;
    
  return new;
}

/*
 * Function: GCM_free_request
 *
 *   Frees up space and removes request from the local list.
 *
 * Returns:     Nothing
 * Parameters:  request -  request to be freed
 *
 * History:
 * zappo   9/17/94         Created
 */
void GCM_free_request(request)
     struct RequestObject *request;
{
  if(request == Q_first)
    {
      if(request == Q_last)
	{
	  Q_first = Q_last = NULL; /* nothing left */
	}
      else
	{
	  request->next->prev = NULL; /* just the first guy */
	  Q_first = request->next;
	}
    }
  else
    {
      if(request == Q_last)
	{
	  request->prev->next = NULL; /* the last guy */
	  Q_last = request->prev;
	}
      else
	{
	  request->next->prev = request->prev; /* somewhere in the middle */
	  request->prev->next = request->next;
	}
    }

  if(verbose)
    printf("Freeing request type %s for [%s] from [%s]...\n",
	   msg_types[request->request.type], 
	   request->request.r_name, request->request.l_name);

  if(request->extend) free(request->extend);
  free(request);
}


/*
 * Function: GCM_timeout_messages
 *
 *   This functions is called periodically to see if any functions
 * need to be deleted due to age.  When there are no messages left,
 * exit when the daemon flag is set to do so.
 *
 * Returns:     Nothing
 * Parameters:  Ctxt - Context
 *              dev  - Pointer to device
 * History:
 * zappo   9/18/94    Created
 */
void GCM_timeout_messages(Ctxt, dev)
     struct DaemonContext *Ctxt;
     struct InputDevice *dev;
{
  struct timeval        timev;     
  struct timezone       timezone;
  struct RequestObject *tmp = Q_first;
  int                   numq = 0, numt = 0;

  /* stamp it for deletion later
   */
  gettimeofday(&timev, &timezone);

  while(tmp)
    {
      if((timev.tv_sec - tmp->stamp.tv_sec) > MAX_LIFE)
	{
	  struct RequestObject *t;

	  t = tmp;
	  tmp = tmp->next;
	  if(verbose)
	    printf("timeout: ");

	  GCM_free_request(t);
	  numt++;
	}
      else
	{
	  tmp = tmp->next;
	  numq++;
	}
    }
  if(!Q_first)
    {
      /* When there is nothing left, exit completly unless the forever
       * flag is set
       */
      if(Ctxt->forever == FALSE)
	exit(0);
      if(verbose)
	printf("timeout: Nothing to do...\n");
    }
  else
    {
      /* Reset the checkout rate to make sure that things in the Q do
       * eventually get disposed of 
       */
      dev->timeout = Ctxt->checkrate;
    }
  if(verbose)
    {
      printf("Processed %d requests in Q with %d timeouts.\n", numq, numt);
    }
}


/*
 * Function: GCM_lookfor
 *
 *   Looks for a "LOOKUP" match in an "INVITE" message from RO.  The
 * r_name, and l_name must match in both directions.
 *
 * Returns:     struct RequestObject * - found object.
 * Parameters:  ro - Pointer to request object to look up.
 *
 * History:
 * zappo   9/17/94         Created
 */
struct RequestObject *GCM_lookfor(ro)
     struct RequestObject *ro;
{
  struct RequestObject *tmp = Q_first;

  if(verbose)
    printf("Looking for invite match to  %s for %s\n",
	   msg_types[ro->request.type], ro->request.r_name);

  while(tmp)
    {
      if(verbose)
	printf("Examining request type %s for %s\n",
	       msg_types[tmp->request.type], tmp->request.r_name);

      /* We have found what we want if the found object is an 
       * invitation, and if the r_name/l_name pairs match.
       */
      if((tmp->request.type == LEAVE_INVITE) &&
	 !strcmp(tmp->request.r_name, ro->request.l_name) &&
	 !strcmp(tmp->request.l_name, ro->request.r_name))
	return tmp;
      
      tmp = tmp->next;
    }

  if(verbose)
    printf("Returning pointer %p\n", tmp);
  return tmp;
}


/*
 * Function: GCM_looksame
 *
 *   Looks for an identical object just like RO in our list.  (if it
 * has the same ptr as RO, then ignore, however.)
 *
 * Returns:     struct RequestObject * - the found objct
 * Parameters:  ro - Pointer to request object
 *
 * History:
 * zappo   9/17/94    Created
 */
struct RequestObject *GCM_looksame(ro)
     struct RequestObject *ro;
{
  struct RequestObject *tmp = Q_first;

  if(verbose)
    printf("Looking for same match to %s for %s\n",
	   msg_types[ro->request.type], ro->request.r_name);

  while(tmp)
    {
      if(verbose)
	printf("Examining request type %s for %s\n",
	       msg_types[tmp->request.type], tmp->request.r_name);

      /* We have found what we want if the found object is an 
       * invitation, and if the r_name/l_name pairs match.
       */
      if((tmp != ro) && (tmp->request.type == ro->request.type) &&
	 !strcmp(tmp->request.r_tty, ro->request.r_tty) &&
	 !strcmp(tmp->request.r_name, ro->request.r_name) &&
	 !strcmp(tmp->request.l_name, ro->request.l_name))
	{
	  if(verbose)
	    printf("Found match.\n");
	  return tmp;
	}
      
      tmp = tmp->next;
    }
  if(verbose)
    printf("Returning pointer %p\n", tmp);
  return tmp;
}


/*
 * Function: GCM_lookid
 *
 *   locates a saved request by the id number (for deletes and such).
 *
 * Returns:     struct RequestObject * - the request
 * Parameters:  ro - Pointer to request object
 *
 * History:
 * zappo   9/17/94    Created
 */
struct RequestObject *GCM_lookid(ro)
     struct RequestObject *ro;
{
  struct RequestObject *tmp = Q_first;

  if(verbose)
    printf("Looking for id match to %s for %s id %ld\nChecking ...",
	   msg_types[ro->request.type], ro->request.r_name,
	   ro->request.id_num);

  while(tmp)
    {
      if(verbose)
	printf("[%s, id %ld]",
	       msg_types[tmp->request.type],
	       tmp->request.id_num);

      if((tmp != ro) && (tmp->request.id_num == ro->request.id_num))
	{
	  if(verbose)
	    printf("Found match.\n");
	  return tmp;
	}
      tmp = tmp->next;
    }
  if(verbose && tmp) printf("\n");

  return tmp;
}

/*
 * Function: GCM_findannounce
 *
 *   Finds a Request object created by an announcing process.  This is
 * used for looking up addresses of people who announced messages to
 * people.
 *
 * Returns:     struct RequestObject * - the found object
 * Parameters:  ro - Pointer to the request object to compare against
 *
 * History:
 * zappo   11/16/94   Created
 */
struct RequestObject *GCM_findannounce(ro)
     struct RequestObject *ro;
{
  struct RequestObject *tmp = Q_first;

  if(verbose)
    printf("Looking for announce match to %s for %s\n",
	   msg_types[ro->request.type], ro->request.r_name);

  while(tmp)
    {
      if(verbose)
	printf("Examining request type %s for %s\n",
	       msg_types[tmp->request.type], tmp->request.r_name);

      /* We have found what we want if the found object is an 
       * invitation, and if the r_name/l_name pairs match.
       */
      if((tmp->request.type == ANNOUNCE) &&
	 !strcmp(tmp->request.r_name, ro->request.l_name))
	return tmp;
      
      tmp = tmp->next;
    }

  if(verbose)
    printf("Returning pointer %p\n", tmp);
  return tmp;
}


/*
 * Function: GCM_print
 *
 *   Prints out the list of all requests currently stored.
 *
 * Returns:     Nothing
 * Parameters:  None
 *
 * History:
 * zappo   9/17/94         Created
 */
void GCM_print()
{
  printf("Not supported.\n");
}






