/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: write.c,v 1.7.2.1 92/06/24 13:55:51 marker Exp $"

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


/*	Program to communicate with other users of the system.		*/
/*	Usage:	write user [line]					*/

#include	<stdio.h>
#include	<signal.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/utsname.h>
#include	<utmp.h>
#include	<pwd.h>
#include	<fcntl.h>

#define		TRUE	1
#define		FALSE	0
#define		FAILURE	-1

extern	time_t time();
struct	utsname utsn;

FILE	*fp ;	/* File pointer for receipient's terminal */
char *rterm,*receipient ;	/* Pointer to receipient's terminal and name */
char	*thissys;

main(argc,argv)

int argc ;
char **argv ;

  {
	register int i ;
	register struct utmp *ubuf ;
	static struct utmp self ;
	char ownname[sizeof(self.ut_user) + 1] ;
	static char rterminal[] = "/dev/\0 2345678901";
	extern char *rterm,*receipient ;
	char *terminal,*ownterminal, *oterminal,*ttyname() ;
	short count ;
	extern FILE *fp ;
	extern int openfail(),eof() ;
	char input[134] ;
	register char *ptr ;
	long tod ;
	char *time_of_day ;
	struct passwd *passptr ;
	extern struct utmp *getutent() ;
	extern struct passwd *getpwuid() ;
	extern char *ctime() ;
	char badterm[20][20];
	register int bad = 0;
	int	myuid;

	myuid = geteuid();
	uname(&utsn);
	thissys = utsn.nodename;

/*	Set "rterm" to location where receipient's terminal will go.	*/

	rterm = &rterminal[sizeof("/dev/") - 1] ;
	terminal = NULL ;

	if (--argc <= 0)
	  {
	    fprintf(stderr,"Usage: write user [terminal]\n") ;
	    exit(1) ;
	  }
	else
	  {
	    receipient = *++argv ;
	  }

/*	Was a terminal name supplied?  If so, save it.			*/

	if (--argc > 1)
	  {
	    fprintf(stderr,"Usage: write user [terminal]\n") ;
	    exit(1) ;
	  }
	else terminal = *++argv ;

/*	One of the standard file descriptors must be attached to a	*/
/*	terminal in "/dev".						*/

	if ((ownterminal = ttyname(fileno(stdin))) == NULL &&
	    (ownterminal = ttyname(fileno(stdout))) == NULL &&
	    (ownterminal = ttyname(fileno(stderr))) == NULL)
	  {
	    fprintf(stderr,"I cannot determine your terminal name.\
  No reply possible.\n") ;
	    ownterminal = "/dev/???" ;
	  }

/*	Set "ownterminal" past the "/dev/" at the beginning of		*/
/*	the device name.						*/

	oterminal = ownterminal + sizeof("/dev/")-1 ;

/*	Scan through the "utmp" file for your own entry and the		*/
/*	entry for the person we want to send to.			*/

	for (self.ut_pid=0,count=0;(ubuf = getutent()) != NULL;)
	  {
/*	Is this a USER_PROCESS entry?					*/

	    if (ubuf->ut_type == USER_PROCESS)
	      {
/*	Is it our entry?  (ie.  The line matches ours?)			*/

		if (strncmp(&ubuf->ut_line[0],oterminal,
		    sizeof(ubuf->ut_line)) == 0) self = *ubuf ;

/*	Is this the person we want to send to?				*/

		if (strncmp(receipient,&ubuf->ut_user[0],
		    sizeof(ubuf->ut_user)) == 0)
		  {
/*	If a terminal name was supplied, is this login at the correct	*/
/*	terminal?  If not, ignore.  If it is right place, copy over the	*/
/*	name.								*/

		    if (terminal != NULL)
		      {
			if (strncmp(terminal,&ubuf->ut_line[0],
			    sizeof(ubuf->ut_line)) == 0)
			  {
			    strncpy(rterm,&ubuf->ut_line[0],
				sizeof(ubuf->ut_line)+1) ;
			    if (myuid && !permit(rterminal)) {
				bad++;
				rterm[0] = '\0';
			    }
			  }
		      }

/*	If no terminal was supplied, then take this terminal if no	*/
/*	other terminal has been encountered already.			*/

		    else
		      {
/*	If this is the first encounter, copy the string into		*/
/*	"rterminal".							*/

			if (*rterm == '\0')
			  {
			    strncpy(rterm,
			    &ubuf->ut_line[0],sizeof(ubuf->ut_line)+1) ;
			    if (myuid && !permit(rterminal)) {
				strcpy(badterm[bad++],rterm);
				rterm[0] = '\0';
			    }
			    else if (bad > 0)
			      {
				fprintf(stderr, "%s is logged on more than one\
 place.\nYou are connected to \"%s\".\nOther locations are:\n",
				    receipient,rterm) ;
				for (i = 0 ; i < bad ; i++)
				    fprintf(stderr,"%s\n",badterm[i]) ;
			      }
			}

/*	If this is the second terminal, print out the first.  In all	*/
/*	cases of multiple terminals, list out all the other terminals	*/
/*	so the user can restart knowing what her/his choices are.	*/

			else if (terminal == NULL)
			  {
			    if (count == 1 && bad == 0)
			      {
				fprintf(stderr, "%s is logged on more than one\
 place.\nYou are connected to \"%s\".\nOther locations are:\n",
				    receipient,rterm) ;
			      }
			    fwrite(&ubuf->ut_line[0],sizeof(ubuf->ut_line),
				1,stderr) ;
			    fprintf(stderr,"\n") ;
			  }

			count++ ;
		      }			/* End of "else" */
		  }			/* End of "else if (strncmp" */
	      }			/* End of "if (USER_PROCESS" */
	  }		/* End of "for(count=0" */

/*	Did we find a place to talk to?  If we were looking for a	*/
/*	specific spot and didn't find it, complain and quit.		*/

	if (terminal != NULL && *rterm == '\0')
	  {
	    if (bad > 0)
		fprintf(stderr,"Permission denied.\n") ;
	    else
		fprintf(stderr,"%s is not at \"%s\".\n",receipient,terminal) ;
	    exit(1) ;
	  }

/*	If we were just looking for anyplace to talk and didn't find	*/
/*	one, complain and quit.						*/
/*	If permissions prevent us from sending to this person - exit	*/

	else if (*rterm == '\0')
	  {
	    if (bad > 0)
		fprintf(stderr,"Permission denied.\n") ;
	    else
		fprintf(stderr,"%s is not logged on.\n",receipient) ;
	    exit(1) ;
	  }

/*	Did we find our own entry?					*/

	else if (self.ut_pid == 0)
	  {
/*	Use the user id instead of utmp name if the entry in the	*/
/*	utmp file couldn't be found.					*/

	    if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL)
	      {
		fprintf(stderr,"Cannot determine who you are.\n") ;
		exit(1) ;
	      }
	    strncpy(&ownname[0],&passptr->pw_name[0],sizeof(ownname)) ;
	  }
	else
	  {
	    strncpy(&ownname[0],self.ut_user,sizeof(self.ut_user)) ;
	  }
	ownname[sizeof(ownname)-1] = '\0' ;

	if (!permit1(1))
	    fprintf(stderr,"Warning: You have your terminal set to \"mesg -n\".\
  No reply possible.\n") ;

