btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
#include <stdio.h>
#include <stdlib.h>
#include <gc/gc.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <time.h>
#include <event.h>
#include <netdb.h>
#include <signal.h>
#define DEBUG
#include <debug.h>
#include <rbtree.h>
#include "network.h"
#include "telnet.h"
#include "client.h"

static void telnet_send_req(DESC * client, int how, int request);
static void telnet_send_subop(DESC * client, int how, int option);
static void telnet_handle_will(DESC * client, int option);
static void telnet_handle_wont(DESC * client, int option);
static int telnet_handle_subop(DESC * client, unsigned char *buffer,
							   int length);

int telnet_init(DESC * client)
{
	dprintk("telnet initializing.");
	strncpy(client->termtype, "vt100", 16);
	client->telnet_win_width = 80;
	client->telnet_win_height = 25;

	telnet_send_req(client, TELNET_DO, TELNET_OPT_TERMTYPE);
	telnet_send_req(client, TELNET_DO, TELNET_OPT_WINSIZE);
	return 1;
}

/* Client Ring Buffer Handling */
static void telnet_send_req(DESC * client, int how, int request)
{
	unsigned char buffer[16] = { TELNET_IAC, how, request };
	network_write(client, (void *) buffer, 3);
}
static void telnet_send_subop(DESC * client, int option, int how)
{
	unsigned char buffer[16] =
		{ TELNET_IAC, TELNET_SB, option, how, TELNET_IAC, TELNET_SE };
	network_write(client, (void *) buffer, 6);
}

static void telnet_handle_will(DESC * client, int option)
{
	switch (option) {
	case TELNET_OPT_TERMTYPE:
		client->telnet_has_termtype = TSTATE_SUP;
		dprintk("client acknowledged TERMTYPE");
		break;
	case TELNET_OPT_WINSIZE:
		client->telnet_has_winsize = TSTATE_SUP;
		dprintk("client acknowledged WINSIZE");
		break;
	}
}

static void telnet_handle_wont(DESC * client, int option)
{
	switch (option) {
	case TELNET_OPT_TERMTYPE:
		client->telnet_has_termtype = TSTATE_UNSUP;
		strncpy(client->termtype, "vt100", 16);
		break;
	case TELNET_OPT_WINSIZE:
		client->telnet_has_winsize = TSTATE_UNSUP;
		client->telnet_win_width = 80;
		client->telnet_win_height = 25;
		break;
	}
}

static void telnet_check_options(DESC * client)
{
	if(client->telnet_has_termtype == TSTATE_SUP) {
		client->telnet_has_termtype = TSTATE_REQUEST;
		telnet_send_subop(client, TELNET_OPT_TERMTYPE, TELNET_SUB_REQUIRED);
	} else if(client->telnet_has_termtype == TSTATE_REQUEST) {
		client->telnet_has_termtype = TSTATE_UNSUP;
	}

	if(client->telnet_has_winsize == TSTATE_SUP) {
		client->telnet_has_winsize = TSTATE_REQUEST;
		telnet_send_subop(client, TELNET_OPT_WINSIZE, TELNET_SUB_REQUIRED);
	} else if(client->telnet_has_winsize == TSTATE_REQUEST) {
		client->telnet_has_termtype = TSTATE_UNSUP;
	}
}

static int telnet_handle_subop(DESC * client, unsigned char *buffer,
							   int length)
{
	int iter;
	if(buffer[1] != TELNET_SUB_SUPPLIED)
		dfail("unexpected reply in telnet suboption negotiation.");
	iter = 2;
	switch (buffer[0]) {
	case TELNET_OPT_TERMTYPE:
		memset(client->termtype, 0, 16);
		for(iter = 2; iter < length && iter < 18 && buffer[iter] != 0xFF;
			iter++) {
			client->termtype[iter - 2] = buffer[iter];
		}
		iter++;
		dprintk("Received Terminal Type '%s'", client->termtype);
		client->telnet_has_termtype = TSTATE_REPLIED;
		break;
	case TELNET_OPT_WINSIZE:
		client->telnet_win_width = buffer[2];
		client->telnet_win_height = buffer[4];
		iter = 6;
		dprintk("Received Window Size %dx%d", client->telnet_win_width,
				client->telnet_win_height);
		client->telnet_has_winsize = TSTATE_REPLIED;
		break;
	default:
		dfail("unexpected reply in telnet usboption negotiation.");
	}
	return iter;
}

