/*****************************************************************************\
*                                                                             *
* socklink.c -- links a socket to stdio                                       *
*                                                                             *
* Copyright (c) 1995 by Sam Yee.                                              *
* Source freely redistributable in unmodified form.                           *
*                                                                             *
* $ Author:       Sam Yee (samy@sfu.ca) $                                     *
* $ Project:      socklink $                                                  *
* $ Date Started: May 17, 1995 $                                              *
* $ Version:      1.00 (1.9.95) $                                             *
*                                                                             *
* History:                                                                    *
* Ver. YY/MM/DD Who                Modifications                              *
* --------------------------------------------------------------------------- *
* 1.00 95/09/01 Sam Yee            Original release                           *
*                                                                             *
\*****************************************************************************/

#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <devices/timer.h>
#include <dos/dos.h>
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <errno.h>
#include <stdlib.h>

#ifdef _AMITCP
#include <bsdsocket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <amitcp/socketbasetags.h>
#include <string.h>

#else

#include <proto/all.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>

#include <netdb.h>
#include <sys/ioctl.h>
#include <ss/socket.h>

#define WaitSelect selectwait
#define CloseSocket s_close
#define IoctlSocket s_ioctl

#endif /* _AMITCP */

#ifndef TRUE
#define TRUE    1
#define FALSE   0
#endif /* TRUE */

#define VERSION "1.00"

#ifndef BOOL
#define short BOOL
#endif /* BOOL */

#define RAWIN_RUNTIME
#include "RawIN.h"

/*****************************************************************************/
const char version[] = "\0$VER: SockLink/"
#ifdef _AMITCP
"AmiTCP"
#else
"AS225r2"
#endif /* _AMITCP */
                       " "VERSION" (1.9.95)";

extern struct DosLibrary    *DOSBase;

#ifdef _AMITCP
extern STRPTR   _ProgramName;   /* provided by SAS/C startup */
struct Library  *SocketBase;
int             h_errno = 0;
#else
struct Library  *SockBase;
#endif /* !_AMITCP */

/*****************************************************************************/
/* prototypes */
void __regargs __chkabort(void);    /* disable SAS/C ^C checking */
void __regargs __chkabort(){}
void __regargs _CXBRK(void);
void __regargs _CXBRK(){}

int connect_server(char *prog_name, char *host_name, int port);
int init_socklib(void);
void cleanup_socklib(void);

/*****************************************************************************/
int
main(int  argc,
     char *argv[])
{
    struct Library  *RawINBase;
    struct Line     *line;
    BOOL            sock_writing = FALSE;
    char            *prog_name,
                    sockout_buf[256],
                    stdout_buf[256];
    int             sock,
                    select_return,
                    read_len,
                    break_count = 0,
                    wait_seconds = 0,
                    rc = 10;
    fd_set          r_fds,
                    w_fds,
                    readmask,
                    writemask;
    u_long          wait_mask,
                    return_mask;

    prog_name = FilePart(argv[0]);

    if (argc < 3)
        Printf("Usage: %s <host> <port> [start-delay-seconds]\n",prog_name);
    else
    {
        if (!init_socklib())
        {
            if ((sock = connect_server(prog_name,argv[1],atoi(argv[2]))) >= 0)
            {
                if (RawINBase = OpenLibrary(RAWIN_NAME,0))
                {
                    if (line = RI_AllocLine(2L,2L))
                    {
                        rc = 0;
                        Printf("SockLink " VERSION " (" __DATE__ " " __TIME__ ") -- links stdio to socket\n"
                               "Copyright (c) by Sam Yee (samy@sfu.ca).  Freely distributable.\n"
                               "Press ^C^C^C (3 Control-C's) to terminate.\n\n"
                               "Connected to %s at port %ld.\n"
                               ,argv[1],atoi(argv[2]));

                        if (argc > 3)
                        {
                            struct timeval tv;

                            wait_seconds = atol(argv[3]);
                            Printf("Linking stdio to socket in %ld seconds.\n\n",wait_seconds);
#ifdef _AMITCP
                            tv.tv_sec = wait_seconds;
                            tv.tv_usec = 0;
#else
                            tv.tv_secs = wait_seconds;
                            tv.tv_micro = 0;
#endif /* _AMITCP */
                            select(0,NULL,NULL,NULL,&tv);
                        }
                        else
                            Printf("\n");

                        wait_mask = SIGBREAKF_CTRL_C |
                                    (1L << line->ReadReplyPort->mp_SigBit);
                        RI_SendReadRequest(line);   /* send a request to the input handler */
                        FD_SET(sock,&r_fds);
                        FD_SET(sock,&w_fds);

                        /* keeping going until something goes bad */
                        while (!(line->flags & (LNF_EOF|LNF_BREAKC|LNF_READERROR|LNF_WRITEERROR)))
                        {
                            return_mask = wait_mask;
                            readmask = r_fds;
                            writemask = w_fds;

                            select_return = WaitSelect(sock+1,&readmask,
                                                       sock_writing ? &writemask : NULL,
                                                       NULL,NULL,&return_mask);

                            if ((select_return < 0) && (errno != EINTR))
                            {
                                Printf("\n\n%s: select returns %ld; errno=%ld\n",
                                       prog_name,select_return,errno);
                                break;
                            }

                            if (FD_ISSET(sock,&readmask))
                            {
                                /* wait for a write to finish if needed */
                                RI_WaitWrite(line);

                                if ((read_len = recv(sock,stdout_buf,sizeof(stdout_buf),0)) > 0)
                                    RI_SendWriteRequest(line,stdout_buf,read_len);
                                else
                                {
                                    Printf("\n\nEOF Connection closed by foreign host.\n");
                                    break;
                                }
                            }

                            if (sock_writing)
                            {
                                if (FD_ISSET(sock,&writemask))
                                    sock_writing = FALSE;
                            }

                            /* we can't read from stdin if we are already writing to socket */
                            if (!sock_writing)
                            {
                                if ((read_len = RI_GetBlock(line,FALSE,FALSE,sockout_buf,
                                                            sizeof(sockout_buf))) > 0L)
                                {
                                    if (sockout_buf[0] == 0x03) /* ^C? */
                                        break_count++;
                                    else
                                        break_count = 0;

                                    send(sock,sockout_buf,read_len,0);
                                    sock_writing = TRUE;

                                    if (break_count != 3)
                                        RI_SendReadRequest(line);
                                    else
                                        break;  /* ^C^C^C quits */
                                }
                            }

                            if (return_mask & SIGBREAKF_CTRL_C)
                                break;
                        }

                        RI_FreeLine(line);
                    }

                    CloseLibrary(RawINBase);
                }

                CloseSocket(sock);
            }

            cleanup_socklib();
        }
        else
            Printf("%s: TCP/IP stack not running\n",prog_name);
    }

    return(rc);
}

