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/
/*
 * bans.cpp
 *  Various functions to ban a person from the mud
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___ 
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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
 *
 */

// C++ Includes
//#include <sstream>
//#include <iostream>

// C Includes
//#include <string.h>
//#include <stdlib.h>
//#include <libxml/xmlmemory.h>
//#include <libxml/parser.h>


// Mud Includes
#include "mud.h"
#include "bans.h"

Ban::Ban() {
	reset(); 
}

void Ban::reset() {
	site = ""; 
	by = ""; 
	time = ""; 
	reason = ""; 
	password = ""; 
	duration = unbanTime = 0; 
	isPrefix = isSuffix = false;	
}

bool Ban::matches(const char* toMatch) {

	if(site == "*")
		return(true);
	else if(isPrefix && isSuffix && strstr(toMatch, site.c_str()))
		return(true);
	else if(isPrefix && strPrefix(toMatch, site.c_str()))
		return(true);
	else if(isSuffix && strSuffix(toMatch, site.c_str()))
		return(true);
	else if(!strcmp(toMatch, site.c_str()))
		return(true);
	
	return(false);
}




int dmListbans(Player* player, cmd* cmnd) {
	int found=0;
	std::ostringstream banStr;
	
	banStr << "Current bans:\n";

	std::list<Ban*>::iterator it;
	Ban* ban;

	for(it = gConfig->bans.begin() ; it != gConfig->bans.end() ; it++) {
		found++;
		ban = (*it);

		banStr << "^c#" << found;

		banStr << "^xSite: ";
		if(ban->site[0] == '\0')
			banStr << "^CEveryone! ";
		else {
			banStr << "^C";
			if(ban->isSuffix)
				banStr << "*";
			banStr << ban->site;
			if(ban->isPrefix)
				banStr << "*";
		}
		banStr << "^XDuration: ";
		if(ban->duration <= 0)
			banStr << "Indefinite ";
		else
			banStr << ban->duration << " Day(s) ";

		banStr << "^wBanned by: ";
		banStr << "^c" << ban->by << "\n";

		banStr << "    ^wBan Time: ";
		banStr << "^c" << ban->time << " ";

		banStr <<  "^wExpires: ";
		if(ban->unbanTime)
			banStr << "^g" << ctime(&ban->unbanTime);
		else
			banStr << "^gNever!\n";

		banStr << "    ^wPassword: ";
		if(ban->password == "")
			banStr << "None. ";
		else {
			banStr << "^c" << ban->password << " ";
		}

		banStr << "^wBan Reason: ";

		banStr << "^c" << ban->reason << "^x\n\n";
	}
	bstring toPrint = banStr.str();
	player->printColor("%s", toPrint.c_str());
	
	if(!found)
		player->print("No bans!\n");
	else
		player->print("%d Ban(s) found\n", found);
	return(0);
}

/*
 * Unused, apparently.
 * 
int dmLoadbans(Creature* player, cmd* cmnd) {
	if(!player->isDm())
		return(PROMPT);
	gConfig->loadBans();
	player->print("Bans reloaded.\n");
	dmListbans(player, NULL);
	return(0);

}

int dmSavebans(Creature* player, cmd* cmnd) {
	gConfig->saveBans();
	player->print("Bans saved.\n");
	return(0);
}
*/

