/*

    S R I A L P O P  (POP over serial lines)

    Author: Rudi van Houten <R.vanHouten@cc.ruu.nl>
        Ac.Comp.Centr.Utrecht (Netherlands)
    With thanks to:
        Steve Dorner <sdorner@qualcomm.com>
        Scott Hannahs <sth@slipknot.mit.edu>
        Mogen Trab Damsgaard <trab@kubism.ku.dk>

    see the file README for an introduction and hints for installation

*/
static char sccsid[]= "@(#)srialpop.c	1.2";

#define LOGOUTGRACE 3
/*
    this is the time to wait (seconds) after a logout command before
    actually breaking the connection
*/

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#ifdef SYSV
#define bcopy(a,b,c) memcpy(b,a,c)
#include <termio.h>
#else
#include <sgtty.h>
#endif
#if __STDC__==1
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#define CRLF "\r\n"

#define iBuflen 512
static char sBuf[iBuflen];

static short iValidPort[]=
        {25     /* SMTP */
        ,105    /* PH server */
        ,106    /* password server */
        ,109    /* POP2 */
        ,110    /* POP3 */
        };
#define iValidPortsCount sizeof(iValidPort)/sizeof(short)

#ifdef SYSV
static struct termio sTermHold, sTermCurr;
#else
static struct sgttyb sTermHold, sTermCurr;
#endif

#if __STDC__==1
static void errorexit(char*, ... );
void main(void);
# ifdef EXTRATTYFLAGS
static void extrattyflags(void);
# endif
#endif

#ifdef EXTRATTYFLAGS
static void extrattyflags()
{
/*
 * here code can be placed to add/clear extra flags in
 * the tty discipline descriptions
 * here is an example that Scott Hannahs used on SGI
 */
sTermCurr.c_iflag |= (IXON | IXOFF);
return;
} /* void extrattyflags */
#endif

