/* gtd_annc.c - Handle the sending of an announcement from root daemon
 *
 * 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:
 * 
 *  Handles the sending of an announcement message.  By using the
 * knowledge of predicessors, due to blocking, we start a subprocess
 * so the daemon doesn't get bogged down in all the details.
 * 
 * History:
 * zappo   9/17/94    Created
 *
 * $Log: gtd_annc.c,v $
 * Revision 1.11  1995/07/23  21:52:05  zappo
 * In previous fix forgot to change sizeof the address being used.
 *
 * Revision 1.10  1995/07/23  21:19:42  zappo
 * Forwarding can mess up the printed name of hosts.  Grab the TARGET
 * address for the name first, and if that fails, then grab the return
 * address.
 *
 * Revision 1.9  1995/04/11  03:16:40  zappo
 * Used ioctl to modify the opened tty for writting messages.  This is
 * only when the symbol needed for the open command doesn't exist.  Also
 * changed the waitpid to wait.  This is because waitpid doesn't exist on
 * all systems.
 *
 * Revision 1.8  1995/04/10  22:00:57  zappo
 * Added ifdef around #include "errno.h"
 *
 * Revision 1.7  1995/04/01  17:03:20  zappo
 * Advent of extended name forces me to allow for excesses caused by
 * nasty people sending big app names.
 *
 * Revision 1.6  1995/03/25  04:22:07  zappo
 * Updated copyright
 *
 * Revision 1.5  1995/03/04  14:48:15  zappo
 * Added use of syslog to report errors when daemon not run on tty
 *
 * Revision 1.4  1995/03/03  02:52:06  zappo
 * Announcement can now take an extended parameter which represents the
 * name of the calling application.
 *
 * Revision 1.3  1995/02/12  13:15:28  zappo
 * Added == 1 to ifdefs due to use of config.h
 *
 * Revision 1.1  1995/02/01  03:49:09  zappo
 * Initial revision
 *
 * Tokens: ::Header:: gtalkd.h
 */
#include "etalklib.h"
#include "gtalkd.h"

#if HAVE_ERRNO_H == 1
#include <errno.h>
#endif
extern int errno;

#if HAVE_FCNTL_H == 1
#include <fcntl.h>
#else
/* What to put here if I do not have fcntl?? */
#endif

#if HAVE_SYS_WAIT_H == 1
#include <sys/wait.h>
#else
/* What should I do in this case?? */
#endif

#if HAVE_SYS_IOCTL_H
#include "sys/ioctl.h"
#endif


/*
 * Function: ANN_sendto
 *
 *   Sends an announce message to the person indicated by RO.  Returns
 * Fail if the person is unreachable for some reason.  Returns success
 * when a subprocess is spawned to write to the terminal.  If some
 * other method is taken (phones, auto-answer, etc) then don't start a
 * sub-process.
 *
 * Returns:     int - answer status for control response
 * Parameters:  uo - Pointer to user object
 *              ro - Pointer to request object
 * History:
 * zappo   9/17/94    Created
 * zappo   11/20/94   Added parameter uo
 */
int ANN_sendto(uo, ro)
     struct UserObject    *uo;
     struct RequestObject *ro;
{
  char              *histty;
  pid_t              kid;
  int                kidstat;

  /* Grab the tty where this person is living just now. */
  histty = FUSR_lookup(uo);

  if(!histty)
    {
      /* Opps! That person is not logged on */
      if(verbose)
	printf("ANN_sendto: User not found.\n");
      else if(syslogdebug > 1)
	syslog(LOG_NOTICE, "ANN_sendto: User not found.");
      return NOT_HERE;
    }
  else if(verbose)
    printf("User found on %s\n", histty);
      

  /* do a fork just in case something weird happens while we try to do
   * this.  Wait for process to return to get the exit signal. 
   */
  kid = fork();

  if(verbose)
    printf("Response to fork is %d\n", kid);

