/* MCM  -  MultiUser Communications Manager

   Copyright (C) 1991,2 Michael Sawyer

   This code may be reproduced without charge provided that this
   notice remains intact on all copies distributed.

   Any part of this code may be used in other programs provided that
   some citation of this work is given.
*/

#include "mcm.h"

#ifndef	lint
static char mcm_sourceid[] =
    "@(#)mcm.c,v 1.2 1992/08/18 00:40:00 msawyer";
#endif

#ifdef NEED_USLEEP
/*-
 * usleep.c - OS dependant implementation of usleep().
 *
 * Copyright (c) 1991 by Patrick J. Naughton.
 *
 * Revision History:
 * 30-Aug-90: written.
 *
 */

#include <sys/types.h>
#include <sys/time.h>

#ifndef	lint
static char sleep_sourceid[] =
    "@(#)usleep.c,v 1.4 1992/06/28 05:38:33 bjoerns Exp";
#endif


int
usleep(usec)
    unsigned long usec;
{
/*#ifdef SYSV
    poll((struct poll *) 0, (size_t) 0, usec / 1000);	/* ms RES */
/*#else*/
    struct timeval timeout;
    timeout.tv_usec = usec % (unsigned long) 1000000;
    timeout.tv_sec = usec / (unsigned long) 1000000;
    (void) select(0, (void *) 0, (void *) 0, (void *) 0, &timeout);
/*#endif*/
    return 0;
}
#endif

void bailout(exitcode, message)
int exitcode;
char message[];
{
  int i;
  char *textptr;
  char output[256];

  switch (exitcode)
  {
  case 1:
  case 2:
    sprintf (output,"(sys) SERVER FAILED SANITY CHECK: %s\r\n",message);
    perror (message);
    break;
  case 0:
    sprintf (output,"(sys) SERVER SHUTDOWN: %s\r\n",message);
    break;
  case 3:
    sprintf (output,"(sys) SERVER FAILURE: %s\r\n",message);
    break;
  default:
    strcpy (output,"(sys) UNKNOWN SERVER FAILURE --- Shutting down\r\n");
  }
  fputs (output,stderr);
  fputs (output,logfp);
  fputs (output,masterfp);
  fclose (masterfp);
  for (i=0;i<NUMJOBS;i++)
    if (job[i].fd!=0)
    {
      write (job[i].fd,output,strlen(output));
      close (job[i].fd);
    }
  if (s!=0)
  {
    close (s);
  }
  exit (exitcode);
}

void sigprocessor(sig)
int sig;
{
  char string[256];
  FILE *fp;
  int i;

  switch (sig)
  {
  case SIGUSR1:
    sprintf (string,"/tmp/%d-message",portid);
    if ((fp=fopen(string,"r"))==NULL)
      return; /* There is no message there */
    fgets (string,80,fp);
    fclose (fp);
    string[strlen(string)-1]=0;
    tell (-1,-1,"(sys)",string);
    sprintf (string,"rm /tmp/%d-message",portid);
    system (string);
    signal (SIGUSR1,sigprocessor);
    return;
  case SIGTERM:
  case SIGINT:
  case SIGQUIT:
  case SIGABRT:
    bailout (0,"Shutdown from system operator");
  case SIGILL:
    bailout (3,"Illegal Instruction Trap");
  case SIGEMT:
    bailout (3,"Emulator Trap");
  case SIGFPE:
    bailout (3,"Floating Point Trap");
  case SIGBUS:
    bailout (3,"Bus Error Trap");
  case SIGSEGV:
    bailout (3,"Segment Violation Trap");
  case SIGSYS:
    bailout (3,"System Trap");
  default:
    bailout (3,"Unknown system fault");
  }
}

int testjob(jobid)
int jobid;
{
  if ((jobid<0)||(jobid>NUMJOBS))
    return FALSE;
  if (job[jobid].mode!=3)
    return FALSE;
  return TRUE;
}

int main(argc,argv)
int argc;
char **argv;
{
  int i; 
  long g;
  time_t tm, last_tm;
  char string[80];

  msgtext[0]=0;
  s=0;
  signal (SIGUSR1,sigprocessor);
  signal (SIGUSR2,per_sig2);
  signal (SIGHUP,SIG_IGN);
  signal (SIGINT,sigprocessor);
  signal (SIGQUIT,sigprocessor);
  signal (SIGILL,sigprocessor);
  signal (SIGABRT,sigprocessor);
  signal (SIGEMT,sigprocessor);
  signal (SIGFPE,sigprocessor);
  signal (SIGBUS,sigprocessor);
  signal (SIGSEGV,sigprocessor);
  signal (SIGSYS,sigprocessor);
  signal (SIGPIPE,SIG_IGN);
  signal (SIGALRM,SIG_IGN);
  signal (SIGTERM,sigprocessor);
  signal (SIGURG,SIG_IGN);

  listen_mode= -1;
  snoopmode=FALSE;
  confhear=TRUE;
  conftalk=TRUE;
  whogagged=1;
  asstleader=0;
  masterfp=NULL;
  msgcount=0;
  for (i=0;i<NUMJOBS;i++)
  {
    job[i].buf[0]=0;
    job[i].name[0]=0;
    job[i].from[0]=0;
    job[i].mode=0;
    job[i].gagged=0;
    job[i].privconf=0;
    job[i].maybeleader=0;
#ifdef TERMCAP
    job[i].termtype[0]=0;
#endif
  }

  if (argc!=2)
  {
    fputs ("Usage: mcm portid\r\n",stderr);
    exit (1);
  }
  portid=atoi(argv[1]);
  if ((masterfp=fopen("master-log","w"))==NULL)
  {
    perror ("master-log");
    bailout(2,"open master-log");
  }
  if ((logfp=fopen("mcm-log","w"))==NULL)
  {
    perror ("mcm-log");
    bailout(2,"open mcm-log");
  }
  time(&last_tm);
  fprintf (logfp,"(sys) Startup on port %d at %s",portid,ctime(&last_tm));
  fprintf (masterfp,"(sys) Startup on port %d at %s",portid,ctime(&last_tm));
  sin.sin_family=AF_INET;
  sin.sin_addr.s_addr=htonl(INADDR_ANY);
  sin.sin_port=htons(portid);
  if ((s=socket(AF_INET,SOCK_STREAM,0))<0)
    bailout (2,"mcm socket");
  i=1;
  setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i));
  if (bind(s, (struct sockaddr_in *)&sin,sizeof (struct sockaddr_in))<0)
    bailout (2,"mcm bind");
  listen (s,5);

  i=1; /* We are going to turn on non-blocking IO */
  if (ioctl(s,FIONBIO,&i)<0)
    bailout (2,"mcm ioctl_nbio");
  if (ioctl(s,FIOASYNC,&i)<0)
    bailout (2,"mcm ioctl_async");
  i=0;

  setpriority (PRIO_PROCESS,getpid(),HOWNICE); /* Make this nice */
  srand48 (getpid());

  per_init();

#ifdef DELTAT
  last_tm=(last_tm/DELTAT)*DELTAT;
#endif
  while (TRUE)
  {
#ifdef DELTAT
    time(&tm);
    if (tm-last_tm>=DELTAT)
    {
      sprintf (string,"%s",ctime(&tm));
      tell (-1,-1,"(time)",string);
      last_tm=(tm/DELTAT)*DELTAT;
    }
#endif
    check_connections(); /* Check for new connections */
    if (communicate()==FALSE)
    {
      usleep (DELAYTIME);
      fflush (logfp);
      fflush (masterfp);
    }
    per_timestep();
  }
}

