#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include "kb_server.h"
static int COPY = 1;
static int TRUE = 1;
static int write_to_fd(int fd, const char *s1);
static int kb_index(const char *s1, const char *s2);
static void close_kbt_socket(int *sfd, fd_set *rfds, fd_set *tfds, int wfd);
static void concat(char *s1, const char *s2, int is2, int js2,
                   int nullout_s1);
static int int_to_char(int in, char *s1);
static void left_blank_unpad(char *s1);

int main(void)
{
  struct sockaddr_in address;
  struct hostent *hostinfo;
  int i, iread, nbytes, nread, fd, fd_max, result, lfl, icolon;
  int portnum, iend;
  fd_set readfds, testfds;
  char fifo_line[PACKET_LENGTH];
  char host_node[PACKET_LENGTH], msg[PACKET_LENGTH];
  char addr_dq[16], portnum_char[6];
  const char *kbt_prompt = "kb_telnet>";
  const char *fifos[2] = {"kbfifo_r", "kbfifo_w"};
  int telnet_sockfd=-1, fd_fifos[2] = {-1, -1};

  fd_fifos[1] = open(fifos[1], O_RDONLY|O_NONBLOCK);
  if(fd_fifos[1]>-1)
   {
    fd_max = fd_fifos[1];
    fd_fifos[0] = open(fifos[0], O_WRONLY);
   }
  if(fd_fifos[0]>-1)
   {
    if(fd_fifos[0]>fd_max)
     fd_max = fd_fifos[0];
    printf("kb_telnet client ports opened on fds %d and %d\n",
           fd_fifos[0], fd_fifos[1]);
   }
  else
   {
    printf("cannot open kb_telnet client ports\n");
    exit(1);
   }
  FD_ZERO(&readfds);
  FD_SET(fd_fifos[1], &readfds);
  signal(SIGPIPE, SIG_IGN);
  write_to_fd (fd_fifos[0], kbt_prompt);
  while(TRUE)
   {
    testfds = readfds;
    if(select(fd_max+1, &testfds, (fd_set *)0, (fd_set *)0,
       (struct timeval *)0)<1)
     {
      perror("kb_telnet-select");
      exit(2);
     }
    for(fd=0; fd<=fd_max; fd++)
     if(FD_ISSET(fd, &testfds))
      {
       if(fd==fd_fifos[1])
        {
         ioctl(fd, FIONREAD, &nread);
         if(!nread)
          {
           for(i=0; i<2; i++)
            if(fd_fifos[i]>-1)
             close(fd_fifos[i]);
           if(telnet_sockfd>-1)
            close(telnet_sockfd);
           exit(3);
          }
         nbytes = 0;
         while(nbytes<PACKET_LENGTH-1)
          {
           iread = read(fd_fifos[1], fifo_line+nbytes, PACKET_LENGTH-1-nbytes);
           if(iread>0)
            nbytes += iread;
           else
            break;
          }
         fifo_line[nbytes] = '\0';
         if(telnet_sockfd==-1)
          {
           if(!kb_index(fifo_line, "telnet"))
            {
             portnum = 23;
             lfl = strlen(fifo_line);
             iend = lfl-2;
             if(lfl>8)
              {
               icolon = kb_index(fifo_line, ":");
               if(icolon>7 && lfl>9 && (lfl-icolon)<8)
                {
                 concat(portnum_char, fifo_line, icolon+1, lfl-2, COPY);
                 portnum = atoi(portnum_char);
                 iend = icolon-1;
                }
               else if(icolon!=-1)
                portnum = -1;
               if(portnum>0)
                {
                 concat(host_node, fifo_line, 7, iend, COPY);
                 left_blank_unpad (host_node);
                 if(!(hostinfo = gethostbyname(host_node)))
                  {
                   strcpy(msg, "host not found\n");
                   strcat(msg, kbt_prompt);
                   write_to_fd (fd_fifos[0], msg);
                  }
                 else
                  {
                   int_to_char(portnum, portnum_char);
                   strcpy(addr_dq,
                    inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0]));
                   address.sin_addr.s_addr = inet_addr(addr_dq);
                   address.sin_family = AF_INET;
                   address.sin_port = htons(portnum);
                   telnet_sockfd = socket(AF_INET, SOCK_STREAM, 0);
                   printf("telnet_sockfd=%d\n", telnet_sockfd);
                   result = connect(telnet_sockfd,
                             (struct sockaddr *)&address, sizeof(address));
                   if(result==-1)
                    {
                     if(telnet_sockfd>-1)
                      {
                       close(telnet_sockfd);
                       telnet_sockfd = -1;
                      }
                     strcpy(msg, "connect failed\n");
                     strcat(msg, kbt_prompt);
                     write_to_fd (fd_fifos[0], msg);
                    }
                   else
                    {
                     fcntl(telnet_sockfd, F_SETFL,
                           fcntl(telnet_sockfd, F_GETFL) | O_NONBLOCK);
                     if(telnet_sockfd>fd_max)
                      fd_max = telnet_sockfd;
                     FD_SET(telnet_sockfd, &readfds);
                     strcpy(msg, "connecting to ");
                     strcat(msg, addr_dq);
                     strcat(msg, "\n");
                     write_to_fd (fd_fifos[0], msg);
                    }
                  }
                }
               else
                {
                 strcpy(msg, "??? invalid port number\n");
                 strcat(msg, kbt_prompt);
                 write_to_fd (fd_fifos[0], msg);
                }
              }
             else
              {
               strcpy(msg, "??? syntax: telnet host or telnet host:port\n");
               strcat(msg, kbt_prompt);
               write_to_fd (fd_fifos[0], msg);
              }
            }
           else
            {
             strcpy(msg, "??? invalid command\n");
             strcat(msg, kbt_prompt);
             write_to_fd (fd_fifos[0], msg);
            }
          }
         else
          {
           if(!kb_index(fifo_line, "close"))
            close_kbt_socket (&telnet_sockfd, &readfds, &testfds, fd_fifos[0]);
           else if(write_to_fd (telnet_sockfd, fifo_line)==EPIPE)
            close_kbt_socket (&telnet_sockfd, &readfds, &testfds, fd_fifos[0]);
          }
        }
       else if(fd==telnet_sockfd)
        {
         ioctl(fd, FIONREAD, &nread);
         if(!nread)
          {
           close_kbt_socket (&telnet_sockfd, &readfds, &testfds, fd_fifos[0]);
           continue;
          }
         nbytes = 0;
         while(nbytes<PACKET_LENGTH-1)
          {
           iread = read(telnet_sockfd, fifo_line+nbytes, PACKET_LENGTH-1-nbytes);
           if(iread>0)
            nbytes += iread;
           else
            break;
          }
         fifo_line[nbytes] = '\0';
         write_to_fd (fd_fifos[0], fifo_line);
        }
       else
        printf("kb_telnet:-----help me!  help me!----- fd=%d\n", fd);
      }
   }
  return 0;
}

