[alt.sources] CHAT V2.8 -- freely redistributable software

karl@ddsw1.MCS.COM (Karl Denninger) (08/04/89)

The following is our chat program for System V machines.  BSD users will
need to do some modification work to make this run.

CHAT is a multiuser real-time "talk" program.  It runs on all terminals, and
does not require termcap or terminfo knowledge.  A modified line mode is
used for communications.

The package is designed to be a high performance low-impact implementation.
One limitation which does exist is that use is limited to 15 users unless
modifications are made to the code.

This package is NOT public domain, but it is freely redistributable.  See
the README file for details.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile README chat.1 chat.h client.c decl.h funcs.c
#   link.c server.c
# Wrapped by karl@ddsw1 on Fri Aug  4 10:34:21 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(832 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for Chat 
X#
X# See the README file -- this should be ok for most users.
X#
X# Note: The '-lx' in the linkage lines below is needed for SCO Xenix
X#	systems.  Remove it for all others.
X#
CC=cc
CFLAGS= -M2e -DVERSION=\"2.8\" -s 
LDFLAGS= -M2e -s -o
INC_DEP=/usr/include/stdio.h\
X	/usr/include/sys/types.h\
X	/usr/include/sys/stat.h\
X	/usr/include/fcntl.h\
X	/usr/include/signal.h\
X	/usr/include/setjmp.h\
X	/usr/include/pwd.h\
X	/usr/include/termio.h\
X	/usr/include/sys/file.h\
X	/usr/include/memory.h\
X	chat.h\
X	decl.h
XFILES=	client.o\
X	funcs.o
X
all: chat comserver 
X
chat: $(INC_DEP) $(FILES)
X	$(CC) $(LDFLAGS) chat $(FILES) -lx
X
comserver: $(INC_DEP) server.o funcs.o
X	$(CC) $(LDFLAGS) comserver server.o funcs.o
X
link: $(INC_DEP) link.o funcs.o
X	$(CC) link.o funcs.o $(LDFLAGS) link -lx
X
clean:
X	rm -f *.o chat comserver link
END_OF_FILE
if test 832 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(4455 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
X******************************************************************
X
X		CHAT	- Multiuser chat program
X
X******************************************************************
X
X
Please read this entire file through before attempting to install 'CHAT'.
This file contains important information about the package, installation,
and other programs available from MCS.
X
X
CHAT is a product of and Copyright 1987/88/89 by Macro Computer Solutions, Inc.
X
This package is hereby given to the net as freely redistributable software.
It is NOT Public Domain.  Specifically, you may not use this software for
gainful purposes.
X
You are encouraged to copy, distribute, give away, port, and otherwise make
available this software to others, providing that you don't attempt to profit
from it or it's use, or pretend you wrote it.  If you distribute modified 
copies you must annotate and document your changes to the software supplied.
X
X
PORTING and INSTALLATION
X========================
X
X
NOTE:
X	The file 'link.c' is an experimental and incomplete linkage
X	mechanism for real-time links with other systems running CHAT.
X	Hack on it if you wish, but don't expect it to function as-is.
X
X
X
You absolutely need NAMED PIPES (System III and later)
X
If you are not on a System V machine (ie: BSD) you will need to go through
the code and change a number of things.  Specifically, you should start
with:
X
X	The terminal is put into CBREAK mode.  Thus, the code for "ioctl()"s
X	will need to be modified.
X
X	There is a variable called "index" in two of the programs.  Needless
X	to say this will conflict with the system routine of the same name
X	under BSD.
X
Other than this, the code should work under Berkely Unix, although not
having a system available to us we haven't been able to try it.
X
X
X
You will need:
X
X	1) A group you can use for CHAT, with access to a directory.
X	2) Root access.
X
XEdit 'chat.h'.  You need to indicate where you want the files built, as well
as declare the presense or absense of a "nap()" call in your version of Unix.  
X
XEdit the Makefile to include or excude any specific flags your compiler
requires.  The Makefile as distributed is set up for SCO Xenix systems.
X
MICROPORT USERS should include the "-Ml" definition in CFLAGS and LDFLAGS;
some Xenix users may have to include something similar.
X
Type 'make' (cross fingers, pray, etc.).
X
Copy 'comserver' to a public directory.  It should be owned by root, group
the group you have defined for this purpose, and make it SGID.  Permissions
should be 2711 (SGID, executable by anyone, and only executable).
X
Copy 'chat' to a public directory, make it SGID your group, executable by world.
X
Copy (and optionally format with nroff -man) the manual page (chat.1)
X
Note that the capacity of this version is 15 users, which is due to the
open-file limit and rather sparse, but high-performance approach to the
problem.  For most small systems this is not an important limitation.  Users
will be notified if 'chat' if currently full, and asked to try later.
X
If you are using XENIX, which has a 60 file-per-process limit (recent
versions anyway), you can change some constants internally to permit up to
X50 people in CHAT at once.  Of course, you might not be able to keep up with
this much material :-)
X
The pipe files from the server are set to NDELAY.  This will keep the
server from hanging if a user should shell escape for a while or otherwise
not be reading his/her end of the pipe.  Note that the user who has stopped
their copy of 'chat' can and will lose responses in this circumstance, but 
no other harm should occur.  As the capacity of a pipe is at least 4096
characters, you would need to be gone for quite a while (about 100 lines
worth) before this becomes a concern.
X
Problems, corrections, enhancements, etc. welcome!
X
X
X
X
CHAT is a part of our multi-user conferencing solution for Unix.  If you are
interested in the remainder of what we have to offer, including menuing, 
a multisite conferencing system, and new user registration, please contact 
MCS using the information below.  These other packages are available
commercially in both source and binary format, and in general will run on
most System V machines.  The complete system can be seen by dialing one of
the modem numbers below, public access is available to all 24 hours a day.
X
X
Thanks -- and enjoy!
X
Karl Denninger
Macro Computer Solutions, Inc.
X	
karl@ddsw1.MCS.COM, ...!ddsw1!karl
Voice: (312) 566-8910
Modem: (312) 566-8911
X
END_OF_FILE
if test 4455 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'chat.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chat.1'\"
else
echo shar: Extracting \"'chat.1'\" \(4372 characters\)
sed "s/^X//" >'chat.1' <<'END_OF_FILE'
X.TH CHAT 1 "Macro Computer Solutions"
X.SH NAME
chat \- Chat in real-time with other users
X.SH SYNOPSIS
X.B chat
X.SH DESCRIPTION
X.I Chat
is a advanced server-based program utilizing named pipes to provide simple
yet effective communication between multiple users.  The chat system can
accomodate up to 15 simultaneous users communicating at one time.
X
The first user to run 'chat' will be asked to wait a moment while the system
starts up the server process.  The server will remain active until the last
user exits the 'chat' program; at that time it will disappear (alternately,
the system administrator may place 'comserver f' in the inittab file which
will cause the server to become permanently resident).
X
A user calling CHAT may append to the command names of users with which the
chatting user wishes to converse.  If those user(s) can be located and their
terminal(s) are writable, they will be asked to join the conversation.
X
Chat will also take an initial "alias" or "handle" from the environment
variable "CHATALIAS" if that variable exists.
X
X
When you enter CHAT, you are 'on the air' with the other users who are
running CHAT.  Chat is always willing to accept a line of input from you
which is either (1) a shell escape, (2) a command to CHAT, or (3) a line of
text to show to everyone else.
X
Any line beginning with "!" is a shell escape, and the remainder of the line
will be passed to the shell for interpretation.
X
A line beginning with "/" is a command.  The following commands are known to
the CHAT program:
X
X/?	\- Get assistance.  This shows you the internal help for CHAT.
X
X/c	\- Changes your chat channel to the number following the 'c'.
X
X/e	\- Toggle echo.  Controls the echo of lines to your screen.
X
X/k	\- Kills a user in CHAT.  Should be used with discretion.
X
X/n	\- Set aliased name - changes your name in CHAT.
X
X/p	\- Sends a private message to another user in CHAT.
X
X/s	\- Shows you the users who are in CHAT.
X
X/t	\- Sends a message to any logged in user.
X
X/w	\- Displays the system users on your terminal.
X
X/h	\- Same as /?
X
X.sp 1
Special characters:
X.sp 1
X<CTRL>X and <CTRL>U	\- Delete current line, display [Cancelled].
X
X<CTRL>H			\- Backspace.
X
A line which does not begin with either of these two characters is
considered a data line and is sent to all the other connected terminals.
All lines wrap at 68 characters.  If you are using a command such as '/p' or
X'/t', the prefix will be kept 'sticky' so you can send multi-line private
messages without worrying about the line breaks.
X
X.SH SECURITY
X
CHAT uses named pipes for all data transmission, thus, there is no record of
the conversation kept inside of CHAT.  Users who wish to record their
conversations in CHAT are on their own.
X
In general, you can consider messages passed via '/p' secure.  '/t' uses a
system command and as such is a less secure method of passing sensitive
information between users.
X
The /k command can be restricted if the Site Administrator desires.
Generally speaking, misuse of this command will prompt most administrators
to restrict it's use.  Users should keep use of this command to emergencies
and genuine need.  See the source for the file of 'kill'usable users.
X
It is possible for a user to deliberately confuse others by adopting another's
name as a pseudonym, etc... The offender will be obvious on a status display.
X
Note that the use of obscure channel numbers permit users to effectively
hide from others, as there is no way to discern the user's channel number
from the status displays.
X
X
X.SH COPYRIGHT
X
CHAT is Copyrighted 1987 by Macro Computer Solutions, Inc. and available 
to the public as a 'sharware' program.
X
The suggested donation for this package is $19.00 per executing CPU.
X
CHAT may be freely redistributed providing:
X
You clearly state that this code is available at no cost from other sources.
You provide the entire contents of the original SHAR file.
You retain any copyright or other notices on the package.
X
If you wish to include this source or object derived from this source on a
tape archive of several programs, or otherwise distribute it as a part of a
collection, please let us know so we can keep you current with the latest
versions and information!
X
X.SH AUTHOR
X
Created September 1987 at Macro Computer Solutions, Inc. by Karl Denninger
X.sp 1
X415 S.E. Garfield
X.sp 1
Mundelein, IL  60060
X.sp 1
X+1 312 566-8910
X
END_OF_FILE
if test 4372 -ne `wc -c <'chat.1'`; then
    echo shar: \"'chat.1'\" unpacked with wrong size!
