#define I_SYS
#define I_ERRNO
#define I_STRING
#define I_INET
#define I_IOCTL
#include "includes.h"

extern char com_result[1024];

struct sockaddr *make_sockaddr(unsigned int port, char *host,
  unsigned long defaultaddr);

char *sockaddr_to_str(struct sockaddr *,int trans);

/* This binds to the specified port, and optionally listens for connections.
 */

int bind_tcp_listen(unsigned int port, int queue)
{
  struct sockaddr_in sin, *addr_in;
  int s;

  addr_in = (struct sockaddr_in *)make_sockaddr(port,NULL,INADDR_ANY);

  if (term_localaddr == INADDR_ANY) 
    get_term_localaddr(inet_addr("127.0.0.1"));
  memset(&sin, 0, sizeof(sin));
  sin.sin_addr.s_addr = term_localaddr;
  sin.sin_port = htons( port );
  sin.sin_family = AF_INET;

  s = socket(sin.sin_family, SOCK_STREAM, 0); 

  if (s == -1) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  if (bind(s, (struct sockaddr * ) addr_in, sizeof(sin)) < 0) {
    if (errno != EADDRINUSE) {	/* if it wasn't in use */
      if ( addr_in != &sin ) {
         addr_in = &sin;
      }else {
        close(s);
        return -1; 		/* then we can't handle it, so abort. */
      }
    }else {
      close(s);
      return -2;		/* handled specially by some clients. */
    }
  }

  if (! queue) return s;

  if (listen(s, queue) == -1) {	/* If we can't listen... */
    sprintf(com_result,"XXXlisten:%s",strerror(errno));
    				/* then just dump. We can't handle */
				/* errors here. */
    close(s);
    return -1;
  }
  return s;
}

int bind_tcp(unsigned int s) {
  return bind_tcp_listen(s,5);
}

/*      if NO_UNIX_DOMAIN
 *
 *	We bind the socket, and then write the address to the requested
 *	file.  We are the server.
 */

/*	if STREAMS_PIPE
 *
 *	This is a bit of a hack.  If STREAMS_PIPE is defined, we look for the
 *	environment variable TERM_BORROWED_DISPLAY_NUMBER, and interpret the
 *	value as an X-server display number, and use that streams pipe as
 *	our client/server IPC channel.
 *
 *	This is for SCO.  It would probably work on other systems if you
 *	became root and set up the appropriate /dev/X?R and /dev/X?S entries
 */