static int write_to_fd(int fd, const char *s1)
{
  int ls1, iwrite;
  int nbytes=0, wtf=0;

  ls1 = strlen(s1);
  while(nbytes<ls1)
   {
    iwrite = write(fd, s1+nbytes, ls1-nbytes);
    if(iwrite>0)
     nbytes += iwrite;
    else
     {
      if(iwrite<0)
       wtf = errno;
      printf("kb_telnet: iwrite=%d fd=%d\n", iwrite, fd);
      break;
     }
   }
  return wtf;
}

static void close_kbt_socket(int *sfd, fd_set *rfds, fd_set *tfds, int wfd)
{
  close(*sfd);
  FD_CLR(*sfd, rfds);
  FD_CLR(*sfd, tfds);
  *sfd = -1;
  write_to_fd (wfd, "Communications disconnect");
}

static int kb_index(const char *s1, const char *s2)
{
  const char *p1, *p2;
  int i;

  for(i=0; s1[i]; i++)
   {
    p1 = &s1[i];
    p2 = s2;
    while(*p2 && *p2==*p1)
     {
      p1++;
      p2++;
     }
    if(!*p2) return i;
   }
  return -1;
}

static void concat(char *s1, const char *s2, int is2, int js2,
                   int nullout_s1)
{
  int i;

  if(!nullout_s1) while(*s1) s1++;
  for(i=is2; i<=js2; i++)
   {
    *s1 = *(s2+i);
    s1++;
   }
  *s1 = '\0';
}

static int int_to_char(int in, char *s1)
{
  int i;
  int nout=1;
  const int ipten[10]={1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};

  while(in>ipten[nout]-1)
   nout++;
  for(i=nout; i>0; i--)
   s1[nout-i] = ((in%ipten[i])/ipten[i-1])+48;
  s1[nout] = '\0';
  return nout;
}

static void left_blank_unpad(char *s1)
{
  char s1_hold[PACKET_LENGTH];
  int i=0;

  while(s1[i]==' ') i++;
  concat(s1_hold, s1, i, strlen(s1)-1, COPY);
  strcpy(s1, s1_hold);
}