/*	Try to open up the line to the receipient's terminal.		*/

	signal(SIGALRM,openfail) ;
	alarm(5) ;
	fp = fopen(&rterminal[0],"w") ;
	alarm(0) ;

/*	Catch signals SIGHUP, SIGINT, SIGQUIT, and SIGTERM, and send	*/
/*	<EOT> message to receipient before dying away.			*/

	setsignals(eof) ;

/*	Get the time of day, convert it to a string and throw away the	*/
/*	year information at the end of the string.			*/

	time(&tod) ;
	time_of_day = ctime(&tod) ;
	*(time_of_day + 19) = '\0' ;
	fprintf(fp,"\n\007\007\007\tMessage from %s on %s (%s) [ %s ] ...\n",
	    &ownname[0],thissys,oterminal,time_of_day) ;
	fflush(fp) ;
	fprintf(stderr,"\007\007") ;	

/*	Get input from user and send to receipient unless it begins	*/
/*	with a !, when it is to be a shell command.			*/

	while ((ptr = fgets(&input[0],sizeof(input),stdin)) != NULL)
	  {
/*	Is this a shell command?					*/

	    if (*ptr == '!')
	      {
		shellcmd(++ptr) ;
	      }

/*	Send line to the receipient.					*/

	    else
	      {
		if (myuid && !permit1(fileno(fp))) {
			fprintf(stderr,"Can no longer write to %s\n",rterminal);
			break;
		}
		fputs(ptr,fp) ;
		fflush(fp) ;
	      }
	  }

/*	Since "end of file" received, send <EOT> message to receipient.	*/

	eof() ;
  }

setsignals(catch)

int (*catch)() ;

  {
	signal(SIGHUP,catch) ;
	signal(SIGINT,catch) ;
	signal(SIGQUIT,catch) ;
	signal(SIGTERM,catch) ;
  }

shellcmd(command)

char *command ;

  {
	register int child ;
	extern int eof() ;

	if ((child = fork()) == FAILURE)
	  {
	    fprintf(stderr,"Unable to fork.  Try again later.\n") ;
	    return ;
	  }
	else if (child == 0)
	  {
/*	Reset the signals to the default actions and exec a shell.	*/

	    execl("/bin/sh","sh","-c",command,0) ;
	    exit(0) ;
	  }
	else
	  {
/*	Allow user to type <del> and <quit> without dying during	*/
/*	commands.							*/

	    signal(SIGINT,SIG_IGN) ;
	    signal(SIGQUIT,SIG_IGN) ;

/*	As parent wait around for user to finish spunoff command.	*/

	    while(wait(NULL) != child) ;

/*	Reset the signals to their normal state.			*/

	    setsignals(eof) ;
	  }
	fprintf(stdout,"!\n") ;
  }

openfail()
  {
	extern char *rterm,*receipient ;

	fprintf(stderr,"Timeout trying to open %s's line(%s).\n",
	    receipient,rterm) ;
	exit(1) ;
  }

eof()
  {
	extern FILE *fp ;

	fprintf(fp,"<EOT>\n") ;
	exit(0) ;
  }

/* permit: check mode of terminal - if not writable by all disallow writing to
	 (even the user him/herself cannot therefore write to their own tty) */

permit (term)
char *term;
  {
	struct stat buf;
	int fildes;

	if ((fildes = open(term,O_WRONLY)) < 0)
		return(0);
	fstat(fildes,&buf);
	close(fildes);
	return(buf.st_mode & 0000002);
  }



/* permit1: check mode of terminal - if not writable by all disallow writing to
	 (even the user him/herself cannot therefore write to their own tty) */
/* this is used with fstat (which is faster than stat) where possible */

permit1 (fildes)
int fildes;
  {
	struct stat buf;

	fstat(fildes,&buf);
	return(buf.st_mode & 0000002);
  }
