/* Copyright (c) 1993
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ****************************************************************
 */

#include "rcs.h"
RCS_ID("$Id: socket.c,v 1.1.1.1 1993/08/15 18:32:56 nate Exp $ FAU")

#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifndef sgi
# include <sys/file.h>
#endif
#ifndef NAMEDPIPE
#include <sys/socket.h>
#endif
#include <fcntl.h>
#ifndef NAMEDPIPE
#include <sys/un.h>
#endif
#include <signal.h>
#ifndef M_XENIX
#include <sys/time.h>
#endif /* M_XENIX */
#ifdef DIRENT
# include <dirent.h>
#else
# include <sys/dir.h>
# define dirent direct
#endif

#include "screen.h"

#ifdef USEVARARGS
# if defined(__STDC__)
#  include <stdarg.h>
# else
#  include <varargs.h>
# endif
#endif

#include "extern.h"

#if defined(_SEQUENT_) && !defined(NAMEDPIPE)
# define connect sconnect	/* _SEQUENT_ has braindamaged connect */
static int sconnect __P((int, struct sockaddr *, int));
#endif

extern char *RcFileName, *extra_incap, *extra_outcap;
extern int ServerSocket, real_uid, real_gid, eff_uid, eff_gid;
extern int dflag, iflag, rflag, lsflag, quietflag, wipeflag, xflag;
extern char *attach_tty, *LoginName, HostName[];
extern struct display *display, *displays;
extern struct win *fore, *wtab[], *console_window, *windows;
extern struct NewWindow nwin_undef;
#ifdef NETHACK
extern nethackflag;
#endif
#ifdef MULTIUSER
extern char *multi;
#endif

#ifdef PASSWORD
extern int CheckPassword;
extern char Password[];
#endif
extern char *getenv();

char SockPath[MAXPATHLEN];
char *SockNamePtr, *SockName;

#ifdef MULTIUSER
# define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0) | (multi ? 1 : 0))
#else
# define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0))
#endif

int
RecoverSocket()
{
#ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
#endif
  (void) unlink(SockPath);
#ifndef NOREUID
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
#endif
  close(ServerSocket);
  if ((ServerSocket = MakeServerSocket()) < 0)
    return 0;
  return 1;
}

/*
 * Socket mode 700 means we are Attached. 600 is detached.
 * We return how many sockets we found. If it was exactly one, we come
 * back with a SockPath set to it and open it in a fd pointed to by fdp.
 * If fdp == 0 we simply produce a list if all sockets.
 */