void check_connections()
{
  int newfd;
  int i;
  char string[256];
  char *name;

  i=sizeof(sin);
  newfd=accept(s,(struct sockaddr *)&sin,&i);
  if (newfd<0)
    return; /* Nothing there... */
  setsockopt(s,SOL_SOCKET,SO_LINGER,&i,sizeof(int));
  for (i=1;i<NUMJOBS;i++)
  {
    if (job[i].fd==0)
    {
      job[i].fd=newfd;
#ifdef TERMCAP
      job[i].termtype[0]=0;
#endif
      job[i].addrs[0]=sin.sin_addr.S_un.S_un_b.s_b1;
      job[i].addrs[1]=sin.sin_addr.S_un.S_un_b.s_b2;
      job[i].addrs[2]=sin.sin_addr.S_un.S_un_b.s_b3;
      job[i].addrs[3]=sin.sin_addr.S_un.S_un_b.s_b4;
      job[i].firstrun=TRUE;
      job[i].buf[0]=0;
      /* Set line-at-a-time mode */
      write (job[i].fd,"\377\374\001\377\374\003",6);
      sprintf (string,"Connection established on port %d job id %d",
	       portid, i);
      tell (-1,i,"(sys)",string);
      if (!access("mcm-welcome",R_OK))
	usertype (i,"mcm-welcome");
      tell (-1,i,"","Enter your name:");
      job[i].mode=1;
      return;
    }
  }
  write (newfd,"Sorry, job limit has been reached.\r\n",35);
  close (newfd);
}

int communicate()
{
  int anyhits;
  int i;
  int j;
  int len;
  char inbuf[255];
  char outbuf[512];
  char *textptr;

  anyhits=FALSE;
  for (i=0;i<NUMJOBS;i++)
  {
    if (job[i].fd!=0)
    {
      errno=0;
      if ((len=read(job[i].fd,inbuf,255))<=0)
      {
	if (errno!=EWOULDBLOCK)
	{
	  if (testjob(i))
	    tell (i,-1,"(sys)","DISCONNECTED");
	  job[i].mode=0;
	  endsession (i);
	}
      }
      else /* we got some data */
      {
	anyhits=TRUE;
	for (j=0;j<len;j++)
	{
	  if (isprint(inbuf[j]))
	  {
	    if (strlen(job[i].buf)<250)
	    {
	      job[i].buf[strlen(job[i].buf)+1]=0;
	      job[i].buf[strlen(job[i].buf)]=inbuf[j];
#ifdef TERMCAP
	      if (job[i].termtype[0]!=0)
		splchar (i,inbuf[j]);
#endif
	    }
	  }
	  else if (inbuf[j]=='\377')
	  {
	    j++;
	    if (j>='\373')
	      j++;
	  }
	  else if ((inbuf[j]=='\b')||(inbuf[j]=='\177'))
	  {
	    if (strlen(job[i].buf)>0)
	    {
	      job[i].buf[strlen(job[i].buf)-1]=0; /* Backspace */
#ifdef TERMCAP
	      if (job[i].termtype[0]!=0)
		splbackup (i);
#endif
	    }
	  }
	  else if (inbuf[j]=='\014')
	  {
#ifdef TERMCAP
	    if (job[i].termtype[0]!=0)
	      splredraw(i);
#endif
	  }
	  else if ((inbuf[j]=='\025')||(inbuf[j]=='\030'))
	  {
	    job[i].buf[0]=0;
#ifdef TERMCAP
	    if (job[i].termtype[0]!=0)
	    {
	      splnewinp(i);
	    }
#endif
	  }
	  else if (inbuf[j]=='\027')
	  {
	    if ((textptr=strrchr(job[i].buf,' '))!=NULL)
	    {
	      textptr[0]=0;
#ifdef TERMCAP
	      if (job[i].termtype[0]!=0)
		splredraw(i);
#endif
	    }
	  }
	  else if ((inbuf[j]=='\r')||
		   ((inbuf[j]=='\n')&&(inbuf[j+1]!='\r')&&(inbuf[j-1]!='\r')))
	  {
	    process (i,job[i].buf);
	    job[i].buf[0]=0;
#ifdef TERMCAP
	    if (job[i].termtype[0]!=0)
	    {
	      splnewinp(i);
	    }
#endif
	  }
	}
      }
    }
  }
  return (anyhits);
}

/* Word wrap a string.*/
void wwrap(string)
char *string;
{
  char newstring[512];
  char *stpos, *stsow, *steow, *nspos, *nssol;
  int len=80;

  if (strchr(string,'\n')!=NULL)
    return; /* Already wrapped */
  nssol=newstring;
  nspos=newstring;
  newstring[0]=0;
  stpos=string;
  stsow=string;
  for (;;)
  {
    if ((steow=strchr(stpos,' '))==NULL)
    {
      if (strlen(nssol)+strlen(stpos)>len-5)
      {
	*nspos++='\r';
	*nspos++='\n';
	nssol=nspos;
      }
      for (;stpos[0]!=0;stpos++)
	*nspos++= *stpos;
      *nspos=0;
      sprintf (string,"%s\r\n",newstring);
      return;
    }
    *steow=0;
    if (strlen(nssol)+strlen(stpos)>len-5)
    {
      *nspos++='\r';
      *nspos++='\n';
      nssol=nspos;
    }
    for (;stpos<steow;stpos++)
      *nspos++= *stpos;
    *nspos++=' ';
    *nspos=0;
    stpos++;
  }
}

/* New tell routines */

void txout(msgfrom, msgto, system, text, privmsg)
int msgfrom, msgto, privmsg;
char *system, *text;
{
  int i;

  /* -1 means 0 and ass't leader (Yeah, I know its ugly to have txout */
  /* to -1 mean something different from tell -1...  Sorry. */
  if (msgto== -1)
    i=0;
  else
    i=msgto;
  if (!validjob(i))
  {
    return;
  }

#ifdef TERMCAP
  if (job[i].termtype[0]!=0)
    spltext(msgfrom, i, system, text, privmsg);
  else
#endif
    easytext(msgfrom, i, system, text, privmsg);
  if ((msgto== -1)&&(asstleader!=0)&&(msgfrom>=0)&&(msgfrom!=asstleader))
    txout (msgfrom,asstleader,system,text,privmsg);
}