fi
# end of 'chat.1'
fi
if test -f 'chat.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chat.h'\"
else
echo shar: Extracting \"'chat.h'\" \(1124 characters\)
sed "s/^X//" >'chat.h' <<'END_OF_FILE'
X/*	chat.h	- include file of definitions for chat */
X
X#include	<stdio.h>
X#include	<fcntl.h>
X#include	<errno.h>
X#include	<signal.h>
X#include	<string.h>
X#include	<pwd.h>
X#include	<sys/types.h>
X#include	<sys/utsname.h>
X#include	<sys/stat.h>
X#include	<memory.h>
X#include	<time.h>
X#include	<termio.h>
X#include	<utmp.h>
X
X#define	CHATDIR		"/tmp/"			/* Base chat directory */
X#define	LINESIZ		132			/* Line size */
X#define	NAP					/* System has short sleep */
X						/* if defined.... */
X
X/* Do not change below this line unless you know what you're doing! */
X
X#ifdef	NAP
X#define	NAPTIME		250			/* Sleep for initial entry */
X#define	MAXTRIES	20			/* Maximum number of tries */
X#else
X#define	NAPTIME		1			/* Sleep for initial entry */
X#define	MAXTRIES	5			/* Maximum number of tries */
X#endif
X
X/* System declarations (why the system cannot include these...) */
X
extern 	long 	time();			/* System should declare this! */
extern 	struct 	passwd *getpwuid();	/* Declare passwd lookup function */
extern	char	*malloc();		/* Memory allocation	*/
extern	char	*getenv();		/* Environment get */
extern	long	lseek();		/* Lseek returns long */
X
END_OF_FILE
if test 1124 -ne `wc -c <'chat.h'`; then
    echo shar: \"'chat.h'\" unpacked with wrong size!
