/*  irc.c                (c) Steve Adam 1995         steve@netinfo.com.au
 *
 *      A simple IRC client.
 *
 *	This massive kludge is about to be rekludged by having
 *	DCC added...  Wish me luck!  (Who am I talking to???)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <tos.h>

#include "\include\drivers.h"
#include "\include\transprt.h"

/* These definitions are necessary.  transprt.h has external
 * declarations for them.
 */
DRV_LIST *drivers = (DRV_LIST *)NULL;
TPL *tpl = (TPL *)NULL;

/* functions in skio.c          */
extern int16 scr_init(void);
extern void  scr_close(void);
extern void  scr_mvcursor(int16, int16);
extern void  scr_curson(void);
extern void  scr_cursoff(void);
extern void  scr_conout(char);
extern void  scr_conws(char *);
extern void  scr_ldel(int16);
extern void  scr_setwrap(int16);
extern void  scr_clear(void);
extern void  scr_setsregn(int16, int16);
extern void  scr_save(void);
extern void  scr_restore(void);
extern void  scr_setgrend(int16); 
extern void  scr_getcurpos(int16 *, int16 *);
extern void  scr_getcurmax(int16 *, int16 *);
extern void  scr_lclear(int16);
extern void  scr_bell(void);
extern long  scr_conin(int16);
extern int16 scr_gets(char *, int16);
extern int16 scr_conis(void);

extern long  scr_getkey(int16);
extern int16 scr_gets(char *, int16);
extern int16 scr_conis(void);

/* Screen control defines, for functions in skio.c    */
#define SCRE_BOLD       1
#define SCRE_LIGHT      2
#define SCRE_ITALIC     4
#define SCRE_UNDERLINE  8
#define SCRE_OUTLINE   16
#define SCRE_SHADOW    32
#define SCRE_REVERSE  256   /* This is NOT a VDI attribute  */

#define min(x,y)    (x<=y?x:y)

char irc_server[100] = "";
char irc_port[10] = "6667";
char chan[20] = "";
char nick[20] = "NoNick";
static int16 cn, xmax, ymax;
static uint32 cntime;

extern long atol();


/* Data structure that defines the connection/channel we're
 * currently talking to.  Max of 10 sounds cool...
 */

#define WNONE	0	/* This window not in use	*/
#define WDFLT	1	/* Default window (no chan)	*/
#define WCHAN	2	/* Normal channel chat		*/
#define WCHAT	3	/* DCC CHAT window			*/
#define WFILE	4	/* DCC FILE window			*/

#define DOUT	1	/* We initiated				*/
#define DIN		2	/* They initiated			*/

#define SNULL	0	/* No status							*/
#define SAWAIT	1	/* Wait for user to accept request		*/
#define SACTIVE	2	/* DCC request in progress				*/
#define SPWAIT  3	/* Waiting for remote to accept...		*/

#define MW	20		/* Maximum windows						*/

static struct {
	char	who[20];	/* nick or channel we're talking to	*/
	int  	type;		/* Type of this window				*/
	int16	cn;			/* Connection handle for DCC		*/
	int		direction;	/* Direction of DCC request			*/
	int		status;		/* status of DCC connection			*/
	int		fd;			/* File descriptor for DCC FILE		*/
	char	fname[50];	/* Name of the file for DCC FILE	*/
	uint32	rhost;		/* Remote host ip address			*/
	uint16	rport;		/* Remote port						*/
	uint32	cnt;		/* Count of bytes sent/received		*/
	uint32	lastack;	/* Most recent acknowledgement		*/
	uint32	oldcnt;		/* For updating display...			*/
} iw[MW] = {
	"", WDFLT, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
	"", WNONE, -1, 0, SNULL, 0, "", 0, 0, 0, 0, 0,
};

static int cw;				/*	Current window		*/
static char prompt[80];		/*	Current prompt		*/
static int	prlen;			/*	Prompt length		*/

int16 equ_txtn(char *s1, char *s2, int n)
{
    while (n--) {
        if (toupper(*s1) != toupper(*s2)) {
            return (FALSE);
        }
        if (*s1 == '\0')
            break;

        s1 += 1;
        s2 += 1;
    }
    return (TRUE);
}

/* reset_iline()    Clear the input line and redraw string at offset
 */

static void reset_iline(char *s, int16 o)
{
    scr_lclear(-1); /* Clear current line   */

	if (o == 0)
		scr_conws(prompt);

    scr_conws(&s[o]);
}

static void ch_win(int w)
{
	char lts[10];

	switch (iw[w].type) {
	case WNONE	:
		return;

	case WCHAN	:
	case WDFLT	:
		scr_lclear(-1);
		strcpy(chan, iw[w].who);
		strcpy(prompt, chan);
		strcat(prompt, ": ");
		prlen = (int)strlen(prompt);
		reset_iline("", 0);
		cw = w;
		break;

	case WCHAT	:
		scr_lclear(-1);
		chan[0] = '\0';
		strcpy(prompt, "CHAT ");
		strcat(prompt, iw[w].who);
		strcat(prompt, ": ");
		if (iw[w].status == SAWAIT)
			strcat(prompt, "Accept? Y/N ");
		else if (iw[w].status == SPWAIT)
			strcat(prompt, "Waiting for remote to accept");

		prlen = (int)strlen(prompt);
		reset_iline("", 0);
		cw = w;
		break;

	case WFILE	:
		scr_lclear(-1);
		chan[0] = '\0';
		strcpy(prompt, "FILE ");
		strcat(prompt, iw[w].fname);
		if (iw[w].direction == DIN)
			strcat(prompt, " RECV from ");
		else
			strcat(prompt, " SEND to ");

		strcat(prompt, iw[w].who);
		strcat(prompt, ": ");

		if (iw[w].status == SAWAIT)
			strcat(prompt, "Accept? Y/N ");
		else if (iw[w].status == SPWAIT)
			strcat(prompt, "Waiting for remote to accept");
		else {
			strcat(prompt, ltoa(iw[w].cnt, lts, 10));
			iw[w].oldcnt = iw[w].cnt;
		}
		prlen = (int)strlen(prompt);
		reset_iline("", 0);
		cw = w;
		break;
	}
}

