/* gtd_fusr.c - Find User functions
 *
 * 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:
 * 
 *   This file contains functions used to lookup users.  It should be
 * flexible enough to handle GNU finger style lookups, or standard
 * /etc/utmp (UTMP_FILE) lookups.
 * 
 * History:
 * zappo   9/26/94    Created
 * 
 * $Log: gtd_fusr.c,v $
 * Revision 1.10  1995/07/13  01:17:22  zappo
 * Ifdef'd out "free(pws)" which is returned as static space on some
 * systems.
 *
 * Revision 1.9  1995/05/09  23:00:46  zappo
 * Last fix assigned temp variable in the wrong place.  Fixed
 *
 * Revision 1.8  1995/05/03  23:09:27  zappo
 * Utmp name field is NOT null terminated!  Make sure we move the data
 * somewhere useful where it is null terminated.
 *
 * Revision 1.7  1995/04/08  20:03:13  zappo
 * Replaced several malloc/strcpy combinations with strdup.
 *
 * Revision 1.6  1995/04/04  01:11:36  zappo
 * Added references to UTMP_FILE instead of the direct string /etc/utmp
 *
 * Revision 1.5  1995/04/01  18:56:03  zappo
 * Modified to use a configure variable to access a /etc/utmp field
 * whose name is different on different systems.
 *
 * Revision 1.4  1995/03/25  04:22:29  zappo
 * Updated copyright
 *
 * Revision 1.3  1995/03/04  14:46:08  zappo
 * Added use of syslog to report error conditions
 *
 * Revision 1.2  1995/02/11  17:18:52  zappo
 * changed formatting of contents print statement
 *
 * Revision 1.1  1995/02/01  03:49:35  zappo
 * Initial revision
 *
 * Tokens: ::Header:: gtalkd.h
 */
#include "etalklib.h"
#include "gtalkd.h"

#include "sitecnfg.h"

#ifdef HAVE_UTMP_H
#include <utmp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

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

/* static definitions */
static char *FUSR_utmp_lookup();
static int FUSR_print_ou();


/*
 * Function: FUSR_alloc
 *
 *   Allocates and initializes a new user user object which will
 * define a new individual, and various important attributes.
 *
 * Returns:     struct UserObject  - the new user
 * Parameters:  name - Name of the user
 *              tty  - Tty requested for pestering
 * History:
 * zappo   11/20/94   Created
 */
struct UserObject *FUSR_alloc(name, tty)
     char *name;
     char *tty;
{
  char   fnbuff[180];
  struct passwd *pws;
  struct UserObject *new;

  new = Q_first;

  /* If we know this guy, revive that knowledge from simple data loop.
   */
  while(new && strcmp(new->name, name))
    new = new->next;
  if(new) 
    {
      if(verbose)
	{
	  printf("User allocator recycling old user object.\n");
	}
      /* Free any tty already in there */
      if(new->tty) 
	{
	  free(new->tty);
	  new->tty = NULL;
	}
      if(strlen(tty) > 1)
	{
	  new->tty = strdup(tty);
	  if(!new->tty)
	    {
	      if(verbose) printf("Malloc failure.\n");
	      else syslog(LOG_ERR, "FUSR_alloc: malloc failed.");
	      exit(1);
	    }
	}
      else
	new->tty = NULL;

      FUSR_print_ou(new);

      return new;
    }

  /* Hmm, this is new, try finding out more from the operating system.
   */
  pws = getpwnam(name);
  
  if(pws == NULL)
    {
      if(verbose)
	printf("FUSR_alloc: getpwname: Cannot find user %s\n", name);
      else
	syslog(LOG_ERR, "FUSR_alloc: getpwname: Cannot find user %s\n", name);
      return NULL;
    }
  
  /* Now that we know more about him, allocate something for im!
   *
   * NOTE: With the exception of forever mode, gtalkd will exit when
   * there are no usefull MESSAGES.  This means we can allocate stuff,
   * and it will all be freed up when the program exits.  Your typical
   * talk daemon is not active for more that one or two calls at most
   * anyways.
   */
  new = (struct UserObject *)malloc(sizeof(struct UserObject));

  if(new == NULL)
    {
      if(verbose)
	printf("FUSR_alloc: Malloc failure!\n");
      else 
	syslog(LOG_ERR, "FUSR_alloc: malloc failed.");
      exit(1);
    }

  new->name = strdup(name);
  if(!new->name)
    {
      if(verbose)
	printf("FUSR_alloc: Malloc failure!\n");
      else 
	syslog(LOG_ERR, "FUSR_alloc: malloc failed.");
      exit(1);
    }
  new->uid = pws->pw_uid;

  if(strlen(tty) > 1)
    {
      new->tty = strdup(tty);
      if(!new->tty)
	{
	  if(verbose)
	    printf("FUSR_alloc: Malloc failure!\n");
	  else 
	    syslog(LOG_ERR, "FUSR_alloc: malloc failed.");
	  exit(1);
	}
    }
  else
    new->tty = NULL;

  /* lets build some file names */
  strcpy(fnbuff, pws->pw_dir);
  strcat(fnbuff, "/");
  strcat(fnbuff, RINGER_RC);
  new->ringf = strdup(fnbuff);
  if(!new->ringf)
    {
      if(verbose)
	printf("FUSR_alloc: Malloc failure!\n");
      else 
	syslog(LOG_ERR, "FUSR_alloc: malloc failed.");
      exit(1);
    }

  /* Stuff it in our list.
   */
  if(!Q_last) 
    Q_first    = new;
  else
    Q_last->next = new;
  new->next    = NULL;
  new->prev    = Q_last;
  Q_last       = new;  

  /* This is not true on all systems.  Since the talk daemon exits
   * after a minimal delay, if this is a memory leak on some systems,
   * then it certainly  won't be a bad one.
   */
#ifdef FREE_PWS_STRUCTURE
  /* The password structure is allocated, so we must free it. */
  free(pws);
#endif
  FUSR_print_ou(new);

  return new;
}

