1stMUD4.0/bin/
1stMUD4.0/doc/MPDocs/
1stMUD4.0/player/
1stMUD4.0/win32/
1stMUD4.0/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-2003 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
#define TELOPTS
#define TELCMDS

#include "merc.h"
#include "telnet.h"

const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, NUL };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, NUL };
const char echo_dont[] = { IAC, DONT, TELOPT_ECHO, NUL };
const char echo_do[] = { IAC, DO, TELOPT_ECHO, NUL };

char eor_do[] = { IAC, DO, TELOPT_EOR, NUL };
char eor_will[] = { IAC, WILL, TELOPT_EOR, NUL };
char eor_dont[] = { IAC, DONT, TELOPT_EOR, NUL };
char eor_wont[] = { IAC, WONT, TELOPT_EOR, NUL };

char iac_logout[] = { IAC, DO, TELOPT_LOGOUT, NUL };

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

char naws_will[] = { IAC, WILL, TELOPT_NAWS, NUL };
char naws_dont[] = { IAC, DONT, TELOPT_NAWS, NUL };
char naws_do[] = { IAC, DO, TELOPT_NAWS, NUL };
char naws_wont[] = { IAC, WONT, TELOPT_NAWS, NUL };
char naws_sb[] = { IAC, SB, TELOPT_NAWS, NUL };

char iac_ip[] = { IAC, IP, NUL };
char iac_susp[] = { IAC, SUSP, NUL };
char iac_brk[] = { IAC, BREAK, NUL };

char iac_se[] = { IAC, SE, NUL };

const char go_ahead_str[] = { IAC, GA, NUL };
const char will_suppress_ga_str[] = { IAC, WILL, TELOPT_SGA, NUL };
const char wont_suppress_ga_str[] = { IAC, WONT, TELOPT_SGA, NUL };

#if !defined(NO_MCCP)
char compress1_will[] = { IAC, WILL, TELOPT_COMPRESS, NUL };
char compress1_do[] = { IAC, DO, TELOPT_COMPRESS, NUL };
char compress1_dont[] = { IAC, DONT, TELOPT_COMPRESS, NUL };
char compress1_wont[] = { IAC, WONT, TELOPT_COMPRESS, NUL };

char compress2_will[] = { IAC, WILL, TELOPT_COMPRESS2, NUL };
char compress2_do[] = { IAC, DO, TELOPT_COMPRESS2, NUL };
char compress2_dont[] = { IAC, DONT, TELOPT_COMPRESS2, NUL };
char compress2_wont[] = { IAC, WONT, TELOPT_COMPRESS2, NUL };
#endif

char msp_will[] = { IAC, WILL, TELOPT_MSP, NUL };
char msp_do[] = { IAC, DO, TELOPT_MSP, NUL };
char msp_dont[] = { IAC, DONT, TELOPT_MSP, NUL };
char msp_wont[] = { IAC, WONT, TELOPT_MSP, NUL };

char mxp_will[] = { IAC, WILL, TELOPT_MXP, NUL };
char mxp_do[] = { IAC, DO, TELOPT_MXP, NUL };
char mxp_dont[] = { IAC, DONT, TELOPT_MXP, NUL };
char mxp_wont[] = { IAC, WONT, TELOPT_MXP, NUL };

char s_mxp_supports[] = { "" MXPMODE(1) "<SUPPORTS" };
char s_mxp_version[] = { "" MXPMODE(1) "<VERSION" };

// Init string of Pueblo (pueblo.mozdev.org)
// followed by the version
char pueblo_str[] = { "PUEBLOCLIENT " };
char ptype_3klient[] = { "3klient " };
char imp_str[] = { "v1." };

char ttype_do[] = { IAC, DO, TELOPT_TTYPE, NUL };
char ttype_dont[] = { IAC, DONT, TELOPT_TTYPE, NUL };
char ttype_sb[] = { IAC, SB, TELOPT_TTYPE, NUL };
char ttype_send[] = { IAC, SB, TELOPT_TTYPE, SEND, IAC, SE, NUL };
char ttype_will[] = { IAC, WILL, TELOPT_TTYPE, NUL };
char ttype_wont[] = { IAC, WONT, TELOPT_TTYPE, NUL };

char binary_do[] = { IAC, DO, TELOPT_BINARY, NUL };
char binary_dont[] = { IAC, DONT, TELOPT_BINARY, NUL };
char binary_will[] = { IAC, WILL, TELOPT_BINARY, NUL };
char binary_wont[] = { IAC, WONT, TELOPT_BINARY, NUL };

PROTOTYPE(void init_mxp, (DESCRIPTOR_DATA *));
PROTOTYPE(void mxp_support, (DESCRIPTOR_DATA *, int, unsigned char *));
PROTOTYPE(void mxp_version, (DESCRIPTOR_DATA *, int, unsigned char *));