fi
# end of 'chat.h'
fi
if test -f 'client.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'client.c'\"
else
echo shar: Extracting \"'client.c'\" \(12719 characters\)
sed "s/^X//" >'client.c' <<'END_OF_FILE'
X/*	Client party program	*/
X/*	Part of CHAT, Copyright 1988 Macro Computer Solutions, Inc	*/
X	
X#include	"chat.h"
X#include	"decl.h"
X
char	filen[132];
int	echo = 0;
struct	termio	oldbuf, tbuf;
X
X 
X /*
X  *	Write a message to a logged in  user.
X  */
X 
X wr_user(name, victim, msg)
X char	*name, *victim, *msg;
X {
X 	FILE	*ttynq;
X 	struct utmp *ptr, *user, *getutent();
X 	char	temp[132];
X 	int	x;
X 
X 	x = 0;
X 	setutent();	/* Find user */
X 	while ((ptr = getutent()) != NULL)   {
X 		if (!strncmp(ptr->ut_user, victim, (strlen (victim) -1))) {
X 			user = ptr;
X 			x = 1;
X 			break;
X 		}
X 	}
X 	if ( !x)   {
X 		printf ("%s is not logged on\n", victim);
X 		return;
X 	}
X 	strcpy (temp, "/dev/");
X 	strcat (temp, user->ut_line);
X 	alarm(10);
X 	if ((ttynq = fopen (temp, "w")) == NULL)   {
X 		printf ("User %s has disabled writes\n", user->ut_user);
X 	} else {
X 		printf ("Sending to %s: %s\n", victim, msg);
X 		fflush (stdout);
X		sprintf(temp, "\n%c;;From %s: %s\n", 7, name, msg);
X		fprintf(ttynq, temp);
X		fclose(ttynq);
X 	}
X}
X
X
execcmd(cmd)
char	*cmd;
X
X{
char	*arg[30];
int	x=0;
char	*ptr;
long	wloc;
X
X	if (!fork()) {
X		ptr = strtok(cmd,":;, ");
X		arg[x++]=ptr;
X		while ((arg[x++] = strtok((char *) NULL, " ,")) != NULL);;
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X		setgid(getgid());			/* Turn off permissions */
X		exit(execvp(arg[0], arg));
X	} else wait(&wloc);
X	return((int) wloc);
X}
X
X
getline(line)
char	*line;
X
X{
X
X	int	ch;
X	int	x, y;
X	int	flags;
X	int	index = 1;
X	static	char	line2[80] = {0};
X	char	tmpx[80], tmpy[80];
X
X	if (strlen(line2)) {
X		printf("> %s", line2);
X		fflush(stdout);
X		strcpy(line, line2);
X		index = strlen(line2);
X		line2[0] = 0;
X		goto readmore;
X	}
loop1:;
X	alarm(1);
X	if ((ch = getchar()) == EOF)  {
X		alarm(0);
X		return(0);
X	}
X	if ((ch == 24) || (ch == 21)) {
X		puts("\n[Cancelled]");
X		alarm(0);
X		return(0);
X	}
X	if ((ch == 13) || (ch == 10)) 
X		return(0);
X	if ((ch < 32) || (ch > 126)) 
X		goto loop1;
X	line2[0] = 0;
X	line[0] = ch;
X	if (!echo) {
X		printf("> %c",ch);
X		fflush(stdout);
X	}
X
readmore:;
X
X	alarm(120);				/* 120 seconds on the line */
X	x = 0;
X	while (index < 68) {			/* We get up to 68 chars */
X		if (index) 
X			x = -1; 
X		else 
X			x = 0;
X		ch = getchar();			/* Get one */
X		if (ch == EOF) {		/* If EOF.... */
X			line[index] = 0;
X			puts("");
X			x = 0;
X			break;
X		}
X		if ((ch == 24) || (ch == 21)) {	/* Cancel request... */
X			puts("\n[Cancelled]");
X			alarm(0);
X			return(0);
X		}
X		if ((ch == 13) || (ch == 10)) {	/* End of line */
X			line[index] = 0;
X			if (!echo) 
X				puts("");
X			alarm(0);
X			if (!index) 
X				return(0);
X			return(-1);
X		}
X		if (ch == 8) {			/* Backspace */
X			if (index > 0) {	/* Do if anything here */
X				if (!echo) {
X					putchar(ch);
X					putchar(32);
X					putchar(ch);
X					fflush(stdout);
X				}
X				index--;
X			}
X			continue;
X		}
X		if ((ch < 32) || (ch > 126)) continue;	/* Do nothing */
X		line[index++] = ch;		/* Add the character */
X		if (!echo) {
X			putchar(ch);
X			fflush(stdout);
X		}
X	}					/* Falling off end... */
X	x = -1;					/* Init return code */
X	if (!index)
X		return(0);			/* Nothing on the line */
X	if (index == 68) {			/* Look for break location */
X		index--;
X		y = line[index--];		/* Get last character */
X		while ((index > 0) && (y != ' ')) {	/* Find last space */
X			if (!echo) {		/* Do we echo?? */
X				putchar(8);
X				putchar(32);		/* Cover thy spaces! */
X				putchar(8);
X			}
X			y = line[index--];		/* Check next back */
X		}
X		if (!index) 
X			return(-1);		/* No break, send it */
X		index++;				/* Break at next */
X		line[index] = 0;			/* Store break */
X		ch = line[1];
X		if (ch >= 'a') ch = tolower(ch);	/* Make lower case */
X		if ((line[0] == '/') && ((ch == 'p') || (ch == 't'))) {
X			sprintf(tmpx, "%s", &line[2]);
X			sscanf(tmpx, " %[!-~]", tmpy);	/* Get name */
X			strncpy(line2, line, 2);	/* Get command */
X			line2[2] = 0;
X			sprintf(tmpx, "%s %s", tmpy, &line[index + 1]);
X			strcat(line2, tmpx);		/* Add on rest */
X		} else 
X			strcpy(line2, &line[index + 1]);/* Copy rest.. */
X		x = strlen(line2);			/* Get length */
X		if (!x) 
X			x = -1;			/* Return length */
X		if (!echo) 
X			puts("");		/* New line */
X	}
X	alarm(0);					/* No alarms please */
X	return(x);					/* Exit w/status */
X}
X
X/*	And here we find a variety of signal catching routines.
X
X	catch() exists only to satisfy the alarm call requirements.
X
X	The rest are exits of various kinds, some forced by external
X	circumstances, others requested by either the daemon or the user.
X
X*/
X
catch()
X{
X	signal(SIGALRM, catch);
X	return;
X}
X
servdied()
X{
X	puts("[Server disconnected]");
X	die1();
X}
X
samename()
X{
X	puts("Only one chat per user id allowed!");
X	die1();
X}
X
toss()
X{
X	puts("You have been removed from CHAT by another user");
X	die1();
X}
X
full()
X{
X	puts("CHAT is full, please try again later");
X	die1();
X}
X
die1()
X{
X	puts("Bye-bye");
X	unlink(filen);
X	ioctl(0,TCSETAW,&oldbuf);
X	exit(0);
X}
X
X/*	This is it... not many comments, but then again this is
X	pretty simple code... Shouldn't be hard to understand..
X
X*/
X
main(argc, argv)
int	argc;
char	*argv[];
X
X{
X	char	temp[132];
X	char	line[132], line2[132];
X	char	victim[20], msg[80];
X	int	out;
X	int	cp;
X	FILE	*ttynq;
X	int	fid, wr;
X	char	key;
X	int	x;
X	struct	passwd	*psw;
X	struct utmp *ptr, *user, *getutent();
X	char	name[20];
X	char	cmd[5] ;
X	int	channel = 0;
X
X	umask(0117);
X	if ((argc == 3) && (!strcmp(argv[1], "secure"))) 
X		strcpy(name, argv[2]);
X	else {
X		psw = getpwuid(getuid());	/* A shot at the user */
X		strcpy(name, psw->pw_name);	/* Pick up a starting name */
X	}
X	printf("Welcome to Chat V%s, %s\n", VERSION, name);
X	puts("Type /h for help\n");
X	strcpy(cmd,"w");			/* Base command */
X	signal(SIGALRM,catch);			/* Do nothing here */
X	signal(SIGQUIT, toss);			/* Toss user */
X	signal(SIGINT, die1);			/* Quit if requested */
X	signal(SIGUSR1, full);			/* No channels available */
X	signal(SIGUSR2, samename);		/* No second by same name */
X	signal(SIGPIPE, servdied);		/* The server was killed */
X	out = 0;
X	sprintf(line, "%s/pipe%d", CHATDIR, getpid());/* infeed */
X	strcpy(filen, line);			/* Save for later */
X
X/* This had better not fail; if it does, then you're effectively screwed as
the server cannot find you.  One way around this is to put these in the /tmp
directory, which is nearly always writable.  We do that here	*/
X
X	if (mknod(line, 010770, 0)) {
X		puts("Insufficient privilege for communication channel;");
X		perror("mknod");
X		puts("Please contact an administrator");
X		exit();
X	}
X	ioctl(0,TCGETA,&tbuf);			/* Get working copy */
X	ioctl(0,TCGETA,&oldbuf);		/* And copy for restore later */
X	tbuf.c_lflag &= ~(ICANON|ECHO|ISIG);	/* Clean input, echo */
X	tbuf.c_cc[4] = 1;			/* 1 character, 1 second */
X	tbuf.c_cc[5] = 1;
X	ioctl(0,TCSETAW,&tbuf);			/* Set tty */
X	sprintf(temp, "%s/pipe", CHATDIR);
X	if ((wr = open(temp, O_WRONLY|O_NDELAY)) == -1) {
X		if (open("/tmp/chat.lock", O_WRONLY|O_EXCL|O_CREAT, 000) >= 0) {
X			puts("Wait, attempting to start comserver");
X			execcmd("comserver");
X		}
X		sleep(1);
X		for (x = 0; x < MAXTRIES; x++) {
X			if ((wr = open(temp, O_WRONLY|O_NDELAY)) != -1) 
X				break;
X#ifdef	NAP
X			nap(NAPTIME);
X#else
X			sleep(NAPTIME);
X#endif
X		}
X		if (wr == -1) {
X			puts("Cannot establish communications, please try later");
X			ioctl(0,TCSETAW,&oldbuf);	/* Restore terminal */
X			unlink(line);
X			exit(1);
X		}
X	}
X	sprintf(temp, "%d o %d %s >%s\n", getpid(), channel, name, name);
X	write(wr, temp, LINESIZ);		/* Write one buffer. */
X	alarm(10);
X	fid = 0;				/* Make pipe id impossible */
X	fid = open(line, O_RDONLY);		/* Open pipe for reading */
X	if (fid <= 0) {
X		puts("The server seems to have died...");
X		ioctl(0,TCSETAW,&oldbuf);	/* Leave if no server */
X		unlink(line);
X		exit(1);
X	}
X	setuid(getuid());			/* We have the files open */
X	alarm(0);				/* Cancel outstanding alarm */
X
X 	/*
X 	 *	If we were not called from 'akcs' notify the users
X 	 *	listed on the command line to run 'chat'.
X 	 */
X 
X 	if(argc > 1 && strcmp(argv[1], "secure"))
X 		for(x = 1 ; x < argc ; ++x)
X 			wr_user(name, argv[x], "Please invoke chat");
X 
X 	/*
X 	 *	If CHAT_ALIAS is set in the invironment, start up
X 	 *	with that alias.
X 	 */
X 
X 	if (getenv("CHAT_ALIAS") != NULL) {
X 		sprintf(temp, "%d n %d %s >%s\n",
X 		    getpid(), channel, name, getenv("CHAT_ALIAS"));
X 		write(wr, temp, LINESIZ);		/* Write one buffer. */
X 	}
X	sprintf(temp, "%d %s %d %s >%s\n",getpid(), "s", channel, name, "test");
X	write(wr, temp, LINESIZ);
X	while (!out) {				/* While still scanning */
X		alarm(1);			/* Interrupt in one second */
X		while (read(fid, temp, LINESIZ) == LINESIZ) {
X			stripj(temp);		/* Read/print while available */
X			puts(temp);		/* Get next line */
X			alarm(1);		/* Reset alarm */
X		}
X		if (getline(line)) {	/* Did we get user input? */
X			if ((*line == '!') && (strcmp(argv[1], "secure"))) {/* Shell escape */
X				ioctl(0,TCSETAW,&oldbuf);
X				alarm(0);	/* Turn off alarm(s)	*/
X				system(&line[1]);	/* Protected below... */
X				while ((wait() != -1) || (errno != ECHILD));;
X				ioctl(0,TCSETAW,&tbuf);
X				puts("!");
X				continue;
X			}
X			if (*line == '/') {	/* Command request... */
X				switch(line[1]) {
X					case '?':
X					case 'h':	/* Help */
X					case 'H':
X						printf("System V Chat V%s\n", VERSION);
X						puts("Copyright 1987 MCS/Karl Denninger");
X						puts("Not-for-profit distribution authorized\n");
X						puts("!cmd - Execute Unix command");
X						puts("/?   - Same as (help)");
X						puts("/c n - Change channel to #n (0 = default)");
X						puts("/e   - Echo (toggle)");
X						puts("/h   - Print this help");
X						puts("/k   - Kill a chatting user");
X						puts("/n   - Set aliased name");
X						puts("/p   - Send a private msg");
X						puts("/q   - Quit chatting");
X						puts("/s   - Show chat users");
X						puts("/t   - Tell a user something");
X						puts("/w   - Show system users");
X						puts("\nControl chars: CTRL-X = cancel line");
X						puts("               CTRL-H = backspace\n");
X						puts("To enter a response just type\n");
X						break;
X					case 'n':
X					case 'N':
X						if (sscanf(&line[2], " %[!-~ ]", victim) == 1) {
X							sprintf(temp, "%d n %d %s >%s\n", getpid(), channel,name, victim);
X							write(wr, temp, LINESIZ);
X						} else puts("Requires alias");
X						break;
X					case 'p':	/* Private message */
X					case 'P':
X						if (sscanf(&line[2], " %s %[!-~ ]", victim, msg) == 2) {
X							sprintf(temp, "%d %s %d %s >%s\n",getpid(), "p", channel, victim, msg);
X							write(wr, temp, LINESIZ);
X						} else puts("Requires user and message");
X						break;
X					case 'q':	/* Exit from CHAT */
X					case 'Q':
X						sprintf(temp, "%d q 0 %s >%s\n",getpid(), name, "[Departed from chat]");
X						write(wr, temp, LINESIZ);
X						ioctl(0,TCSETAW,&oldbuf);
X						die1();
X						break;
X					case 'e':	/* Echo toggle */
X					case 'E':		
X						if (echo == 1) {
X							puts("Echo on");
X							echo = 0;
X						} else {
X							echo = 1;
X							puts("Echo off");
X						}
X						break;
X					case 't':	/* Tell someone */
X					case 'T':
X						if (sscanf(&line[2], " %s %[!-~ 	]",victim, msg) < 2) {
X							puts("Requires victim and message");
X							break;
X						}
X						x = 0;
X						setutent();	/* Find user */
X						while ((ptr = getutent()) != NULL)   {
X							if (!strncmp(ptr->ut_user, victim, (strlen (victim) -1)))   {
X								user = ptr;
X								x = 1;
X								break;
X							}
X						}
X						if ( !x)   {
X							printf ("User is not logged on\n");
X							break;
X						}
X						sprintf(temp, "/dev/%s", user->ut_line);
X						alarm(10);
X						if ((ttynq = fopen (temp, "w")) == NULL)   {
X							printf ("User %s has disabled writes\n", user->ut_user);
X						} else {
X							printf ("Sending to %s: %s\n", victim, msg);
X							sprintf(temp, "\n%c;;From %s: %s\n", 7, name, msg);
X							fflush (stdout);
X							fprintf(ttynq, temp);
X							fclose(ttynq);
X						}
X						break;
X					case 'k':	/* Request kill */
X					case 'K':
X						if (getgid() != getegid()) {
X							printf("No privilege for kill operation\n");
X						} else {
X							sprintf(temp, "%d %s %d %s >%s\n",getpid(), "k", channel, &line[2], "test");
X							write(wr, temp, LINESIZ);
X						}
X						break;
X					case 'c':
X					case 'C':
X						sprintf(temp, "%d %s %d %s >%s\n", getpid(), "t", atoi(&line[2]), " ", " ");
X						channel = atoi(&line[2]);
X						write(wr, temp, LINESIZ);
X						break;
X					case 'w':	/* Show who is here */
X					case 'W':
X						puts("System users");
X						system("who");
X						break;
X					case 'S':
X					case 's':
X						sprintf(temp, "%d %s %d %s >%s\n",getpid(), "s", channel, name, "test");
X						write(wr, temp, LINESIZ);
X						break;
X					default:	/* Bad command */
X						puts("Unknown command, try '/h'");
X				}
X			} else {		/* Otherwise send the line */
X				sprintf(temp, "%d %s %d %s >%s\n", getpid(), cmd, channel , name, line);
X				write(wr, temp, LINESIZ);
X			}
X		}
X	}
X}
X
END_OF_FILE
if test 12719 -ne `wc -c <'client.c'`; then
    echo shar: \"'client.c'\" unpacked with wrong size!