/*
 * Function: FUSR_lookup
 *
 *   Lookup user defined in UO and return their TTY device.  If GNU
 * finger is available, use that, otherwise look up locally in
 * /etc/utmp, which has all the current logins listed.
 *
 * Returns:     char * - the tty
 * Parameters:  uo - Pointer to a user object
 *
 * History:
 * zappo   9/26/94    Created 
 */
char *FUSR_lookup(uo)
     struct UserObject *uo;
{
#ifdef GNUFINGER
  if(I have gnu finger)
    return FUSER_gnufinger_lookup(uo)
  else
#endif
    return FUSR_utmp_lookup(uo);
}


/*
 * Function: FUSR_utmp_lookup
 *
 *   Locally defined function which looks up a user in /etc/utmp based
 * on the user object UO.  Returns a string such as "/dev/ttyp1" to
 * indicate what to write to.
 *
 * Returns:     static char * - the device to write to.
 * Parameters:  uo - Pointer to user object
 *
 * History:
 * zappo   9/26/94    Created
 */
static char *FUSR_utmp_lookup(uo)
     struct UserObject *uo;
{
  static struct utmp u;
  static char        device[30];
  char  namebuff[20];		/* temp name buffer */
  FILE *ustream;
  int   status;

  ustream = fopen(UTMP_FILE, "r");

  if(!ustream)
    {
      if(verbose)
	printf("Cannot open %s.  Cannot announce.\n", UTMP_FILE);
      else
	syslog(LOG_ERR, "Cannot open %s.  Cannot announce.", UTMP_FILE);
      exit(1);
    }

  /* find a match! */
  if(verbose)
    printf("Scanning %s for pattern: [%s on %s] ...\n", UTMP_FILE,
	   uo->name, uo->tty?uo->tty:"NoTTY");

  if(syslogdebug)
    syslog(LOG_DEBUG, "Scanning %s for pattern: [%s on %s] ...\n", UTMP_FILE,
	   uo->name, uo->tty?uo->tty:"NoTTY");
  do
    {
      status = fread(&u, sizeof(u), 1, ustream);

      strncpy(namebuff, u.UT_USER_OR_NAME, 8);
      namebuff[8] = 0;

      if(verbose)
	{
	  /*
	   * Use configure macro to fill in the name of the field
	   * that holds the username.  This can be ut_user on linux,
	   * and ut_name on scumos.
	   */
	  printf("(%d)[%s on %s] ", status,
		 namebuff, u.ut_line);
	}
    }
  while((status > 0) &&
	!(!strcmp(uo->name, namebuff) &&
	  (!uo->tty || !strcmp(uo->tty, u.ut_line))));
  
  if(verbose) printf("\n");

  strcpy(device, "/dev/");
  strcat(device, u.ut_line);

  fclose(ustream);

  /* If we have a match, go for it! */
  if(status > 0)
    return device;
  else
    return NULL;
}


/*
 * Function: FUSR_print_ou
 *
 *   Locally defined function which prints out user information found
 * in a user structure.
 *
 * Returns:     static int  - 
 * Parameters:  uo - Pointer to the user object.
 *
 * History:
 * zappo   1/14/95    Created
 */
static int FUSR_print_ou(uo)
     struct UserObject *uo;
{
  if(verbose)
    {
      printf("Contents of user object @ %p\n", uo);
      printf("name  : %s\n", uo->name);
      printf("uid   : %d\n", uo->uid);
      printf("tty   : %s\n", uo->tty?uo->tty:"None");
      printf("ringf : %s\n", uo->ringf);
    }
  return 0;
}