static void ch_dn(void)
{
	int x;

	for (x = cw - 1; x >= 0; --x) {
		if (iw[x].type != WNONE) {
			ch_win(x);
			return;
		}
	}
	for (x = MW - 1; x > cw; --x) {
		if (iw[x].type != WNONE) {
			ch_win(x);
			return;
		}
	}
}

static void ch_up(void)
{
	int x;

	for (x = cw + 1; x < MW; ++x) {
		if (iw[x].type != WNONE) {
			ch_win(x);
			return;
		}
	}
	for (x = 0; x < cw; ++x) {
		if (iw[x].type != WNONE) {
			ch_win(x);
			return;
		}
	}
}

static int new_win(int type, char *name)
{
	int x;

	if (type == WCHAN) {
		for (x = 0; x < MW; ++x) {
			if (iw[x].type == WCHAN) {
				if (equ_txtn(iw[x].who, name, (int)strlen(name))) {
					return (x);
				}
			}
		}
	}
	for (x = 0; x < MW; ++x) {
		if (iw[x].type == WNONE) {
			iw[x].type = type;
			strcpy(iw[x].who, name);
			return (x);
		}
	}
	return (-1);
}

static void exit_wi(int i)
{
	if (i == 0)
		return;

	if (iw[i].type == WNONE)
		return;

	if (iw[i].cn >= 0) {
		TCP_close(iw[i].cn, 5);
		iw[i].cn = -1;
	}
	if (iw[i].fd > 0) {
		Fclose(iw[i].fd);
		iw[i].fd = 0;
	}
	iw[i].type = WNONE;
	iw[i].status = SNULL;
	iw[i].cnt = 0;
	iw[i].oldcnt = 0;
	iw[i].lastack = 0;
	iw[i].direction = 0;
	iw[i].fname[0] = '\0';
	iw[i].who[0] = '\0';

	if (i == cw)
		ch_dn();
}

static void exit_all(void)
{
	int x;

	for (x = 1; x < MW; ++x)
		exit_wi(x);
}

/* display_str()    Just sends a string to the display, but interprets
 *                  some special IRC control characters, like ^B for
 *                  underline.  Ignores ^A, too...
 *
 *                  (Only ^A and ^B are recognised so far...)
 */


static void display_str(char *s)
{
    int16 bold = FALSE, ul = FALSE, rv = FALSE;
    int16 modes = 0;


    while (*s) {
        if (*s == '\001')
            ;
        else if (*s == '\002') {
            if (bold) {
                bold = FALSE;
                modes &= ~SCRE_BOLD;
                scr_setgrend(modes);
            }
            else {
                bold = TRUE;
                modes |= SCRE_BOLD;
                scr_setgrend(modes);
            }
        }
        else if (*s == '\037') {
            if (ul) {
                ul = FALSE;
                modes &= ~SCRE_UNDERLINE;
                scr_setgrend(modes);
            }
            else {
                ul = TRUE;
                modes |= SCRE_UNDERLINE;
                scr_setgrend(modes);
            }
        }
        else if (*s == '\026') {
            if (rv) {
                rv = FALSE;
                modes &= ~SCRE_REVERSE;
                scr_setgrend(modes);
            }
            else {
                rv = TRUE;
                modes |= SCRE_REVERSE;
                scr_setgrend(modes);
            }
        }
        else
            scr_conout(*s);

        s += 1;
    }
    scr_setgrend(0);
}

/* if force == TRUE, redraw regardless of time diff  */
static void draw_time(int16 force)
{
    int32 timenow, hours, minutes, seconds;
    char ts[21], *tstr = &ts[1], *h, *m, *s;
	char lts[20];
    static uint32 lasttime = 0L;

    time(&timenow);
    timenow -= cntime;
    hours   = timenow / 3600;
    minutes = (timenow / 60) % 60;
    seconds = timenow % 60;

    if (!force) {
        if (timenow == lasttime || seconds % 5)
            return;

        lasttime = timenow;
    }

    strcpy(tstr, "00");
    h = ltoa(hours, lts, 10);
    strcpy(&tstr[2-strlen(h)], h);      /* Will fail if hours > 99!!    */

    strcat(tstr, ":00");
    m = ltoa(minutes, lts, 10);
    strcpy(&tstr[5-strlen(m)], m);      /* Cross fingers..              */

    strcat(tstr, ":00");
    s = ltoa(seconds, lts, 10);
    strcpy(&tstr[8-strlen(s)], s);      /* Why?  It can't really fail!! */

    scr_save();
    scr_mvcursor(ymax - 2, xmax - 8);
    scr_setgrend(SCRE_REVERSE);
    scr_conws(tstr);
    scr_restore();
}

static void draw_status_bar(void)
{
    char sbar[100];
    int x;

    strcpy(sbar, nick);
    strcat(sbar, " on Server = ");
    strcat(sbar, irc_server);

    sbar[xmax-10] = '\0';    /* Make sure it doesn't interfere with the time */

    for (x = (int)strlen(sbar); x < xmax; ++x)
        sbar[x] = ' ';
    sbar[x] = '\0';

    scr_save();
    scr_mvcursor(ymax - 2,0);
    scr_setgrend(SCRE_REVERSE);
    scr_conws(sbar);
    scr_restore();

    draw_time(TRUE);
}

static void str_toupper(char *s)
{
    while (*s) {
        *s = toupper(*s);
        ++s;
    }
}

static int basename(char *s)
{
	char news[50], *p;

	p = &s[strlen(s)-1];

	while (p > s) {
		if (*p == '\\' || *p == '/' || *p == ':' || *p == ']') {
			++p;
			break;
		}
		--p;
	}
	strcpy(news, p);
	strcpy(s, news);

	return ((int)strlen(s));
}