int bind_unix(char *path) 
{
#if defined(NO_UNIX_DOMAIN)
  struct sockaddr_in sock_in;
  int size_in;
  int fd;
#else
  struct sockaddr_un sock_un;
#endif
#ifdef STREAMS_PIPE
  char *borrowed_display_number;
#endif

  int s;

#ifdef STREAMS_PIPE
  borrowed_display_number = getenv("TERM_BORROWED_DISPLAY_NUMBER");
  if (borrowed_display_number != 0) {
    s = open_stream_pipe(atoi(borrowed_display_number));
    if (s < 0) {
      sprintf(com_result, "XXXcannot borrow X display channel %s", borrowed_display_number);
      return -1;
    } else {
      sprintf(com_result, "XXXBorrowed X display channel %d\n", atoi(borrowed_display_number));
      return s;
    }
  }

#endif

#if defined(NO_UNIX_DOMAIN)
  if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  sock_in.sin_family = AF_INET;
  sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
  sock_in.sin_port = htons(0);
  if (bind(s, (struct sockaddr *) &sock_in, sizeof(sock_in)) < 0) {
    sprintf(com_result,"XXXbind:%s",strerror(errno));
    close(s);
    return -1;
  }

  size_in = sizeof(sock_in);
  if (getsockname(s, (struct sockaddr *) &sock_in, &size_in) < 0) {
    sprintf(com_result,"XXXgetsockname:%s",strerror(errno));
    close(s);
    return -1;
  }
  fd = open(path, O_WRONLY|O_CREAT, 0666);
  if (fd < 0) {
    sprintf(com_result,"XXXopen:%s",strerror(errno));
    close(s);
    return -1;
  }

  { char port[10];

    sprintf(port,"%u\n",ntohl(sock_in.sin_port));
    if (write(fd, port, sizeof(port)) < 0) {
      sprintf(com_result,"XXXwrite:%s",strerror(errno));
      close(s);
      return -1;
    }
  }

  sprintf(com_result, "XXXOK, Port is %d", ntohs(sock_in.sin_port));
#else
  if ((s = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  sock_un.sun_family = AF_UNIX;
  
  strcpy(sock_un.sun_path, path);
  unlink(sock_un.sun_path);
  if (bind(s, (struct sockaddr *) &sock_un, strlen(sock_un.sun_path) + 2)) {
    sprintf(com_result,"XXXbind:%s\n%s %s\n",strerror(errno),
      "Can't bind a socket at %s",path);
    close(s);
    return -1;
  }

#endif
  /* ok. Start looking for connections. */
  if (listen(s, 5) < 0) {
    sprintf(com_result,"XXXlisten%s",strerror(errno));
    close(s);
    return -1;
  }
  return s;
}

/* Connect to the specified port/address */

int connect_tcp(int s, char *hostname, unsigned int port) {
  struct sockaddr *addr;

  if(term_localaddr == INADDR_ANY)
    get_term_localaddr(inet_addr("127.0.0.1"));

  if (!(addr=make_sockaddr(port,hostname,htonl(term_localaddr)))) {
    sprintf(com_result,"XXXmake_sockaddr:%s",strerror(errno));
    return -1;
  }

  if (s < 0) if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  if (connect(s,addr,sizeof(struct sockaddr))<0) {
    sprintf(com_result,"XXXconnect: %s", strerror(errno));
    close(s);
    return  -1;
  }
  return s;
}

/* This routine allows one to duplicate a term connection without accessing
 * the unix socket.
 */

int duplicate_connect(int s, int Sock)
{
  char port[10];

  if (s < 0) if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  if(send_command(Sock,C_BINDS,1,"%d",0)<0){
    sprintf(com_result,"XXXC_BINDS:%s",command_result);
    close(s);
    return -1;
  }
  if(read(Sock,port,sizeof(port))<0){
    sprintf(com_result,"XXXread:%s",strerror(errno));
    close(s);
    return -1;
  }
  if(port[2]!= I_OK){
    sprintf(com_result,"XXXC_BINDS: port not ok");
    close(s);
    return -1;
  }
  return connect_tcp(s,NULL,(unsigned int)atoi(&port[3]));
}

/* This routine connects to a unix socket, intended to be term...
 */

/* if STREAMS_PIPE
 *
 *	This is a bit of a hack.  If STREAMS_PIPE is defined, we look for the
 *	environment variable TERM_BORROWED_DISPLAY_NUMBER, and interpret the
 *	value as an X-server display number, and use that streams pipe as
 *	our client/server IPC channel.
 *
 *	This is for SCO.  It would probably work on other systems if you
 *	became root and set up the appropriate /dev/X?R and /dev/X?S entries
 */


int open_unix(char *p){
  return connect_unix(-1,p);
}

/*      To hack things even more, I now have tried to make it possible to
 *      connect non-unix type sockets to term with the command C_BINDS. (bcr)
 */

int connect_unix(int S, char *p)
{
  struct sockaddr_in sock_in;
#if !defined(NO_UNIX_DOMAIN)
  struct sockaddr_un sock_un;
#endif
#ifdef STREAMS_PIPE
  char *borrowed_display_number;
#endif
  int s = -1;

#ifdef STREAMS_PIPE
  borrowed_display_number = getenv("TERM_BORROWED_DISPLAY_NUMBER");
  if (borrowed_display_number != 0) {
    if(S<0)
      s = MakeStreamPipeConnection(atoi(borrowed_display_number));
    else
      s = S;
    if (s < 0) {
      sprintf(com_result, "XXXcannot borrow X display channel %s", borrowed_display_number);
      return -1;
    } else {
      return s;
    }
  }

#endif

#if !defined(NO_UNIX_DOMAIN)
	/* First we find out if S is a unix domain socket. */
	/* If it is, we can connect it directly to the term socket. */
  if(S>=0){
    int size_in;

    size_in = sizeof(sock_in);
    if (getsockname(S, (struct sockaddr *) &sock_in, &size_in) < 0) {
      sprintf(com_result,"XXXgetsockname:%s",strerror(errno));
      close(s);
      return -1;
    }
    if(sock_in.sin_family==AF_UNIX) s=S;
  }
  if (s<0) if ((s = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0) {
    sprintf(com_result,"XXXsocket:%s",strerror(errno));
    return -1;
  }

  sock_un.sun_family =  AF_UNIX; 
  
  sprintf(sock_un.sun_path,"%s",p);
  
  if (connect(s, (struct sockaddr *) &sock_un, 
	      strlen(sock_un.sun_path) + 2)) {
    sprintf(com_result,"XXXconnect 2:%s",strerror(errno));
    close(s);
    return -1;
  }
 	/* If S is a tcp socket, then we duplicate the connection as tcp */
	/* and then close the unix socket we just created. */
  if(S>=0 && S!=s){
    S = duplicate_connect(S, s);
    close(s);
    s = S;
  }
  return s;
}
#else /* NO_UNIX_DOMAIN */

	/* Instead of a unix socket, we just have a file with the port # to */
	/* connect to. */

  {
    int fd;
    char port[10];

    fd = open(p, O_RDONLY);
    if (fd < 0) {
      sprintf(com_result,"XXXopen:%s",strerror(errno));
      return -1;
    }

    if (read(fd, port, sizeof(port)) < 0) {
      sprintf(com_result,"XXXread:%s",strerror(errno));
      close(fd);
      return -1;
    }

    close(fd);
  }

	/* If needed open a socket. */

  return connect_tcp(S,NULL,atoi(port));
}
#endif

