1stMUD/corefiles/
1stMUD/gods/
1stMUD/log/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
#define TELOPTS
#define TELCMDS

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include "merc.h"
#include "telnet.h"

char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' };
char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' };
char echo_dont[] = { IAC, DONT, TELOPT_ECHO, '\0' };
char echo_do[] = { IAC, DO, TELOPT_ECHO, '\0' };

/* Telnet End-Of-Record */
char eor_do[] = { IAC, DO, TELOPT_EOR, '\0' };
char eor_will[] = { IAC, WILL, TELOPT_EOR, '\0' };
char eor_dont[] = { IAC, DONT, TELOPT_EOR, '\0' };
char eor_wont[] = { IAC, WONT, TELOPT_EOR, '\0' };

/* Telnet window size */
char naws_will[] = { IAC, WILL, TELOPT_NAWS, '\0' };
char naws_dont[] = { IAC, DONT, TELOPT_NAWS, '\0' };
char naws_do[] = { IAC, DO, TELOPT_NAWS, '\0' };
char naws_wont[] = { IAC, WONT, TELOPT_NAWS, '\0' };
char naws_sb[] = { IAC, SB, TELOPT_NAWS, '\0' };

/* kill the socket after receiving either of those 2 strings */
char iac_ip[] = { IAC, IP, '\0' };
char iac_susp[] = { IAC, SUSP, '\0' };
char iac_brk[] = { IAC, BREAK, '\0' };

/* telnet subnegotiation end */
char iac_se[] = { IAC, SE, '\0' };

/* go ahead after prompt string */
const char go_ahead_str[] = { IAC, GA, '\0' };

/* TTYSPEED .. used only to find out if the client is telopt capable */
char tspd_will[] = { IAC, WILL, TELOPT_TSPEED, '\0' };
char tspd_do[] = { IAC, DO, TELOPT_TSPEED, '\0' };
char tspd_wont[] = { IAC, WONT, TELOPT_TSPEED, '\0' };
char tspd_dont[] = { IAC, DONT, TELOPT_TSPEED, '\0' };

#if !defined(NO_MCCP)
/* mccp: compression negotiation strings */
char compress_will[] = { IAC, WILL, TELOPT_COMPRESS, '\0' };
char compress_do[] = { IAC, DO, TELOPT_COMPRESS, '\0' };
char compress_dont[] = { IAC, DONT, TELOPT_COMPRESS, '\0' };
#endif

#define MTELOPT(string, command, len)					\
	    if (!memcmp(&buf[i],(string),strlen((string))))		\
	    { 								\
		i += strlen((string)) - 1; 				\
		(command); 						\
		i += len;						\
		idx = i;						\
		continue;						\
	    }

/* descriptor flags are used because some mud clients initiate the telnet
   negotiation before the player logs on, this way as long there is a socket
   open there is also flag strip that can indicate various results of the
   negotiation.
 */
void set_desc_flags(DESCRIPTOR_DATA * d)
{
	CHAR_DATA *ch = CH(d);

	if (!ch)
		return;

	if (IS_SET(d->d_flags, DESC_TELOPT_EOR))
		SET_BIT(ch->comm, COMM_TELNET_EOR);
	else
		REMOVE_BIT(ch->comm, COMM_TELNET_EOR);

	if (IS_SET(ch->comm, COMM_NOCOLOUR))
		REMOVE_BIT(d->d_flags, DESC_COLOUR);

	return;
}

/* init_telnet initiates the telnet negotiation with the mud client, although
   most of the mud clients support it, there is still few dinosaurs that don't
   gmud is one of them.
 */
void init_telnet(DESCRIPTOR_DATA * d)
{
	write_to_descriptor(d, tspd_will, 0);

	return;
}

void telopt_init(DESCRIPTOR_DATA * d)
{

	/* telnet end-of-record */
	write_to_descriptor(d, eor_will, 0);

#if !defined(NO_MCCP)
	write_to_descriptor(d, compress_will, 0);
#endif

	/* telnet window size negotiation */
	write_to_descriptor(d, naws_do, 0);

	return;
}

void telopt_ignore(void)
{
	return;
}

/* telopt_send is just a wrapper for write_to_descriptor, if you ever need to
   send any string to the client that contains '\0' then you will have to modify
   this so it reports the correct string lenght to the write_to_descriptor!
 */
void telopt_send(DESCRIPTOR_DATA * d, char *string)
{
	write_to_descriptor(d, string, strlen(string));
	return;
}

/* IAC IP or IAC SUSP means that the clients machine lost control connection
   and you can safely drop the link of that player */
void telopt_close(DESCRIPTOR_DATA * d)
{
	wiznet("IAC IP/SUSP received, closing link to $N.", CH(d), NULL,
		   WIZ_LINKS, 0, 0);
	close_socket(d);
	return;
}

/* TELOPT_EOR works with few clients exactly the same way as TELOPT_GA the only
   advantage of it is that it can be auto-negotiated so players won't have to
   turn it on or off
 */
void telopt_eor(DESCRIPTOR_DATA * d, bool state)
{
	if (state)
		SET_BIT(d->d_flags, DESC_TELOPT_EOR);
	else
		REMOVE_BIT(d->d_flags, DESC_TELOPT_EOR);
	return;
}

void telopt_echo(DESCRIPTOR_DATA * d, bool state)
{
	if (state)
		SET_BIT(d->d_flags, DESC_TELOPT_ECHO);
	else
		REMOVE_BIT(d->d_flags, DESC_TELOPT_ECHO);
	return;
}

#if !defined(NO_MCCP)
void telopt_compress(DESCRIPTOR_DATA * d, bool state)
{
	if (state)
		compressStart(d);
	else
		compressEnd(d);
	return;
}
#endif