char *make_display_line(char *out, char *in)
{
    char *prefix, *command, *param, *eol, *ptr;
    static char *inval = "Invalid command string from server.\r\n";
	char lts[20], from_chan[100], dnick[20], dfile[100];
    int nickmatch = FALSE, i, nw;
    long t;
	uint32 rip;
	int16 rport;
    extern char *strstr();

    eol = in;
    while (*eol && *eol != '\r' && *eol != '\n')
        ++eol;

    *eol = '\0';

    /* Copy the string straight to -out- in case we want to keep it */
    strcpy(out, in);

    if (*in == ':') {
        command = prefix = &in[1];
        while (*command && *command != ' ')
            ++command;

        if (*command == '\0')
            return (inval);

        *command++ = '\0';
        while (*command == ' ')
            ++command;
    }
    else {
        prefix = "";
        command = in;
    }
    param = command;

    if (*param != '\0') {
        while (*param && *param != ' ')
            ++param;

        *param++ = '\0';

        if (*param != '\0') {
            while (*param && *param == ' ')
                ++param;
        }
        else {
            return (command);   /* No params, so don't worry too much.. */
        }
    }
    /* OK, now we have an optional prefix,
     * definately a command and parameters.
     * The next step is to seperate the user generated from the
     * server generated.  Server generated `commands' (that we can
     * ignore, but still display) consist of 3 decimal digits. (eg: 999)
     */

    if (isdigit(command[0]) && isdigit(command[1]) && isdigit(command[2])) {
        /* First remove my NICK from the msg */
        while (*param && *param != ' ')
			++param;

        if (*param != '\0')
            ++param;

        if (*param == ':')
            ++param;

        return (param); /* Easy */
    }

    /* Now we need to decide how to act upon various text commands that
     * might show up.
     */

    str_toupper(command);

    if (equ_txtn(command, "PING", 4)) {
        while (*param && *param != ' ')
            ++param;
        *param = '\0';

        strcpy(out, "PONG ");
        strcat(out, param);
        strcat(out, " :");
        strcat(out, nick);
        strcat(out, "\r\n");
        TCP_send(cn, out, (int16)strlen(out));
        return ("You were pinged");
    }

    if (equ_txtn(command, "PRIVMSG", 7) || equ_txtn(command, "NOTICE", 6)) {
        eol = prefix;
        while (*eol && *eol != '!')
            ++eol;
        *eol = '\0';

        /* Always strip first part of param string              */
        /* But first check to see if it matches current /nick   */

        if (equ_txtn(param, nick, (int16)strlen(nick)))
            nickmatch = TRUE;   /* If nickmatch && PRIVMSG, this is /msg    */

		i = 0;

        while (*param && *param != ' ')	/* Copy nick or chan name */
			from_chan[i++] = *param++;

		from_chan[i] = '\0';

        if (*++param == ':')
            ++param;

        if (*param == '\001') {
            ++param;
            if (equ_txtn(param, "ACTION", 6)) {
                param = &param[6];
                strcpy(out, "* ");
                strcat(out, prefix);
                strcat(out, " ");
                eol = &out[strlen(out)];
                strcat(out, param);
                while (*eol != '\001')
                    eol += 1;
                *eol = '\0';
            }
            if (equ_txtn(param, "dcc", 3)) {
				strcpy(dnick, prefix);
                param = &param[3];
				while (isspace(*param))
					++param;

				if (equ_txtn(param, "chat", 4)) {
	                param = &param[4];
					while (isspace(*param))
						++param;
					while (*param && !isspace(*param))
						++param;
					while (isspace(*param))
						++param;
					sscanf(param, "%lu %u", &rip, &rport);

					nw = new_win(WCHAT, dnick);
					if (nw < 0) {
						sprintf(out, "Can't accept DCC CHAT, out of windows");
						return (out);
					}
					iw[nw].status = SAWAIT;
					iw[nw].rhost = rip;
					iw[nw].rport = rport;
					sprintf(out, "DCC chat requested by %s", dnick);
					return(out);
				}
				else if (equ_txtn(param, "send", 4)) {
	                param = &param[4];
					while (*param && isspace(*param))
						++param;
					i = 0;
					while (*param && !isspace(*param))
						dfile[i++] = *param++;
					dfile[i] = '\0';

					while (*param && isspace(*param))
						++param;
					if (basename(dfile) <= 0) {
						sprintf(out, "DCC FILE invalid filename from %s\r\n", dnick);
						return (out);
					}
					sscanf(param, "%lu %u", &rip, &rport);

					nw = new_win(WFILE, dnick);
					if (nw < 0) {
						sprintf(out, "Can't accept DCC FILE, out of windows");
						return (out);
					}
					iw[nw].status = SAWAIT;
					iw[nw].rhost = rip;
					iw[nw].rport = rport;
					strcpy(iw[nw].fname, dfile);
					iw[nw].direction = DIN;
					sprintf(out, "DCC %s sending %s", dnick, dfile);
					return (out);
				}
            }
			else if (equ_txtn(param, "VERSION", 7)) {
                if (equ_txtn(command, "NOTICE", 6)) {
                    strcpy(out, prefix);
                    strcat(out, " ");
                    strcat(out, param);
                    return (out);
                }
                else {
                    strcpy(out, "NOTICE ");
                    strcat(out, prefix);
                    strcat(out, " :\001VERSION STiKiRC v00.50 Atari ST/TT/Falcon\001");
                    strcat(out, "\r\n");
                    TCP_send(cn, out, (int16)strlen(out));

                    strcpy(out, prefix);
                    strcat(out, " VERSIONed you.");
                    return (out);
                }
			}
			else if (equ_txtn(param, "FINGER", 6)) {
                if (equ_txtn(command, "NOTICE", 6)) {
                    strcpy(out, prefix);
                    strcat(out, " ");
                    strcat(out, param);
                    return (out);
                }
                else {
                    strcpy(out, "NOTICE ");
                    strcat(out, prefix);
                    strcat(out, " :\001FINGER Unconfigured but who cares, it's all lies anyway...\001");
                    strcat(out, "\r\n");
                    TCP_send(cn, out, (int16)strlen(out));

                    strcpy(out, prefix);
                    strcat(out, " FINGERed you.");
                    return (out);
                }
			}
            else if (equ_txtn(param, "PING", 4)) {
                if (equ_txtn(command, "NOTICE", 6)) {
                    time(&t);
                    t -= atol(&param[5]);

                    strcpy(out, prefix);
                    strcat(out, " PONGed you.  ");
                    strcat(out, ltoa(t, lts, 10));
                    strcat(out, " seconds");

                    return (out);
                }
                else {
                    param = &param[4];
                    strcpy(out, "NOTICE ");
                    strcat(out, prefix);
                    strcat(out, " :\001PING ");
                    strcat(out, param);
                    strcat(out, "\r\n");
                    TCP_send(cn, out, (int16)strlen(out));

                    ptr = getvstr("IRCPONGMSG");
                    if (*ptr != '0' && *ptr != '1') {
                        strcpy(out, "NOTICE ");
                        strcat(out, prefix);
                        strcat(out, " :");
                        strcat(out, ptr);
                        strcat(out, "\r\n");
                        TCP_send(cn, out, (int16)strlen(out));
                    }

                    strcpy(out, prefix);
                    strcat(out, " pinged you.");
                    return (out);
                }
            }
        }
        else {
            if (nickmatch) {
                strcpy(out, "**msg[");
                strcat(out, prefix);
                strcat(out, "] :");
                strcat(out, param);
            }
            else {
				if (!equ_txtn(from_chan, chan, (int16)strlen(from_chan))) {
					strcpy(out, from_chan);
					strcat(out, ": <");
				}
				else {
                    strcpy(out, "<");
				}
                strcat(out, prefix);
                strcat(out, "> ");
                strcat(out, param);
            }
        }
        return (out);
    }
    if (equ_txtn(command, "JOIN", 4)) {
        if (*prefix != 0) {
            eol = prefix;
            while (*eol && *eol != '!')
                ++eol;
            *eol = '\0';

            strcpy(out, prefix);
            strcat(out, " has joined channel ");
        }
        else {
            strcpy(out, "You have joined channel ");
        }
        strcat(out, param);
        return(out);
    }
    if (equ_txtn(command, "PART", 4)) {
        if (*prefix != 0) {
            eol = prefix;
            while (*eol && *eol != '!')
                ++eol;
            *eol = '\0';

            strcpy(out, prefix);
            strcat(out, " has left channel ");
        }
        else {
            strcpy(out, "You have left channel ");
        }
        strcat(out, param);
        return(out);
    }
    /* The default option...     */
    /* Lucky we saved a copy !   */

    return (out);
}