/* ARGSUSED */
int
FindSocket(how, fdp)
int how;
int *fdp;
{
  register int s, lasts = 0, found = 0, deadcount = 0, wipecount = 0;
  register int l = 0;
  register DIR *dirp;
  register struct dirent *dp;
  register char *Name;
  struct stat st;
  struct foundsock
    {
      char *name;
      int mode;
    } foundsock[100];	/* 100 is hopefully enough. */
  int foundsockcount = 0;

  /* User may or may not give us a (prefix) SockName. We want to search. */
  debug("FindSocket:\n");
  if (SockName)
    {
      debug1("We want to match '%s'\n", SockName);
      l = strlen(SockName);
#ifdef NAME_MAX
      if (l > NAME_MAX)
	l = NAME_MAX;
#endif
    }

#ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
#endif
  debug1("FindSock searching... '%s'\n", SockPath);
  /*
   * this is a hack: SockName may point to Filename(Sockpath)...
   */
  found = *SockNamePtr;
  *SockNamePtr = '\0';
  if ((dirp = opendir(SockPath)) == NULL)
    {
      Panic(0, "Cannot opendir %s", SockPath);
      /* NOTREACHED */
    }
  *SockNamePtr = found;
  found = 0;
  while ((dp = readdir(dirp)) != NULL)
    {
      Name = dp->d_name;
      /* 
       * there may be a file ".termcap" here. 
       * Ignore it just like "." and "..". 
       */
      debug1("- %s\n", Name);
      if (Name[0] == '.')
	continue;
      if (SockName && l)
	{
	  register char *n = Name;

	  debug2("Attach found: '%s', needed '%s'\n", Name, SockName);
	  /*
	   * The SockNames "hf", "ttyhf", "1", "12345.tty", "12345.ttyhf.medusa"
	   * all match the Name "12345.ttyhf.medusa".
	   */

	  if ((*SockName <= '0' || *SockName > '9') && (*n > '0' && *n <= '9'))
	    {
	      while (*++n)
		if (*n == '.')
		  {
		    n++;
		    break;
		  }
	      if (strncmp("tty", SockName, 3) && !strncmp("tty", n, 3))
		n += 3;
	    }
	  if (strncmp(n, SockName, l))
	    {
	      debug3("strncmp('%s', '%s', %d)\n", n, SockName, l);
	      continue;
	    }
	}
      /*
       * ATTENTION! MakeClientSocket adds SockName to SockPath! 
       * Anyway, we need it earlier.
       */
      strcpy(SockNamePtr, Name);
      if (stat(SockPath, &st))
        {
          debug1("could not stat! (%d)\n", errno);
	  continue;
        }
#ifndef SOCK_NOT_IN_FS
# ifdef NAMEDPIPE
      if (!S_ISFIFO(st.st_mode))
	{
	  debug1("'%s' is not a pipe, ignored\n", SockPath);
	  continue;
	}
# else /* NAMEDPIPE */
#  ifdef S_ISSOCK
      if (!S_ISSOCK(st.st_mode))
	{
	  debug1("'%s' is not a socket, ignored\n", SockPath);
	  continue;
	}
#  endif
# endif /* NAMEDPIPE */
#endif
      if (st.st_uid != real_uid)
        {
          debug2("uid mismatch (%d - %d) - ignored\n", st.st_uid, real_uid);
	  continue;
        }
      s = st.st_mode & 0777;
      debug2("FindSocket: %s has mode %04o...\n", Name, s);
#ifdef MULTIUSER
      if (multi && ((s & 0677) == 0600))
	continue;
#endif
      foundsock[foundsockcount].name = SaveStr(Name);
#ifdef MULTIUSER
      foundsock[foundsockcount].mode = s ^ (multi ? 1 : 0);
#else
      foundsock[foundsockcount].mode = s;
#endif
      {
	/* 
	 * marc parses the socketname again
	 */
	int sockmpid = 0;
	char *nam = Name;

	while (*nam)
	  {
	    if (*nam > '9' || *nam < '0')
	      break;
	    sockmpid = 10 * sockmpid + *nam - '0';
	    nam++;
	  }
	/*
	 * A socket is counted as dead, when there is no
	 * process belongin to it. If there is one, and the
	 * socket mode indicates "single user detached", then
	 * we should be able to connect.
	 * If successfull, thats o.k. Otherwise we record that mode as -1.
	 * MakeClientSocket() must be careful not to block forever.
	 */
	if (((sockmpid > 2) && kill(sockmpid, 0) == -1 && errno == ESRCH) ||
	    (((s & 0677) == 0600) && (s = MakeClientSocket(0, Name)) == -1))
	  {
	    foundsock[foundsockcount].mode = -1;
	    deadcount++;
	  }
	else
	  close(s);
      }
      if (++foundsockcount >= 100)
	break;
    }
  closedir(dirp);

  if (wipeflag)
    {
      for (s = 0; s < foundsockcount; s++)
	{
	  if (foundsock[s].mode == -1)
	    {
              strcpy(SockNamePtr, foundsock[s].name);
	      debug1("wiping '%s'\n", SockPath);
	      if (unlink(SockPath) == 0)
	        {
		  foundsock[s].mode = -2;
	          wipecount++;
		}
	    }
	}
    }
debug3("found=%d, dflag = %d, xflag = %d\n", found, dflag, xflag);
  for (s = 0; s < foundsockcount; s++)
    if ((foundsock[s].mode) == (dflag ? 0700 : 0600)
        || (xflag && foundsock[s].mode == 0700)) 
      {
	found++;
debug2("mode = %d --> found = %d\n", foundsock[s].mode, found);
	lasts = s;
      }
  if (quietflag && (lsflag || (found != 1 && rflag != 2)))
    eexit(10 + found);
  debug2("attach: found=%d, foundsockcount=%d\n", found, foundsockcount);
  if (found == 1 && lsflag == 0)
    {
      if ((lasts = MakeClientSocket(0, SockName = foundsock[lasts].name)) == -1)
        found = 0;
    }
  else if (!quietflag && foundsockcount > 0)
    {
      switch (found)
        {
        case 0:
          if (lsflag)
	    {
#ifdef NETHACK
              if (nethackflag)
	        printf("Your inventory:\n");
	      else
#endif
	      printf((foundsockcount > 1) ?
	             "There are screens on:\n" : "There is a screen on:\n");
	    }
          else
	    {
#ifdef NETHACK
              if (nethackflag)
	        printf("Nothing fitting exists in the game:\n");
	      else
#endif
	      printf((foundsockcount > 1) ?
	             "There are screens on:\n" : "There is a screen on:\n");
	    }
          break;
        case 1:
#ifdef NETHACK
          if (nethackflag)
            printf((foundsockcount > 1) ?
                   "Prove thyself worthy or perish:\n" : 
                   "You see here a good looking screen:\n");
          else
#endif
          printf((foundsockcount > 1) ? 
                 "There are several screens on:\n" :
                 "There is a possible screen on:\n");
          break;
        default:
#ifdef NETHACK
          if (nethackflag)
            printf((foundsockcount > 1) ? 
                   "You may whish for a screen, what do you want?\n" : 
                   "You see here a screen:\n");
          else
#endif
          printf((foundsockcount > 1) ?
                 "There are several screens on:\n" : "There is a screen on:\n");
          break;
        }
      for (s = 0; s < foundsockcount; s++)
	{
	  switch (foundsock[s].mode)
	    {
	    case 0700:
	      printf("\t%s\t(Attached)\n", foundsock[s].name);
	      break;
	    case 0600:
	      printf("\t%s\t(Detached)\n", foundsock[s].name);
	      break;
#ifdef MULTIUSER
	    case 0701:
	      printf("\t%s\t(Multi, attached)\n", foundsock[s].name);
	      break;
	    case 0601:
	      printf("\t%s\t(Multi, detached)\n", foundsock[s].name);
	      break;
#endif
	    case -1:
#if defined(__STDC__) || defined(_AIX) || defined(SVR4)
	      printf("\t%s\t(Dead ??\?)\n", foundsock[s].name);
#else
	      printf("\t%s\t(Dead ???)\n", foundsock[s].name);
#endif
	      break;
	    case -2:
	      printf("\t%s\t(Removed)\n", foundsock[s].name);
	      break;
	    default:
	      printf("\t%s\t(Wrong mode)\n", foundsock[s].name);
	      break;
	    }
	}
    }
  if (deadcount && !quietflag)
    {
      if (wipeflag)
        {
#ifdef NETHACK
          if (nethackflag)
            printf("You hear%s distant explosion%s.\n",
	       (deadcount > 1) ? "" : " a", (deadcount > 1) ? "s" : "");
          else
#endif
          printf("%d Socket%s wiped out.\n", deadcount, (deadcount > 1)?"s":"");
	}
      else
	{
#ifdef NETHACK
          if (nethackflag)
            printf("The dead screen%s touch%s you. Try 'screen -wipe'.\n",
	       (deadcount > 1) ? "s" : "", (deadcount > 1) ? "" : "es");
          else
#endif
          printf("Remove dead Sockets with 'screen -wipe'.\n");
	}
    }

  for (s = 0; s < foundsockcount; s++)
    Free(foundsock[s].name);
  if (found == 1 && fdp)
    *fdp = lasts;
#ifndef NOREUID
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
#endif
  if (fdp)
    return found;
  return foundsockcount - wipecount;
}