#define MTELOPT(string, command, len)					\
	    if (!memcmp(&buf[i],(string),strlen((string))))		\
	    { 								\
		unsigned char *p = &buf[i];					\
		telopt_debug(d,p,strlen((string))+(len)+telopt_lskip,FALSE);\
		i += strlen((string)) - 1; 				\
		(command); 						\
		i += len;						\
		i += telopt_lskip;					\
		telopt_lskip = 0;					\
		idx = i;						\
		continue;						\
	    }

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

/* 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)
{
	d_write(d, tspd_will, 0);

	return;
}

void telopt_init(DESCRIPTOR_DATA * d)
{

	d_write(d, eor_will, 0);

	d_write(d, naws_do, 0);

#if !defined(NO_MCCP)
	d_write(d, compress2_will, 0);

	d_write(d, compress1_will, 0);
#endif

	d_write(d, msp_will, 0);

	d_write(d, mxp_will, 0);

	/* terminal type */
	d_write(d, ttype_do, 0);

	d_write(d, binary_will, 0);

	return;
}

void telopt_debug(DESCRIPTOR_DATA * d, unsigned char *string, int length,
				  bool send)
{
	int i, j;
	static char buf[MSL];		/* static so gdb hardware watchpoint works */
	unsigned char c;
	bool sb = FALSE;

	if (!d)
		return;

	sprintf(buf, "Telopt: [%d][%s][ ", d->descriptor, (send) ? "send" : "recv");

	if (string[0] != IAC)
		return;

	for (i = 0; i < length; i++)
	{
		c = string[i];

		if (TELCMD_OK(c))
		{
			sprintf(buf + strlen(buf), "%s ", TELCMD(c));

			if (c == SB)
				sb = TRUE;
			if (c == SE)
				sb = FALSE;

			continue;
		}
		else if (TELOPT_OK(c))
		{
			sprintf(buf + strlen(buf), "%s ", TELOPT(c));

			if (sb && c == TELOPT_NAWS)
			{
				for (j = 0; j < 4; j++)
				{
					i++;
					c = string[i];
					sprintf(buf + strlen(buf), "%u ", c);
				}
			}

			if (sb && c == TELOPT_TTYPE)
			{
				i++;
				c = string[i];
				if (c == SEND)
				{
					strcat(buf, "SEND ");
				}
				else if (c == IS)
				{
					strcat(buf, "IS ");
					do
					{
						i++;
						c = string[i];
						sprintf(buf + strlen(buf), "%c", c);
					}
					while (c != IAC);
					strcat(buf, " ");
				}
				else
				{
					sprintf(buf + strlen(buf), "%u ", c);
				}
			}

			continue;
		}
		else
		{
			sprintf(buf + strlen(buf), "%u ", c);
		}
	}

	sprintf(buf + strlen(buf), "]");
	wiznet(buf, NULL, NULL, WIZ_TELNET, 0, MAX_LEVEL - 2);
	return;
}

void telopt_ignore(void)
{
	return;
}

/* telopt_send is just a wrapper for d_write, 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 length to the d_write!
 */
void telopt_send(DESCRIPTOR_DATA * d, char *string, int len)
{
	d_write(d, string, len);
	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_msp(DESCRIPTOR_DATA * d, bool state)
{
	if (state)
		SET_BIT(d->d_flags, DESC_MSP);
	else
		REMOVE_BIT(d->d_flags, DESC_MSP);
	return;
}

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

#if !defined(NO_MCCP)
void telopt_compress(DESCRIPTOR_DATA * d, bool state, int version)
{
	if (d->out_compress)
		return;

	if (state)
		compressStart(d, version);
	else
		compressEnd(d, version);
	return;
}
#endif

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;
}

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

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

	if (TELCMD_OK(c))
	{
		sprintf(cmd, "%s", TELCMD(c));
		if (c == IAC)
			len = 1;

		else if (c >= SB)
			len = 2;

		else
			len = 1;

	}

	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 if (t == 90)
			sprintf(opt, "MSP");
		else if (t == 91)
			sprintf(opt, "MXP");
		else
			sprintf(opt, "[%u]", t);

		switch (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);
		d_write(d, buf, 0);
		sprintf(buf, "Unknown to client: IAC %s %s", cmd, opt);
		log_string(buf);
	}
	return len;
}

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

	d_write(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, unsigned char *inbuf)
{
	int x = 0, y = 0, t1, t2;

	SET_BIT(d->d_flags, DESC_TELOPT_NAWS);

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

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

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

	return;
}

void telopt_ttype(DESCRIPTOR_DATA * d, int i, int len, unsigned char *inbuf)
{
	int n;
	int skip = 0;

	d->ttype[0] = NUL;

	SET_BIT(d->d_flags, DESC_TELOPT_TTYPE);

	for (n = 0; n <= len; n++)
	{
		if (inbuf[n + i + 2] == IAC)
		{
			if (inbuf[n + i + 3] == SE)
			{
				telopt_lskip = skip;
				return;
			}
		}
		else
		{
			if (n < 59)
			{
				d->ttype[n] = inbuf[n + i + 2];
				d->ttype[n + 1] = NUL;
			}
			skip++;
		}
	}

	return;
}

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