void tell(msgfrom, msgto, system, text)
int msgfrom, msgto;
char *system, *text;
{
  char string[512];
  char newsys[80];
  int i;

  if (!per_tell(msgfrom,msgto,system,text))
    return;
  /* First deal with private messages... */
  if (msgto>=0)
  {
    if (msgfrom<0) /* Its a system message; he can get it */
    {
      txout (msgfrom,msgto,system,text,TRUE);
    }
    else /* Its from someone */
    {
      /* Make sure that the jobs are valid */
      if (!testjob(msgfrom))
	return;
      if (!testjob(msgto))
      {
	txout(-1,msgfrom,"(sys)","Attempt to send to invalid job.",TRUE);
	return;
      }

      /* First, check that if the rec. is on co, he can hear */
      if ((!confhear)&&(job[msgto].privconf)&&(msgto!=0)
	  &&(msgto!=asstleader)&&(msgfrom!=0)&&(msgfrom!=asstleader))
      {
	txout (-1,msgfrom,"(sys)","Users in conference cannot be contacted."
	       ,TRUE);
	return;
      }

      /* And check the sender also */
      if ((!conftalk)&&(job[msgfrom].privconf)&&(msgfrom!=0)
	  &&(msgfrom!=asstleader)&&(msgto!=0)&&(msgto!=asstleader))
      {
	txout (-1,msgfrom,"(sys)",
	      "You cannot send to users outside of conference.", TRUE);
	return;
      }

      /* Check gagging */
      if ((job[msgfrom].gagged)&&(msgto!=0)&&(msgto!=asstleader))
      {
	txout (-1,msgfrom,"(sys)","You are gagged.",TRUE);
	return;
      }

      /* Check for deaf users */
/*      if ((job[msgto].deaf)&&(msgfrom!=0)&&(msgfrom!=asstleader))
*      {
*	txout (-1,msgfrom,"(sys)","That user cannot hear you.",TRUE);
*	return;
*      }
*/

      /* OK, so now we can send the message */
      sprintf (newsys,"(private)%s",system);
      txout (msgfrom,msgto,newsys,text,TRUE);
      if (snoopmode)
      {
	sprintf (string,"%d-%s: %s",msgto,job[msgto].name,text);
	txout (msgfrom,-1,newsys,string,TRUE);
	sprintf (string,"%s %d-%s> %d-%s: %s",newsys,msgfrom,
		 job[msgfrom].name,msgto,job[msgto].name,text);
	wwrap (string);
	fputs (string,masterfp);
	return;
      }
    }
  }
  else /* Dealing with a public message; not as easy! */
  {
    msgcount++;
    if (msgfrom < 0) /* System global message */
    {
      sprintf (string," %s %s",system,text);
      wwrap (string);
      fputs (string,logfp);
      fputs (string,masterfp);
      for (i=0;i<NUMJOBS;i++)
      {
	if (testjob(i))
	  tell (-1,i,system,text);
      }
      return;
    }

    if (!testjob(msgfrom))
      return;
      
    /* Need to resolve msgto -3 first (All or me and operator) */
    if (msgto== -3)
    {
      if (  ((listen_mode!= -1)&&(listen_mode!=msgfrom)&&(msgfrom!=0))
	  || (job[msgfrom].gagged))
      {
	sprintf (newsys,"(private)%s",system);
	txout (msgfrom,msgfrom,newsys,text,FALSE);
	txout (msgfrom,-1,newsys,text,FALSE);
	return;
      }
      msgto= -1;
    }
    
    /* Now resolve -1 (all) */
    if (msgto== -1)
    {
      if (msgfrom>=0)
	msgto=job[msgfrom].privconf?-10:-2;
      else
	msgto= -2;
    }
    
    /* -2 (Public all) */
    if (msgto== -2)
    {
      if (job[msgfrom].gagged)
      {
	txout (-1,msgfrom,"(sys)",
	       "You are gagged; only room coord. will see your message",TRUE);
	sprintf (newsys,"(gag)%s",system);
	txout (msgfrom,-1,newsys,text,TRUE);
	sprintf (string,"%s %d-%s> %s",newsys,msgfrom,
		 job[msgfrom].name,text);
	wwrap (string);
	fputs (string,masterfp);
	return;
      }
      if ((job[msgfrom].privconf)&&(!conftalk)&&(msgfrom!=0)
	  &&(msgfrom!=asstleader))
      {
	txout (-1,msgfrom,"(sys)",
	       "Cannot send messages out of conference now.",TRUE);
	return;
      }
      if ((listen_mode!= -1)&&(msgfrom!=listen_mode)&&(msgfrom!=0))
      {
	txout (-1,msgfrom,"(sys)",
	       "Room listen-only; only room coord. will see your message"
	       ,TRUE);
	sprintf (newsys,"(lisn)%s",system);
	txout (msgfrom,-1,newsys,text,TRUE);
	sprintf (string,"%s %d-%s> %s",newsys,msgfrom,
		 job[msgfrom].name,text);
	wwrap (string);
	fputs (string,masterfp);
	return;
      }
      for (i=0;i<NUMJOBS;i++)
      {
	if (testjob(i))
	{
	  if ((confhear)||(!job[i].privconf)||(i==0)||(i==asstleader))
	    txout (msgfrom,i,system,text,FALSE);
	}
      }
      sprintf (string,"%s %d-%s> %s",system,msgfrom,
	       job[msgfrom].name,text);
      wwrap (string);
      fputs (string,masterfp);
      fputs (string,logfp);
      return;
    }
    
    /* -10 (All in conf) */
    if (msgto== -10)
    {
      if ((!job[msgfrom].privconf)&&(msgfrom!=0))
	/* Should never get here! */
	return;
      if ((job[msgfrom].gagged)&&(msgfrom!=0))
      {
	txout (-1,msgfrom,"(sys)",
	       "You are gagged; only room coord. will see your message",TRUE);
	sprintf (newsys,"(gag)%s",system);
	txout (msgfrom,-1,newsys,text,TRUE);
	sprintf (string,"%s %d-%s> %s",newsys,msgfrom,
		 job[msgfrom].name,text);
	wwrap (string);
	fputs (string,masterfp);
	return;
      }
      sprintf (newsys,"(conf)%s",system);
      for (i=0;i<NUMJOBS;i++)
      {
	if (testjob(i))
	{
	  if ((job[i].privconf)||(i==0)||(i==asstleader))
	    txout (msgfrom,i,newsys,text,FALSE);
	}
      }
      sprintf (string,"%s %d-%s> %s",newsys,msgfrom,
	       job[msgfrom].name,text);
      wwrap (string);
      fputs (string,masterfp);
      return;
    }
  }
}


void easytext(msgfrom, msgto, system, text, privmsg)
int msgfrom, msgto, privmsg;
char *system, *text;
{
  char outbuf[512];
  char tempstring[64];

  if (msgfrom>=0)
  {
    outbuf[0]=0;
    if (msgcount>99)
      msgcount=0;
    if ((job[msgto].showcount)&&(!privmsg))
      sprintf (outbuf,"[%02d]",msgcount);
    strcat (outbuf,system);
    if (job[msgto].showjob)
    {
      sprintf (tempstring,"%d-",msgfrom);
      strcat (outbuf,tempstring);
    }
    if (!job[msgto].padname)
      strcat (outbuf,job[msgfrom].name);
    else
    {
      sprintf (tempstring,"%-10s",job[msgfrom].name);
      strcat (outbuf,tempstring);
    }
    strcat (outbuf,"> ");
    strcat (outbuf,text);
  }
  else
    sprintf (outbuf,"%s %s",system,text);
  wwrap (outbuf);
  write (job[msgto].fd,outbuf,strlen(outbuf));
}