  if(kid == 0)
    {
      static char *ampm[] = { "am", "pm" };
      int        ap, hr;
      struct HostObject *host;
      char       outbuff1[80];
      char       outbuff2[90];
      /* This buffer could potentially have 256 + sizeof name + sizeof host
       * displayed in it!
       */
      char       outbuff3[300];
      struct tm *clock;
      int        ttyd;

      if(verbose)
	printf("Child write process executing.\n");

      /* Now that we know where that sucker is, attempt to access his
       * terminal with open.  By attaching O_NOCTTY to mode, we
       * guarantee that we don't get any stray TTY type signals.  This
       * replaces the 4 tier checking, setting method used in BSD.
       */
#ifdef O_NOCTTY
      ttyd = open(histty, O_WRONLY | O_NOCTTY);
#else
      ttyd = open(histty, O_WRONLY);
      /* If open doesn't do it for you, do it the hard way.  This hard
       * way doesn't work on all machines.  If you don't have the
       * extra parameter on you system, lets hope that you have decent
       * ioctl instead.
       */
#if HAVE_IOCTL && defined TIOCNOTTY
      ioctl(ttyd, TIOCNOTTY, ( void *)NULL);
#endif /* have ioctl, and defined tiocnotty */
#endif /* O_NOCTTY */

      /* Check for errors in new and interesting ways to
       * say "oops"
       */
      if(ttyd == -1)
	switch(errno)
	  {
	  case EACCES:
	  case EROFS:
	    if(verbose)
	      perror("Opening terminal permission denied");
	    exit(PERMISSION_DENIED);
	  default:
	    if(verbose)
	      perror("Opening terminal");
	    exit(FAILED);
	  }

      /* Get the hostname for the message we are sending. */
      host = HOST_gen_host_by_addr((struct sockaddr *)&ro->request.addr,
				   sizeof(ro->request.addr));
      /* We just grabbed the TARGET host.  This is the address the
	 other guy wants us to announce as.  If it fails, then grab
	 the name of the machine which just sent us the address.  This
	 could be wrong due to forwarding, but we need to advertise
	 something. */
      if(!host)
	host = HOST_gen_host_by_addr((struct sockaddr *)&ro->sender->raddr,
				     sizeof(ro->sender->raddr));
      if(!host)
	{
	  if(verbose)
	    printf("Error finding printable host name!\n");
	  exit(BADADDR);
	}

      /* Okie dokie.  Let write out a nice little message.
       */
      clock = localtime(&ro->stamp.tv_sec);
      
      /* Find the 12 hour clock time.
       */
      if(clock->tm_hour > 12)
	{
	  ap = 1;
	  hr = clock->tm_hour - 12;
	}
      else
	{
	  ap = 0;
	  hr = clock->tm_hour;
	}

      sprintf(outbuff1, "\n\n\r\07Message from GNU Talk Daemon: %d/%d/%d %d:%02d%s\n\r",
	      clock->tm_mon, clock->tm_mday, clock->tm_year,
	      hr, clock->tm_min, ampm[ap]);
      sprintf(outbuff2, "gtalk: connection requested by %s@%s.\n\r",
	      ro->request.l_name, host->name);

      /* We must be careful to tell the user what kind of response to use.
       */
      if(ro->request.vers == 0)
	sprintf(outbuff3, "gtalk: respond with \"otalk %s@%s\"\n\n\r",
		ro->request.l_name, host->name);
      else if((ro->request.vers == TALK_VERSION) ||
	      (ro->request.extended == 0))
	sprintf(outbuff3, "gtalk: respond with \"talk %s@%s\"\n\n\r",
		ro->request.l_name, host->name);
      else
	sprintf(outbuff3, "gtalk: respond with \"%s %s@%s\"\n\n\r",
		ro->extend, ro->request.l_name, host->name);

      if(write(ttyd, outbuff1, strlen(outbuff1)) < 0)
	{
	  if(verbose)
	    perror("Write to terminal");
	  exit(FAILED);
	}
      /* Lets just assume this works after 1 try. */
      write(ttyd, outbuff2, strlen(outbuff2));
      write(ttyd, outbuff3, strlen(outbuff3));
      
      /* Return that things are way kool */
      exit(SUCCESS);
    }

  if(kid != -1)
    {
      /* Chill until the kid returns.  kidstat will tell us the exit code
       * (bottom 8 bits) to see how to fill our return value.
       */
      if(wait(&kidstat) != kid)
	{
	  if(verbose)
	    printf("waitpid: Failed wait for child writing to terminal.\n");
	  /* If we don't get the pid back, somethins up. */
	  return FAILED;
	}
    }
  else
    {
      /* In this case, the fork failed
       */
      if(verbose)
	perror("announcement fork");
      return FAILED;
    }

  /* check the return status (bottom byte) and if it's ok, return
   * the return status in the top byte.
   */
  if(kidstat & 255)
    {
      if(verbose)
	printf("Terminal write appears to have failed!\n");

      return FAILED;
    }
  else
    {
      if(verbose)
	printf("Terminal write appears to be successful!\n");

      return (kidstat >> 8) & 255;
    }
}
