roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * io.cpp
 *	 Socket input/output/establishment functions.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include <arpa/telnet.h>
#include "mud.h"

#include <stdio.h>
#include <sys/types.h>

#include <netinet/in.h> // Needs: htons, htonl, INADDR_ANY, sockaddr_in

#include <sys/ioctl.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>



#include "login.h"
#include "socket.h"
#include "timer.h"
#include "clans.h"

#include <sstream>

#define TELOPT_COMPRESS2       86
#define buflen(a,b,c)	(a-b + (a<b ? c:0))
#define saddr		addr.sin_addr.s_addr
#define MAXPLAYERS	100

int	Numplayers;
int	controlSock;
extern unsigned short Port;

//********************************************************************
//						broadcast
//********************************************************************

bool hearBroadcast(Creature* target, Socket* ignore1, Socket* ignore2, bool showTo(Socket*)) {
	if(!target)
		return(false);
	if(target < 0)
		return(false);
	if(target->getSock() == ignore1 || target->getSock() == ignore2)
		return(false);

	if(showTo && !showTo(target->getSock()))
		return(false);

	Player* pTarget = target->getAsPlayer();
	if(pTarget) {
		if(	ignore1 != NULL &&
			ignore1->getPlayer() &&
			pTarget->isGagging(ignore1->getPlayer()->getName())
		)
			return(false);
		if(	ignore2 != NULL &&
			ignore2->getPlayer() &&
			pTarget->isGagging(ignore2->getPlayer()->getName())
		)
			return(false);
	}

	return(true);
}


// global broadcast
void doBroadCast(bool showTo(Socket*), bool showAlso(Socket*), const char *fmt, va_list ap, Creature* player) {
	for(Socket* sock : gServer->sockets) {
		const Player* ply = sock->getPlayer();

		if(!ply)
			continue;
		if(ply->fd < 0)
			continue;
		if(!showTo(sock))
			continue;
		if(showAlso && !showAlso(sock))
			continue;
		// No gagging staff!
		if(player && ply->isGagging(player->getName()) && !player->isCt())
			continue;

		ply->vprint(ply->customColorize(fmt).c_str(), ap);
		ply->printColor("^x\n");
	}
}


// room broadcast
void doBroadcast(bool showTo(Socket*), Socket* ignore1, Socket* ignore2, const Container* container, const char *fmt, va_list ap) {
	if(!container)
		return;

	for(Player* ply : container->players) {
        if(!hearBroadcast(ply, ignore1, ignore2, showTo))
            continue;
        if(ply->flagIsSet(P_UNCONSCIOUS))
            continue;

        ply->vprint(ply->customColorize(fmt).c_str(), ap);
        ply->printColor("^x\n");

	}
}


// room broadcast, 1 ignore
void broadcast(Socket* ignore, const Container* container, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	doBroadcast(0, ignore, NULL, container, fmt, ap);
	va_end(ap);
}

// room broadcast, 2 ignores
void broadcast(Socket* ignore1, Socket* ignore2, const Container* container, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	doBroadcast(0, ignore1, ignore2, container, fmt, ap);
	va_end(ap);
}

// room broadcast, 1 ignore, showTo function
void broadcast(bool showTo(Socket*), Socket* ignore, const Container* container, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	doBroadcast(showTo, ignore, NULL, container, fmt, ap);
	va_end(ap);
}

// simple broadcast
void broadcast(const char *fmt,...) {
	va_list ap;
	va_start(ap, fmt);
	doBroadCast(yes, 0, fmt, ap);
	va_end(ap);
}

// broadcast with showTo function
void broadcast(bool showTo(Socket*), bool showAlso(Socket*), const char *fmt,...) {
	ASSERTLOG(showTo);
	va_list ap;
	va_start(ap, fmt);
	doBroadCast(showTo, showAlso, fmt, ap);
	va_end(ap);
}

// broadcast with showTo function
void broadcast(bool showTo(Socket*), const char *fmt,...) {
	ASSERTLOG(showTo);
	va_list ap;
	va_start(ap, fmt);
	doBroadCast(showTo, 0, fmt, ap);
	va_end(ap);
}

void broadcast(Creature* player, bool showTo(Socket*), int color, const char *fmt,...) {
	ASSERTLOG(showTo);
	va_list ap;
	va_start(ap, fmt);
	doBroadCast(showTo, (bool(*)(Socket*))NULL, fmt, ap, player);
	va_end(ap);
}