void process(msgfrom, command)
int msgfrom;
char *command;
{
  int msgto;
  int i;
  char system[40];
  char outtext[256];
  char *textptr;

  if (!validjob(msgfrom))
    return;

  if (job[msgfrom].firstrun)
  {
    fprintf (masterfp,"(sys) CONNECT: job %d from %d.%d.%d.%d\n"
	     ,msgfrom,(int)job[msgfrom].addrs[0],(int)job[msgfrom].addrs[1]
	     ,(int)job[msgfrom].addrs[2],(int)job[msgfrom].addrs[3]);
    job[msgfrom].firstrun=FALSE;
  }

  if (job[msgfrom].mode==1)
  {
    strncpy (job[msgfrom].name,command,19);
    loginuser(msgfrom);
    return;
  }
  if (job[msgfrom].mode==2)
  {
    strncpy (job[msgfrom].pass,command,19);
    loginuser(msgfrom);
    return;
  }

  if (strcmp("*+",command)==0) /* For someone to become a leader */
  {
    if (job[msgfrom].maybeleader==0)
    {
      tell (-1,msgfrom,"(sys)","You cannot become room coordinator.");
      return;
    }
    if (job[0].fd!=0)
    {
      tell (-1,msgfrom,"(sys)","There is already a room coordinator.");
      return;
    }
    sprintf (outtext,"New room coordinator: 0-%s",job[msgfrom].name);
    tell (msgfrom,-1,"(sys)",outtext);
    job[0].buf[0]=0;
    strcpy(job[0].name,job[msgfrom].name);
    strcpy(job[0].pass,job[msgfrom].pass);
    strcpy(job[0].extra,job[msgfrom].extra);
    strcpy(job[0].from,job[msgfrom].from);
    job[0].fd=job[msgfrom].fd;
    job[0].mode=job[msgfrom].mode;
    job[0].gagged=job[msgfrom].gagged;
    job[0].privconf=job[msgfrom].privconf;
    job[0].maybeleader=job[msgfrom].maybeleader;
    job[0].showcount=job[msgfrom].showcount;
    job[0].showjob=job[msgfrom].showjob;
    job[0].padname=job[msgfrom].padname;
    job[0].userbt0=job[msgfrom].userbt0;
    job[0].userbt1=job[msgfrom].userbt1;
    job[0].userbt2=job[msgfrom].userbt2;
    job[0].userbt3=job[msgfrom].userbt3;
    job[0].userby0=job[msgfrom].userby0;
    job[0].userby1=job[msgfrom].userby1;
    job[0].userby2=job[msgfrom].userby2;
    job[0].userby3=job[msgfrom].userby3;
    job[0].userby4=job[msgfrom].userby4;
    job[0].userby5=job[msgfrom].userby5;
    job[0].userwd0=job[msgfrom].userwd0;
    job[0].userwd1=job[msgfrom].userwd1;
    strcpy (job[0].userstr,job[msgfrom].userstr);
#ifdef TERMCAP
    strcpy (job[0].termtype,job[msgfrom].termtype);
    strcpy (job[0].al,job[msgfrom].al);
    strcpy (job[0].bc,job[msgfrom].bc);
    strcpy (job[0].cd,job[msgfrom].cd);
    strcpy (job[0].ce,job[msgfrom].ce);
    strcpy (job[0].cl,job[msgfrom].cl);
    strcpy (job[0].dc,job[msgfrom].dc);
    strcpy (job[0].dl,job[msgfrom].dl);
    strcpy (job[0].so,job[msgfrom].so);
    strcpy (job[0].se,job[msgfrom].se);
    strcpy (job[0].sr,job[msgfrom].sr);
    strcpy (job[0].cm,job[msgfrom].cm);
    strcpy (job[0].cs,job[msgfrom].cs);
    strcpy (job[0].rs,job[msgfrom].rs);
    strcpy (job[0].xdo,job[msgfrom].xdo);
    job[0].co=job[msgfrom].co;
    job[0].li=job[msgfrom].li;
#endif
    job[msgfrom].fd=0;
    job[msgfrom].mode=0;
    job[msgfrom].name[0]=0;
    job[msgfrom].pass[0]=0;
    job[msgfrom].from[0]=0;
#ifdef TERMCAP
    job[msgfrom].termtype[0]=0;
#endif
    command[0]=0;
    return;
  }

  if (strcmp("*-",command)==0) /* Warning: whoever is RL is dropped */
  {
    if (job[msgfrom].maybeleader==0)
    {
      tell (-1,msgfrom,"(sys)","You can't get rid of the room leader.");
      return;
    }
    if (job[0].fd==0)
    {
      tell (-1,msgfrom,"(sys)","There is no room leader to get rid of.");
      return;
    }
    for (i=0;i<NUMJOBS;i++)
    {
      if (job[i].fd==0)
	goto FOUNDSLOT; /* Yeah, I know, GOTOs are bad... */
    }
    tell (-1,msgfrom,"(sys)","There are no available user slots");
    return;
FOUNDSLOT:
    sprintf (outtext,"0-%s is no longer room coordinator",job[0].name);
    tell (msgfrom,-1,"(sys)",outtext);
    job[i].buf[0]=0;
    strcpy(job[i].name,job[0].name);
    strcpy(job[i].pass,job[0].pass);
    strcpy(job[i].from,job[0].from);
    strcpy(job[i].extra,job[0].extra);
    job[i].fd=job[0].fd;
    job[i].mode=job[0].mode;
    job[i].gagged=job[0].gagged;
    job[i].privconf=job[0].privconf;
    job[i].maybeleader=job[0].maybeleader;
    job[i].showcount=job[0].showcount;
    job[i].showjob=job[0].showjob;
    job[i].padname=job[0].padname;
    job[i].userbt0=job[0].userbt0;
    job[i].userbt1=job[0].userbt1;
    job[i].userbt2=job[0].userbt2;
    job[i].userbt3=job[0].userbt3;
    job[i].userby0=job[0].userby0;
    job[i].userby1=job[0].userby1;
    job[i].userby2=job[0].userby2;
    job[i].userby3=job[0].userby3;
    job[i].userby4=job[0].userby4;
    job[i].userby5=job[0].userby5;
    job[i].userwd0=job[0].userwd0;
    job[i].userwd1=job[0].userwd1;
    strcpy (job[i].userstr,job[0].userstr);
#ifdef TERMCAP
    strcpy (job[i].termtype,job[0].termtype);
    strcpy (job[i].al,job[0].al);
    strcpy (job[i].bc,job[0].bc);
    strcpy (job[i].cd,job[0].cd);
    strcpy (job[i].ce,job[0].ce);
    strcpy (job[i].cl,job[0].cl);
    strcpy (job[i].dc,job[0].dc);
    strcpy (job[i].dl,job[0].dl);
    strcpy (job[i].so,job[0].so);
    strcpy (job[i].se,job[0].se);
    strcpy (job[i].sr,job[0].sr);
    strcpy (job[i].cm,job[0].cm);
    strcpy (job[i].cs,job[0].cs);
    strcpy (job[i].rs,job[0].rs);
    strcpy (job[i].xdo,job[0].xdo);
    job[i].co=job[0].co;
    job[i].li=job[0].li;
#endif
    job[0].fd=0;
    job[0].mode=0;
    job[0].name[0]=0;
    job[0].pass[0]=0;
    job[0].from[0]=0;
#ifdef TERMCAP
    job[0].termtype[0]=0;
#endif
    command[0]=0;
    endsession(0); /* Clear out all of the coord stuff. */
    return;
  }

  if (!per_process(msgfrom,command))
    return;

  if (command[0]=='~')
  {
    tell (msgfrom,-2,"",command+1);
    return;
  }
  if (command[0]=='*')
  {
    starcommand (msgfrom, command+1);
    return;
  }
  if (command[0]=='/')
  {
    slashcommand (msgfrom, command+1);
    return;
  }
  else
  {
    tell (msgfrom,-1,"",command);
  }
}