#ifdef NAMEDPIPE

int
MakeServerSocket()
{
  register int s;
  struct stat st;

  strcpy(SockNamePtr, SockName);
# ifdef NAME_MAX
  if (strlen(SockNamePtr) > NAME_MAX)
    {
      debug2("MakeClientSocket: '%s' truncated to %d chars\n",
	     SockNamePtr, NAME_MAX);
      SockNamePtr[NAME_MAX] = '\0';
    }
# endif /* NAME_MAX */

# ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
# endif /* NOREUID */
  if ((s = open(SockPath, O_WRONLY | O_NDELAY)) >= 0)
    {
      debug("huii, my fifo already exists??\n");
      if (quietflag)
	{
	  Kill(d_userpid, SIG_BYE);
	  eexit(11);
	}
      printf("There is already a screen running on %s.\n",
	     Filename(SockPath));
      if (stat(SockPath, &st) == -1)
	Panic(errno, "stat");
      if (st.st_uid != real_uid)
	Panic(0, "Unfortunatelly you are not its owner.");
      if ((st.st_mode & 0700) == 0600)
	Panic(0, "To resume it, use \"screen -r\"");
      else
	Panic(0, "It is not detached.");
      /* NOTREACHED */
    }
# ifndef NOREUID
  (void) unlink(SockPath);
  if (mknod(SockPath, S_IFIFO | SOCKMODE, 0))
    Panic(0, "mknod fifo %s failed", SockPath);
#  ifdef BROKEN_PIPE
  if ((s = open(SockPath, O_RDWR | O_NDELAY, 0)) < 0)
#  else
  if ((s = open(SockPath, O_RDONLY | O_NDELAY, 0)) < 0)
#  endif
    Panic(errno, "open fifo %s", SockPath);
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
  return s;
# else /* NOREUID */
  if (UserContext() > 0)
    {
      (void) unlink(SockPath);
      if (mknod(SockPath, S_IFIFO | SOCKMODE, 0))
	UserReturn(0);
      UserReturn(1);
    }
  if (UserStatus() <= 0)
    Panic(0, "mknod fifo %s failed", SockPath);
#  ifdef BROKEN_PIPE
  if ((s = secopen(SockPath, O_RDWR | O_NDELAY, 0)) < 0)
#  else
  if ((s = secopen(SockPath, O_RDONLY | O_NDELAY, 0)) < 0)
#  endif
    Panic(errno, "open fifo %s", SockPath);
  return s;
# endif /* NOREUID */
}


