/* ITALK.C:Internet Talking:  Copyright 1991, 1992 Steven D. Wallace */
/*   Public domain.  For non-commercial use only.
/*                                                                         */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <sys/file.h>
#include <ctype.h>
#include <signal.h>
#define TIOCGETP        _IOR(0, 8,struct sgttyb)/* get parameters -- gtty */
#define TIOCSETP        _IOW(0, 9,struct sgttyb)/* set parameters -- stty */ 
/*
 * Structure for TIOCGETP and TIOCSETP ioctls.
 */
 
#ifndef _SGTTYB_
#define _SGTTYB_
struct  sgttyb {
        char    sg_ispeed;              /* input speed */
        char    sg_ospeed;              /* output speed */
        char    sg_erase;               /* erase character */
        char    sg_kill;                /* kill character */
        short   sg_flags;               /* mode flags */
};
#endif
#if 1 /* 0 for computers that don't have h_addr_list in struct hostent */
#define h_addr  h_addr_list[0] /* for backwards compatibilty */
#endif

#define HOSTFILE ".ithost"
#define FILE1 "/home/Isengard/mordor/bin/.lphost"
#define SDWFILE FILE1

#define MAXMACRO 100
#define MAXDIGIT 2

/* TERM.C */

extern void     new_term(), old_term();
extern char     get_key(), in_key();

extern errno;
int sock;
int df_i = 1;
int df_o = 2;
char quiet = 0;

void aborting();
void get_address();
void split_string();

main(argc, argv)
int argc;
char *argv[];
{
  char  address[30], port[10], path[80], startup[20], macro[20], blank[80];
  char  *address_ptr, *port_ptr, *startup_ptr, *macro_ptr;
  int   i = 0;
  char	command[MAXMACRO][80];

  address[0] = port[0] = path[0] = startup[0] = macro[0] = blank[0] = 0;
  signal(SIGPIPE,SIG_IGN);

  i = 1;
  if(i < argc) {
    if(argv[i][0] == '-' && argv[i][1] == 'q') {
      quiet = 1;
      i++;
    }
  }
  if(i < argc) {
    strcpy(address, argv[i++]);
    if(i < argc)
      strcpy(port, argv[i++]);
  }
  else
    get_address(address, port);

  /* determine user's pathname to their .ithost file */
  sprintf(path, "%s/%s", getenv("HOME"), HOSTFILE);

  /* if no dot in address string argument, good chance it is a symbol */
  if (!dot_in_string(address)) {

    address_ptr = address;

    /* if no valid port given as argument, read in new startup */
    if (!*port)
      port_ptr = port;
    else
      port_ptr = blank;

    /* then look up address in .ithost in user's home directory */
    if (!read_address(path, address, address_ptr, port_ptr, startup, macro))

      /* not in their home directory, so try ~swallace/.lphost */
      read_address(SDWFILE, address, address_ptr, port_ptr, startup, macro);
    else

      /* lookup good, see if address is a reference to global symbol */
      if (*address == '!') {

	if (*port != '!')
	  port_ptr = blank;

	/* if no valid startup given, read in global startup */
	if (!*startup || *startup == '#')
	  startup_ptr = startup;
	else
	  startup_ptr = blank;

	/* if no valid macro given, read in global startup */
	if (!*macro || *macro == '#')
	  macro_ptr = macro;
	else
	  macro_ptr = blank;

        read_address(SDWFILE, address + 1, address_ptr, port_ptr, startup_ptr, 
macro_ptr);
      }

    }

  if (*port == '#') 	*port = 0;
  if (*startup == '#')	*startup = 0;
  if (*macro == '#')	*macro = 0;

/*  printf("addr: %s, port: %s, startup: %s, macro: %s\n", address, port, start
up, macro); */

  sock = connect_sock(address, port);
  if (sock == -1)
    aborting(3);

  /* clear macros */
  for(i=0; i != MAXMACRO; i++)
    command[i][0] = 0;

  /* determine user's pathname to their .macro or whatever file */
  sprintf(path, "%s/%s", getenv("HOME"), macro);

  /* read macro from user's file */
  if(*macro)
   if (read_macro(path, command) && !quiet)
    fprintf(stderr, "Macros read from file %s.\n", path);

  if (*startup) {
    sprintf(path, "%s/%s", getenv("HOME"), startup);
    write_file(path);
  }

  if (socket_io_handler(sock, command) == -1)
    aborting(4);

}