bool yes(Creature* player) {
	return(true);
}
bool yes(Socket* sock) {
	return(true);
}
bool wantsPermDeaths(Socket* sock) {
	Player* ply = sock->getPlayer();
	return(ply && ply && !ply->flagIsSet(P_NO_BROADCASTS) && ply->flagIsSet(P_PERM_DEATH));
}
void announcePermDeath(Creature* player, const char *fmt,...) {
	va_list ap;

	if(player->isStaff())
		return;

	va_start(ap, fmt);
	doBroadCast(wantsPermDeaths, 0, fmt, ap);
	va_end(ap);
}

//********************************************************************
//						broadcast_login
//********************************************************************
// This function broadcasts a message to all the players that are in the
// game. If they have the NO-BROADCAST flag set, then they will not see it.

void broadcast_login(Player* player, BaseRoom* inRoom, int login) {
	std::ostringstream preText, postText, extra, room;
	bstring text = "", illusion = "";
	int    logoff=0;

	ASSERTLOG( player );
	ASSERTLOG( !player->getName().empty() );

	preText << "### " << player->fullName() << " ";

	if(login) {
		preText << "the " << gConfig->getRace(player->getDisplayRace())->getAdjective();
		// the illusion will be checked here
		postText << " " << player->getTitle().c_str();

		if(player->getDeity()) {
			const DeityData* deity = gConfig->getDeity(player->getDeity());
			postText << " (" << deity->getName() << ")";
		} else if(player->getClan()) {
			const Clan* clan = gConfig->getClan(player->getClan());
			postText << " (" << clan->getName() << ")";
		}

		postText << " just logged in.";

		extra << "### (Lvl: " << (int)player->getLevel() << ") (Host: " << player->getSock()->getHostname().c_str() << ")";
	} else {
		logoff = 1;
		preText << "just logged off.";
	}

	// format text for illusions
	text = preText.str() + postText.str();
	illusion = preText.str();
	if(login && player->getDisplayRace() != player->getRace())
		illusion += " (" + gConfig->getRace(player->getRace())->getAdjective() + ")";
	illusion += postText.str();

	if(inRoom) {
		if(inRoom->isUniqueRoom())
			room << " (" << inRoom->getAsUniqueRoom()->info.str() << ")";
		else if(inRoom->isAreaRoom())
			room << " " << inRoom->getAsAreaRoom()->mapmarker.str();
	}

	// TODO: these are set elsewhere, too... check that out
	if(!player->isStaff()) {
		player->clearFlag(P_DM_INVIS);
		player->removeEffect("incognito");
	} else if(player->getClass()==BUILDER) {
		player->addEffect("incognito", -1);
	}

	if(player->flagIsSet(P_DM_INVIS) || player->isEffected("incognito")) {
		if(player->isDm())
			broadcast(isDm, "^g%s", illusion.c_str());
		else if(player->getClass() == CARETAKER)
			broadcast(isCt, "^Y%s", illusion.c_str());
		else if(player->getClass() == BUILDER) {
			broadcast(isStaff, "^G%s", illusion.c_str());
		}
	} else {


		Player* target=0;
		for(std::pair<bstring, Player*> p : gServer->players) {
			target = p.second;

			if(!target->isConnected())
				continue;
			if(target->isGagging(player->getName()))
				continue;
			if(target->flagIsSet(P_NO_LOGIN_MESSAGES))
				continue;

			if(target->getClass() <= BUILDER && !target->isWatcher()) {
				if(	!player->isStaff() &&
					!(logoff && target->getClass() == BUILDER)
				)
					target->print("%s\n", player->willIgnoreIllusion() ? illusion.c_str() : text.c_str());
			} else if((target->isCt() || target->isWatcher()) && player->getClass() != BUILDER) {
				target->print("%s%s\n", player->willIgnoreIllusion() ? illusion.c_str() : text.c_str(), room.str().c_str());
				if(login && target->isCt())
					target->print("%s\n", extra.str().c_str());
			}
		}
	}
}


//********************************************************************
//						broadcast_rom
//********************************************************************
// This function outputs a message to everyone in the room specified
// by the integer in the second parameter.  If the first parameter
// is greater than -1, then if the player specified by that file
// descriptor is present in the room, they are not given the message