void relogin (msgfrom, command)
int msgfrom;
char *command;
{
  char *textptr;

  fprintf (masterfp,"(sys) RELOGIN: %d-%s\n",msgfrom,job[msgfrom].name);
  if ((textptr=strtok(command," "))==NULL)
  {
    tell (-1,msgfrom,"","Enter your name:");
    job[msgfrom].mode=1;
    return;
  }
  strncpy (job[msgfrom].name,textptr,20);
  job[msgfrom].mode=2;
  loginuser(msgfrom);
  return;
}

void loginuser(id)
int id;
{
  FILE *fp;
  char string[128];
  char name[20];
  char pass[16];
  char *textptr;
  int i;

  if (!validjob(id))
    return;

  if ((fp=fopen("mcm-accts","r"))==NULL)
  {
    strcpy(job[id].name,name);
    job[id].from[0]=0;
    job[id].maybeleader=0;
    job[id].gagged=defaultgag;
    job[id].privconf=0;
    job[id].mode=3;
#ifdef DFCOUNTS
    job[id].showcount=TRUE;
#else
    job[id].showcount=FALSE;
#endif
#ifdef DFJOBNUMS
    job[id].showjob=TRUE;
#else
    job[id].showjob=FALSE;
#endif
#ifdef DFNAMEPAD
    job[id].padname=TRUE;
#else
    job[id].padname=FALSE;
#endif
    job[id].userbt0=0;
    job[id].userbt1=0;
    job[id].userbt2=0;
    job[id].userbt3=0;
    job[id].userby0=0;
    job[id].userby1=0;
    job[id].userby2=0;
    job[id].userby3=0;
    job[id].userby4=0;
    job[id].userby5=0;
    job[id].userwd0=0;
    job[id].userwd1=0;
    job[id].userstr[0]=0;
    tell (id,-1,"(sys)","User logging on");
    per_login(id);
    return; /* Let anyone in */
  }
  fgets (string,128,fp);
  while (!feof(fp))
  {
    if (string[0]=='#')
      goto COMMENTLOGIN; /* Yeah, I know I shouldn't GOTO */
    if ((textptr=strtok(string,":\n"))==NULL)
      textptr="*";
    strcpy (string,textptr);
    if ((textptr=strtok(NULL,":\n"))==NULL)
      textptr="*";
    strcpy (pass,textptr);
    if ((textptr=strtok(NULL,":\n"))==NULL)
      textptr="*";
    strcpy (job[id].from,textptr);
    if ((textptr=strtok(NULL,":\n"))==NULL)
      textptr="*";
    strcpy (job[id].extra,textptr);
    if (string[0]=='/')
    {
      strcpy (name,string+1);
      job[id].maybeleader=TRUE;
    }
    else
    {
      strcpy (name,string);
      job[id].maybeleader=FALSE;
    }
    strcap (name);
    strcap (job[id].name);
    strcap (pass);
    strcap (job[id].pass);
    if (strlen(job[id].name)==0)
    {
      goto NAMEPLEASE; /* Sorry */
    }
    if ((strncmp(name,job[id].name,20)==0)||(strcmp(name,"*")==0))
    {
      fclose (fp);
      if ((job[id].mode==1)&&(pass[0]!='*'))
      {
	job[id].mode=2;
	tell (-1,id,"","Restricted username; enter password:");
	return;
      }
      if ((job[id].mode==2)&&(pass[0]!='*'))
      {
	if (strncmp(pass,job[id].pass,16)!=0)
	{
	  fprintf (masterfp,"(sys) BADPASS: %s\n",job[id].name);
	  tell (-1,id,"","Invalid password entered.");
NAMEPLEASE:
	  tell (-1,id,"","Enter your name:");
	  job[id].mode=1;
	  return;
	}
      }
      job[id].gagged=defaultgag;
      job[id].privconf=0;
      job[id].mode=3;
      job[id].showcount=TRUE;
      job[id].showjob=TRUE;
      job[id].padname=FALSE;
      job[id].userbt0=0;
      job[id].userbt1=0;
      job[id].userbt2=0;
      job[id].userbt3=0;
      job[id].userby0=0;
      job[id].userby1=0;
      job[id].userby2=0;
      job[id].userby3=0;
      job[id].userby4=0;
      job[id].userby5=0;
      job[id].userwd0=0;
      job[id].userwd1=0;
      job[id].userstr[0]=0;
      tell (id,-1,"(sys)","User logging in.");
      usertype (id,"mcm-hello");
      per_login(id);
      if (msgtext[0]!=0)
      {
	tell (-1,id,"(note)","There is a message on the door:");
	tell (-1,id,"",msgtext);
      }
      sprintf (string,"msg-%s",job[id].name);
      struncap(string);
      if (!access(string,R_OK))
      {
	tell (-1,id,"(msg)","There is a message waiting for you from the sysadmin:");
	usertype (id,string);
	tell (-1,id,"(msg)","Use the command /D to delete the note.");
      }
      return;
    }
COMMENTLOGIN:
    fgets (string,128,fp);
  }
  strcpy (string,"Login incorrect.  Try again.\r\nEnter your name:");
  tell (-1, id, "", string);
  job[id].mode=1;
  fclose (fp);
  return;
}

void strcap(string)
char *string;
{
  int i;

  for (i=0;i<strlen(string);i++)
    string[i]=toupper(string[i]);
}
void struncap(string)
char *string;
{
  int i;

  for (i=0;i<strlen(string);i++)
    string[i]=tolower(string[i]);
}

void starcommand(msgfrom, command)
int msgfrom;
char *command;
{
  int i,j;
  char string[256];
  char *textptr;

  if ((msgfrom!=0)&&(asstleader!=msgfrom))
  {
    tell (-1,msgfrom,"(sys)",
	  "Must be the room coordinator to use * commands");
    return;
  }
  if (strncmp("E!",command,2)==0)
    bailout(0,"Coordinator shutdown");
  command[0]=toupper(command[0]);
  if ((msgfrom!=0)&&(strchr("DGTOLKM",command[0])==NULL))
  {
    command[0]='?';
  }
  switch (command[0])
  {
  case 'M':
    modecommand(msgfrom,command+1);
    return;
  case 'D':
    doormessage(msgfrom,command+1);
    return;
  case 'G':
    gagpeople(msgfrom,command+1);
    return;
  case 'C':
    privconf(msgfrom,command+1);
    return;
  case 'T':
    struncap (command);
    typefile (msgfrom,command+1);
    return;
  case 'O':
    tell (msgfrom,-1,"(sys)","Floor is now open for discussion");
    listen_mode= -1;
    return;
  case 'L':
    listenonly(msgfrom,command+1);
    return;
  case 'K':
    i=atoi(command+1);
/*    shutdown (job[i].fd,2);/**/
    endsession (i);
    return;
  case 'A':
    setasst(command+1);
    return;
  default:
    tell (-1,msgfrom,"","Valid * commands are: (* = avail to asst coord)");
    if (!per_starhelp(msgfrom))
      return;
    tell (-1,msgfrom,"","*D (text)    Put a 1 line message on the door*");
    tell (-1,msgfrom,"","*E!          End session; Shut down server*");
    tell (-1,msgfrom,"","*E-          Quit being room coordinator");
    tell (-1,msgfrom,"","*K (uid)     Kill a user*");
    tell (-1,msgfrom,"","*T (file)    Type file*");
    tell (-1,msgfrom,"","*G+(uid)     Gag user (uid) (*=all users)*");
    tell (-1,msgfrom,"","*G-(uid)     Ungag user (uid) (*=all users)*");
    tell (-1,msgfrom,"","*L (uid)     Put room in listen-only.  Give optional (uid) the floor*");
    tell (-1,msgfrom,"","*O           Open the floor for general discussion*");
    tell (-1,msgfrom,"","*C           Clear Private conference");
    tell (-1,msgfrom,"","*C(uid_list) Bring selected users into private conference");
    tell (-1,msgfrom,"","*A(uid)      Set the ass't coord (0=no ass't)");
    tell (-1,msgfrom,"","*M(mode)     Set system modes (*M? for more help)*");
    return;
  }
}      