void pueblo_client(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf)
{
	char *buf = (char *) &inbuf[i];
	char send[MSL];

	sscanf(buf, "PUEBLOCLIENT %lf", &d->pueblo_vers);

	telopt_lskip = strlen(buf);

	SET_BIT(d->d_flags, DESC_PUEBLO);
	sprintf(send, "\n\rPueblo version %.2f detected...", d->pueblo_vers);
	d_write(d, send, 0);

	return;
}

void portal_3klient(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf)
{
	char *buf = (char *) &inbuf[i];
	static char tbuf[MSL];
	int n;

	SET_BIT(d->d_flags, DESC_TELOPT_TTYPE);
	SET_BIT(d->d_flags, DESC_PORTAL);

	sscanf(buf, "3klient %u~%s\r\n", &d->portal.keycode, tbuf);

	for (n = 0; tbuf != NUL; n++)
		if (isdigit(tbuf[n]))
			break;

	snprintf(d->portal.version, 20, "Portal %s", &tbuf[n]);

	telopt_lskip = strlen(buf);

	d_print(d, "\n\rPortal GT Detected...", 0);

	return;

}

void fire_client(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf)
{
	char *buf = (char *) &inbuf[i];
	int vers;

	SET_BIT(d->d_flags, DESC_IMP);

	sscanf(buf, "v1.%2d", &vers);

	telopt_lskip = strlen(buf);

	d->imp_vers = (double) ((vers * .01) + 1);
	d_printf(d, "\n\rFire Client version v%.2f detected...", d->imp_vers);

	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, unsigned char *buf)
{
	int i, idx;
	int iStart = strlen(d->inbuf);

	if (len <= 0)
		return;

	for (i = 0; i <= len; i++)
	{
		if (buf[i] == 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);

			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);

			/* 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);

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

#if !defined(NO_MCCP)
			MTELOPT(compress2_will, telopt_compress(d, TRUE, 2), 0);
			MTELOPT(compress2_do, telopt_compress(d, TRUE, 2), 0);
			MTELOPT(compress2_wont, telopt_compress(d, FALSE, 2), 0);
			MTELOPT(compress2_dont, telopt_compress(d, FALSE, 2), 0);

			MTELOPT(compress1_will, telopt_compress(d, TRUE, 1), 0);
			MTELOPT(compress1_do, telopt_compress(d, TRUE, 1), 0);
			MTELOPT(compress1_wont, telopt_compress(d, FALSE, 1), 0);
			MTELOPT(compress1_dont, telopt_compress(d, FALSE, 1), 0);
#endif

			MTELOPT(msp_will, telopt_msp(d, TRUE), 0);
			MTELOPT(msp_do, telopt_msp(d, TRUE), 0);
			MTELOPT(msp_wont, telopt_msp(d, FALSE), 0);
			MTELOPT(msp_dont, telopt_msp(d, FALSE), 0);

			MTELOPT(mxp_will, telopt_mxp(d, TRUE), 0);
			MTELOPT(mxp_do, telopt_mxp(d, TRUE), 0);
			MTELOPT(mxp_wont, telopt_mxp(d, FALSE), 0);
			MTELOPT(mxp_dont, telopt_mxp(d, FALSE), 0);

			/* Terminal Type */
			MTELOPT(ttype_will, telopt_send(d, ttype_send, 6), 0);
			MTELOPT(ttype_sb, telopt_ttype(d, i, len, buf), 1);
			MTELOPT(ttype_do, telopt_ignore(), 0);
			MTELOPT(ttype_wont, telopt_ignore(), 0);
			MTELOPT(ttype_dont, telopt_ignore(), 0);

			MTELOPT(binary_will, telopt_binary(d, TRUE), 1);
			MTELOPT(binary_do, telopt_binary(d, TRUE), 1);
			MTELOPT(binary_dont, telopt_binary(d, FALSE), 1);
			MTELOPT(binary_wont, telopt_binary(d, FALSE), 1);

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

			MTELOPT(iac_logout, telopt_ignore(), 0);

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

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

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

			/* No match for IAC sequence was found */
			i += telopt_unknown(d, buf[i + 1], buf[i + 2], FALSE);
		}
		else
		{
			MSTRING(pueblo_str, pueblo_client(d, i, buf), 0);
			MSTRING(ptype_3klient, portal_3klient(d, i, buf), 0);
			MSTRING(imp_str, fire_client(d, i, buf), 0);
			MSTRING(s_mxp_version, mxp_version(d, i, buf), 0);
			MSTRING(s_mxp_supports, mxp_support(d, i + 4, buf), 0);

			switch (buf[i])
			{

			case '~':			// turn any ~ into {- 
				d->inbuf[iStart] = ANSI_KEY;
				iStart++;
				d->inbuf[iStart] = '-';
				break;

			default:
				d->inbuf[iStart] = buf[i];
				break;
			}

			iStart++;
		}
	}
	return;
}

/* 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;
}