int connect_sock(address, port)
char *address;
char *port;
{
  int  temp_int;
  char name[80];
  char number[20];
  unsigned long addr;
  unsigned long saddr;
  struct sockaddr_in sin;
  struct hostent *host;

  if(isdigit(address[0])) {
    if((addr = inet_addr(address)) == -1)
      aborting(5);
    else
      saddr = ntohl(addr);
      host = gethostbyaddr((char *)&saddr, sizeof(saddr), AF_INET);
      if(host)
        strncpy(name, host->h_name, 79);
      else
        strcpy(name, address);

      strcpy(number, address);
  }
  else {
    host = gethostbyname(address);
    if(!host) aborting(6);
    addr = *(long *)(host->h_addr);
    strcpy(name, host->h_name);

    saddr = ntohl(addr);
    sprintf(number, "%d.%d.%d.%d",
        (unsigned)(saddr)>>24,
        (saddr & 0x00ff0000)>>16,
        (saddr & 0x0000ff00)>>8,
        (saddr & 0x000000ff));
  }
  
  sin.sin_addr.s_addr = addr;
  sin.sin_family = AF_INET;
  sin.sin_port = htons(atoi(port) ? atoi(port) : 23);

  if(!quiet) {
    if(*port)
      fprintf(stderr, "Trying %s, port %s ...\n", number, port);
    else
      fprintf(stderr, "Trying %s ...\n", number);
  }

  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("Socket");
    return(-1);
  }
  if(connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    perror("Connect");
    return(-1);
  }

  temp_int = 1;
  ioctl(sock, FIONBIO, &temp_int);

  if(!quiet) {
    if(*port)
      fprintf(stderr, "Connected to %s, port %s.\n", name, port);
    else
      fprintf(stderr, "Connected to %s.\n", name);
  }

  return(sock);
}

int socket_io_handler(sock, command)
int sock;
char *command;
{
  int  readfds;
  int  len, i;
  char echo = 1;
  char buf[1024];
  char *temp, tc;
  struct sgttyb arg;

  while(1) {
    readfds = 1<<df_i | 1<<sock;

    while(!select(16, &readfds, 0, 0, 0)) ;
    if(readfds & (1<<df_i)) {
      len = read(df_i, buf, 1023);
      if(len == 0) aborting(1);
      if( ( (buf[0] >= '0') && (buf[0] <= '9') ) &&
      	 !( (buf[MAXDIGIT] >= '0') && (buf[MAXDIGIT] <= '9') ) ) {
	tc = buf[MAXDIGIT];
	buf[MAXDIGIT] = 0;
	i = atoi(buf);
        temp = command + i*80;
	if(*temp && (i < MAXMACRO)) {
          if(write(sock, temp, (len = strlen(temp))) < 0) {
            perror("write sock");
            return(0);
          }
	}
	else {
	  buf[MAXDIGIT] = tc;
          if(write(sock, buf, len) < 0) {
            perror("write sock");
            return(0);
          }
	}
      }
      else
        if(write(sock, buf, len) < 0) {
          perror("write sock");
          return(0);
        }
    }

    if(readfds & (1<<sock)) {
      len = read(sock, buf, 1023);
      if(len == 0) aborting(2);

      if (buf[0] == -1) {

	if (buf[1] == -5 && buf[2] == 1) {
	  ioctl(0,TIOCGETP,&arg);	/* Get initial setup */
	  arg.sg_flags &= ~(ECHO);	/* Turn off terminal ECHO */
	  ioctl(0,TIOCSETP,&arg);	/* Store new settings */
	  echo = 0;
      	    if (len > 3) if(write(df_o, buf+3, len-3) < 0) {
              perror("write terminal");
              return(0);
			 }
	}

	else if (buf[1] == -4 && buf[2] == 1) {
	  ioctl(0,TIOCGETP,&arg);	/* Get initial setup */
	  arg.sg_flags |= (ECHO);	/* Turn on terminal ECHO */
	  ioctl(0,TIOCSETP,&arg);	/* Store new settings */
	  echo = 1;
      	    if (len > 3) if(write(df_o, buf+3, len-3) < 0) {
              perror("write terminal");
              return(0);
			 }
	}

        else if(write(df_o, buf, len) < 0) {
          perror("write terminal");
          return(0);
        }

      } /* end FF special check */

      else if(write(df_o, buf, len) < 0) {  /* no FF, so output string */
        perror("write terminal");
        return(0);
      }

      if(!echo) {
	for(i = 0; i < len-2; i++)
	  if(buf[i] == -1)
            if(buf[i] == -1 && buf[i+1] == -4 && buf[i+2] == 1) {
	      ioctl(0,TIOCGETP,&arg);	/* Get initial setup */
	      arg.sg_flags |= (ECHO);	/* Turn on terminal ECHO */
	      ioctl(0,TIOCSETP,&arg);	/* Store new settings */
              echo = 1;
	    }
      } /* end no echo to echo check */

    } /* end socket out check */

  }  /* end infinite loop */
} /* end io_handler */