void modecommand(msgfrom, command)
int msgfrom;
char *command;
{
  char string[80];
  if (msgfrom!=0)
    command[0]='?';

  if (!per_mode(msgfrom,command))
    return;
  switch (toupper(command[0]))
  {
  case 'S':
    if (command[1]=='+')
    {
      snoopmode=TRUE;
      tell (-1,msgfrom,"(sys)","Snoop mode on");
      return;
    }
    if (command[1]=='-')
    {
      snoopmode=FALSE;
      tell (-1,msgfrom,"(sys)","Snoop mode off");
      return;
    }
  case 'D':
    if (command[1]=='+')
    {
      defaultgag=TRUE;
      tell (-1,msgfrom,"(sys)","Default gag mode on");
      return;
    }
    if (command[1]=='-')
    {
      defaultgag=FALSE;
      tell (-1,msgfrom,"(sys)","Default gag mode off");
      return;
    }
  case 'H':
    if (command[1]=='+')
    {
      confhear=TRUE;
      tell (-1,msgfrom,"(sys)","Conference hearing mode on");
      return;
    }
    if (command[1]=='-')
    {
      confhear=FALSE;
      tell (-1,msgfrom,"(sys)","Conference hearing off");
      return;
    }
  case 'T':
    if (command[1]=='+')
    {
      conftalk=TRUE;
      tell (-1,msgfrom,"(sys)","Conference talk mode on");
      return;
    }
    if (command[1]=='-')
    {
      conftalk=FALSE;
      tell (-1,msgfrom,"(sys)","Conference talk mode off");
      return;
    }
  default:
    if (!per_modehelp(msgfrom))
      return;
    tell (-1,msgfrom,"(info)","The following modes are available:");
    sprintf (string,"*MD(+/-)	Set default gag mode (%c)",defaultgag?'+':'-');
    tell (-1,msgfrom,"",string);
    sprintf (string,"*MT(+/-)	Set conf. talk mode (%c)",conftalk?'+':'-');
    tell (-1,msgfrom,"",string);
    sprintf (string,"*MH(+/-)	Set conf. hear mode (%c)",confhear?'+':'-');
    tell (-1,msgfrom,"",string);
    sprintf (string,"*MS(+/-)	Set snoop mode (%c)",snoopmode?'+':'-');
    tell (-1,msgfrom,"",string);
    return;
  }
}

void setasst(command)
char *command;
{
  char string[132];

  asstleader=atoi(command);
  if (!testjob(asstleader))
    asstleader=0;
  if (asstleader!=0)
  {
    sprintf (string,"New ass't coordinator: %d-%s",asstleader,
	     job[asstleader].name);
    tell (0,-1,"(sys)",string);
  }
  else
    tell (0,-1,"(sys)","There is no longer an ass't coordinator");
  return;
}

void slashcommand(msgfrom, command)
int msgfrom;
char *command;
{
  int i;
  char string[256];
  char *textptr;

  if (!testjob(msgfrom))
    return;

  command[0]=toupper(command[0]);
  switch (command[0])
  {
  case 'W':
  case 'U':
    whothere(msgfrom,command);
    return;
  case 'E':
  case 'X':
  case 'Q':
/*    shutdown (job[msgfrom].fd,2);/**/
    endsession (msgfrom);
    return;
  case 'D':
    sprintf (string,"msg-%s",job[msgfrom].name);
    struncap(string);
    unlink(string);
    tell (-1,msgfrom,"(sys)","Any pending messages deleted.");
    return;
  case 'R':
    rolldice (msgfrom,command+1,1);
    return;
  case 'P':
    rolldice (msgfrom,command+1,0);
    return;
  case 'H':
    struncap (command);
    userhelp (msgfrom,command+1);
    return;
  case 'L':
    relogin (msgfrom,command+1);
    return;
  case 'S':
    sendmsg (msgfrom,command+1);
    return;
  case 'V':
    tell (-1,msgfrom,"(sys)",VERSION);
    return;
#ifdef TERMCAP
  case 'T':
    struncap (command);
    dowins (msgfrom,command+1);
    return;
  case '#':
    set_lines(msgfrom,atoi(command+1));
    return;
#endif
  case 'C':
    set_count(msgfrom,command+1);
    return;
  case 'J':
    set_jobid(msgfrom,command+1);
    return;
  case 'N':
    set_namepad(msgfrom,command+1);
    return;
  default:
    tell (-1,msgfrom,"","Valid / commands are:");
    if (!per_slashhelp(msgfrom))
      return;
    tell (-1,msgfrom,"","/C [+/-]     	 Set/Clear display of message counter");
    tell (-1,msgfrom,"","/E              Exit this system");
    tell (-1,msgfrom,"","/H              Additional help text");
    tell (-1,msgfrom,"","/J [+/-]	 Set/Clear display of job ids");
    tell (-1,msgfrom,"","/L [name]       Re-login under different name");
    tell (-1,msgfrom,"","/N [+/-]	 Set/Clear name padding");
    tell (-1,msgfrom,"","/P [dice]       Privately roll dice");
    tell (-1,msgfrom,"","/R [dice]       Roll dice  (example: /R 1d6+2)");
    tell (-1,msgfrom,"","/S (job) (msg)  Send private message to user");
#ifdef TERMCAP
    tell (-1,msgfrom,"","/T (term_type)  Invoke split-screen mode");
#endif
    tell (-1,msgfrom,"","/U   		 Brief list of users online");
    tell (-1,msgfrom,"","/V              Get version ID of server");
    tell (-1,msgfrom,"","/W              List who is online");
#ifdef TERMCAP
    tell (-1,msgfrom,"","/# (number)     Set lines per page (split screen mode");
#endif
  }
}

void listenonly(msgfrom, command)
int msgfrom;
char *command;
{
  char string[128];

  if (!testjob(msgfrom))
    return;

  if (listen_mode== -1)
    tell (msgfrom,-2,"(sys)","Floor is now in listen-only mode.");
  listen_mode=atoi(command);
  if (!testjob(listen_mode))
    listen_mode=0;
  sprintf (string,"User %d-%s now has the floor",listen_mode,
	     job[listen_mode].name);
  tell (msgfrom,-2,"(sys)",string);
}

void set_count(msgfrom,command)
int msgfrom;
char *command;
{
  if (!testjob(msgfrom))
    return;

  if (command[0]=='+')
    job[msgfrom].showcount=TRUE;
  else if (command[0]=='-')
    job[msgfrom].showcount=FALSE;
  else
    job[msgfrom].showcount=job[msgfrom].showcount?FALSE:TRUE;
  if (job[msgfrom].showcount)
    tell (-1,msgfrom,"(sys)","Count display will now be shown");
  else
    tell (-1,msgfrom,"(sys)","Count display will no longer be shown");
}