int dmBan(Player* player, cmd* cmnd) {
	Player* target=0;
	int		i=0, j=0, strLen, len, dur, iTmp = 0;
	int		isPrefix = 0, isSuffix = 0;
	char	*pass=0;
	char	log[128], log2[128], log3[128], log4[128];
	Ban*	newBan;
	char	str[255], who[255], site[255], comment[255];
	long	t;

	strcpy(log,"");
	strcpy(log2,"");
	strcpy(log3,"");
	strcpy(log4,"");

	strLen = cmnd->fullstr.length();
	// This kills all leading whitespace
	while(i<strLen && isspace(cmnd->fullstr[i]))
		i++;

	// This kills the command itself
	while(i<strLen && !isspace(cmnd->fullstr[i]))
		i++;

	// This kills all the white space before the who
	while(i<strLen && isspace(cmnd->fullstr[i]))
		i++;

	len = strlen(&cmnd->fullstr[i]);
	if(!len) {
		dmListbans(player, NULL);
		//player->print("Syntax: *ban <player/site> [dur] [comment] [-p [password]]\n");
		return(0);
	}
	// This finds out how long the who is
	while(i+j < strLen && isgraph(cmnd->fullstr[i+j]))
		j++;

	memcpy(who, &cmnd->fullstr[i], j);
	who[j] = '\0';
	strcpy(site, who);
	lowercize(who, 1);
	target = gServer->findPlayer(who);
	if(!target || target == player) {
		if((!strstr(site, "*") && !strstr(site, ".")) || !player->isDm()) {
			player->print("%s is not on.\n", who);
			return(0);
		}
	}
	i = i+j;
	j=0;

	len = strlen(&cmnd->fullstr[i+1]);
	if(!len)
		dur = 0;
	else {
		// See if we have a password
		pass = strstr(&cmnd->fullstr[i], "-p ");
		if(pass)	{ // There is a password string then
			iTmp = i;
			while(iTmp < strLen && &cmnd->fullstr[iTmp] != pass)
				iTmp++;
			pass += 3;
			while(isspace(*pass))
				pass++;
			// Kill trailing whitespace
			len = strlen(pass);
			while(isspace(pass[len-1]))
				len--;
			pass[len] = '\0';
		}
		if(iTmp) { // We have a password, everything else stops at the beginging of the -p
			strLen = iTmp;
			cmnd->fullstr[iTmp] = '\0';
		}
		// Kill white space
		while(i < strLen && isspace(cmnd->fullstr[i]))
			i++;

		// Find length of the str
		while(i+j < strLen && isgraph(cmnd->fullstr[i+j]))
			j++;

		memcpy(str, &cmnd->fullstr[i], j);
		str[j] = '\0';
		// If its a digit, assume its the length of the ban, otherwise no length
		// was specified, assume indefinite, and use the rest of the str as the
		// comment.
		if(isdigit(str[0])) {
			dur = atoi(str);
			// Anything less than 0 becomes 0 (indefinite) anything greater than
			// 1825 (5 years) becomes 1825
			dur = MAX(MIN(dur, 1825), 0);
			i=i+j;
			// Kill all whitespace before the comment
			while(i < strLen && isspace(cmnd->fullstr[i]))
				i++;
		} else
			dur = 0;
	}

	strcpy(comment, &cmnd->fullstr[i]);
	// Kill trailling whitespace on comment
	len = strlen(comment);
	while(isspace(comment[len-1]))
		len--;
	comment[len] = '\0';
	if(target) {
		if(target->getClass() > BUILDER) {
			player->print("You can't ban staff! Dumbass.\n");
			target->printColor("^r%s tried to siteban you! Kill %s!\n", player->getCName(), player->himHer());
			return(0);
		}
	}
	if(!strlen(comment) && !player->isDm()) {
		player->print("You must give a reason for banning someone.\n");
		return(0);
	}
	if(target)
		strcpy(site, target->getSock()->getHostname().c_str());

	if(strlen(site) > 1) {
		if(site[0] == '*') {
			isSuffix = 1;
			memmove(&site[0], &site[1], strlen(site));
		}
		if(site[strlen(site) - 1] == '*') {
			isPrefix = 1;
			site[strlen(site) - 1] = '\0';
		}
	}
	if(gConfig->isBanned(site)) {
		player->print("The address '%s' is already banned.\n", site);
		return(0);
	}

	newBan = new Ban;

	// The site of the ban
	newBan->site = site;
	// Prefix or Suffix?
	if(isPrefix == 1)
		newBan->isPrefix = 1;
	if(isSuffix == 1)
		newBan->isSuffix = 1;

	// Who set the ban
	newBan->by = player->getName();
	// Set the ban duration
	newBan->duration = dur;
	// Set the ban reason
	if(strlen(comment))
		newBan->reason = comment;
	else
		newBan->reason = "No reason given";

	// Set the ban time
	t = time(0);
	char *timeStr = strdup((char *)ctime(&t));
	timeStr[strlen(timeStr)-1] = '\0';
	// Get rid of the '\n'
	newBan->time = timeStr;
	
	free(timeStr);
	
	if(newBan->duration > 0)// NOW  +  # of days * # of seconds in a day
		newBan->unbanTime = time(0) + ((long)newBan->duration * 60*60*24L);

	// Set the password
	if(pass)
		newBan->password = pass;

	gConfig->addBan(newBan);
	if(target)
		sprintf(log, "%s (%s) has been banned", who, newBan->site.c_str());
	else
		sprintf(log, "'%s' has been banned", site);

	if(dur > 0)
		sprintf(log2, " for %d day(s) by %s.", newBan->duration, player->getCName());
	else
		sprintf(log2, " indefinitely.");

	if(newBan->reason != "")
		sprintf(log3, " Reason: '%s' by %s.", newBan->reason.c_str(), player->getCName());


	if(newBan->password != "")
		sprintf(log4, " Password: '%s'.\n", newBan->password.c_str());
	else
		sprintf(log4, "\n");

	strcat(log, log2);
	strcat(log, log3);
	strcat(log, log4);

	log_immort(true, player, "%s", log);
	logn("log.bans", "%s: %s", player->getCName(), log);

	gConfig->saveBans();
	
	if(target) {
		char tmpBuf[1024];
		sprintf(tmpBuf, "\n\rThe watcher just arrived.\n\rThe watcher says, \"Begone from this place!\".\n\rThe watcher banishes your soul from this world.\n\r\n\r\n\r");
		target->getSock()->write(tmpBuf);
		target->getSock()->disconnect();
	} else { // Determine who's effected by the siteban and drop them *excluding staff*
		gServer->checkBans();
	}
	return(0);
}