int
MakeClientSocket(err, name)
int err;
char *name;
{
  register int s = 0;

  strcpy(SockNamePtr, name);
# ifdef NAME_MAX
  if (strlen(SockNamePtr) > NAME_MAX)
    {
      debug2("MakeClientSocket: '%s' truncated to %d chars\n",
	     SockNamePtr, NAME_MAX);
      SockNamePtr[NAME_MAX] = '\0';
    }
# endif /* NAME_MAX */
  
  if ((s = secopen(SockPath, O_WRONLY | O_NDELAY, 0)) >= 0)
    {
      (void) fcntl(s, F_SETFL, 0);
      return s;
    }
  if (err)
    Msg(errno, "open: %s (but continuing...)", SockPath);
  debug1("MakeClientSocket() open %s failed\n", SockPath);
  return -1;
}

#else	/* NAMEDPIPE */

int
MakeServerSocket()
{
  register int s;
  struct sockaddr_un a;
  struct stat st;

  if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
    Panic(errno, "socket");
  a.sun_family = AF_UNIX;
  strcpy(SockNamePtr, SockName);
# ifdef NAME_MAX
  if (strlen(SockNamePtr) > NAME_MAX)
    {
      debug2("MakeServerSocket: '%s' truncated to %d chars\n",
	     SockNamePtr, NAME_MAX);
      SockNamePtr[NAME_MAX] = '\0';
    }
# endif /* NAME_MAX */

  strcpy(a.sun_path, SockPath);
# ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
# endif /* NOREUID */
  if (connect(s, (struct sockaddr *) &a, strlen(SockPath) + 2) != -1)
    {
      debug("oooooh! socket already is alive!\n");
      if (quietflag)
	{ 
	  Kill(d_userpid, SIG_BYE);
	  /* 
	   * oh, well. nobody receives that return code. papa 
	   * dies by signal.
	   */
	  eexit(11);
	}
      printf("There is already a screen running on %s.\n",
	     Filename(SockPath));
      if (stat(SockPath, &st) == -1)
	Panic(errno, "stat");
      if (st.st_uid != real_uid)
	Panic(0, "Unfortunatelly you are not its owner.");
      if ((st.st_mode & 0700) == 0600)
	Panic(0, "To resume it, use \"screen -r\"");
      else
	Panic(0, "It is not detached.");
      /* NOTREACHED */
    }
#if defined(m88k) || defined(sysV68)
  close(s);	/* we get bind: Invalid argument if this is not done */
  if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
    Panic(errno, "reopen socket");
#endif
  (void) unlink(SockPath);
  if (bind(s, (struct sockaddr *) & a, strlen(SockPath) + 2) == -1)
    Panic(errno, "bind (%s)", SockPath);
#ifdef SOCK_NOT_IN_FS
    {
      int f;
      if ((f = secopen(SockPath, O_RDWR | O_CREAT, SOCKMODE)) < 0)
        Panic(errno, "shadow socket open");
      close(f);
    }
#else
  chmod(SockPath, SOCKMODE);
# ifdef NOREUID
  chown(SockPath, real_uid, real_gid);
# endif /* NOREUID */
#endif /* SOCK_NOT_IN_FS */
  if (listen(s, 5) == -1)
    Panic(errno, "listen");
# ifdef F_SETOWN
  fcntl(s, F_SETOWN, getpid());
  debug1("Serversocket owned by %d\n", fcntl(s, F_GETOWN, 0));
# endif /* F_SETOWN */
# ifndef NOREUID
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
# endif /* NOREUID */
  return s;
}