int quit;   /* Do we terminate? flag    */

static int16 cx = 0, cy = 0;

static void display(char *s)
{
    scr_cursoff();
    scr_save();
    scr_mvcursor(cy, cx);

    display_str(s);

    scr_getcurpos(&cy, &cx);
    scr_restore();
    scr_curson();
}

void irc_prep(char *s1, char *s2)
{
    char *chan_s, lts[20], dnick[20], dfile[100];
    int chan_e, i, dtype = WNONE, nw;
    long t;
    CIB *cb;
    extern char *strchr();

    if (*s2 == '/') {
        if (equ_txtn(s2, "/join", 5)) {
            chan_s = strchr(s2, '#');
            if (chan_s == (char *)NULL) {
                display("\r\nNo '#' in input line");
                s1[0] = '\0';
                return;
            }
            chan_e = (int)strcspn(chan_s, " ");
            strncpy(chan, chan_s, chan_e);
            chan[chan_e] = '\0';
            strcpy(s1, "JOIN ");
            strcat(s1, chan);
            strcat(s1, "\r\n");
            draw_status_bar();
			ch_win(new_win(WCHAN, chan));
        }
        else if (equ_txtn(s2, "/msg", 4)) {
            scr_cursoff();
            strcpy(s1, "PRIVMSG ");
            display("\r\n**msg>> ");

            s2 = &s2[4];
            while (*s2 == ' ')
                s2 += 1;

            chan_e = (int)strcspn(s2, " ");
            s2[chan_e] = '\0';
            strcat(s1, s2);         /* Append their nick to PRIVMSG     */

            display(s2);
            display(" :");

            s2 = &s2[chan_e + 1];
            strcat(s1, " :");       /* Plus : + text, + \r\n == DONE    */
            strcat(s1, s2);
            strcat(s1, "\r\n");

            display(s2);

            scr_curson();
        }
        else if (equ_txtn(s2, "/dcc", 4)) {
            scr_cursoff();
            s2 = &s2[4];
            while (*s2 == ' ')	/* Skip `/dcc ' */
                s2 += 1;

			if (equ_txtn(s2, "chat", 4))
				dtype = WCHAT;
			else if (equ_txtn(s2, "send", 4))
				dtype = WFILE;
			else {
				display("\r\nBad dcc type ");
				display(s2);
				s1[0] = '\0';
				return;
			}
			s2 = &s2[4];
            while (*s2 == ' ')	/* Skip `chat' or `file' */
                s2 += 1;

			i = 0;
			while (*s2 && !isspace(*s2))
				dnick[i++] = *s2++;

			dnick[i] = '\0';
			if (i == 0) {
				display("\r\nDCC no nick given");
				s1[0] = '\0';
				return;
			}

			if (dtype == WFILE) {
				i = 0;
            	while (*s2 == ' ')
                	s2 += 1;

				while (*s2 && !isspace(*s2))
					dfile[i++] = *s2++;
				dfile[i] = '\0';

				if (i == 0) {
					display("\r\nDCC SEND: no file given");
					s1[0] = '\0';
					scr_curson();
					return;
				}
			}
			if (dtype == WCHAT) {
				nw = new_win(WCHAT, dnick);
				if (nw < 0) {
					display("\r\nCan't start DCC CHAT.  No more windows");
					s1[0] = '\0';
					scr_curson();
					return;
				}
				iw[nw].cn = TCP_open(0L, 0, 0, 500);
				iw[nw].status = SPWAIT;
				ch_win(nw);
				cb = CNgetinfo(iw[nw].cn);
				if (iw[nw].cn < 0 || cb == (CIB *)NULL) {
					display("\r\nDCC CHAT can't do passive open");
					s1[0] = '\0';
					exit_wi(nw);
					scr_curson();
					return;
				}
				sprintf(s1, "PRIVMSG %s :\001DCC CHAT chat %lu %u\001\n",
						dnick, stik_cfg->client_ip, cb->lport);
			}
			else if (dtype == WFILE) {
				nw = new_win(WFILE, dnick);
				if (nw < 0) {
					display("\r\nCan't start DCC SEND.  No more windows");
					s1[0] = '\0';
					scr_curson();
					return;
				}
				iw[nw].fd = (int)Fopen(dfile, 0);
				if (iw[nw].fd < 0) {
					display("\r\nCan't open ");
					display(dfile);
					exit_wi(nw);
					s1[0] = '\0';
					scr_curson();
					return;
				}
				iw[nw].cn = TCP_open(0L, 0, 0, 600);
				iw[nw].status = SPWAIT;
				iw[nw].direction = DOUT;
				cb = CNgetinfo(iw[nw].cn);
				if (cb == (CIB *)NULL) {
					display("\r\nDCC SEND can't do passive open");
					s1[0] = '\0';
					exit_wi(nw);
					scr_curson();
					return;
				}
				basename(dfile);
				strcpy(iw[nw].fname, dfile);
				sprintf(s1, "PRIVMSG %s :\001DCC SEND %s %lu %u\001\n",
						dnick, dfile, stik_cfg->client_ip, cb->lport);
			}
            scr_curson();
        }
        else if (equ_txtn(s2, "/me", 3)) {
            s2 = &s2[3];
            while (*s2 == ' ')
                s2 += 1;

            strcpy(s1, "PRIVMSG ");
            strcat(s1, chan);
            strcat(s1, " :\001ACTION ");    /* CTCP encapsulation   */
            strcat(s1, s2);
            strcat(s1, "\001\r\n");

            scr_cursoff();
            display("\r\n* ");
            display(nick);
            display(" ");
            display(s2);
            scr_curson();
        }
        else if (equ_txtn(s2, "/nick", 5)) {
            chan_s = strchr(s2, ' ');
            if (chan_s == (char *)NULL) {
                display("\r\nNo nick specified");
                s1[0] = '\0';
                return;
            }
            while (*chan_s && *chan_s == ' ')
                ++chan_s;
            if (*chan_s == '\0') {
                display("\r\nNo nick specified");
                s1[0] = '\0';
                return;
            }
            chan_e = (int)strcspn(chan_s, " \r\n");
            strncpy(nick, chan_s, chan_e);
            nick[chan_e] = '\0';

            strcpy(s1, "NICK ");
            strcat(s1, nick);
            strcat(s1, "\r\n");
            draw_status_bar();
        }
        else if (equ_txtn(s2, "/ping", 5)) {
            chan_e = (int)strcspn(s2, " ");
            while (s2[chan_e] == ' ')
                chan_e += 1;

            scr_cursoff();
            display("\r\nYou pinged ");
            strcpy(s1, "PRIVMSG ");
            strcat(s1, &s2[chan_e]);
            display(&s2[chan_e]);
            chan_e = 0;
            while (s1[chan_e] != '\r')
                chan_e += 1;
            s2[chan_e] = '\0';
            strcat(s1, " :\001PING ");
            time(&t);
            strcat(s1, ltoa(t, lts, 10));
            strcat(s1, "\001\r\n");

            scr_curson();
        }
		else if (equ_txtn(s2, "/version", 8)) {
            chan_e = (int)strcspn(s2, " ");
            while (s2[chan_e] == ' ')
                chan_e += 1;

            scr_cursoff();
            display("\r\nYou versioned ");
            strcpy(s1, "PRIVMSG ");
            strcat(s1, &s2[chan_e]);
            display(&s2[chan_e]);
            chan_e = 0;
            while (s1[chan_e] != '\r')
                chan_e += 1;
            s2[chan_e] = '\0';
            strcat(s1, " :\001VERSION\001\r\n");

            scr_curson();
        }
		else if (equ_txtn(s2, "/finger", 7)) {
            chan_e = (int)strcspn(s2, " ");
            while (s2[chan_e] == ' ')
                chan_e += 1;

            scr_cursoff();
            display("\r\nYou fingered ");
            strcpy(s1, "PRIVMSG ");
            strcat(s1, &s2[chan_e]);
            display(&s2[chan_e]);
            chan_e = 0;
            while (s1[chan_e] != '\r')
                chan_e += 1;
            s2[chan_e] = '\0';
            strcat(s1, " :\001FINGER\001\r\n");

            scr_curson();
        }
        else if (equ_txtn(s2, "/part", 5) || equ_txtn(s2, "/leave", 6)) {
            if (chan[0] == '\0') {
                display("\r\nYou are not in a channel");
                s1[0] = '\0';
                return;
            }
            strcpy(s1, "PART ");
            strcat(s1, chan);
            strcat(s1, "\r\n");
            exit_wi(cw);
            draw_status_bar();
        }
        else if (equ_txtn(s2, "/quit", 5)) {
            strcpy(s1, "QUIT\r\n");
            quit = 1;
            nick[0] = '\0';
            chan[0] = '\0';
        }
        else {
            strcpy(s1, &s2[1]); /* Otherwise copy verbatim without '/'  */
            strcat(s1, "\r\n");
        }
    }
    else {
        if (strlen(s2) > 0) {
            strcpy(s1, "PRIVMSG ");
            strcat(s1, chan);
            strcat(s1, " :");
            strcat(s1, s2);
            strcat(s1, "\r\n");

            scr_cursoff();
            display("\r\n<");
            display(nick);
            display("> ");
            display(s2);
            scr_curson();
        }
        else {
            s1[0] = '\0';
        }
    }
}