// TODO: Dom: remove
void broadcast_rom_LangWc(int lang, Socket* ignore, Location currentLocation, const char *fmt,...) {
	char	fmt2[1024];
	va_list ap;

	va_start(ap, fmt);
	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");

	Player* ply;
	for(std::pair<bstring, Player*> p : gServer->players) {
		ply = p.second;
		Socket* sock = ply->getSock();

		if(!sock->isConnected())
			continue;
		if(	ignore != NULL &&
			ignore->getPlayer() &&
			ignore->getPlayer()->inSameRoom(ply) &&
			ply->isGagging(ignore->getPlayer()->getName())
		)
			continue;
		if(	(	(currentLocation.room.id && ply->currentLocation.room == currentLocation.room) ||
				(currentLocation.mapmarker.getArea() != 0 && currentLocation.mapmarker == ply->currentLocation.mapmarker)
			) &&
			sock->getFd() > -1 &&
			sock != ignore &&
			!ply->flagIsSet(P_UNCONSCIOUS) &&
			(ply->languageIsKnown(lang) || ply->isEffected("comprehend-languages")))
		{
			ply->printColor("%s", get_lang_color(lang));
			ply->vprint(fmt2, ap);
		}
	}
	va_end(ap);
}

//********************************************************************
//						broadcastGroup
//********************************************************************
// this function broadcasts a message to all players in a group except
// the source player

void broadcastGroupMember(bool dropLoot, Creature* player, const Player* listen, const char *fmt, va_list ap) {

	if(!listen)
		return;
	if(listen == player)
		return;

	// dropLoot broadcasts to all of this
	if(!dropLoot) {
		if(listen->isGagging(player->getName()))
			return;
		if(listen->flagIsSet(P_IGNORE_ALL))
			return;
		if(listen->flagIsSet(P_IGNORE_GROUP_BROADCAST))
			return;
	} else {
		if(!player->inSameRoom(listen))
			return;
	}

	// staff don't broadcast to mortals when invis
	if(player->isPlayer() && player->isStaff() && player->getClass() > listen->getClass()) {
		if(player->flagIsSet(P_DM_INVIS))
			return;
		if(player->isEffected("incognito") && !player->inSameRoom(listen))
			return;
	}

	listen->vprint(listen->customColorize(fmt).c_str(), ap);
}

void broadcastGroup(bool dropLoot, Creature* player, const char *fmt,...) {
	Group* group = player->getGroup();
	if(!group)
		return;
	va_list ap;
	va_start(ap, fmt);

//	broadcastGroupMember(dropLoot, player, leader, fmt, ap);
	for(Creature* crt : group->members) {
		broadcastGroupMember(dropLoot, player, crt->getAsConstPlayer(), fmt, ap);
	}
}

//**********************************************************************
//						inetname
//**********************************************************************
// This function returns the internet address of the address structure
// passed in as the first parameter.

char *inetname(struct in_addr in) {
	register char *cp=0;
	static char line[50];

	if(in.s_addr == INADDR_ANY)
		strcpy(line, "*");
	else if(cp)
		strcpy(line, cp);
	else {
		in.s_addr = ntohl(in.s_addr);

		sprintf(line, "%u.%u.%u.%u",
			(int)(in.s_addr >> 24) & 0xff,
			(int)(in.s_addr >> 16) & 0xff,
			(int)(in.s_addr >> 8) & 0xff,
			(int)(in.s_addr) & 0xff
		);
	}
	return (line);
}

//**********************************************************************
//						child_died
//**********************************************************************
// This function gets called when a SIGCHLD signal is sent to the
// program.

void child_died(int sig) {
	gServer->childDied();
	std::cout << "Child died: " << gServer->getDeadChildren() << " children dead now.\n";
	signal(SIGCHLD, child_died);
}
//**********************************************************************

//	This causes the game to shutdown in one minute.  It is used
//  by signal to force a  shutdown in response to a HUP signal
//  (i.e. kill -HUP pid) from the system.

void quick_shutdown(int sig) {
	Shutdown.ltime = time(0);
	Shutdown.interval = 120;
}

//**********************************************************************
//						broadcastGuild
//**********************************************************************
// Broadcasts to everyone in the same guild as player

void broadcastGuild(int guildNum, int showName, const char *fmt,...) {
	char	fmt2[1024];
	va_list ap;

	va_start(ap, fmt);
	if(showName) {
		strcpy(fmt2, "*CC:GUILD*[");
		strcat(fmt2, getGuildName(guildNum));
		strcat(fmt2, "] ");
	} else
		strcpy(fmt2, "");
	strcat(fmt2, fmt);
	strcat(fmt2, "\n");

	Player* target=0;
	for(std::pair<bstring, Player*> p : gServer->players) {
		target = p.second;

		if(!target->isConnected())
			continue;
		if(target->getGuild() == guildNum && target->getGuildRank() >= GUILD_PEON && !target->flagIsSet(P_NO_BROADCASTS)) {
			target->vprint(target->customColorize(fmt2).c_str(), ap);
		}
	}
	va_end(ap);

}