int
MakeClientSocket(err, name)
int err;
char *name;
{
  register int s;
  struct sockaddr_un a;

  if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
    Panic(errno, "socket");
  a.sun_family = AF_UNIX;
  strcpy(SockNamePtr, name);
# ifdef NAME_MAX
  if (strlen(SockNamePtr) > NAME_MAX)
    {
      debug2("MakeClientSocket: '%s' truncated to %d chars\n",
	     SockNamePtr, NAME_MAX);
      SockNamePtr[NAME_MAX] = '\0';
    }
# endif /* NAME_MAX */

  strcpy(a.sun_path, SockPath);
# ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
# else /* NOREUID */
  if (access(SockPath, W_OK))
    {
      if (err)
	Msg(errno, "%s", SockPath);
      debug2("MakeClientSocket: access(%s): %d.\n", SockPath, errno);
      close(s);
      return -1;
    }
# endif /* NOREUID */
  if (connect(s, (struct sockaddr *) &a, strlen(SockPath) + 2) == -1)
    {
      if (err)
	Msg(errno, "%s: connect", SockPath);
      debug("MakeClientSocket: connect failed.\n");
      close(s);
      s = -1;
    }
# ifndef NOREUID
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
# endif /* NOREUID */
  return s;
}
#endif /* NAMEDPIPE */


void
SendCreateMsg(s, nwin)
int s;
struct NewWindow *nwin;
{
  struct msg m;
  register char *p;
  register int len, n;
  char **av = nwin->args;

  debug1("SendCreateMsg() to '%s'\n", SockPath);
  bzero((char *)&m, sizeof(m));
  m.type = MSG_CREATE;
  strcpy(m.m_tty, attach_tty);
  p = m.m.create.line;
  n = 0;
  if (nwin->args != nwin_undef.args)
    for (av = nwin->args; *av && n < MAXARGS - 1; ++av, ++n)
      {
        len = strlen(*av) + 1;
        if (p + len >= m.m.create.line + MAXPATHLEN - 1)
	  break;
        strcpy(p, *av);
        p += len;
      }
  if (nwin->aka != nwin_undef.aka && p + strlen(nwin->aka) + 1 < m.m.create.line + MAXPATHLEN)
    strcpy(p, nwin->aka);
  else
    *p = '\0';
  m.m.create.nargs = n;
  m.m.create.aflag = nwin->aflag;
  m.m.create.flowflag = nwin->flowflag;
  m.m.create.lflag = nwin->lflag;
  m.m.create.hheight = nwin->histheight;
#ifdef SYSV
  if (getcwd(m.m.create.dir, sizeof(m.m.create.dir)) == 0)
#else
  if (getwd(m.m.create.dir) == 0)
#endif
    {
      Msg(errno, "%s", m.m.create.dir);
      return;
    }
  if (nwin->term != nwin_undef.term)
    strncpy(m.m.create.screenterm, nwin->term, 19);
  m.m.create.screenterm[19] = '\0';
  debug1("SendCreateMsg writing '%s'\n", m.m.create.line);
  if (write(s, (char *) &m, sizeof m) != sizeof m)
    Msg(errno, "write");
}