static char *mkping(void)
{
    static char png[50];

    strcpy(png, "PING ");
    strcat(png, nick);
    strcat(png, "\r\n");

    return (png);
}

static void show_free_mem(void)
{
	char lts[20];

    scr_cursoff();
    display("\r\nFree memory = ");
    display(ltoa(KRgetfree(FALSE), lts, 10));
    display(".  Largest free block = ");
    display(ltoa(KRgetfree(TRUE), lts, 10));
    display(".  (System(");
    display(ltoa((long)Malloc(-1L), lts, 10));
    display(")");
    scr_curson();
}


/* Any required DCC processing. */

static void do_dcc_chat(int w)
{
	int16 stat, ch;
	char lo[500], *lp = lo;

	if (iw[w].status == SNULL || iw[w].status == SAWAIT)
		return;

	stat = CNbyte_count(iw[w].cn);
	if (iw[w].status == SPWAIT && stat >= E_NODATA)
		iw[w].status = SACTIVE;

	if (iw[w].status == SPWAIT)
		return;

	else if (stat < E_NODATA) {
		display("\r\nCLOSING DCC CHAT with ");
		display(iw[w].who);
		display(". - ");
		display(get_err_text(stat));
		exit_wi(w);
		return;
	}
	if (stat <= 0)
		return;

	display("\r\nCHAT <");
	display(iw[w].who);
	display("> ");

	while (stat--) {
		ch = CNget_char(iw[w].cn);
		*lp++ = ch;
	}
	while (lp > lo && isspace(lp[-1]))
		--lp;

	*lp = '\0';
	display(lo);
}