fi
# end of 'client.c'
fi
if test -f 'decl.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'decl.h'\"
else
echo shar: Extracting \"'decl.h'\" \(154 characters\)
sed "s/^X//" >'decl.h' <<'END_OF_FILE'
X
X/* Our functions used here */
X
extern	char	*itoa();
extern	long 	getsize();
extern 	char 	*capitalize();
extern 	void 	openfile();
extern	FILE	*popen();
END_OF_FILE
if test 154 -ne `wc -c <'decl.h'`; then
    echo shar: \"'decl.h'\" unpacked with wrong size!
fi
# end of 'decl.h'
fi
if test -f 'funcs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'funcs.c'\"
else
echo shar: Extracting \"'funcs.c'\" \(706 characters\)
sed "s/^X//" >'funcs.c' <<'END_OF_FILE'
X/*	functions!		*/
X
X#include	"chat.h"
X
X
X/*	Itoa		- convert integer to ascii */
char	*itoa(number)
int	number;
X{
static	char	temp[11];
int	x;
X	
X	for (x=0; x<9; x++) temp[x]=0;
X	do {
X		temp[x] = (number % 10) + 48;		/* Place character.. */
X		number = number / 10;
X		x--;
X	} while (number); 
X	temp[10]=0; x++;
X	return((char *) &temp[x]);
X}
X
die(line)
char	*line;
X
X{
X	printf("%s\n", line);
X	exit(1);
X}
X
X
X/*	Stripj	- strip line of ^j's...	*/
stripj(ptr)
char	*ptr;
X{
int	x;
X	
X	for (x=0; x < 80; x++) {
X		if (*(ptr + x) == 7)		/* Allow bells to go through */
X			continue;
X		if (*(ptr+x) == 10) *(ptr+x) = 0;
X		if ((*(ptr+x) <= 31) && (*(ptr+x))) *(ptr+x) = 32;
X		if (*(ptr+x) > 126) *(ptr+x) = 32;
X	}
X	return;
X}
X
END_OF_FILE
if test 706 -ne `wc -c <'funcs.c'`; then
    echo shar: \"'funcs.c'\" unpacked with wrong size!