void
#ifdef USEVARARGS
/*VARARGS1*/
# if defined(__STDC__)
SendErrorMsg(char *fmt, ...)
# else /* __STDC__ */
SendErrorMsg(fmt, va_alist)
char *fmt;
va_dcl
# endif /* __STDC__ */
{ /* } */
  static va_list ap;
#else /* USEVARARGS */
/*VARARGS1*/
SendErrorMsg(fmt, p1, p2, p3, p4, p5, p6)
char *fmt;
unsigned long p1, p2, p3, p4, p5, p6;
{
#endif /* USEVARARGS */
  register int s;
  struct msg m;

#ifdef USEVARARGS
# if defined(__STDC__)
  va_start(ap, fmt);
# else /* __STDC__ */
  va_start(ap);
# endif /* __STDC__ */
  (void) vsprintf(m.m.message, fmt, ap);
  va_end(ap);
#else /* USEVARARGS */
  sprintf(m.m.message, fmt, p1, p2, p3, p4, p5, p6);
#endif /* USEVARARGS */
  debug1("SendErrorMsg: '%s'\n", m.m.message);
  if (display == 0)
    return;
  s = MakeClientSocket(1, SockName);
  m.type = MSG_ERROR;
  strcpy(m.m_tty, d_usertty);
  debug1("SendErrorMsg(): writing to '%s'\n", SockPath);
  (void) write(s, (char *) &m, sizeof m);
  close(s);
  sleep(2);
}

#ifdef PASSWORD
static int
CheckPasswd(pwd, pid, utty)
int pid;
char *pwd, *utty;
{
  if (CheckPassword && 
      strcmp(crypt(pwd, (strlen(Password) > 1) ? Password : "JW"),
	     Password))
    {
      if (*pwd)
	{
# ifdef NETHACK
          if (nethackflag)
	    Msg(0, "'%s' tries to explode in the sky, but fails. (%s)", utty, pwd);
          else
# endif /* NETHACK */
	  Msg(0, "Illegal reattach attempt from terminal %s, \"%s\"", utty, pwd);
	}
      debug1("CheckPass() wrong password kill(%d, SIG_PW_FAIL)\n", pid);
      Kill(pid, SIG_PW_FAIL);
      return 0;
    }
  debug1("CheckPass() from %d happy\n", pid);
  Kill(pid, SIG_PW_OK);
  return 1;
}
#endif	/* PASSWORD */

static void
ExecCreate(mp)
struct msg *mp;
{
  struct NewWindow nwin;
  char *args[MAXARGS];
  register int n;
  register char **pp = args, *p = mp->m.create.line;

  nwin = nwin_undef;
  for (n = mp->m.create.nargs; n > 0; --n)
    {
      *pp++ = p;
      p += strlen(p) + 1;
    }
  *pp = 0;
  if (*p)
    nwin.aka = p;
  if (*args)
    nwin.args = args;
  nwin.aflag = mp->m.create.aflag;
  nwin.flowflag = mp->m.create.flowflag;
  if (*mp->m.create.dir)
    nwin.dir = mp->m.create.dir;
  nwin.lflag = mp->m.create.lflag;
  nwin.histheight = mp->m.create.hheight;
  if (*mp->m.create.screenterm)
    nwin.term =  mp->m.create.screenterm;
  MakeWindow(&nwin);
}

static int
CheckPid(pid)
int pid;
{
  debug1("Checking pid %d\n", pid);
  if (pid < 2)
    return(-1);
  if (eff_uid == real_uid)
    return kill(pid, 0);
  if (UserContext() == 1)
    {
      UserReturn(kill(pid, 0));
    }
  return UserStatus();
}

void
ReceiveMsg()
{
  int left, len, i;
  static struct msg m;
  char *p;
  int ns = ServerSocket;
  struct mode Mode;
#ifdef UTMPOK
  struct win *wi;
#endif
#ifdef REMOTE_DETACH
  struct display *next;
#endif

#ifdef NAMEDPIPE
  debug("Ha, there was someone knocking on my fifo??\n");
  if (fcntl(ServerSocket, F_SETFL, 0) == -1)
    Panic(errno, "DELAY fcntl");
#else
  struct sockaddr_un a;

  len = sizeof(a);
  debug("Ha, there was someone knocking on my socket??\n");
  if ((ns = accept(ns, (struct sockaddr *) &a, &len)) < 0)
    {
      Msg(errno, "accept");
      return;
    }
#endif				/* NAMEDPIPE */

  p = (char *) &m;
  left = sizeof(m);
  while (left > 0)
    {
      len = read(ns, p, left);
      if (len < 0 && errno == EINTR)
	continue;
      if (len <= 0)
	break;
      p += len;
      left -= len;
    }

#ifdef NAMEDPIPE
# ifndef BROKEN_PIPE
  /* Reopen pipe to prevent EOFs at the select() call */
  close(ServerSocket);
  if ((ServerSocket = secopen(SockPath, O_RDONLY | O_NDELAY, 0)) < 0)
    Panic(errno, "reopen fifo %s", SockPath);
# endif
#else
  close(ns);
#endif

  if (len < 0)
    {
      Msg(errno, "read");
      return;
    }
  if (left > 0)
    {
      if (left != sizeof(m))
        Msg(0, "Message %d of %d bytes too small", left, sizeof(m));
      else
	debug("No data on socket.\n");
      return;
    }
  debug2("*** RecMsg: type %d tty %s\n", m.type, m.m_tty);
  for (display = displays; display; display = display->_d_next)
    if (strcmp(d_usertty, m.m_tty) == 0)
      break;
  debug2("display: %s display %sfound\n", m.m_tty, display ? "" : "not ");
  if (!display)
    {
      struct win *w;

      for (w = windows; w; w = w->w_next)
        if (!strcmp(m.m_tty, w->w_tty))
	  {
            display = w->w_display;
	    debug2("but window %s %sfound.\n", m.m_tty, display ? "" : "deatached (ignoring) ");
	    break;
          }
    }

  /* Remove the status to prevent garbage on the screen */
  if (display && d_status)
    RemoveStatus();

  switch (m.type)
    {
    case MSG_WINCH:
      if (display)
        CheckScreenSize(1); /* Change fore */
      break;
    case MSG_CREATE:
      if (display)
	ExecCreate(&m);
      break;
    case MSG_CONT:
	if (display && d_userpid != 0 && kill(d_userpid, 0) == 0)
	  break;		/* Intruder Alert */
      debug2("RecMsg: apid=%d,was %d\n", m.m.attach.apid, display ? d_userpid : 0);
      /* FALLTHROUGH */
    case MSG_ATTACH:
      if (CheckPid(m.m.attach.apid))
	{
	  debug1("Attach attempt with bad pid(%d)\n", m.m.attach.apid);
	  Msg(0, "Attach attempt with bad pid(%d) !", m.m.attach.apid);
          break;
	}
      if ((i = secopen(m.m_tty, O_RDWR | O_NDELAY, 0)) < 0)
	{
	  debug1("ALERT: Cannot open %s!\n", m.m_tty);
#ifdef NETHACK
          if (nethackflag)
	    Msg(errno, 
	        "You can't open (%s). Perhaps there's a Monster behind it",
	        m.m_tty);
          else
#endif
	  Msg(errno, "Attach: Could not open %s", m.m_tty);
	  Kill(m.m.attach.apid, SIG_BYE);
	  break;
	}
#ifdef PASSWORD
      if (!CheckPasswd(m.m.attach.password, m.m.attach.apid, m.m_tty))
	{
	  debug3("RcvMsg:Checkpass(%s,%d,%s) failed\n",
		 m.m.attach.password, m.m.attach.apid, m.m_tty);
	  close(i);
	  break;
	}
#else
# ifdef MULTIUSER
      Kill(m.m.attach.apid, SIGCONT);
# endif
#endif				/* PASSWORD */
      if (display)
	{
	  debug("RecMsg: hey, why you disturb, we are not detached. hangup!\n");
	  close(i);
	  Kill(m.m.attach.apid, SIG_BYE);
	  Msg(0, "Attach msg ignored: We are not detached.");
	  break;
	}

#ifdef MULTIUSER
      if (strcmp(m.m.attach.auser, LoginName))
        if (*FindUserPtr(m.m.attach.auser) == 0)
	  {
              write(i, "Access to session denied.\n", 26);
	      close(i);
	      Kill(m.m.attach.apid, SIG_BYE);
	      Msg(0, "Attach: access denied for user %s", m.m.attach.auser);
	      break;
	  }
#endif

      errno = 0;
      debug2("RecMsg: apid %d is o.k. and we just opened '%s'\n", m.m.attach.apid, m.m_tty);
      /* turn off iflag on a multi-attach... */
      if (iflag && displays)
	{
	  iflag = 0;
	  display = displays;
#if defined(TERMIO) || defined(POSIX)
	  d_NewMode.tio.c_cc[VINTR] = VDISABLE;
	  d_NewMode.tio.c_lflag &= ~ISIG;
#else /* TERMIO || POSIX */
	  d_NewMode.m_tchars.t_intrc = -1;
#endif /* TERMIO || POSIX */
	  SetTTY(d_userfd, &d_NewMode);
	}

      /* create new display */
      GetTTY(i, &Mode);
      if (MakeDisplay(m.m.attach.auser, m.m_tty, m.m.attach.envterm, i, m.m.attach.apid, &Mode) == 0)
        {
	  write(i, "Could not make display.\n", 24);
	  close(i);
	  Msg(errno, "Attach: could not make display for user %s", m.m.attach.auser);
	  Kill(m.m.attach.apid, SIG_BYE);
	  break;
        }
#ifdef ultrix
      brktty(d_userfd);	/* for some strange reason this must be done */
#endif
#if defined(pyr) || defined(xelos) || defined(sequent)
      /*
       * Kludge for systems with braindamaged termcap routines,
       * which evaluate $TERMCAP, regardless weather it describes
       * the correct terminal type or not.
       */
      debug("unsetenv(TERMCAP) in case of a different terminal");
      unsetenv("TERMCAP");
#endif
    
      /*
       * We reboot our Terminal Emulator. Forget all we knew about
       * the old terminal, reread the termcap entries in .screenrc
       * (and nothing more from .screenrc is read. Mainly because
       * I did not check, weather a full reinit is save. jw) 
       * and /etc/screenrc, and initialise anew.
       */
      if (extra_outcap)
	free(extra_outcap);
      if (extra_incap)
	free(extra_incap);
      extra_incap = extra_outcap = 0;
      debug2("Message says size (%dx%d)\n", m.m.attach.columns, m.m.attach.lines);
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
      if ((p = getenv("SYSSCREENRC")))
	StartRc(p);
      else
# endif
	StartRc(ETCSCREENRC);
#endif
      StartRc(RcFileName);
      if (InitTermcap(m.m.attach.columns, m.m.attach.lines))
	{
	  FreeDisplay();
	  Kill(m.m.attach.apid, SIG_BYE);
	  break;
	}
      InitTerm(m.m.attach.adaptflag);
      if (displays->_d_next == 0)
        (void) chsock();
      signal(SIGHUP, SigHup);
#ifdef UTMPOK
      /*
       * we set the Utmp slots again, if we were detached normally
       * and if we were detached by ^Z.
       */
      RemoveLoginSlot();
      if (displays->_d_next == 0)
        for (wi = windows; wi; wi = wi->w_next)
	  if (wi->w_slot != (slot_t) -1)
	    SetUtmp(wi);
#endif
      SetMode(&d_OldMode, &d_NewMode);
      SetTTY(d_userfd, &d_NewMode);

      d_fore = NULL;
      if (d_user->u_detachwin >= 0) 
        fore = wtab[d_user->u_detachwin];
      if (!fore || fore->w_display
#ifdef MULTIUSER
				   || AclCheckPermWin(d_user, ACL_WRITE, fore)
#endif
				   					      )
        {
	  /* try to get another window */
#ifdef MULTIUSER
	  for (wi = windows; wi; wi = wi->w_next)
	    if (!wi->w_display && !AclCheckPermWin(d_user, ACL_WRITE, fore))
	      break;
	  if (!wi)
	    for (wi = windows; wi; wi = wi->w_next)
	      if (!wi->w_display && !AclCheckPermWin(d_user, ACL_READ, fore))
	        break;
	  if (!wi)
#endif
	    for (wi = windows; wi; wi = wi->w_next)
	      if (!wi->w_display)
	        break;
	  fore = wi;
	}
      if (fore)
        SetForeWindow(fore);
      Activate(0);
      if (!d_fore)
	ShowWindows();
      if (displays->_d_next == 0 && console_window)
	{
	  if (TtyGrabConsole(console_window->w_ptyfd, 1, "reattach") == 0)
	    Msg(0, "console %s is on window %d", HostName, console_window->w_number);
	}
      debug("activated...\n");
      break;
    case MSG_ERROR:
      Msg(0, "%s", m.m.message);
      break;
    case MSG_HANGUP:
      SigHup(SIGARG);
      break;
#ifdef REMOTE_DETACH
    case MSG_DETACH:
# ifdef POW_DETACH
    case MSG_POW_DETACH:
# endif				/* POW_DETACH */
      for (display = displays; display; display = next)
	{
	  next = display->_d_next;
# ifdef POW_DETACH
	  if (m.type == MSG_POW_DETACH)
	    Detach(D_REMOTE_POWER);
	  else
# endif				/* POW_DETACH */
	  if (m.type == MSG_DETACH)
	    Detach(D_REMOTE);
	}
      break;
#endif
    default:
      Msg(0, "Invalid message (type %d).", m.type);
    }
}

#if defined(_SEQUENT_) && !defined(NAMEDPIPE)
#undef connect

/*
 *  sequent_ptx socket emulation must have mode 000 on the socket!
 */
static int
sconnect(s, sapp, len)
int s, len;
struct sockaddr *sapp;
{
  register struct sockaddr_un *sap;
  struct stat st;
  int x;

  sap = (struct sockaddr_un *)sapp;
  if (stat(sap->sun_path, &st))
    return -1;
  chmod(sap->sun_path, 0);
  x = connect(s, (struct sockaddr *) sap, len);
  chmod(sap->sun_path, st.st_mode);
  return x;
}
#endif

int
chsock()
{
  int r, euid = geteuid();
  if (euid != real_uid)
    {
      if (UserContext() <= 0)
        return UserStatus();
    }
  r = chmod(SockPath, SOCKMODE);
  if (euid != real_uid)
    UserReturn(r);
  return r;
}