static void start_chat(void)
{
	iw[cw].cn = TCP_open(iw[cw].rhost, iw[cw].rport, 0, 1000);
	iw[cw].status = SACTIVE;
}

static void do_dcc_file(int w)
{
	char buf[520], lts[20];
	long stat;
	int16 tstat;

	if (iw[w].direction == DIN) {
		if (iw[w].status == SAWAIT)
			return;
		tstat = CNbyte_count(iw[w].cn);
		if (tstat < E_NODATA) {
			display("\r\nFile ");
			display(iw[w].fname);
			display(" receive finished.  ");
			display(ltoa(iw[w].cnt, lts, 10));
			display(" bytes received. ");
			display(get_err_text(tstat));
			exit_wi(w);
			return;
		}
		else if (tstat > 0) {
			if (tstat > 512)
				tstat = 512;

			CNget_block(iw[w].cn, buf, tstat);
			Fwrite(iw[w].fd, (long)tstat, buf);
			iw[w].cnt += tstat;
			TCP_send(iw[w].cn, (char *)&iw[w].cnt, 4);
		}
	}
	else if (iw[w].direction == DOUT) {
		tstat = CNbyte_count(iw[w].cn);
		if (iw[w].status == SPWAIT && tstat >= E_NODATA)
			iw[w].status = SACTIVE;

		if (iw[w].status == SPWAIT)
			return;

		else if (tstat < E_NODATA) {
			display("\r\nDCC error sending ");
			display(iw[w].fname);
			display(" ");
			display(ltoa(iw[w].cnt, lts, 10));
			display(" bytes sent.  ");
			display(get_err_text(tstat));
			exit_wi(w);
			return;
		}
		else if (tstat >= 4)
			CNget_block(iw[w].cn, (char *)&iw[w].lastack, 4);

		if (iw[w].lastack == iw[w].cnt) {
			if (iw[w].fd == -1) {
				display("\r\nDCC SEND ");
				display(iw[w].fname);
				display(" complete, ");
				display(ltoa(iw[w].cnt, lts, 10));
				display(" bytes sent.");
				exit_wi(w);
			}
			stat = Fread(iw[w].fd, (long)512, buf);
			if (stat > 0)
				tstat = TCP_send(iw[w].cn, buf, (int16)stat);
			if (stat < 512) {
				Fclose(iw[w].fd);
				iw[w].fd = -1;
			}
			iw[w].cnt += stat;
		}
	}
}

static void start_file(void)
{
	iw[cw].cn = TCP_open(iw[cw].rhost, iw[cw].rport, 0, 100);
	iw[cw].status = SACTIVE;

	iw[cw].fd = (int)Fcreate(iw[cw].fname, 0);
}

void do_dcc_stuff(void)
{
	int x;

	for (x = 1; x < MW; ++x) {
		if (iw[x].type == WCHAT)
			do_dcc_chat(x);
		else if (iw[x].type == WFILE)
			do_dcc_file(x);
	}
}