fi
# end of 'funcs.c'
fi
if test -f 'link.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'link.c'\"
else
echo shar: Extracting \"'link.c'\" \(8909 characters\)
sed "s/^X//" >'link.c' <<'END_OF_FILE'
X/*	Link party program	*/
X/*	Part of CHAT, Copyright 1988 Macro Computer Solutions, Inc	*/
X	
X#include	"chat.h"
X#include	"decl.h"
X
char	filen[132];
int	echo = 1;			/* Start with echo off */
struct	termio	oldbuf, tbuf;
X
execcmd(cmd)
char	*cmd;
X
X{
char	*arg[30];
int	x=0;
char	*ptr;
long	wloc;
X
X	if (!fork()) {
X		ptr = strtok(cmd,":;, ");
X		arg[x++]=ptr;
X		while ((arg[x++] = strtok((char *) NULL, " ,")) != NULL);;
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X		exit(execvp(arg[0], arg));
X	} else wait(&wloc);
X	return((int) wloc);
X}
X
X
getline(line)
char	*line;
X
X{
X
X	int	ch;
X	int	x, y;
X	int	flags;
X	int	index = 1;
X	static	char	line2[80] = {0};
X	char	tmpx[80], tmpy[80];
X
X	if (strlen(line2)) {
X		printf("> %s", line2);
X		fflush(stdout);
X		strcpy(line, line2);
X		index = strlen(line2);
X		line2[0] = 0;
X		goto readmore;
X	}
loop1:;
X	alarm(1);
X	if ((ch = getchar()) == EOF)  {
X		alarm(0);
X		return(0);
X	}
X	if ((ch == 13) || (ch == 10)) 
X		return(0);
X	if ((ch < 32) || (ch > 126)) 
X		goto loop1;
X	if (ch == '^')
X		ch = 10;
X	line2[0] = 0;
X	line[0] = ch;
X	if (!echo) {
X		printf("> %c",ch);
X		fflush(stdout);
X	}
X
readmore:;
X
X	alarm(120);				/* 120 seconds on the line */
X	x = 0;
X	while (index < 68) {			/* We get up to 68 chars */
X		if (index) 
X			x = -1; 
X		else 
X			x = 0;
X		ch = getchar();			/* Get one */
X		if (ch == EOF) {		/* If EOF.... */
X			line[index] = 0;
X			puts("");
X			x = 0;
X			break;
X		}
X		if ((ch == 24) || (ch == 21)) {	/* Cancel request... */
X			puts("\n[Cancelled]");
X			alarm(0);
X			return(0);
X		}
X		if ((ch == 13) || (ch == 10)) {	/* End of line */
X			line[index] = 0;
X			if (!echo) 
X				puts("");
X			alarm(0);
X			if (!index) 
X				return(0);
X			return(-1);
X		}
X		if (ch == '^')
X			ch = 10;
X		if (ch == '}')
X			ch = 10;
X		if ((ch < 32) || (ch > 126)) continue;	/* Do nothing */
X		line[index++] = ch;		/* Add the character */
X		if (!echo) {
X			putchar(ch);
X			fflush(stdout);
X		}
X	}					/* Falling off end... */
X	x = -1;					/* Init return code */
X	if (!index)
X		return(0);			/* Nothing on the line */
X	if (index == 68) {			/* Look for break location */
X		index--;
X		y = line[index--];		/* Get last character */
X		while ((index > 0) && (y != ' ')) {	/* Find last space */
X			if (!echo) {		/* Do we echo?? */
X				putchar(8);
X				putchar(32);		/* Cover thy spaces! */
X				putchar(8);
X			}
X			y = line[index--];		/* Check next back */
X		}
X		if (!index) 	
X			return(-1);		/* No break, send it */
X		index++;				/* Break at next */
X		line[index] = 0;			/* Store break */
X		ch = line[1];
X		if (ch >= 'a') ch = tolower(ch);	/* Make lower case */
X		if ((line[0] == '/') && ((ch == 'p') || (ch == 't'))) {
X			sprintf(tmpx, "%s", &line[2]);
X			sscanf(tmpx, " %[!-~]", tmpy);	/* Get name */
X			strncpy(line2, line, 2);
X			line2[2] = 0;
X			strcat(line2, tmpy);		/* Add name */
X			strcat(line2, " ");
X			strcat(line2, &line[index + 1]);
X		} else 
X			strcpy(line2, &line[index + 1]);/* Copy rest.. */
X		x = strlen(line2);			/* Get length */
X		if (!x) 
X			x = -1;			/* Return length */
X		if (!echo) 
X			puts("");		/* New line */
X	}
X	alarm(0);					/* No alarms please */
X	return(x);					/* Exit w/status */
X}
X
X/*	And here we find a variety of signal catching routines.
X
X	catch() exists only to satisfy the alarm call requirements.
X
X	The rest are exits of various kinds, some forced by external
X	circumstances, others requested by either the daemon or the user.
X
X*/
X
catch()
X{
X	signal(SIGALRM, catch);
X	return;
X}
X
servdied()
X{
X	puts("[Server disconnected]");
X	die1();
X}
X
samename()
X{
X	puts("Only one chat per user id allowed!");
X	die1();
X}
X
toss()
X{
X	puts("You have been removed from CHAT by another user");
X	die1();
X}
X
full()
X{
X	puts("CHAT is full, please try again later");
X	die1();
X}
X
die1()
X{
X	puts("Bye-bye");
X	unlink(filen);
X	ioctl(0,TCSETAW,&oldbuf);
X	exit(0);
X}
X
X/*	This is it... not many comments, but then again this is
X	pretty simple code... Shouldn't be hard to understand..
X
X*/
X
main(argc, argv)
int	argc;
char	*argv[];
X
X{
X	char	temp[132];
X	char	line[132], line2[132];
X	char	victim[20], msg[80];
X	int	out;
X	int	cp;
X	FILE	*ttynq;
X	int	fid, wr;
X	char	key;
X	int	x;
X	struct	passwd	*psw;
X	struct utmp *ptr, *user, *getutent();
X	char	name[20];
X	char	cmd[5] ;
X	int	channel = 0;
X
X	umask(0117);
X	if (argc == 2) 
X		strcpy(name, argv[1]);
X	else {
X		psw = getpwuid(getuid());	/* A shot at the user */
X		strcpy(name, psw->pw_name);	/* Pick up a starting name */
X	}
X	fprintf(stderr,"Link/Chat V2.2, %s\n", name);
X	strcpy(cmd,"z");			/* Base command */
X	signal(SIGALRM,catch);			/* Do nothing here */
X	signal(SIGQUIT, toss);			/* Toss user */
X	signal(SIGINT, die1);			/* Quit if requested */
X	signal(SIGUSR1, full);			/* No channels available */
X	signal(SIGUSR2, samename);		/* No second by same name */
X	signal(SIGPIPE, servdied);		/* The server was killed */
X	out = 0;
X	sprintf(line, "%spipe%d", CHATDIR, getpid());/* infeed */
X	strcpy(filen, line);			/* Save for later */
X
X/* This had better not fail; if it does, then you're effectively screwed as
the server cannot find you.  One way around this is to put these in the /tmp
directory, which is nearly always writable.  We do that here	*/
X
X	if (mknod(line, 010770, 0)) {
X		puts("Insufficient privilege for communication channel;");
X		perror("mknod");
X		puts("Please contact an administrator");
X		exit();
X	}
X	ioctl(0,TCGETA,&tbuf);			/* Get working copy */
X	ioctl(0,TCGETA,&oldbuf);		/* And copy for restore later */
X	tbuf.c_lflag &= ~(ICANON|ECHO|ISIG);	/* Clean input, echo */
X	tbuf.c_cc[4] = 1;			/* 1 character, 1 second */
X	tbuf.c_cc[5] = 1;
X	ioctl(0,TCSETAW,&tbuf);			/* Set tty */
X	strcpy(temp, CHATDIR);			/* THIS one is private */
X	strcat(temp, "pipe");
X	if ((wr = open(temp, O_WRONLY|O_NDELAY)) == -1) {
X		puts("Wait, attempting to start comserver");
X		execcmd("comserver");
X		for (x = 0; x < MAXTRIES; x++) {
X			if ((wr = open(temp, O_WRONLY|O_NDELAY)) != -1) 
X				break;
X#ifdef	NAP
X			nap(NAPTIME);
X#else
X			sleep(NAPTIME);
X#endif
X
X		}
X		if (wr == -1) {
X			puts("Cannot establish communications, please try later");
X			ioctl(0,TCSETAW,&oldbuf);	/* Restore terminal */
X			unlink(line);
X			exit(1);
X		}
X	}
X	sprintf(temp, "%d o %d %s >%s\n", getpid(), channel, name, name);
X	write(wr, temp, LINESIZ);		/* Write one buffer. */
X	alarm(10);
X	fid = 0;				/* Make pipe id impossible */
X	fid = open(line, O_RDONLY);		/* Open pipe for reading */
X	if (fid <= 0) {
X		ioctl(0,TCSETAW,&oldbuf);	/* Leave if no server */
X		unlink(line);
X		exit(1);
X	}
X	setgid(getgid());			/* Turn off permissions */
X	setuid(getuid());			/* We have the files open */
X	alarm(0);				/* Cancel outstanding alarm */
X	sprintf(temp, "%d %s %d %s >%s\n",getpid(), "s", channel, name, "test");
X	write(wr, temp, LINESIZ);
X	while (!out) {			/* While still scanning */
X		alarm(1);			/* Interrupt in one second */
X		while (read(fid, temp, LINESIZ) == LINESIZ) {
X			stripj(temp);		/* Read/print while available */
X			puts(temp);		/* Get next line */
X			alarm(1);		/* Reset alarm */
X		}
X		if (getline(line)) {	/* Did we get user input? */
X			if (*line == '/') {	/* Command request... */
X				switch(line[1]) {
X					case 'p':	/* Private message */
X					case 'P':
X						if (sscanf(&line[2], " %s %[!-~ ]", victim, msg) == 2) {
X							sprintf(temp, "%d %s %d %s >%s\n",getpid(), "p", channel, victim, msg);
X							write(wr, temp, LINESIZ);
X						} else puts("Requires user and message");
X						break;
X					case 't':	/* Tell someone */
X					case 'T':
X						if (sscanf(&line[2], " %s %[!-~ 	]",victim, msg) < 2) {
X							puts("Chat/Link: Requires victim and message");
X							break;
X						}
X						x = 0;
X						setutent();	/* Find user */
X						while ((ptr = getutent()) != NULL)   {
X							if (!strncmp(ptr->ut_user, victim, (strlen (victim) -1)))   {
X								user = ptr;
X								x = 1;
X								break;
X							}
X						}
X						if ( !x)   {
X							printf ("User is not logged on\n");
X							break;
X						}
X						strcpy (temp, "/dev/");
X						strcat (temp, user->ut_line);
X						alarm(10);
X						if ((ttynq = fopen (temp, "w")) == NULL)   {
X							printf ("User %s has disabled writes\n", user->ut_user);
X						} else {
X							fclose (ttynq);
X							sprinf(temp, "echo \"\n%c;;From ", 7);
X							strcat (temp, name);
X							strcat (temp, ": ");
X							strcat (temp, msg);
X							strcat (temp, " \" >/dev/");
X							strcat (temp, user->ut_line);
X							alarm(10);
X							system (temp);
X						}
X						alarm(0);
X						break;
X					case 'k':	/* Request kill */
X					case 'K':
X						sprintf(temp, "%d %s %d %s >%s\n",getpid(), "k", channel, &line[2], "test");
X						write(wr, temp, LINESIZ);
X						break;
X					case 'c':
X					case 'C':
X						sprintf(temp, "%d %s %d %s >%s\n", getpid(), "t", atoi(&line[2]), " ", " ");
X						channel = atoi(&line[2]);
X						write(wr, temp, LINESIZ);
X						break;
X					default:	/* Bad command */
X						break;
X				}
X			} else {		/* Otherwise send the line */
X				sprintf(temp, "%d %s %d %s >%s\n", getpid(), cmd, channel , name, line);
X				write(wr, temp, LINESIZ);
X			}
X		}
X	}
X}
X
END_OF_FILE
if test 8909 -ne `wc -c <'link.c'`; then
    echo shar: \"'link.c'\" unpacked with wrong size!