#define ring_avail(client) (client->ringtail > client->ringhead ? RING_LENGTH - client->ringtail + client->ringhead : \
        client->ringhead - client->ringtail)
#define ring_char(client, x) (client->ringbuffer[(client->ringhead+x)%RING_LENGTH])
#define ring_length(client) (client->ringtail >= client->ringhead ? client->ringtail - client->ringhead : \
        RING_LENGTH - client->ringhead + client->ringtail)
#define ring_eat(client, x) (client->ringhead = (client->ringhead+x) % RING_LENGTH)

void telnet_read_ring(DESC * client)
{
	int iter, complete;
	unsigned char subopt_buffer[32];

	dprintk
		("Ring Read started, input length = %d, ringhead = %d, ringtail = %d. ",
		 ring_length(client), client->ringhead, client->ringtail);

	while (ring_length(client) > 0) {
		dprintk
			("Parsing data 0x%02x,  %d bytes in ring, ringhead = %d, ringtail = %d",
			 ring_char(client, 0), ring_length(client), client->ringhead,
			 client->ringtail);
		switch (ring_char(client, 0)) {
		case TELNET_IAC:
			if(ring_length(client) < 2)
				return;
			dprintk("TELNET COMMADN %d", ring_char(client, 1));
			switch ((unsigned char) ring_char(client, 1)) {
			case TELNET_WILL:
				telnet_handle_will(client, ring_char(client, 2));
				ring_eat(client, 3);
				break;
			case TELNET_WONT:
				telnet_handle_wont(client, ring_char(client, 2));
				ring_eat(client, 3);
				break;
			case TELNET_SB:
				complete = 0;
				for(iter = 2; !complete && iter < ring_avail(client); iter++) {
					subopt_buffer[iter - 2] = ring_char(client, iter);
					if(ring_char(client, iter) == TELNET_SE) {
						telnet_handle_subop(client, subopt_buffer, iter - 2);
						complete = 1;
						ring_eat(client, iter + 1);
					}
				}
				if(!complete)
					return;
				break;
			case TELNET_IAC:
				client->inputbuffer[client->inputtail++] = TELNET_IAC;
			default:
				ring_eat(client, 2);
				break;
			}
			break;
		case 0:
			ring_eat(client, 1);
			break;
		case '\r':
		case '\n':
			if(ring_length(client) > 1 && (ring_char(client, 1) == '\r' ||
										   ring_char(client, 1) == '\n')) {
				ring_eat(client, 1);
			}
			if(client->inputtail == 0) {
				ring_eat(client, 1);
				continue;
			}
			client->inputbuffer[client->inputtail] = 0;
			client_accept_input(client);
			dprintk("Would Parse: %s", client->inputbuffer);
			client->inputtail = 0;
			ring_eat(client, 1);
			break;
		default:
			client->inputbuffer[client->inputtail++] = ring_char(client, 0);
			ring_eat(client, 1);
			break;
		}
	}
	if(client->ringhead == client->ringtail) {
		client->ringhead = client->ringtail = 0;
	}

	telnet_check_options(client);
	dprintk
		("Ring Read finished. RingState = { head = %d, tail = %d }, InputState = { tail = %d }",
		 client->ringhead, client->ringtail, client->inputtail);
}

void telnet_disconnect(DESC * client)
{
	network_disconnect(client);
}

void telnet_write(DESC * client, char *buffer, int length)
{
	dprintk("telnet_write");
	// Color translation should occur here.
	network_write(client, buffer, length);
}