/*****************************************************************************/
/* connects to a host at a specifc port */
int /* socket descriptor if connection succeeded, else -1 */
connect_server(char *prog_name,
               char *host_name,
               int  port)
{
    struct in_addr      defaddr;
    struct hostent      *hp;
    struct sockaddr_in  sin;
    int                 sock;
    long                onoff = 1;

    /* lookup the IP address of a host */
    if (hp = gethostbyname(host_name))
    {
        sin.sin_family = hp->h_addrtype;
        memcpy(&sin.sin_addr,hp->h_addr,hp->h_length);
    }
    else
    {
        if ((defaddr.s_addr = inet_addr(host_name)) == -1)
        {
            Printf("%s: unknown host: %s\n",prog_name,host_name);
            return(-1);
        }

        sin.sin_family = PF_INET;
        memcpy(&sin.sin_addr,&defaddr,sizeof(struct in_addr));
    }

    sin.sin_port = htons(port);

    /* open a socket */
    if ((sock = socket(AF_INET,SOCK_STREAM,0)) >= 0)
    {
        /* connect sock to host */
        if (connect(sock,(struct sockaddr *)&sin,sizeof(sin)) == -1)
        {
            Printf("%s: connection refused: errno=%ld\n",prog_name,errno);
            CloseSocket(sock);
            sock = -1;
        }
        else
            IoctlSocket(sock,FIOASYNC,(caddr_t)&onoff);
    }

    return(sock);
}

/*****************************************************************************/
/* opens socket library, or non-zero for error */
int
init_socklib(void)
{
#ifdef _AMITCP
    if (SocketBase = OpenLibrary("bsdsocket.library",0L))
    {
        if (!SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno,
                            SBTM_SETVAL(SBTC_HERRNOLONGPTR), &h_errno,
                            SBTM_SETVAL(SBTC_LOGTAGPTR), _ProgramName,
                            TAG_END))
            return(0);

        CloseLibrary(SocketBase);
    }

#else

    if (SockBase = OpenLibrary("socket.library",0L))
    {
        setup_sockets(10,&errno);
        return(0);
    }

#endif /* _AMITCP */
    return(-1);
}

/*****************************************************************************/
void
cleanup_socklib(void)
{
#ifdef _AMITCP
    CloseLibrary(SocketBase);
#else
    cleanup_sockets();
    CloseLibrary(SockBase);
#endif /* _AMITCP */
}

/*****************************************************************************/
/* EOF socklink.c */