void irc(uint32 ct)
{
    int16 si, ti, key, bcnt, showing, x, code;
    char *sin, *sout, *s2out, *rdn;
    char  *ptr;
    int16 tstat, port;
    uint32 skey;
	uint32 rhost;

	cw = 0;
	prompt[0] = 0;
	prlen = 0;

    cntime = ct;
    scr_getcurmax(&ymax, &xmax);

    sin = (char *)KRmalloc((long)600);
    if (sin == (char *)NULL) {
        scr_conws("Can't allocate string buffer\r\n");
        return;
    }
    sout = (char *)KRmalloc((long)600);
    if (sout == (char *)NULL) {
        scr_conws("Can't allocate string buffer\r\n");
        KRfree(sin);
        return;
    }

    s2out = (char *)KRmalloc((long)600);
    if (s2out == (char *)NULL) {
        scr_conws("Can't allocate string buffer\r\n");
        KRfree(sin);
        KRfree(sout);
        return;
    }

    /* Build USER and NICK strings  */

    ptr = getvstr("USERNAME");
    if (ptr[1] == '\0' && (ptr[0] == '0' || ptr[0] == '1')) {
        scr_conws("irc(): USERNAME not set\r\n");
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
        return;
    }

    strcpy(sin, "USER ");
    strcat(sin, ptr);
    strcat(sin, " ");

    ptr = getvstr("HOSTNAME");
    if (ptr[1] == '\0' && (ptr[0] == '0' || ptr[0] == '1')) {
        scr_conws("irc(): HOSTNAME not set\r\n");
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
        return;
    }

    strcat(sin, ptr);
    strcat(sin, " * :");

    ptr = getvstr("FULLNAME");
    if (ptr[1] == '\0' && (ptr[0] == '0' || ptr[0] == '1')) {
        scr_conws("irc(): FULLNAME not set\r\n");
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
        return;
    }

    strcat(sin, ptr);
    strcat(sin, "\r\n");

    strcat(sin, "NICK ");

    ptr = getvstr("IRCNICK");
    if (ptr[1] == '\0' && (ptr[0] == '0' || ptr[0] == '1')) {
        ptr = getvstr("USERNAME");
        if (ptr[1] == '\0' && (ptr[0] == '0' || ptr[0] == '1')) {
            ptr = nick;
        }
        else {
            strcpy(nick, ptr);
        }
    }
    else {
        strcpy(nick, ptr);
    }
    strcat(sin, ptr);
    strcat(sin, "\r\n");

	if (irc_server[0] == '\0') {
	    ptr = getvstr("IRCSERVER");
    	if (ptr[1] != '\0' || (ptr[0] != '0' && ptr[0] != '1'))
			strcpy(irc_server, ptr);
	}

	scr_conws("Enter IRC server name or <ret> for default (");
	scr_conws(irc_server);
	scr_conws(")\r\n");
 
	if (scr_gets(sout, 99) > 0)
		strcpy(irc_server, sout);

    ptr = getvstr("IRCPORT");
    if (ptr[1] != '\0' || (ptr[0] != '0' && ptr[0] != '1'))
		strcpy(irc_port, ptr);

	scr_conws("Enter IRC port or <ret> for default (");
	scr_conws(irc_port);
	scr_conws(")\r\n");

	if (scr_gets(sout, 19) > 0)
		strcpy(irc_port, sout);

    port = (int16)atol(irc_port);

	scr_conws("Resolving ");
	scr_conws(irc_server);
	scr_conws("\r\n");

	x = resolve(irc_server, &rdn, &rhost, 1);
	if (x < 0) {
		scr_conws(get_err_text(x));
		scr_conws("\r\n");
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
		return;
	}
	if (rdn != (char *)NULL) {
		strcpy(irc_server, rdn);
		KRfree(rdn);
	}
    scr_conws("Opening IRC session to \"");
    scr_conws(irc_server);
    scr_conws("\" :");
    scr_conws(irc_port);
    scr_conws("\r\n");

    cn = TCP_open(rhost, port, 0, 1000); /* Open the connection   */

    if (cn < 0) {
        scr_conws("TCP_open() returns ");
		scr_conws(get_err_text(cn));
		scr_conws("\r\n");
    	TCP_close(cn, 0);
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
        return;
    }
    tstat = TCP_wait_state(cn, TESTABLISH, 60);

    if (tstat < 0) {
        scr_conws("TCP_wait_state() returns ");
        scr_conws(get_err_text(tstat));
        scr_conws("\r\n");
        KRfree(sin);
        KRfree(sout);
        KRfree(s2out);
		TCP_close(cn, 0);
        return;
    }
    else {
        scr_setsregn(0, ymax - 3);
        scr_clear();

        draw_status_bar();
    }
    TCP_send(cn, sin, (int16)strlen(sin)); /* Send USER and NICK commands  */

    scr_setwrap(TRUE);    /* Set wrap mode */

    si = ti = 0;
    showing = 0;
    quit = 0;
    cx = 0;
    cy = ymax - 3;
    
    scr_mvcursor(ymax - 1, 0);
    while (TRUE) {
        if (carrier_detect() < 0) {
            scr_conws("\r\n** Lost Carrier **\r\n");
            break;
        }
        draw_time(FALSE);

		do_dcc_stuff();	/* Any required DCC processing...	*/

		if (iw[cw].type == WFILE && (iw[cw].cnt != iw[cw].oldcnt))
			ch_win(cw);

        bcnt = CNbyte_count(cn);
        if (tstat < E_NODATA) {
            scr_conws("CNget_char() returns: ");
            scr_conws(get_err_text(tstat));
            scr_conws("\r\n");
            break;
        }
        while (bcnt) {
            tstat = CNget_char(cn);
            if (tstat >= 0) {
               sin[si++] = (char)tstat;
                if (tstat == '\n') {
                    sin[si] = '\0';
                    ptr = make_display_line(s2out, sin);
                    scr_cursoff();
                    display("\r\n");
                    display(ptr);
                    scr_curson();
                    si = 0;
                }
            }
            else if (tstat < E_NODATA ) {	/* error */
				scr_conws(get_err_text(tstat));
				scr_conws("\r\n");
				quit = 3;
				break;
			}

            bcnt -= 1;
        }

		if (quit) {
            if (quit == 1) {
                scr_conws("Quitting\r\n");
				quit = 2;
            }
			if (quit == 3)
				break;

			if (scr_conis())
				if ((scr_conin(FALSE) & 0xff) == 0x1b)
					break;

			continue;
		}

        if (scr_conis()) {
            skey = scr_conin(FALSE);
            key = (int16)skey & 0xff;
            code = (int16)(skey >> 16) & 0xff;

			if (iw[cw].status == SAWAIT) {
				if (key == 'y' || key == 'Y') {
					if (iw[cw].type == WCHAT)
						start_chat();
					else
						start_file();
					prompt[0] = '\0';
					reset_iline("", 0);
					showing = 0;
					ch_win(cw);
				}
				else if (key == 'n' || key == 'N') {
					ti = 0;
					sout[0] = '\0';
					prompt[0] = '\0';
					reset_iline("", 0);
					showing = 0;
					exit_wi(cw);
				}
			}
			else if (code == 0x4b && ti == 0) { /* left arrow  */
                reset_iline("", 0);
				ch_dn();
			}
			else if (code == 0x4d && ti == 0) { /* right arrow */
                reset_iline("", 0);
				ch_up();
			}
			else if (key == 3 && (iw[cw].type == WCHAT || iw[cw].type == WFILE)) {
				ti = 0;
				sout[0] = '\0';
				prompt[0] = '\0';
				reset_iline("", 0);
				showing = 0;
				scr_conws("Closing DCC...");
				exit_wi(cw);
			}
            else if (key == 11) {     /* ^K = kick()  */
				if (iw[cw].type == WCHAT || iw[cw].type == WFILE)
					CNkick(iw[cw].cn);
				else
					CNkick(cn);
			}
            else if (key == 24)     /* ^X = show free mem   */
                show_free_mem();
			else if (iw[cw].type == WFILE) {
				scr_bell();
				continue;
			}
			else if (iw[cw].status == SPWAIT)
				continue;
            else if (key == '\b') {
                if (ti > 0) {
                    ti -= 1;
                    if (showing < ti && (showing < (xmax + 1) / 8)) {
                        showing = min(ti,(xmax+1)/2);
                        sout[ti] = '\0';
                        reset_iline(sout, ti - showing);
						if (ti == showing)
							showing += prlen;
                    }
                    else {
                        showing -= 1;
                        scr_conws("\b \b");
                    }
                }
            }

            else if (key == 16) {	/* ^P = /ping server    */
				ptr = mkping();
                TCP_send(cn, ptr, (int16)strlen(ptr));
			}
            else if (key == '\r') {
                sout[ti] = '\0';
                scr_lclear(-1);
                ti = 0;
                showing = prlen;
				scr_conws(prompt);
				if (iw[cw].type == WCHAN || iw[cw].type == WDFLT) {
	                irc_prep(s2out, sout);
	   	            if (s2out[0] != '\0')   /* Only send if not empty   */
    	   	            TCP_send(cn, s2out, (int16)strlen(s2out));
    	   	    }
    	   	    else if (iw[cw].type == WCHAT) {
					display("\r\nCHAT [");
					display(iw[cw].who);
					display("] >>");
					display(sout);
					strcat(sout, "\n");
					TCP_send(iw[cw].cn, sout, (int16)strlen(sout));
				}
            }
            else if (key != 0) {
                if (ti >= 400) {
                    scr_bell();
                    continue;
                }
                if (showing >= xmax - 1) {
                    showing = (xmax + 1) / 4;
                    sout[ti] = '\0';
                    reset_iline(sout, ti - showing);
                }

                scr_conout((char)key);
                sout[ti++] = (char)key;
                showing += 1;
            }
        }
    }

	exit_all();

    scr_setsregn(0, ymax - 1);

    KRfree(sin);
    KRfree(sout);
    KRfree(s2out);

    if (carrier_detect() < 0) {
        TCP_close(cn, 0);
        return;
    }
    tstat = (int16)TCP_close(cn, 10);
    scr_conws("TCP_close() returns: ");
    scr_conws(get_err_text(tstat));
    scr_conws("\r\n");
}