#if __STDC__==1
static void errorexit(char *sFormat, ...)
{
#else
static void errorexit(va_alist) va_dcl
{
    char *sFormat;
#endif
    va_list ap;
    fputs(CRLF,stderr);
#if __STDC__==1
    va_start(ap,sFormat);
#else
    va_start(ap);
    sFormat= va_arg(ap,char*);
#endif
    vfprintf(stderr,sFormat,ap);
    va_end(ap);
    fputs(CRLF,stderr);
#ifdef SYSV
    ioctl(0,TCSETAF,&sTermHold);
#else
    ioctl(0,TIOCSETP,&sTermHold);
#endif
    sleep(1); exit(1);
} /* errorexit */

void main()
{
int iSock, iPort, iChild;
register int i, l;
struct in_addr sInAddr;
struct sockaddr_in sServer;
struct hostent *psHost, *gethostbyname(), *gethostbyaddr();
char *psCommand, *psName, *psNumber;

    /* get stty structure sTermHold */
#ifdef SYSV
ioctl(0,TCGETA,&sTermHold);
#else
ioctl(0,TIOCGETP,&sTermHold);
#endif
bcopy(&sTermHold,&sTermCurr,sizeof(sTermCurr));
    /* and here prepare the line characteristics */
#ifdef SYSV
sTermCurr.c_iflag  &= ~(ICRNL | IXANY);
sTermCurr.c_oflag  &= ~OPOST;
sTermCurr.c_lflag  &= ~(ECHO | ICANON | ISIG );
sTermCurr.c_cc[VMIN] = 1;
sTermCurr.c_cc[VTIME] = 0;
#else
sTermCurr.sg_flags &= ~(ECHO | CRMOD | XTABS);
sTermCurr.sg_flags |= CBREAK;
#endif
#ifdef EXTRATTYFLAGS
extrattyflags();
#endif
        /* set line characteristics to NOECHO, NOCRLF etc. */
#ifdef SYSV
ioctl(0,TCSETAF,&sTermCurr);
#else
ioctl(0,TIOCSETP,&sTermCurr);
#endif


/* this is the main loop, it reads the telnet or exit commandline */
for (;;)
{
    write(1,"%",1); l= 0;
    while(read(0,&sBuf[l],1) == 1 && sBuf[l] != '\n' && sBuf[l] != '\r')
    {
	write(1,&sBuf[l],1);
	if (++l >= iBuflen) errorexit("Too long commandline",0);
    }
    write(1,CRLF,2);
    if (sBuf[l] == '\n' || sBuf[l] == '\r') sBuf[l]= '\0'; else sBuf[++l]= '\0';
    if (l == 0) continue;

    if (strcmp(sBuf,"logout") == 0
         || strcmp(sBuf,"exit") == 0
         || strcmp(sBuf,"quit") == 0
        )
        break; /* finished, logout now */

            /* next loop splits the commandline in its constituents */
    i= 0; psCommand= psName= psNumber= NULL;
    while (i < l && psNumber == NULL)
    {
        while (isspace(sBuf[i])) i++;
        if (i >= l) break;
        if (psCommand == NULL) psCommand= &sBuf[i]; /* command found */
        else
        if (psName == NULL) psName= &sBuf[i];       /* argument 1 */
        else psNumber= &sBuf[i];                    /* argument 2 */
        do { i++; } while (!isspace(sBuf[i]) && i < l);
        sBuf[i++]= '\0';        /* terminator after last argument */
    }
    /* now we have a command (telnet) with two
       arguments (1=host 2=portnumber) */

    if (strcmp(psCommand,"telnet"))
        errorexit("Sorry, don't know '%s'\n",psCommand);
    		/* telnet is the only known command */

    if (psName == NULL || psNumber == NULL)
        errorexit("Sorry, hostname or portnumber missing\n",0);
			/* and both host and port are required */

    if (l= strlen(psNumber))
    {
        for (i= 0; i < l; i++)
            if (!isdigit(psNumber[i]))
                errorexit("Portnumber (%s) not numeric\n",psNumber);
        iPort= atoi(psNumber);
    }
    else iPort= 0;

			/* check validity of portnumber */
    for (i=0; iPort != iValidPort[i]; i++)
        if (i == iValidPortsCount)
            errorexit("Sorry, %d is not a valid port\n",iPort);

        /*
           (unsigned long)(-1) = 0xffffffff, returnvalue
           from inet_addr() if illegal address passed through.
           I assume then it is a domain name.
           Thanks to Edwin Kremer (edwin@cs.ruu.nl) for the trick
        */
    if ((sInAddr.s_addr= inet_addr(psName)) == 0xffffffff)
        psHost= gethostbyname(psName);
    else
        psHost= gethostbyaddr((char*)&sInAddr,sizeof(struct in_addr),AF_INET);
    if (psHost == 0)
        errorexit("Cannot find server %s (host unknown)\n",psName);
    
        /* create socket */
    if ((iSock= socket(AF_INET,SOCK_STREAM,0)) < 0)
        { perror("Cannot open IP socket"); exit(1); }

        /* fill socket structure .....*/
    sServer.sin_family= AF_INET;
    bcopy(psHost->h_addr,&(sServer.sin_addr),psHost->h_length);
    sServer.sin_port= htons(iPort);
    
        /*........and connect */
    if (connect(iSock,&sServer,sizeof(sServer)) < 0)
        { perror("connecting stream socket"); exit(1); }

	/* now fork a child to copy stdin to socket */
    iChild= fork();
    if (iChild < 0) { perror("Cannot fork"); exit(1); }
    if (iChild == 0)
    { /* the child copies stdin to the socket */
	close(1); close(2); /* now close stdout and stderr */
        while ((l= read(0,sBuf,iBuflen)) > 0) write(iSock,sBuf,l);
        close(0); exit(0);
    } /* if (iChild == 0) */
    /* no else part, child does exit
       the parent copies the socket to stdout
    */
    while ((l= read(iSock,sBuf,iBuflen)) > 0) write(1,sBuf,l);
    close(iSock);

    kill(iChild,SIGTERM); /* superfluous */

} /* main loop */

	/* reset line characteristics */
#ifdef SYSV
ioctl(0,TCSETAF,&sTermHold);
#else
ioctl(0,TIOCSETP,&sTermHold);
#endif

sleep(LOGOUTGRACE); /* wait to satisfy Mac's Hayes Modem Tool */
close(0); close(1); close(2);
exit(0);
} /* main */