//*********************************************************************
//						shutdown_now
//*********************************************************************

void shutdown_now(int sig) {
	broadcast("### Quick shutdown now!");
	gServer->processOutput();
	loge("--- Game shutdown with SIGINT\n");
	gConfig->resaveAllRooms(1);
	gServer->saveAllPly();

	printf("Goodbye.\n");
	kill(getpid(), 9);
}

//*********************************************************************
//						check_flood
//*********************************************************************

int check_flood(int fd) {
//	int i,j=0;
//
//	for(i=0; i<Tablesize ;i++) {
//		if(fd!=i) {
//			if(Ply[i].sock) {
//				if(!strcmp(Ply[i].sock->getHostname().c_str(),sock->getHostname().c_str()) && i != fd) {
//					j++;
//					if(j >= 3) {
//						disconnect(i);
//						return(1);
//					}
//				}
//			}
//		}
//	}
//	gServer->cleanUp();
	return(0);
}

//*********************************************************************
//						checkWinFilename
//*********************************************************************

int checkWinFilename(Socket* sock, const bstring str) {
	// only do this if we're on windows
	#ifdef __CYGWIN__
	if(	str.equals("aux") ||
	        str.equals("prn") ||
	        str.equals("nul") ||
	        str.equals("con") ||
	        str.equals("com1") ||
	        str.equals("com2"))
	{
		if(sock) sock->print("\"%s\" could not be read.\n", str);
		return(0);
	}
	#endif
	return(1);
}

//*********************************************************************
//						pueblo functions
//*********************************************************************

bool Pueblo::is(bstring txt) {
	return(txt.left(activation.getLength()).toLower() == activation);
}

bstring Pueblo::multiline(bstring str) {
	int i=1, plen = activation.getLength()-1;
	if(Pueblo::is(str)) {
		i = plen;
		str.Insert(0, ' ');
	}
	int len = str.getLength();
	for(; i<len; i++) {
		if(	str.getAt(i-1) == '\n' &&
			Pueblo::is(str.right(len-i))
		) {
			str.Insert(i, ' ');
			len++;
			i += plen;
		}
	}
	return(str);
}

//*********************************************************************
//						escapeText
//*********************************************************************

void Monster::escapeText() {
	if(Pueblo::is(getName()))
		setName("clay form");
	description = Pueblo::multiline(description);
}

void Player::escapeText() {
	if(Pueblo::is(description))
		description = "";
}

void Object::escapeText() {
	if(Pueblo::is(getName()))
		setName("clay ball");
	if(Pueblo::is(use_output))
		zero(use_output, sizeof(use_output));
	description = Pueblo::multiline(description);
}

void UniqueRoom::escapeText() {
	if(Pueblo::is(getName()))
		setName("New Room");

	short_desc = Pueblo::multiline(short_desc);
	long_desc = Pueblo::multiline(long_desc);

	for(Exit* exit : exits) {
		exit->escapeText();
	}
}

void Exit::escapeText() {
	if(Pueblo::is(getName()))
		setName("exit");
	if(Pueblo::is(enter))
		enter = "";
	if(Pueblo::is(open))
		open = "";
	description = Pueblo::multiline(description);
}

//*********************************************************************
//						xsc
//*********************************************************************
// Stands for xmlspecialchars (based on htmlspecialchars for PHP) -
// escapes special characters to make the text safe for XML.
// If given Br�gal (which the xml parser cannot save), it will turn it
// into Br&#252;gal (which the xml parser can save). Display will be affected
// on old clients, so this should only be run when saving the string.

bstring xsc(const bstring& txt) {
	bstring ret = "";
	unsigned char c=0;
	int t = txt.getLength();
	for(int i=0; i<t; i++) {
		c = txt.getAt(i);
		// beyond 127 we get into the unsupported character range
		if(c > 127)
			ret += (bstring)"&#"+c+";";
		else
			ret += (char)c;
	}
	return(ret);
}

//*********************************************************************
//						unxsc
//*********************************************************************
// Reverse of xsc - attempts to turn &#252; into �. We do thhis when we load from file.

