1stMUD/corefiles/
1stMUD/gods/
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"

bool write_to_descriptor args ((DESCRIPTOR_DATA * d, char *txt, int length));

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

#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->desc_flags, DESC_TELOPT_EOR))
		SET_BIT (ch->comm, COMM_TELNET_EOR);
	else
		REMOVE_BIT (ch->comm, COMM_TELNET_EOR);

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

	/* 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, strlen (naws_do));

	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->desc_flags, DESC_TELOPT_EOR);
	else
		REMOVE_BIT (d->desc_flags, DESC_TELOPT_EOR);
	return;
}

void telopt_echo (DESCRIPTOR_DATA * d, bool state)
{
	if (state)
		SET_BIT (d->desc_flags, DESC_TELOPT_ECHO);
	else
		REMOVE_BIT (d->desc_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->desc_flags, DESC_TELOPT_NAWS))
		return;

	write_to_descriptor (d, naws_do, strlen (naws_do));
	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->desc_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 (1, x, 150);
	d->scr_height = URANGE (1, y, 80);

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

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