void get_address(address, port)
char *address;
char *port;
{
  char buf[51];

  write_msg("Destination: ");
  read_msg(buf,50);

  split_string(buf, address, port);
}

void split_string(str_in, str_one, str_two)
char *str_in;
char *str_one;
char *str_two;
{
  int i=0, j=0;

  while(str_in[i] != ' ' && str_in[i] != '\n' && str_in[i] != 0)
    str_one[j++] = str_in[i++];
  str_one[j]=0; j=0;
  while(str_in[i] == ' ' || str_in[i] == '\n') i++;
  if(str_in[i])
    while(str_in[i] != 0 && str_in[i] != '\n')
      str_two[j++] = str_in[i++];
  str_two[j]=0;
}

int dot_in_string(str)
char *str;
{
  int i=0;

  while (str[i++] != 0)
    if (str[i-1] == '.')
      return(1);

  return(0);
}

int read_macro(fname, command)
char  *fname;
char  *command;
{
  FILE *fp;
  char buf[81];
  char numbuf[21];
  char *temp;
  int  num, i;

  if((fp=fopen(fname, "r")) == 0)
    return(0);

  while(!feof(fp)) {
    fgets(buf, sizeof(buf), fp);
    if(*buf == '#')
      continue;
    split_string(buf, numbuf, buf);

    num = atoi(numbuf);
    temp = command + num*80;
    if(num) {
      strcpy(temp, buf);
      num = strlen(temp);

      /* replace all '\' with CR/LF? */
      for (i = 0; i != num; i++) {
	if (temp[i] == '\\')
	  temp[i] = 13;
      }

      /* add CR to end of string */
      if (temp[num-1] != 13) {
	temp[num] = 13;
	num = num +1;
      }
      temp[num] = 0;
    }
  }
  fclose(fp);
  return(1);
}

int read_address(fname, symbol, address, port, startup, macro)
char  *fname;
char  *symbol;
char  *address;
char  *port;
char  *startup;
char  *macro;
{
  FILE *fp;
  char buf[81];
  char cur_symbol[21];
  int  i;

  if((fp=fopen(fname, "r")) == 0)
    return(0);
  while(!feof(fp)) {
    fgets(buf, sizeof(buf), fp);/* get rest of line (address, port) */
    split_string(buf, cur_symbol, buf);

    /* check to see if parameter matches current symbol in line */
    for (i=0; symbol[i] != 0; i++)
      if (symbol[i] != cur_symbol[i])
        break;
    if (symbol[i] == cur_symbol[i]) {
      fclose(fp);
      split_string(buf, address, buf);
      if(*buf) split_string(buf, port, buf);
      if(*buf) split_string(buf, startup, buf);
      if(*buf) split_string(buf, macro, buf);
      return(1);
    } 
  }
  fclose(fp);
  return(0);
}

int write_file(path)
char  *path;
{
  int  fd;
  long count;
  char buf[1024];

  fd = open(path, O_RDONLY, 0);
  if(fd < 0) return(0);

  if(!quiet)
    fprintf(stderr, "Sending file %s to remote host.\n", path);

  while(1) {
    count = read(fd, buf, sizeof(buf));
    if(count)
      write(sock, buf, count);
    else
      break;
  }
  return(1);
}

write_msg(str)
char *str;
{
  write(df_o, str, strlen(str));
}

write_sock(str)
char *str;
{
  write(sock, str, strlen(str));
}

read_msg(str,slen)
char *str;
int slen;
{
  int readfds;
  int num=0, len;
  char ch;

  do {
    readfds = 1<<df_i;
    while(!select(16, &readfds, 0, 0, 0)) ;
    len = read(df_i, &ch, 1);
    if(len == 0) aborting(0);
    if(ch == 10 || num==slen-1) ch = 0;
    if(ch != 13) str[num++] = ch;
  } while(ch);

}

void aborting(arg)
int arg;
{
  switch (arg) {
	case 1:
		fprintf(stderr, "\nConnection closed by local host.\n");
		break;
	case 2:
		fprintf(stderr, "\nConnection closed by remote host.\n");
		break;
	case 3:
		fprintf(stderr, "\nUnable to connect to remote host.\n");
		break;
	case 4:
		fprintf(stderr, "\nProblems with sockets.  Connection closed.\n");
		break;
	case 5:
		fprintf(stderr, "\nInvalid host number.\n");
		break;
	case 6:
		fprintf(stderr, "\nInvalid host name.\n");
		break;
	default:
  		fprintf(stderr, "Connection closed (%d).\n",arg);
  }
  signal(SIGPIPE,SIG_IGN);
  close(sock); close(df_i); close(df_o);
  exit(0);
}


