/* Copyright (c) 2002
 *	Marko Boomstra (m.boomstra@chello.nl).  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <conf.h>

#ifdef IAC_DEBUG
  #define TELCMDS
  #define TELOPTS
#endif

#include <arpa/telnet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <ncurses.h>
#include <panel.h>
#include "mudix.h"

#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
                          if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }

void send_telnet(unsigned char command, unsigned char option)
{
    unsigned char iac_buf[3];

#ifdef IAC_DEBUG
    wprintw(wMain, "SEND: %6s - %s\n", TELCMD(command), telopts[option]);
#endif

    iac_buf[0] = IAC;
    iac_buf[1] = command;
    iac_buf[2] = option;
    write(settings->sock, iac_buf, 3); /* bypassing normal send */
}

void send_data_telnet(unsigned char *data, int len)
{
#ifdef IAC_DEBUG
    wprintw(wMain, "SEND: %d - ", len);
    {
	unsigned char *pData = data;
		 int   len2 = len;

	while (len2--) {
	    if (isprint(*pData))
		waddch(wMain, *pData);
	    else
		wprintw(wMain, "(%02X)", *pData);
	    pData++;
	}
	waddch(wMain, '\n');
    }
#endif

    write(settings->sock, data, len); /* bypassing normal send */
}

int telnet_command(unsigned char *pIac)
{
    unsigned char command = pIac[1];
    unsigned char option  = pIac[2];
    unsigned char temp[128], *cp;

    switch (command) {
	case DONT:              /* you are not to use option */
	    send_telnet(WONT, option);
	    break;
	case DO:                /* please, you use option */
            switch (option) {
		default:
	    	    send_telnet(WONT, option);
		    break;
		case TELOPT_TTYPE:
	    	    send_telnet(WILL, option);
		    break;
		case TELOPT_NEW_ENVIRON:
	    	    send_telnet(WILL, option);
		    break;
		case TELOPT_NAWS:
	    	    send_telnet(WILL, option);
		    break;
		case TELOPT_TSPEED:
	    	    send_telnet(WILL, option);
		    break;
		case TELOPT_ECHO:
                    fEchoIn  = TRUE;
                    fEchoOut = TRUE;
	    	    send_telnet(WILL, option);
		    break;
	    }
	    break;
	case WONT:              /* I won't use option */
            switch (option) {
		default:
		    break;
		case TELOPT_ECHO:
                    fEchoIn  = TRUE;
                    fEchoOut = TRUE;
		    break;
	    }
	    break;
	case WILL:              /* I will use option */
            switch (option) {
		default:
		    break;
		case TELOPT_ECHO:
		    if (settings->port == 23)	/* telnet */
		    	fEchoIn = TRUE;	      	/* see what we type */
		    else
			fEchoIn = FALSE;
                    fEchoOut = FALSE;
		    break;
	    }
	    break;
	case SB:	        /* interpret as subnegotiation */
            switch (option) {
		default:
		    break;
		case TELOPT_TTYPE:
		    sprintf((char *)temp, "%c%c%c%c%s%c%c", 
			IAC, SB, option, TELQUAL_IS, termname(), IAC, SE);
		    send_data_telnet(temp, strlen((char *)temp+4)+4);
		    break;
		case TELOPT_NEW_ENVIRON:
		    sprintf((char *)temp, "%c%c%c%c%c%c", 
			IAC, SB, option, TELQUAL_IS, IAC, SE);
		    send_data_telnet(temp, strlen((char *)temp+4)+4);
		    break;
		case TELOPT_NAWS:
		    cp = temp;
		    *cp++ = IAC;
		    *cp++ = SB;
		    *cp++ = option;
		    PUTSHORT(cp, LEN_COL);
		    PUTSHORT(cp, LEN_ROW);
		    *cp++ = IAC;
		    *cp++ = SE;
		    send_data_telnet(temp, cp-temp);
		    break;
		case TELOPT_TSPEED:
		  {
		    long speed = baudrate();

	            sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", 
			IAC, SB, option, TELQUAL_IS, speed, speed, IAC, SE);
		    send_data_telnet(temp, strlen((char *)temp+4)+4);
		    break;
		  }
	    }

	    { /* go to the end of the buffer (IAC SE) */
		unsigned char *ppIac = pIac;

	        while (!(*ppIac == IAC && *(ppIac+1) == SE)) {
		    if (*ppIac == IAC && *(ppIac+1) == IAC)
			ppIac++;
#ifdef IAC_DEBUG
    		    wprintw(wMain, "(%02X)", *ppIac);
#endif
		    ppIac++;
		}

#ifdef IAC_DEBUG
    		    wprintw(wMain, "\n");
#endif
		return ppIac-pIac+2;  /* +2 since we didn't count IAC SE yet */
	    }
	case GA:                /* you may reverse the line */
	case EL:                /* erase the current line */
	case EC:                /* erase the current character */
	case AYT:               /* are you there */
	case AO:                /* abort output--but let prog finish */
	case IP:                /* interrupt process--permanently */
	case BREAK:             /* break */
	case DM:                /* data mark--for connect. cleaning */
	case NOP:               /* nop */
	case SE:                /* end sub negotiation */
	case EOR:               /* end of record (transparent mode) */
	case ABORT:             /* Abort process */
	case SUSP:              /* Suspend process */
	case xEOF:              /* End of file: EOF is already used... */
	default:
	    return 2;
    }

    return 3;			/* normal size of an IAC */
}

int check_iac(unsigned char *pIac)
{
    if (*pIac != IAC)
        return 1;

#ifdef IAC_DEBUG
    wprintw(wMain, "RECV: %6s - %s\n", TELCMD(pIac[1]), telopts[pIac[2]]);
#endif

    return telnet_command(pIac);
}



syntax highlighted by Code2HTML, v. 0.9.1