void Server::checkBans() {
	static const char* banString = "\n\rThe watcher just arrived.\n\rThe watcher says, \"Begone from this place!\".\n\rThe watcher banishes your soul from this world.\n\r\n\r\n\r";

	for(Socket* sock : sockets) {
		if(sock->getPlayer() && (gConfig->isBanned(sock->getIp().c_str()) || 
			gConfig->isBanned(sock->getHostname().c_str()))) {
			if(sock->getPlayer()->getClass() <= BUILDER) {
				sock->write(banString);
				sock->disconnect();
			}
		}
	}
}

int dmUnban(Player* player, cmd* cmnd) {
	int toDel = 0, i;

	// Kill any trailing white space in the fullstr
	i = cmnd->fullstr.length();
	while(isspace(cmnd->fullstr[i-1]))
		i--;
	cmnd->fullstr[i] = '\0';
	// We need a number with the command!
	if(!strncasecmp(cmnd->fullstr.c_str(), "*unban", i))
		toDel = -1;
	else
		toDel = cmnd->val[0];

	if(toDel <= 0) {
		player->print("What ban would you like lifted?\n");
		return(0);
	}

	if(gConfig->deleteBan(toDel)) {
		player->print("Ban #%d successfully deleted.\n", toDel);
		gConfig->saveBans();
	} else
		player->print("Ban #%d could not be deleted\n", toDel);

	return(0);
}
//*********************************************************************
// Misc commands to add new bans, free bans, delete bans and the like
//*********************************************************************

bool Config::addBan(Ban* toAdd) {
	bans.push_back(toAdd);
	return(true);	
}

bool Config::deleteBan(int toDel) {
	std::list<Ban*>::iterator it;
	int count=0;

	for(it = bans.begin() ; it != bans.end() ; it++) {
		count++;
		if(count == toDel) { // We've found the clan to delete, now remove it from the list
			Ban* ban = (*it);
			bans.erase(it);
			delete ban;
			return(true);
		}
	}
	return(false);
}

//***************************
// isLockedOut
//***************************

// This function determines if the player on socket number, fd, is on
// a site that is being locked out.  If the site is password locked,
// then 2 is returned.  If it's completely locked, 1 is returned.  If
// it's not locked out at all, 0 is returned.
int Config::isLockedOut( Socket* sock ) {
	int match=0, count=0;
	char site[80];
	char ip[40];

	strncpy(site, sock->getHostname().c_str(), 80);
	strncpy(ip, sock->getIp().c_str(), 40);
	site[79] = 0;
	ip[39] = 0;

	std::list<Ban*>::iterator it;
	Ban* ban;

	for(it = gConfig->bans.begin() ; it != gConfig->bans.end() ; it++) {
		count++;
		ban = (*it);
		// Better safe than sorry
		if(!ban)
			continue;
		
		if(ban->matches(site) || ban->matches(ip)) {
			match = 1;
			break;
		}
	}
	if(match) {
		
		if(ban->unbanTime != 0 && time(0) > ban->unbanTime) {
			// Ban has expired so delete it
			broadcast(isCt, "^y--- Expiring ban for '%s'", ban->site.c_str());
			gConfig->deleteBan(count);
			gConfig->saveBans();

			// See if they're effected by another ban
			return(isLockedOut(sock));
		}
		if(ban->password != "") {
			strcpy(sock->tempstr[0], ban->password.c_str());
			return (2);
		} else {
			char buf[1024];
			sprintf(buf, "\n\rYour site is locked out.\n\rSend questions to %s.\n\r", questions_to_email);
			sock->write(buf);
			broadcast(isCt, "^yDenying access to '%s(%s)'", sock->getHostname().c_str(), ban->site.c_str());
			return(1);
		}
	}
	return(0);
}

// Returns 1 if the site is on the ban list, 0 otherwise
bool Config::isBanned(const char *site) {
	std::list<Ban*>::iterator it;
	Ban* ban;

	for(it = gConfig->bans.begin() ; it != gConfig->bans.end() ; it++) {
		ban = (*it);
		// Better safe than sorry
		if(!ban)
			continue;
		if(ban->matches(site))
			return(true);
	}
	return (false);
}

// Clears bans
void Config::clearBanList() {
	Ban* ban;
	
	while(!bans.empty()) {
		ban = bans.front();
		delete ban;
		bans.pop_front();
	}
	bans.clear();
}