/* telopt_unknown handles all the negotiation commands that the mud server
   doesn't understand
 */
int telopt_unknown(DESCRIPTOR_DATA * d, unsigned char c, unsigned char t,
				   bool quiet)
{
	char buf[MAX_STRING_LENGTH];
	char cmd[MAX_INPUT_LENGTH];
	char opt[MAX_INPUT_LENGTH];
	char rev;
	char len = 1;

	if (c == '\n' || c == '\r' || c == '\0')
		return 0;

	if (TELCMD_OK(c))
	{
		sprintf(cmd, "%s", TELCMD(c));
		if (c == IAC)
			len = 1;			/* IAC IAC                      */
		else if (c >= SB)
			len = 2;			/* IAC DONT/DO/WONT/WILL/SB ??  */
		else
			len = 1;			/* IAC ??                       */
	}

	if (!quiet)
	{
		if (TELCMD_OK(c))
			sprintf(cmd, "%s", TELCMD(c));
		else
			sprintf(cmd, "[%u]", c);

		if (TELOPT_OK(t))
			sprintf(opt, "%s", TELOPT(t));
		else if (TELCMD_OK(t))
			sprintf(opt, "%s", TELCMD(t));
		else if (t == 85)
			sprintf(opt, "COMPRESS-1");
		else if (t == 86)
			sprintf(opt, "COMPRESS-2");
		else
			sprintf(opt, "[%u]", t);

		switch ((unsigned char) c)
		{
		case WILL:
			rev = WONT;
			break;
		case WONT:
			rev = DONT;
			break;
		case DONT:
			rev = WONT;
			break;
		case DO:
			rev = DONT;
			break;
		default:
			rev = c;
			break;
		}
		sprintf(buf, "%c%c%c", IAC, rev, t);
		write_to_descriptor(d, buf, 0);
	}
	return len;
}

/* this function is called when clients sends IAC WILL NAWS */
void telopt_naws_do(DESCRIPTOR_DATA * d)
{
	return;

	/* IAC WILL NAWS second time around */
	if (IS_SET(d->d_flags, DESC_TELOPT_NAWS))
		return;

	write_to_descriptor(d, naws_do, 0);
	return;
}

/* this function handles the actual NAWS sub negotiation and takes the 4 bytes
   of data between IAC SB NAWS and IAC SE and converts them to the clients
   screen dimensions, then saves them in d->scr_width and d->scr_height
*/
void telopt_naws(DESCRIPTOR_DATA * d, int i, char *inbuf)
{
	unsigned int x = 0, y = 0, t1, t2;

	SET_BIT(d->d_flags, DESC_TELOPT_NAWS);

	t1 = (unsigned char) inbuf[i + 1];
	t2 = (unsigned char) inbuf[i + 2];
	x = t2 + (t1 * 16);

	t1 = (unsigned char) inbuf[i + 3];
	t2 = (unsigned char) inbuf[i + 4];
	y = t2 + (t1 * 16);

	d->scr_width = URANGE(10, x, 250);
	d->scr_height = URANGE(10, y, 250);

	return;
}

/* process_telnet loops thru the buffer passed to it from read_from_descriptor
   and takes care of any IAC sequences found, at the same time it appends
   everything else to the end of d->inbuf
*/
void process_telnet(DESCRIPTOR_DATA * d, int len, char *buf)
{
	unsigned int i, idx;
	int iStart = strlen(d->inbuf);

	if (len <= 0)
		return;					/* nothing to process */

	for (i = 0; i <= (unsigned) len; i++)
	{
		if (buf[i] == (signed char) IAC)
		{
			/* Telnet Window Size Negotiation */
			MTELOPT(naws_sb, telopt_naws(d, i, buf), 6);
			MTELOPT(naws_will, telopt_naws_do(d), 0);
			MTELOPT(naws_wont, telopt_ignore(), 0);

			/* Telnet End-Of-Record Negotiation */
			MTELOPT(eor_will, telopt_eor(d, TRUE), 0);
			MTELOPT(eor_do, telopt_eor(d, TRUE), 0);
			MTELOPT(eor_wont, telopt_eor(d, FALSE), 0);
			MTELOPT(eor_dont, telopt_eor(d, FALSE), 0);

			MTELOPT(tspd_will, telopt_init(d), 0);
			MTELOPT(tspd_do, telopt_init(d), 0);
			MTELOPT(tspd_wont, telopt_init(d), 0);
			MTELOPT(tspd_dont, telopt_init(d), 0);

#if !defined(NO_MCCP)
			MTELOPT(compress_will, telopt_compress(d, TRUE), 0);
			MTELOPT(compress_do, telopt_compress(d, TRUE), 0);
			MTELOPT(compress_dont, telopt_compress(d, FALSE), 0);
#endif

			/* Telnet Echo Negotiation */
			MTELOPT(echo_dont, telopt_echo(d, FALSE), 0);
			MTELOPT(echo_do, telopt_echo(d, TRUE), 0);

			/* IP and SUSP - kill the descriptor */
			MTELOPT(iac_ip, telopt_close(d), 0);
			MTELOPT(iac_susp, telopt_close(d), 0);

			/* BRK - ignore */
			MTELOPT(iac_brk, telopt_ignore(), 0);

			/* SE - end of sub negotiation */
			MTELOPT(iac_se, telopt_ignore(), 0);

			if (buf[i + 1] == (signed char) IAC)
				continue;

			/* No match for IAC sequence was found */
			i += telopt_unknown(d, buf[i + 1], buf[i + 2], FALSE);
		}
		else
		{
			d->inbuf[iStart] = buf[i];
			iStart++;
		}
	}

	d->inbuf[iStart] = '\0';
	return;
}