void set_jobid(msgfrom,command)
int msgfrom;
char *command;
{
  if (!testjob(msgfrom))
    return;

  if (command[0]=='+')
    job[msgfrom].showjob=TRUE;
  else if (command[0]=='-')
    job[msgfrom].showjob=FALSE;
  else
    job[msgfrom].showjob=job[msgfrom].showjob?FALSE:TRUE;
  if (job[msgfrom].showcount)
    tell (-1,msgfrom,"(sys)","Job ID display will now be shown");
  else
    tell (-1,msgfrom,"(sys)","Job ID display will no longer be shown");
}

void set_namepad(msgfrom,command)
int msgfrom;
char *command;
{
  if (!testjob(msgfrom))
    return;

  if (command[0]=='+')
    job[msgfrom].padname=TRUE;
  else if (command[0]=='-')
    job[msgfrom].padname=FALSE;
  else
    job[msgfrom].padname=job[msgfrom].padname?FALSE:TRUE;
  if (job[msgfrom].padname)
    tell (-1,msgfrom,"(sys)","Name padding will now be done");
  else
    tell (-1,msgfrom,"(sys)","Name padding will no longer be done");
}

void sendmsg (msgfrom, command)
int msgfrom;
char *command;
{
  char *textptr;
  int i;

  if ((textptr=strtok(command," \n"))==NULL)
  {
    tell (msgfrom,-1,"(sys)","Syntax: /s (job) (message)");
    return;
  }
  i=atoi(textptr);
  textptr=strtok(NULL,"\n");
  if (!testjob(i))
  {
    tell (-1,msgfrom,"(sys)","Job is not online");
    return;
  }
  tell (-1,msgfrom,"(sys)","Sending private message");
  tell (msgfrom,i,"",textptr);
  return;
}

void doormessage (msgfrom,message)
int msgfrom;
char *message;
{
  int i;

  i=(msgfrom==0)?-1:msgfrom; /* Ha! Let the fortran people figure THIS out! */
  while (message[0]==' ')
  {
    message++;
  }
  if (message[0]==0)
  {
    fprintf (masterfp,"(sys) DOOR: Message cleared\n");
    msgtext[0]=0;
    tell (i,0,"(sys)","Message cleared from door");
    return;
  }
  strcpy (msgtext,message);
  fprintf (masterfp,"(sys) New message on door:\n%s\n",msgtext);
  tell (i,0,"(sys)","Message now on door");
  tell (i,0,"",msgtext);
}


#ifdef TERMCAP
void dowins (msgfrom,command)
int msgfrom;
char *command;
{
  char *textptr;

  textptr=strtok(command," \n");
  if ((job[msgfrom].termtype[0]==0)&&(textptr==NULL))
  {
    tell (-1,msgfrom,"(sys)","Usage: /t (termtype)");
    return;
  }
  if ((job[msgfrom].termtype[0]!=0)&&(textptr==NULL))
  {
    fprintf (masterfp,"(sys) DOWINS: %d-%s exit\n",msgfrom,job[msgfrom].name);
    splend (msgfrom);
    tell (-1,msgfrom,"(sys)","Exiting split screen.");
    return;
  }
  if (job[msgfrom].termtype[0]!=0)
  {
    splend (msgfrom);
  }
  struncap (textptr);
  fprintf (masterfp,"(sys) DOWINS: %d-%s [%s]\n",msgfrom,job[msgfrom].name,
	   textptr);
  tell (-1,msgfrom,"(sys)","Entering split screen.");
  splinit(msgfrom,textptr);
  return;
}
#endif

void rolldice (msgfrom,command,pubroll)
int msgfrom;
char *command;
int pubroll;
{
  int i,j;
  double f;

  int rolls[50],total;
  int number,sides;
  int modifr;
  char string[132];
  char *textptr;
  char string2[32];

  for (i=0;i<50;i++)
    rolls[i]=0;
  if (!per_preroll(msgfrom,command))
    return;
  textptr=strtok(command," \n");
  if (textptr!=NULL)
  {
    if (isdigit(textptr[0]))
      number=atoi(textptr);
    else
      number=1;
    while ((textptr[0]!='D')&&(textptr[0]!='d')&&(textptr[0]!=0))
      textptr++;
    if (textptr[0]!=0)
    {
      textptr++;
      if (isdigit(textptr[0]))
	sides=atoi(textptr);
      else
	sides=6;
      while ((textptr[0]!='+')&&(textptr[0]!='-')&&(textptr[0]!=0))
	textptr++;
      if ((textptr[0]=='+')||(textptr[0]=='-'))
	modifr=atoi(textptr);
      else
	modifr=0;
    }
    else
    {
      sides=6;
      modifr=0;
    }
  }
  else
  {
    number=1;
    sides=6;
    modifr=0;
  }
  total=0;
  if (sides>1000)
    sides=1000;
  if (number>50)
    number=50;
  for (i=0;i<number;i++)
  {
    j=lrand48()%sides;
    j++;
    rolls[i]=j;
    total+=j;
  }
  total += modifr;
  if (modifr==0)
    sprintf (string,"%dd%d -> %d ",number,sides,total);
  else
    sprintf (string,"%dd%d%+d -> %d ",number,sides,modifr,total);
  if (number<=20)
  {
    for (i=0;i<number;i++)
    {
      if (i==0)
	sprintf (string2,"(%d",rolls[i]);
      else
	sprintf (string2,",%d",rolls[i]);
      strcat (string,string2);
    }
    strcat (string,")");
  }
  if (!per_postroll(pubroll,job,string,total,number,sides,rolls))
    return;
  if (pubroll==1)
    tell (msgfrom,-3,"(roll)",string);
  else
  {
    tell (msgfrom,msgfrom,"(roll)",string);
    if ((msgfrom!=0)&&(job[0].mode==3))
      tell (msgfrom,0,"(roll)",string);
  }
  return;
}

  
void endsession(msgfrom)
int msgfrom;
{
  int i;
  per_logout(msgfrom);
  if (msgfrom==asstleader)
    asstleader=0;
  if (msgfrom==0)
  {
    asstleader=0;
    snoopmode=FALSE;
    listen_mode= -1;
    defaultgag=FALSE;
    for (i=1;i<NUMJOBS;i++)
    {
      if (validjob(i))
      {
	job[i].gagged=FALSE;
	job[i].privconf=FALSE;
      }
    }
  }

  if (!validjob(msgfrom))
  {
    return;
  }

  if (job[msgfrom].fd==0)
    return;

#ifdef TERMCAP
  if (job[msgfrom].termtype[0]!=0)
    splend(msgfrom);
#endif

  if (job[msgfrom].mode==3)
  {
    tell (msgfrom,-1,"(sys)","User logging off");
    write (job[msgfrom].fd,"Goodbye.\r\n",10);
  }
  job[msgfrom].mode=0;
  job[msgfrom].name[0]=0;
/*  shutdown (job[msgfrom].fd,2);*/
  close (job[msgfrom].fd);
  job[msgfrom].fd=0;
}