bstring unxsc(const bstring& txt) {
	return(unxsc(txt.c_str()));
}
bstring unxsc(const char* txt) {
	bstring ret = "";
	int c=0, len = strlen(txt);
	for(int i=0; i<len; i++) {
		c = txt[i];

		if(c == '&' && txt[i+1] == '#') {
			// get the number from the string
			c = atoi(&txt[i+2]);
			// advance i appropriately
			i += 2;
			while(txt[i] != ';') {
				i++;
				if(i >= len)
					return("");
			}
		}

		ret += (char)c;
	}
	return(ret);
}

//*********************************************************************
//						keyTxtConvert
//*********************************************************************
// Treat all accented characters like their unaccented version. This makes
// it easier for players without the accents on their keyboards. Make
// sure everything is treated as lower case.

char keyTxtConvert(unsigned char c) {
	// early exit for most characters
	if(isalpha(c))
		return(tolower(c));
	switch(c) {
	case 192:
	case 193:
	case 194:
	case 195:
	case 196:
	case 197:
	case 224:
	case 225:
	case 226:
	case 227:
	case 228:
	case 229:
		return('a');
	case 199:
	case 231:
		return('c');
	case 200:
	case 201:
	case 202:
	case 203:
	case 232:
	case 233:
	case 234:
	case 235:
		return('e');
	case 204:
	case 205:
	case 206:
	case 207:
	case 236:
	case 237:
	case 238:
	case 239:
		return('i');
	case 209:
	case 241:
		return('n');
	case 210:
	case 211:
	case 212:
	case 213:
	case 214:
	case 216:
	case 242:
	case 243:
	case 244:
	case 245:
	case 246:
	case 248:
		return('o');
	case 217:
	case 218:
	case 219:
	case 220:
	case 249:
	case 250:
	case 251:
	case 252:
		return('u');
	case 221:
	case 253:
	case 255:
		return('y');
	default:

		return(tolower(c));
	}
}

//*********************************************************************
//						keyTxtConvert
//*********************************************************************

bstring keyTxtConvert(const bstring& txt) {
	bstring ret = "";
	for(int i=0; i<txt.getLength(); i++) {
		ret += keyTxtConvert(txt.getAt(i));
	}
	return(ret);
}

//*********************************************************************
//						keyTxtCompare
//*********************************************************************
// This does the hard work of comparing two different strings:
// Br�gAl, bRU&#252;gaL, Br�g�l, and brugal must all match. We can do
// this by using keyTxtConvert to take any supported character and make it
// lower case and by manually extracting the ASCII code.

bool keyTxtCompare(const char* key, const char* txt, int tLen) {
	int kI=0, tI=0, convert=0, kLen = strlen(key);
	char kC, tC;
	if(!kLen || !tLen)
		return(false);
	for(;;) {
		// at the end of the input string and no errors
		if(tI >= tLen)
			return(true);
		// at the end of the main string but not at the end of the input string
		if(kI >= kLen)
			return(false);

		// remove color
		if(key[kI] == '^') {
			kI += 2;
			continue;
		}
		if(txt[tI] == '^') {
			tI += 2;
			continue;
		}

		if(key[kI] == '&' && key[kI+1] == '#') {
			// get the number from the string
			convert = atoi(&key[kI+2]);
			// advance kI appropriately
			kI += 2;
			while(key[kI] != ';') {
				kI++;
				if(kI >= kLen)
					return(false);
			}
			// turn it into a character we can compare
			kC = keyTxtConvert(convert);
		} else
			kC = keyTxtConvert(key[kI]);

		tC = keyTxtConvert(txt[tI]);

		if(tC != kC)
			return(false);
		tI++;
		kI++;
	}
}

//*********************************************************************
//						keyTxtEqual
//*********************************************************************

bool keyTxtEqual(const Creature* target, const char* txt) {
	int len = strlen(txt);
	if(	keyTxtCompare(target->key[0], txt, len) ||
		keyTxtCompare(target->key[1], txt, len) ||
		keyTxtCompare(target->key[2], txt, len) ||
		keyTxtCompare(target->getCName(), txt, len) ||
        target->getId() == txt
	)
		return(true);
	return(false);
}

bool keyTxtEqual(const Object* target, const char* txt) {
	int len = strlen(txt);
	if(	keyTxtCompare(target->key[0], txt, len) ||
		keyTxtCompare(target->key[1], txt, len) ||
		keyTxtCompare(target->key[2], txt, len) ||
		keyTxtCompare(target->getCName(), txt, len) ||
		target->getId() == txt
	)
		return(true);
	return(false);
}

//*********************************************************************
//						isPrintable
//*********************************************************************
// wrapper for isprint to handle supported characters

bool isPrintable(char c) {
	return(isprint(keyTxtConvert(c)));
}