/* Function to wait for a keypress	*/

static void pause(void)
{
	scr_conws("Press any key to continue...\r\n"); /* Just like DOS :-)	*/

	while (!scr_conis())
		;
	scr_conin(FALSE);
}

/* Put 'STIK' cookie value into drivers */

typedef struct {
    long cktag;
    long ckvalue;
} ck_entry;

static long init_drivers(void)
{
	long i = 0;
	ck_entry *jar = *((ck_entry **) 0x5a0);

	while (jar[i].cktag) {
		if (!strncmp((char *)&jar[i].cktag, CJTAG, 4)) {
			drivers = (DRV_LIST *)jar[i].ckvalue;
			return (0);
		}
		++i;
	}
	return (0);	/* Pointless return value...	*/
}
static int initialise(void)
{
	static long init_drivers(void);

	Supexec(init_drivers);

	/* See if we got a value	*/

	if (drivers == (DRV_LIST *)NULL) {
		scr_conws("STiK is not loaded\r\n");
		return (FALSE);
	}

	/* Check Magic number	*/

	if (strcmp(MAGIC, drivers->magic)) {
		scr_conws("Magic string doesn't match\r\n");
		return (FALSE);
	}

	/* OK, now we can get the address of the "TRANSPORT" layer
	 * driver.  If this seems unnecessarily complicated, it's
	 * because I tried to create today, what I would like to
	 * use later on.  In future, there will be multiple
	 * drivers accessible via this method.  With luck, your
	 * code will still work with future versions of my software.
	 */

	tpl = (TPL *)get_dftab(TRANSPORT_DRIVER);

	if (tpl == (TPL *)NULL) {
		scr_conws("Transport layer *not* loaded\r\n");
		return (FALSE);
	}
	scr_conws("Transport layer loaded, Author ");
	scr_conws(tpl->author);
	scr_conws(", version ");
	scr_conws(tpl->version);
	scr_conws("\r\n");

	return (TRUE);
}

void main(void)
{
	scr_init();

	if (initialise() == FALSE) {
		pause();
	}
	else while (TRUE) {
		irc(stik_cfg->cn_time);

		scr_conws("\r\nIRC session closed...\r\n\n");
		scr_conws("Start another session?");
		while (!scr_conis())
			housekeep();

		if (toupper((int)scr_conin(TRUE)) != 'Y')
			break;

		scr_clear();
	}
	scr_close();
}