void whothere(msgfrom,command)
int msgfrom;
char *command;
{
  int i,j;
  char string[80];
  char substring[40];

  if (toupper(command[0]=='U'))
  {
    tell (-1,msgfrom,"(info)","Users currently logged into the system");
    j=0;
    for (i=0;i<NUMJOBS;i++)
    {
      if (testjob(i))
      {
	sprintf (substring,"%s%s%d-%-20s  ",
	       job[i].gagged?"G":" ",
	       job[i].privconf?"C":" ", i,
	       job[i].name);
	if (j==0)
	{
	  strcpy (string,substring);
	  j=1;
	}
	else
	{
	  strcat (string,substring);
	  tell (-1,msgfrom,"",string);
	  j=0;
	}
      }
    }
    if (j==1)
      tell (-1,msgfrom,"",string);
    return;
  }
  if ((listen_mode>=0)&&(testjob(listen_mode)))
  {
    sprintf (string,"Floor is listen only; %d-%s has floor",listen_mode,
	     job[listen_mode].name);
    tell (-1,msgfrom,"(info)",string);
  }
  if (job[msgfrom].gagged==1)
    tell (-1,msgfrom,"(info)","You are gagged");
  if (asstleader!=0)
  {
    sprintf (string,"Assistant room coordinator: %d-%s",asstleader,
	     job[asstleader].name);
    tell (-1,msgfrom,"(info)",string);
  }
  tell (-1,msgfrom,"(info)","Users currently logged into the system");
  for (i=0;i<NUMJOBS;i++)
  {
    if (testjob(i))
    {
      if ((job[i].extra[0]!=0)&&((job[i].extra[0]!='*')||(msgfrom==0)))
	strcpy (substring,job[i].extra);
      else
	substring[0]=0;
      sprintf (string,"%s%s%d-%-20s %-32s",
	       job[i].gagged?"(G)":"   ",
	       job[i].privconf?"(C)":"   ", i,
	       job[i].name, substring);
      tell (-1,msgfrom,"",string);
      if ((job[i].from[0]!=0)&&((job[i].from[0]!='*')||(msgfrom==0)))
      {
	sprintf (string,"              E-Mail address: %s",job[i].from);
	tell (-1,msgfrom,"",string);
      }
    }
  }
}

void gagpeople(msgfrom,command)
int msgfrom;
char *command;
{
  int i,j, sender;
  char string[80];

  sender=(msgfrom==0?-1:msgfrom);
  if (!testjob(msgfrom))
    return;

  if (command[0]=='D')
  {
    if (command[1]=='+')
    {
      defaultgag=1;
      tell (sender,0,"(sys)","New users will now be gagged");
      return;
    }
    if (command[1]=='-')
    {
      defaultgag=0;
      tell (sender,0,"(sys)","New users will now be ungagged");
      return;
    }
    return;
  }
  else if (command[0]=='+')
  {
    if (command[1]=='*')
    {
      for (i=1;i<NUMJOBS;i++)
	job[i].gagged=1;
      tell (msgfrom,-1,"(sys)","All users are now gagged.");
      return;
    }
    i=atoi(command+1);
    if (!testjob(i))
    {
      tell (-1,msgfrom,"(sys)","No such user");
      return;
    }
    job[i].gagged=1;
    sprintf (string,"You are gagged, all messages will go to 0-%s",
	     job[0].name);
    tell (msgfrom,i,"(sys)",string);
    sprintf (string,"User %d-%s is now gagged",i,job[i].name);
    tell (-1,0,"(sys)",string);
    return;
  }
  else if (command[0]=='-')
  {
    if (command[1]=='*')
    {
      for (i=0;i<NUMJOBS;i++)
	job[i].gagged=0;
      tell (msgfrom,-1,"(sys)","All users are now ungagged.");
      return;
    }
    i=atoi(command+1);
    if (!testjob(i))
    {
      tell (-1,msgfrom,"(sys)","No such user");
      return;
    }
    job[i].gagged=0;
    tell (msgfrom,i,"(sys)","You are no longer gagged.");
    sprintf (string,"User %d-%s is now ungagged",
	     i,job[i].name);
    tell (-1,0,"(sys)",string);
    return;
  }
}

void privconf(msgfrom,command)
int msgfrom;
char* command;
{
  int i;
  char string[80];
  char *textptr;

  tell (msgfrom,-2,"(sys)","Clearing private conference room.");
  tell (-1,msgfrom,"(sys)","Use ~text to send messages to people out of conf.");
  for (i=0;i<NUMJOBS;i++)
    job[i].privconf=0;
  textptr=strtok(command," ");
  while (textptr!=NULL)
  {
    job[0].privconf=1;
    i=atoi(textptr);
    if (testjob(i))
    {
      job[i].privconf=1;
      sprintf (string,"User %d-%s in private conference with 0-%s.",
		 i,job[i].name,job[0].name);
      tell (msgfrom,-2,"(sys)",string);
    }
    textptr=strtok(NULL," ");
  }
  return;
}

void typefile(msgfrom,command)
int msgfrom;
char *command;
{
  FILE *fp;
  int linect;
  char fname[300];
  int shortct;
  int i;
  char string[256];
  char *textptr;

  if (strchr(command,'/')!=NULL)
  {
    tell (-1,msgfrom,"(sys)","Character / not allowed in filename");
    return;
  }
  textptr=strtok(command," ");
  sprintf (fname,"mcm.%s",textptr);
  if ((fp=fopen(fname,"r"))==NULL)
  {
    sprintf (string,"File %s not found",fname);
    tell (-1,msgfrom,"(sys)",string);
    return;
  }
  tell (msgfrom,-1,"(file)","Start of file transmission...");
  linect=0;
  fgets (string,100,fp);
  while (!feof(fp))
  {
    linect++;
    shortct++;
    if (shortct>5)
    {
      communicate();
      shortct=0;
    }
    if (linect>30)
    {
      tell (msgfrom,-1,"(file)",
	    "File continues beyond 30 lines; will not display.");
      tell (msgfrom,-1,"(file)","End of file transmission");
      fclose (fp);
      return;
    }
    if (strlen(string)!=0)
      string[strlen(string)-1]=0;
    if (job[msgfrom].privconf)
      tell (-1,-10,"",string);
    else
      tell (-1,-1,"",string);
    fgets (string,100,fp);
  }
  tell (msgfrom,-1,"(file)","End of file transmission");
  fclose (fp);
  return;
}

void usertype (id,fname)
int id;
char *fname;
{
  FILE *fp;
  char string[255];

  if ((fp=fopen(fname,"r"))==NULL)
  {
    sprintf (string,"File %s not found",fname);
    tell (-1,id,"(sys)",string);
    return;
  }
  fgets (string,255,fp);
  while (!feof(fp))
  {
    string[strlen(string)-1]=0;
    tell (-1,id,"",string);
    fgets (string,255,fp);
  }
  fclose (fp);
}

void userhelp(id,command)
int id;
char *command;
{
  char *textptr;
  char fname[255];

  if (strchr(command,'/')!=NULL)
  {
    tell (-1,id,"(sys)","Illegal help file name");
    return;
  }
  textptr=strtok(command," ");
  if (textptr==NULL)
    strcpy (fname,"mcm-help");
  else
    sprintf (fname,"mcm-help-%s",textptr);
  fprintf (masterfp,"(sys) HELP: %d-%s: %s\n",id,job[id].name,fname);
  usertype (id,fname);
}