fi
# end of 'link.c'
fi
if test -f 'server.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'server.c'\"
else
echo shar: Extracting \"'server.c'\" \(10092 characters\)
sed "s/^X//" >'server.c' <<'END_OF_FILE'
X/*	Server.c	- server process */
X
X#include	"chat.h"
X#include	"decl.h"
X
X
XFILE *fdup(fp)
register FILE *fp;
X{
X	int newfd;
X	char *mode;
X
X	if ((newfd = dup(fileno(fp))) != -1) {
X		return(fdopen(newfd,"r+"));
X	} else return(NULL);
X}
X
struct	termio	tbuf, oldbuf;
X
die1()
X{
X	exit(1);
X}
X
main(argc, argv)
int	argc;
char	*argv[];
X{
X	
X	FILE	*fid2;
X	char	temp[132];
X	char	savename[21];
X	char	names[400];
X	char	alias[400];
X	int	pids[20], channels[20];
X	int	fids[20], fid;
X	int	count = 0, channel, chan;
X	char	cmd[5];
X	char	name[40], nm[40];
X	char	msg[132];
X	int	pid;
X	char	tmp[132], tmp2[132], tmp3[132];
X	int	x, y, z, out;
X	int	keep = 0;
X	int	anywrite = 0;
X
X	if (argc > 2)
X		printf("[ComServer V%s]\n", VERSION);
X	sprintf(tmp, "%spipe", CHATDIR);
X	unlink(tmp);				/* Insure nothing here */
X	if (mknod(tmp, 010770, 0)) {
X		puts("Cannot make master pipe file");
X		exit(1);
X	}
X	chown(tmp, geteuid(), getegid());	/* Set to proper uid, gid */
X	chmod(tmp, 0660);			/* Set file mode */
X	if (*argv[1] != 'f') {			/* Only if not 'comserver f' */
X	if (fork() > 0) 
X		exit(0);			/* Successfully in background */
X
X/* We are now in the child process, and have everything ready to go.
X   Detach from the terminal, then make a new control terminal and go to it */
X
X	close(0);
X	close(1);
X	close(2);
X	fclose(stdin);
X	fclose(stdout);
X	fclose(stderr);
X	setpgrp();				/* Disassociate terminal */
X	if (argc > 2)
X		fopen("/dev/console", "r+");	/* Make Control terminal */
X	else
X		fopen("/dev/null", "r+");	/* Make null terminal */
X	fdup(stdin);
X	fdup(stdin);
X	setbuf(stdin, NULL);
X	setbuf(stdout, NULL);
X	}
X	fid = open(tmp, O_RDONLY, 0660);	/* Open pipe file */
X	unlink("/tmp/chat.lock");
X	printf("[ComServer] V; startup\n", VERSION);
X	while (read(fid, tmp, LINESIZ) == LINESIZ) {
X	strcpy(temp, tmp);
X	signal(SIGPIPE,SIG_IGN);		/* Ignore writes on pipes */
X	if (sscanf(temp, " %d %s %d %s >%[!-~ 	]", &pid, cmd, &channel, name, msg) >= 2) {
X		puts(temp);
X		switch (*cmd) {
X			case 'k':		/* Kill user */
X				x = 0;
X				while (x < count) {
X					if (pids[x] == pid) {
X						out = fids[x];
X						break;
X					}
X					x++;
X				}
X				x = 0;
X				while (x < count) {
X					if (!strcmp(&names[x*20], name)) {
X						kill(pids[x], SIGQUIT);
X						sprintf(tmp, "[Server]: Killed %s (%d)\n", name, pids[x]);
X						write(out, tmp, LINESIZ);
X						y = x;
X						sprintf(tmp, "%s/pipe%d", CHATDIR, pids[x]);
X						unlink(tmp);
X						break;
X					}
X					x++;
X				}
X				if (x == count) {
X					sprintf(tmp, "No user: %s\n", name);
X					write(out, tmp, LINESIZ);
X				}
X				break;
X			case 'p':		/* Send private message */
X				x = 0;
X				while (x < count) {
X					if (pids[x] == pid) {
X						strcpy(tmp, &names[20*x]);
X						out = fids[x];
X						break;
X					}
X					x++;
X				}
X				x = 0;
X				while (x < count) {
X					if (!strcmp(&names[x*20], name)) {
X						sprintf(temp, "Prv from [%s]: %s\n", tmp, msg);
X						write(fids[x], temp, LINESIZ);
X						sprintf(tmp, "Prv sent to %s\n", name);
X						write(out, tmp, LINESIZ);
X						break;
X					}
X					x++;
X				}
X				if (x == count) {
X					sprintf(temp, "User not in chat: %s\n", name);
X					write(out, temp, LINESIZ);
X				}
X				break;
X			case 's':		/* Request for status */
X				x = 0;
X				printf("Send status to %d\n",pid);
X				while (x < count)  {
X					printf("Pid: %d and %d\n", pids[x], pid);
X					if (pids[x] == pid) {
X						y = fids[x];
X						chan = channels[x];
X						break;
X					}
X					x++;
X				}
X				if (x == count) 
X					break;	/* Invalid */
X				printf("Sending via fid %d\n",y);
X				sprintf(tmp, "");
X				write(y, tmp, LINESIZ);
X				sprintf(tmp, "Chat user   ---Alias---   Chan/Flag\n");
X				write(y, tmp, LINESIZ);
X				anywrite = 0;
X				x = 0;
X				while (x < count) {
X					if (!strcmp(&names[x*20], &alias[x*20]))
X						sprintf(tmp, "%-10s   ** None ** ",&names[x*20]);
X					else
X						sprintf(tmp, "%-10s  (%-10s)",&names[x*20], &alias[x*20]);
X					if ((channels[x] == chan) && (pid != pids[x])) {
X						sprintf(tmp2, "  Channel %d", channels[x]);
X						strcat(tmp, tmp2);
X					} else {
X						if (pid == pids[x]) {
X							sprintf(tmp2, "  Channel %d", channels[x]);
X							strcat(tmp, tmp2);
X						} else {
X							if (channels[x]) {
X								strcat(tmp, "  Private chat");
X							} else {
X								strcat(tmp, "  Public channel");
X							}
X						}
X					}
X					strcat(tmp, "\n");
X					write(y, tmp, LINESIZ);
X					anywrite++;
X					x++;
X				}
X				if (anywrite < 2) {
X					sprintf(tmp, "{No other users in CHAT}");
X					write(y, tmp, LINESIZ);
X				}
X				sprintf(tmp, " ");
X				write(y, tmp, LINESIZ);
X				break;
X			case 'o':	/* Open a new channel */
X				x = 0;
X				while (x < count) {	/* Only one per name */
X					if (!strcmp(name, &names[x*20])) {
X						kill(pid, SIGUSR2);
X						break;
X					}
X					x++;
X				}
X				if (x != count) break;
X				if (count > 15) {
X					kill(pid, SIGUSR1);	/* Special */
X					x = 0;
X					sprintf(tmp, "[Server]: Full, refused %s\n", name);
X					while (x < count) {
X						write(fids[x++], tmp, LINESIZ);
X					}
X					
X					break;
X				}
X				printf("Opening channel: %d\n",pid);
X				sprintf(tmp, "%s/pipe%d", CHATDIR, pid);
X				strcpy(&names[count*20],name);
X				strcpy(&alias[count*20],name);
X				printf("Open pipe file %s\n", tmp);
X				if ((fids[count] = open(tmp,O_WRONLY)) < 0) {
X					perror("Pipe failure:");
X					puts("(someone will be disappointed!)");
X					break;
X				}
X				x = fcntl(fids[count], F_GETFL, 0);
X				fcntl(fids[count], F_SETFL, x |= O_NDELAY);
X				pids[count] = pid;
X				channels[count] = 0;
X				count++;
X				x = 0;
X				printf("Added pid %d\n",pids[count - 1]);
X				sprintf(tmp, "[Connected V%s]\n\n", VERSION);
X				write(fids[count - 1], tmp, LINESIZ);
X				sprintf(tmp, "%c>> Welcome %s.\n", 7, name);
X				while (x < count) {
X					write(fids[x++], tmp, LINESIZ);
X				}
X				break;
X			case 'n':	/* Change name (alias) */
X				x = 0;
X				while (x < count) {
X					if (pid == pids[x]) break;
X					x++;
X				}
X				if (x == count) break;	/* Bad request */
X				printf("Alias: %s to %s\n", &names[x*20], msg);
X				strncpy(&alias[x*20], msg, 20);
X				alias[(x*20)+19] = 0;
X				sprintf(tmp, "[Server]: %s aliased to (%s)\n", &names[x*20], &alias[x*20]);
X				write(fids[x], tmp, LINESIZ);
X				break;
X			case 't':
X				sprintf(tmp, "[Server]: Channel changed to %d\n", channel);
X				chan = 0;
X				for (x = 0; x < count; x++) {
X					if (pids[x] == pid) {
X						chan = channels[x];
X						if (!strcmp(&names[x*20], &alias[x*20]))
X							sprintf(tmp2, "[Server]: %s joined channel\n", &names[x*20]);
X						else
X							sprintf(tmp2, "[Server]: %s (%s) joined channel\n", &names[x*20], &alias[x*20]);
X						if (!strcmp(&names[x*20], &alias[x*20]))
X							sprintf(tmp3, "[Server]: %s has left this channel\n", &names[x*20]);
X						else
X							sprintf(tmp3, "[Server]: %s (%s) has left this channel\n", &names[x*20], &alias[x*20]);
X						break;
X					}
X				}
X				for (x = 0; x < count; x++) {
X					if (channels[x] == channel) {
X						write(fids[x], tmp2, LINESIZ);
X					}
X					if (channels[x] == chan) {
X						write(fids[x], tmp3, LINESIZ);
X					}	
X					if (pids[x] == pid) {
X						channels[x] = channel;
X						write(fids[x], tmp, LINESIZ);
X					}
X				}
X				break;
X			case 'q':	/* Kill a user */
X				x = 0;
X				while (x < count) {
X					if (pid == pids[x]) break;
X					x++;
X				}
X				if (x == count) {
X					puts("Bad quit request");
X					break;	/* Bad request */
X				}
X				printf("Leaving: %s (%d left)\n",&names[x*20],(count - 1));
X				sprintf(temp, "%s/pipe%d", CHATDIR, pid);
X				unlink(temp);		/* Remove pipe file */
X				strcpy(savename, &names[20*x]);
X				y = x;
X				while (y < count) {
X					pids[y] = pids[y + 1];
X					fids[y] = fids[y + 1];
X					strcpy(&names[y*20], &names[(y+1)*20]);
X					strcpy(&alias[y*20], &alias[(y+1)*20]);
X					y++;
X				}
X				count--;		/* Fall through */
X			case 'w':	/* Write a message */
X				x = 0;
X				while (x < count) {
X					if (pid == pids[x]) break;
X					x++;
X				}
X				if (x != count) strcpy(savename, &alias[x*20]);
X				printf("Write message: %s %s\n",savename, msg);
X				sprintf(tmp, "%s: %s\n",savename, msg);
X				savename[0] = 0;	/* Clear name */
X				x = 0;
X				while (x < count) {
X					if (channels[x] != channel) {
X						x++;
X						continue;
X					}
X					if (pid == pids[x]) {
X						x++;
X						continue;
X					}
X					if ((write(fids[x], tmp, LINESIZ) == -1) && (errno = EPIPE)) {
X						y = x;
X						z = pids[x];
X						strcpy(nm, &names[x*20]);
X						while (y < count) {
X							fids[y] = fids[y+1];
X							pids[y] = pids[y+1];
X							strcpy(&names[y*20],&names[(y + 1)*20]);
X							strcpy(&alias[y*20], &alias[(y+1)*20]);
X							y++;
X						}
X						count--;
X						y = 0;
X						sprintf(tmp, "%s/pipe%d", CHATDIR, z);
X						unlink(tmp);
X						sprintf(tmp, "[Server]: %s disappears in a puff of smoke!\n",nm);
X						while (y < count) 
X							write(fids[y++],tmp,LINESIZ);
X						x--;
X					}
X					x++;
X				}
X				break;
X			case 'z':	/* Write QUIET */
X				x = 0;
X				while (x < count) {
X					if (pid == pids[x]) break;
X					x++;
X				}
X				if (x != count) strcpy(savename, &alias[x*20]);
X				printf("Write QUIET message: %s %s\n",savename, msg);
X				sprintf(tmp, "%s:%s\n",savename, msg);
X				savename[0] = 0;	/* Clear name */
X				x = 0;
X				while (x < count) {
X					if (channels[x] != channel) {
X						x++;
X						continue;
X					}
X					if (pid == pids[x]) {
X						x++;
X						continue;
X					}
X					if ((write(fids[x], tmp, LINESIZ) == -1) && (errno = EPIPE)) {
X						y = x;
X						z = pids[x];
X						strcpy(nm, &names[x*20]);
X						while (y < count) {
X							fids[y] = fids[y+1];
X							pids[y] = pids[y+1];
X							strcpy(&names[y*20],&names[(y + 1)*20]);
X							strcpy(&alias[y*20], &alias[(y+1)*20]);
X							y++;
X						}
X						count--;
X						y = 0;
X						sprintf(tmp, "%s/pipe%d", CHATDIR, z);
X						unlink(tmp);
X						sprintf(tmp, "[Server]: %s disappears in a puff of smoke!\n",nm);
X						while (y < count) 
X							write(fids[y++],tmp,LINESIZ);
X						x--;
X					}
X					x++;
X				}
X				break;
X			default:
X				x = 0;
X				while (x < count) {
X					if (pids[x] == pid) {
X						y = fids[x];
X						break;
X					}
X					x++;
X				}
X				if (x == count) break;	/* Invalid */
X				sprintf(tmp, "Illegal server request\n");
X				write(y, tmp, LINESIZ);
X		}
X	}
X	}
X}
END_OF_FILE
if test 10092 -ne `wc -c <'server.c'`; then
    echo shar: \"'server.c'\" unpacked with wrong size!
fi
# end of 'server.c'
fi
echo shar: End of shell archive.
exit 0

--
Karl Denninger (karl@ddsw1.MCS.COM, <well-connected>!ddsw1!karl)
Public Access Data Line: [+1 312 566-8911], Voice: [+1 312 566-8910]
Macro Computer Solutions, Inc.		"Quality Solutions at a Fair Price"