All pastes #1914846 Raw Edit

Untitled

public c v1 · immutable
#1914846 ·published 2010-08-11 01:18 UTC
rendered paste body
#include "ircbot.h"#include <fcntl.h>// Staff channelsstatic const ci::string StaffChannel = "#chanstat.staff";static const ci::string BotChannel = "#chanstat.bots";#define INVITETHROTTLE  4#define COMMANDTHROTTLE 6std::string LogDir;std::string NSLine;std::string NSNLine;std::string NSPW;std::string AppendLink;unsigned MaxChannels;std::string AudMode;unsigned Raw001Pos;bool PersonalBot = false;const ci::string WhiteList[] = {	BotChannel,	"#chanstat",	""};const ci::string DontLog[] = {	BotChannel,	StaffChannel,	""};static int lasthour;/* Time this module was loaded */static time_t loadtime;/* if this is true, a netsplit recently happend * if there is a netsplit the bot will not part * channels for being empty */static bool netsplit = false;/* the time the netsplit was first detected */static time_t netsplit_time = 0;/* this is the nick the irc server (or, bnc rather) tells * the bot he is from the 001 raw upon connecting * if this is not the same as the config nick for some reason * (the correct nick was already online) then this can cause * issues when the bot rejoins channels, because it thinks that * the chanstat who joined is not it, and will part because there * is "another bot" in the channel * * basically what well do if this happens is check to make sure * the "other bot" joining isnt this value, and possibly send a /nick * to the ircd (but then it wouldnt be identified.. and the nick is * probably in use anyway) */std::string ircnick;/* If this is set to the channel name the bot joins, show greet. * I suspose two very rapid joins (getting the second join request * before the first JOIN was confirmed) would cause the first * channel not to show a message.. I don't think this will be * an issue though. */static ci::string greetchannel;/* This is the nick that invited chanstat, passed to the non-hub bots by !dojoin * the same thing applies here as stated above^ * This is used to inform the user of any reason it cant join +bkl etc * Once this is done, the value is cleared (so if they ping and reconnect * we dont end up spamming the crap out of some guy). This is also cleared after * the greet is showed (as this will probably be used in that greet message anyway */static std::string invitedby;class CSChannel;struct Ignore;struct Blacklist;struct Throttle;struct HostAllow;static void SaveDatabase();CSChannel *getcs(const ci::string &channel, bool requestnames = true);static void SwapLogs();/* List of channels */static std::map<ci::string, CSChannel *> Channels;/* botname, channel count */static std::map<ci::string, int> BotsChanCount;/* Ignored hosts */// XXX this should really be in a std::list ..static std::map<ci::string, Ignore *> IgnoredHosts;/* Blacklisted channels */static std::map<ci::string, Blacklist *> Blacklists;/* Map of hosts and duration times for temporary ignores */static std::map<ci::string, Throttle *> Throttles;/* List of people allowed to use this bot */static std::vector<HostAllow *> HostAllows;class CSChannel{  friend CSChannel *getcs(const ci::string &, bool);  friend void SwapLogs();    private:	std::ofstream log;	bool informed_of_error;  public:	ci::string name;	/* users in the channel */	std::vector<ci::string> users;	CSChannel(const ci::string &ciname)	{		informed_of_error = false;		std::string tname = assign(ciname);		for (unsigned i = 0; i < tname.size(); ++i)			tname[i] = std::tolower(tname[i]);		this->name = assign(tname);		std::string channelname = LogDir + std::string(this->name.c_str()) + ".log";		log.open(channelname.c_str(), std::ios_base::app);				if (!log.is_open())		{			IRCdProto->Privmsg(StaffChannel, "!!!ERROR!!! Unable to open logfile %s", channelname.c_str());		}	}	~CSChannel()	{		if (log.is_open())			log.close();		users.clear();		std::map<ci::string, CSChannel *>::iterator it = Channels.find(name);		if (it != Channels.end())		{			Channels.erase(it);		}	}	void AddUser(std::string &nick)	{		AddUser(nick.c_str());	}	void AddUser(const ci::string &nick)	{		ci::string tnick = nick;		AddUser(tnick);	}	void AddUser(ci::string &nick)	{		std::vector<ci::string>::iterator it = std::find(this->users.begin(), this->users.end(), nick);		// XXX Can this happen anymore? Need to check this ...		if (it != this->users.end())			return;		this->users.push_back(nick);	}	void DelUser(const std::string &nick)	{		const ci::string cinick = assign(nick);		DelUser(cinick);	}		void DelUser(const ci::string &nick)	{		std::vector<ci::string>::iterator it = std::find(this->users.begin(), this->users.end(), nick);		if (it != this->users.end())		{			DelUser(it);		}	}	void DelUser(const std::vector<ci::string>::iterator &it)	{		if (it != this->users.end())		{			this->users.erase(it);		}		time_t t = time(NULL);		// If we're in a netsplit check to see if netsplit mode should be over		if (netsplit)		{			if (netsplit_time + 300 <= t)			{				//netsplit is over				netsplit = false;				if (Config->Nick == "ChanStat")				{					IRCdProto->Privmsg(StaffChannel, "(\00303NETSPLIT\003) Netsplit mode is over");				}			}		}		if (!this->users.empty() && this->users.size() <= 1)		{			// Don't part channels during netsplits			if (netsplit)			{				IRCdProto->Privmsg(StaffChannel, "(\00307WARNING\003) %s may be empty, but didn't part because of recent netsplit", this->name.c_str());				return;			}			if (loadtime < t - 60)			{				IRCdProto->Part(this->name, "ChanStat has parted %s because it did not meet the minimum user requirement.", this->name.c_str());				IRCdProto->Privmsg(StaffChannel, "(\00307PARTS\003) Parted %s because it did not meet the user requirement", this->name.c_str());			}			else			{				IRCdProto->Privmsg(StaffChannel, "(\00307WARNING\003) %s may be empty, but didn't part because of a recent reload", this->name.c_str());			}		}	}	void Write(const char *message, ...)	{		if (!message)		{			IRCdProto->Privmsg(StaffChannel, "!!!WARNING!!! Was told to write a nonexistant message to logfile for %s", this->name.c_str());			return;		}		char messagebuf[512];		memset(&messagebuf, 0, sizeof(messagebuf));				va_list vi;		va_start(vi, message);		vsnprintf(messagebuf, sizeof(messagebuf) - 1, message, vi);		va_end(vi);		std::string sbuf = messagebuf;		Write(sbuf);	}	void Write(const std::string &message)	{		if (message.empty())		{			IRCdProto->Privmsg(StaffChannel, "!!!WARNING!!! Was told to write a nonexistant message to logfile for %s", this->name.c_str());			return;		}		if (!this->log.is_open())		{			if (!informed_of_error)			{				informed_of_error = true;				IRCdProto->Privmsg(StaffChannel, "!!!ERROR!!! FD for %s IS NOT OPEN; CAN'T LOG. THIS IS NOT A GOOD THING.", this->name.c_str());			}			return;		}		this->log << message;		this->log << std::endl;	}};struct Blacklist{	std::string nick;	ci::string reason;	time_t date;	time_t expires;};struct Ignore{	std::string nick;	ci::string reason;	time_t date;	time_t expires;};struct Throttle{	time_t expirytime;	int count;	time_t lastused;	bool reached;};struct HostAllow{	std::string host;	std::string creator;};CSChannel *findcs(const ci::string &channel);/** Get the total channel count for this bot, this checks our whiteliste channels */static unsigned GetTotalChannelCount(){	unsigned Extra = 0;	for (unsigned i = 0; i < sizeof(WhiteList); ++i)	{		if (WhiteList[i].empty())			break;		/* Personal bots use real channel count */		if (!findcs(WhiteList[i]) && !PersonalBot)			++Extra;	}	return Channels.size() + Extra;}/* buf is a nick!ident@host format, put nick in nick and ident@host in host */void GetNickHost(std::string buf, std::string &nick, std::string &host){	std::stringstream ss(buf);	if (buf.find('!') == std::string::npos)	{		nick = buf;		host.clear();	}	else	{		std::getline(ss, nick, '!');		std::getline(ss, host, ' ');	}}static void AddCounter(const std::string &uhost, int limit){	Throttle *t;	time_t ct = time(NULL);	std::map<ci::string, Throttle *>::iterator it = Throttles.find(assign(uhost));	if (it != Throttles.end())	{		t = it->second;		t->count++;		if (t->count >= limit)		{			IRCdProto->Privmsg(BotChannel, "(4THROTTLE) Added ignore for %s for 10 minutes due to spam", uhost.c_str());			t->reached = true;			t->expirytime = ct + 600;		}		t->lastused = time(NULL);	}	else	{		t = new Throttle;		t->count = 1;		t->lastused = ct;		t->reached = false;		t->expirytime = ct + 600;		Throttles.insert(std::make_pair(assign(uhost), t));	}}static int match_wild(const char *pattern, const char *str, int docase = 0){	char c;	const char *s;	if (!str || !*str || !pattern || !*pattern)		return 0;	/* This WILL eventually terminate: either by *pattern == 0, or by a	* trailing '*'. */	for (;;)	{		switch (c = *pattern++) {			case 0:				if (!*str)					return 1;				return 0;			case '?':				if (!*str)					return 0;				str++;			break;			case '%':			{				if ((*str < 48) || (*str > 57))					return 0;				str++;				break;			}			case '\\':			{				if (!(*str && *pattern))					return 0;				if (*str != *pattern)				{					return 0;				}				pattern++;				str++;				break;			}			case '*':				if (!*pattern)					return 1;       /* trailing '*' matches everything else */				s = str;				while (*s)				{					if ((docase ? (*s == *pattern)						 : (tolower(*s) == tolower(*pattern)))						&& match_wild(pattern, s, docase))						return 1;					s++;				}				break;			default:				if (docase ? (*str++ != c) : (tolower(*str++) != tolower(c)))					return 0;			break;		}                       /* switch */	}}bool IsWhiteList(const ci::string &chan){	if (chan.empty())		return false;	for (unsigned i = 0; i < sizeof(WhiteList); ++i)	{		if (WhiteList[i].empty())			break;		if (chan == WhiteList[i])			return true;	}	return false;}/** Check if a channel should be logged * @return true to NOT be logged, false to be logged */bool IsDontLog(const ci::string &chan){	if (chan.empty())		return false;		for (unsigned i = 0; i < sizeof(DontLog); ++i)	{		if (DontLog[i].empty())			break;		if (chan == DontLog[i])			return true;	}	/* ONLY the hub will log whitelisted channels */	if (IsWhiteList(chan) && Config->Nick != "ChanStat")		return true;		return false;}bool IsIgnored(const std::string &host){	std::map<ci::string, Ignore *>::iterator it, it2;	std::map<ci::string, Throttle *>::iterator tit, tit2;	time_t ct = time(NULL);	for (tit = Throttles.begin(); tit != Throttles.end(); tit = tit2)	{		tit2 = tit;		++tit2;		Throttle *t = tit->second;		if ((ct - t->lastused) >= 600 || (ct >= t->expirytime))		{			if (t->reached && Config->Nick == "ChanStat")				IRCdProto->Privmsg(BotChannel, "(\00303THROTTLE\003) Expiring throttle for %s", tit->first.c_str());			delete t;			Throttles.erase(tit);		}		else if (t->reached && match_wild(const_cast<char *>(tit->first.c_str()), const_cast<char *>(host.c_str())))			return true;	}	for (it = IgnoredHosts.begin(); it != IgnoredHosts.end(); it = it2)	{		it2 = it;		++it2;		Ignore *i = it->second;		if (i->expires > 0 && i->expires <= ct)		{			if (Config->Nick == "ChanStat")				IRCdProto->Privmsg(StaffChannel, "(\00303IGNORE\003) Expiring ignore for %s", it->first.c_str());			delete i;			IgnoredHosts.erase(it);		}		else if (match_wild(const_cast<char *>(it->first.c_str()), const_cast<char *>(host.c_str())))			return true;	}	return false;}bool IsBlacklisted(const std::string &chan){	if (chan.empty())		return false;	std::map<ci::string, Blacklist *>::iterator it = Blacklists.find(assign(chan));	time_t t = time(NULL);	if (it != Blacklists.end())	{		Blacklist *b = it->second;		if (b->expires > 0 && b->expires <= t)		{			if (Config->Nick == "ChanStat")				IRCdProto->Privmsg(StaffChannel, "(\00303BLACKLIST\003) Expiring blacklist for channel %s", chan.c_str());			delete it->second;			Blacklists.erase(it);			return false;		}		return true;	}	return false;}bool CanUsePersonalCommands(const std::string &host){	if (!PersonalBot)	{		IRCdProto->Privmsg(StaffChannel, "!!!WARNING!!! CanUsePersonalCommands() was called on a nonpersonal bot?");		return false;	}	for (std::vector<HostAllow *>::iterator it = HostAllows.begin(); it != HostAllows.end(); ++it)	{		HostAllow *h = *it;		if (match_wild(const_cast<char *>(h->host.c_str()), const_cast<char *>(host.c_str())))		{			IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) Granted access to %s by matching %s", host.c_str(), h->host.c_str());			return true;		}	}	IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) Denied access to %s", host.c_str());	return false;}/* write to all files */void writeToAll(const std::string &buf){	std::map<ci::string, CSChannel *>::iterator it;	CSChannel *c;	for (it = Channels.begin(); it != Channels.end(); ++it)	{		c = it->second;		c->Write(buf);	}}void HumanReadableTime(time_t t, char *buf){	time_t ctime = time(NULL) - t;	int weeks, days, hours, mins;	weeks = days = hours = mins = 0;	while (ctime > 604800)	{		weeks++;		ctime -= 604800;	}	while (ctime > 86400)	{		days++;		ctime -= 86400;	}	while (ctime > 3600)	{		hours++;		ctime -= 3600;	}	while (ctime > 60)	{		mins++;		ctime -= 60;	}	snprintf(buf, 100, "%i weeks %i days %i hours %i mins", weeks, days, hours, mins);}int file_exists(const char *filename){        struct stat buf;        int i = stat(filename, &buf);        if (i == 0)                return 1;        return 0;}/* Log file swapping system * #chan.log.13 -> #chan.log.14 * #chan.log.12 -> #chan.log.13 * .. * #chan.log2 -> #chan.log.3 * #chan.log -> #chan.log.2 * Delete #chan.log.14 * New file #chan.log is logged to. */static void SwapLogs(){	int i;	char buf[10];	std::map<ci::string, CSChannel *>::iterator it;	ci::string channels;	std::string fname, fname2;	CSChannel *c;		for (it = Channels.begin(); it != Channels.end(); ++it)	{		channels = it->first;		c = it->second;		fname = LogDir + std::string(c->name.c_str()) + ".log";		if (!file_exists(fname.c_str()))			continue;		if (c->log.is_open())			c->log.close();		for (i = 13; i > 0; --i)		{			memset(&buf, 0, sizeof(buf));			sprintf(buf, "%d", i);			fname = LogDir + std::string(c->name.c_str()) + ".log." + std::string(buf);			if (file_exists(fname.c_str()))			{				++i;				memset(&buf, 0, sizeof(buf));				sprintf(buf, "%d", i);				fname2 = LogDir + std::string(c->name.c_str()) + ".log." + std::string(buf);				--i;				rename(fname.c_str(), fname2.c_str());			}		}		//NULL->1		fname = LogDir + std::string(c->name.c_str()) + ".log.1";		fname2 = LogDir + std::string(c->name.c_str()) + ".log";		rename(fname2.c_str(), fname.c_str());		//destroy 14		fname = LogDir + std::string(c->name.c_str()) + ".log.14";		if (file_exists(fname.c_str()))			unlink(fname.c_str());		fname = LogDir + std::string(c->name.c_str()) + ".log";		c->log.open(fname.c_str(), std::ios_base::app);	}}void getTimestamp(char *hourbuf, char *minbuf){	time_t t;	tm curtime;	int hour, min;	char msgbuf[100];	const char *dayofweek, *month;	time(&t);	curtime = *(localtime(&t));	hour = curtime.tm_hour;	min = curtime.tm_min;	if (min <= 9)		snprintf(minbuf, 4, "0%i", min);	else		snprintf(minbuf, 4, "%i", min);	if (hour <= 9)		snprintf(hourbuf, 4, "0%i", hour);	else		snprintf(hourbuf, 4, "%i", hour);	if (lasthour > hour)	{		switch (curtime.tm_wday)		{			case 0:				dayofweek = "Sun";				break;			case 1:				dayofweek = "Mon";				break;			case 2:				dayofweek = "Tue";				break;			case 3:				dayofweek = "Wed";				break;			case 4:				dayofweek = "Thu";				break;			case 5:				dayofweek = "Fri";				break;			case 6:				dayofweek = "Sat";				break;			default:				dayofweek = "XXX";				break;		}		switch (curtime.tm_mon)		{			case 0:				month = "Jan";				break;			case 1:				month = "Feb";				break;			case 2:				month = "Mar";				break;			case 3:				month = "Apr";				break;			case 4:				month = "May";				break;			case 5:				month = "Jun";				break;			case 6:				month = "Jul";				break;			case 7:				month = "Aug";				break;			case 8:				month = "Sep";				break;			case 9:				month = "Oct";				break;			case 10:				month = "Nov";				break;			case 11:				month = "Dec";				break;			default:				month = "XXX";				break;		}		SwapLogs();		if (curtime.tm_mday > 9)			snprintf(msgbuf, sizeof(msgbuf), "[00:00] --- %s %s %i %i", dayofweek, month, curtime.tm_mday, 1900 + curtime.tm_year);		else			snprintf(msgbuf, sizeof(msgbuf), "[00:00] --- %s %s  %i %i", dayofweek, month, curtime.tm_mday, 1900 + curtime.tm_year);		writeToAll(msgbuf);	}	lasthour = hour;}/* Remove a user from every channel they are in (quit) */void removeUser(const std::string &nick, const std::string &uhost){	removeUser(nick.c_str(), uhost.c_str());}void removeUser(const ci::string &nick, const ci::string &uhost){	char hour[4], min[4];	getTimestamp(hour, min);	for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); ++it)	{		ci::string chname = it->first;		CSChannel *c = getcs(chname);		if (!c)		{			IRCdProto->Privmsg(StaffChannel, "WARNING: removeUser() getcs returned NULL for %s saved in the Channels map?", chname.c_str());			continue;		}		std::vector<ci::string>::iterator user = std::find(c->users.begin(), c->users.end(), nick);		if (user != c->users.end())		{			c->Write("[%s:%s] %s (%s) left irc: Quit:", hour, min, nick.c_str(), uhost.c_str());			c->DelUser(user);		}	}}/* Rename a user in every channel they are in (nick) */void renameUser(const ci::string &oldnick, const ci::string &newnick){	char hour[4], min[4];	getTimestamp(hour, min);	for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); ++it)	{		/* We use getcs to reinitialize logfile, if needed */		ci::string chname = it->first;		CSChannel *c = getcs(chname);		if (!c)		{			IRCdProto->Privmsg(StaffChannel, "WARNING: renameUser() getcs() returned NULL for %s saved in the Channels map?", chname.c_str());			continue;		}		/* Get old user.. */		std::vector<ci::string>::iterator user = std::find(c->users.begin(), c->users.end(), oldnick);		if (user != c->users.end())		{			c->Write("[%s:%s] Nick change: %s -> %s", hour, min, oldnick.c_str(), newnick.c_str());			c->users.erase(user);			c->AddUser(newnick);		}	}}CSChannel *findcs(const ci::string &channel){	std::map<ci::string, CSChannel *>::iterator it = Channels.find(channel);	if (it != Channels.end())	{		return it->second;	}	return NULL;}CSChannel *findcs(const std::string &channel){	ci::string chan = assign(channel);	return findcs(chan);}CSChannel *getcs(const ci::string &channel, bool requestnames){	CSChannel *cs;	bool empty_chanlist = Channels.empty();	if (channel.empty())		return NULL;	else if (channel[0] != '#')		return NULL;	/* Don't log this channel */	else if (IsDontLog(channel))		return NULL;	else if (IRCSocket && empty_chanlist) //whois to fill up internal channel list, if internal channel list is EMPTY		IRCSocket->Write("WHOIS %s", Config->Nick.c_str());	std::map<ci::string, CSChannel *>::iterator it = Channels.find(channel);	if (it != Channels.end())	{		cs = it->second;		if (!cs->log.is_open())		{			IRCdProto->Privmsg(StaffChannel, "WARNING: getcs() was about to return a channel with a closed log file: %s", cs->name.c_str());			return NULL;		}	}	else	{		cs = new CSChannel(channel);		if (!cs->log.is_open())		{			delete cs;			IRCdProto->Privmsg(StaffChannel, "FATAL: error, unable to open log file for %s", channel.c_str());			IRCSocket->Write("QUIT");			End = true;			return NULL;		}		Channels.insert(std::make_pair(channel, cs));	}	/* Request names is always set unless we're joining a channel, dont request names here as ircd will send us */	/* It's also not set when we're requesting getcs in do_names - Adam */	/* Additionally, don't request names if Channels is empty - This is becuase the WHOIS sent above gets recieved and calls getcs()	 * which will come back to this code to request NAMES	 * 	 * We check for empty() at the beginning of this function as we don't know if this function added a channel	 * Using .size() == 1 is potentially unsafe if we were in fact in one channel, we would be requesting /names every time	 * something happend in an empty (or, thought to be) channel. - Adam	 */	if (IRCSocket && cs->users.empty() && requestnames && !empty_chanlist)		IRCSocket->Write("NAMES %s", channel.c_str());		return cs;}class ChanStatNotice : public Command{ public:	ChanStatNotice() : Command("!CHANSTATS", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		ci::string chan = target;		chan.erase(chan.begin());		if (IsWhiteList(target) && Config->Nick != "ChanStat")			return;		if (IsIgnored(nick + "!" + uhost))		{			IRCdProto->Privmsg(BotChannel, "(04IGNORES) Ignored !chanstats from %s (%s) on %s", nick.c_str(), uhost.c_str(), target.c_str());			return;		}		AddCounter(nick + "!" + uhost, COMMANDTHROTTLE);		IRCdProto->Privmsg(BotChannel, "(7COMMANDS) %s (%s) used %s in %s", nick.c_str(), uhost.c_str(), this->GetName().c_str(), target.c_str());		IRCdProto->Notice(nick, "(12ChanStats): Channel Statistics for 12%s can be found by going to 12http://chanstat.info/search.php?channel=%s%s", target.c_str(), chan.c_str(), AppendLink.c_str());	}};class ChanStatMessage : public Command{ public:	ChanStatMessage() : Command("@CHANSTATS", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		ci::string chan = target;		chan.erase(chan.begin());		if (IsWhiteList(target) && Config->Nick != "ChanStat")			return;		if (IsIgnored(nick + "!" + uhost))		{			IRCdProto->Privmsg(BotChannel, "(04IGNORES) Ignored @chanstats from %s (%s) on %s", nick.c_str(), uhost.c_str(), target.c_str());			return;		}		AddCounter(nick + "!" + uhost, COMMANDTHROTTLE);		IRCdProto->Privmsg(BotChannel, "(7COMMANDS) %s (%s) used %s in %s", nick.c_str(), uhost.c_str(), this->GetName().c_str(), target.c_str());		IRCdProto->Privmsg(target.c_str(), "(12ChanStats): Channel Statistics for 12%s can be found by going to 12http://chanstat.info/search.php?channel=%s%s", target.c_str(), chan.c_str(), AppendLink.c_str());	}};class ChanStatMapNotice : public Command{ public:	ChanStatMapNotice() : Command("!MAP", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (IsWhiteList(target) && Config->Nick != "ChanStat")			return;		if (IsIgnored(nick + "!" + uhost))		{			IRCdProto->Privmsg(BotChannel, "(04IGNORES) Ignored !map from %s (%s) on %s", nick.c_str(), uhost.c_str(), target.c_str());			return;		}		AddCounter(nick + "!" + uhost, COMMANDTHROTTLE);		ci::string chan = target;		chan.erase(chan.begin());		IRCdProto->Notice(nick, "(12Map): Relationship map for 12%s can be found by going to 12http://chanstat.info/map.php?channel=%s%s", target.c_str(), chan.c_str(), AppendLink.c_str());		IRCdProto->Privmsg(BotChannel, "(7COMMANDS) %s (%s) used %s in %s", nick.c_str(), uhost.c_str(), this->GetName().c_str(), target.c_str());	}};class ChanStatMapMessage : public Command{ public:	ChanStatMapMessage() : Command("@MAP", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (IsWhiteList(target) && Config->Nick != "ChanStat")			return;		if (IsIgnored(nick + "!" + uhost))		{			IRCdProto->Privmsg(BotChannel, "(04IGNORES) Ignored @map from %s (%s) on %s", nick.c_str(), uhost.c_str(), target.c_str());			return;		}		AddCounter(nick + "!" + uhost, COMMANDTHROTTLE);		ci::string chan = target;		chan.erase(chan.begin());		IRCdProto->Privmsg(assign(target), "(12Map): Relationship map for 12%s can be found by going to 12http://chanstat.info/map.php?channel=%s%s", target.c_str(), chan.c_str(), AppendLink.c_str());		IRCdProto->Privmsg(BotChannel, "(7COMMANDS) %s (%s) used %s in %s", nick.c_str(), uhost.c_str(), this->GetName().c_str(), target.c_str());	}};class ChanStatChannel : public Command{ public:	ChanStatChannel() : Command("!CHANNELS", 0, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel && !PersonalBot)		{			if (params.empty() || params[0] == Config->Nick)				IRCdProto->Privmsg(target.c_str(), "!mychannels %i", GetTotalChannelCount());		}	}};class ChanStatDoJoin : public Command{ public:	ChanStatDoJoin() : Command("!DOJOIN", 3, 3) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (Config->Nick == params[0] && target == BotChannel)		{			greetchannel = params[1];			invitedby = assign(params[2]);			IRCdProto->Join(params[1]);		}	}};class ChanStatMyChannels : public Command{ public:	ChanStatMyChannels() : Command("!MYCHANNELS", 1, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		ci::string cinick = assign(nick);		if (target == BotChannel && Config->Nick == "ChanStat")		{			std::map<ci::string, int>::iterator it = BotsChanCount.find(cinick);			if (it != BotsChanCount.end())			{				BotsChanCount.erase(it);			}			BotsChanCount.insert(std::make_pair(cinick, atoi(params[0].c_str())));		}	}};class ChanStatPart : public Command{ public:	ChanStatPart() : Command("!PART", 1, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			if (Config->Nick == "ChanStat")			{				IRCdProto->Privmsg(StaffChannel, "(04PARTS) Parted channel %s", params[0].c_str());			}			IRCdProto->Part(params[0], "This bot has been parted by a ChanStat staff member, please join #ChanStat for more information");		}		else if (PersonalBot && CanUsePersonalCommands(nick + "!" + uhost))		{			IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) %s!%s used PART in %s to part me from %s", nick.c_str(), uhost.c_str(), target.c_str(), params[0].c_str());			IRCdProto->Part(params[0], "This bot has been parted by %s; contact this person for more information", nick.c_str());		}	}};class ChanStatBlacklist : public Command{ public:	ChanStatBlacklist() : Command("!BLACKLIST", 1, 4) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		Blacklist *b;		std::map<ci::string, Blacklist *>::iterator it, it2;		int i;		char datebuf[100], expirebuf[100];		time_t t = time(NULL);		if (target ==  BotChannel)		{			if (Config->Nick == "ChanStat")			{				if (params.size() < 2 && params[0] != "LIST")					return;				if (params[0] == "ADD")				{					b = new Blacklist;					b->nick = nick;					b->date = t;					b->expires = t + 604800; // Expires in one week by default					if (params.size() > 2)					{						if (params[2][0] == '+')						{							ci::string expiry = params[2];							expiry.erase(expiry.begin());							if (!expiry.empty() && expiry.size() > 1)							{								unsigned multiplier = 1;								if (expiry[0] == 'd')								{									multiplier = 86400;								}								else if (expiry[0] == 'h')								{									multiplier = 3600;								}								else if (expiry[0] == 'm')								{									multiplier = 60;								}								if (multiplier != 1)									expiry.erase(expiry.begin());								long num = atol(expiry.c_str());								if (num > 0)								{									b->expires = (t + (num * multiplier));								}							}							else if (!expiry.empty())							{								if (expiry[0] == '0')								{									b->expires = 0;								}							}						}						else							b->reason = params[2];						if (params.size() > 3)						{							if (!b->reason.empty())								b->reason += " ";							b->reason += params[3];						}					}					else						b->reason = "Not specified";					Blacklists.insert(std::make_pair(params[1], b));					IRCdProto->Part(params[1], "Your channel has been blacklisted. Join #ChanStat for more information.");					IRCdProto->Privmsg(BotChannel, "!blacklistpart %s", params[1].c_str());					if (b->expires)						HumanReadableTime(t - (b->expires - t), expirebuf);					else						snprintf(expirebuf, sizeof(expirebuf), "never");					IRCdProto->Privmsg(StaffChannel, "(\00304BLACKLIST\003) Blacklisted channel %s, expires in %s", params[1].c_str(), expirebuf);				}				else if (params[0] == "DEL")				{					IRCdProto->Privmsg(StaffChannel, "(04BLACKLIST) Unblacklisted channel %s", params[1].c_str());					it = Blacklists.find(params[1]);					if (it != Blacklists.end())					{						Blacklists.erase(it);					}				}				else if (!stricmp(params[0].c_str(), "LIST"))				{					IRCdProto->Privmsg(BotChannel, "Blacklisted channels:");					for (it = Blacklists.begin(), i = 1; it != Blacklists.end(); it = it2, i++)					{						it2 = it;						++it2;						b = it->second;						HumanReadableTime(b->date, datebuf);						if (b->expires > 0 && b->expires <= t)						{							if (Config->Nick == "ChanStat")								IRCdProto->Privmsg(StaffChannel, "(\00303BLACKLIST\003) Expiring blacklist for channel %s", it->first.c_str());							delete it->second;							Blacklists.erase(it);							--i;							continue;						}						if (b->expires)							HumanReadableTime(t - (b->expires - t), expirebuf);						else							snprintf(expirebuf, sizeof(expirebuf), "never");						IRCdProto->Privmsg(BotChannel, "(\00307BLACKLIST\003) %i. [\002Channel\002]: %s [\002By\002]: %s [\002Set\002]: %s ago [\002Expires\002]: %s [\002Reason\002]: %s",							i, it->first.c_str(), b->nick.c_str(), datebuf, expirebuf, b->reason.c_str());					}				}			}		}	}};class ChanStatIgnore : public Command{ public:	ChanStatIgnore() : Command("!IGNORE", 1, 4) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		Ignore *i;		int j;		std::map<ci::string, Ignore *>::iterator it, it2;		char datebuf[100], expirebuf[100];		time_t t = time(NULL);		if (target == BotChannel)		{			if (params.size() < 2 && params[0] != "LIST")				return;			if (params[0] == "ADD")			{				i = new Ignore;				i->nick = nick;				i->date = t;				i->expires = t + 604800;				if (params.size() > 2)				{					if (params[2][0] == '+')					{						ci::string expiry = params[2];						expiry.erase(expiry.begin());						if (!expiry.empty() && expiry.size() > 1)						{							unsigned multiplier = 1;							if (expiry[0] == 'd')							{								multiplier = 86400;							}							else if (expiry[0] == 'h')							{								multiplier = 3600;							}							else if (expiry[0] == 'm')							{								multiplier = 60;							}							if (multiplier != 1)								expiry.erase(expiry.begin());							long num = atol(expiry.c_str());							if (num > 0)							{								i->expires = (t + (num * multiplier));							}						}						else if (!expiry.empty())						{							if (expiry[0] == '0')							{								i->expires = 0;							}						}					}					else						i->reason = params[2];						if (params.size() > 3)					{						if (!i->reason.empty())							i->reason += " ";						i->reason += params[3];					}				}				else				{					i->reason = "Not specified";				}				IgnoredHosts.insert(std::make_pair(params[1], i));				if (i->expires)					HumanReadableTime(t - (i->expires - t), expirebuf);				else					snprintf(expirebuf, sizeof(expirebuf), "never");				if (Config->Nick == "ChanStat")					IRCdProto->Privmsg(StaffChannel, "(\00304IGNORE\003) Ignored host %s, expires in %s", params[1].c_str(), expirebuf);			}			else if (params[0] == "DEL")			{				if (Config->Nick == "ChanStat")					IRCdProto->Privmsg(StaffChannel, "(04IGNORE) Unignored host %s", params[1].c_str());				it = IgnoredHosts.find(params[1]);				if (it != IgnoredHosts.end())				{					IgnoredHosts.erase(it);				}			}			else if (params[0] == "LIST")			{				if (Config->Nick == "ChanStat")				{					IRCdProto->Privmsg(BotChannel, "Ignored hosts:");					for (it = IgnoredHosts.begin(), j = 1; it != IgnoredHosts.end(); it = it2, j++)					{						it2 = it;						++it2;						i = it->second;						if (i->expires > 0 && i->expires <= t)						{							if (Config->Nick == "ChanStat")								IRCdProto->Privmsg(StaffChannel, "(\00303IGNORE\003) Expiring ignore for %s", it->first.c_str());							delete it->second;							IgnoredHosts.erase(it);							--i;							continue;						}						HumanReadableTime(i->date, datebuf);						if (i->expires)							HumanReadableTime(t - (i->expires - t), expirebuf);						else							snprintf(expirebuf, sizeof(expirebuf), "never");						IRCdProto->Privmsg(BotChannel, "(\00307IGNORES\003) %i. [\002Host\002]: %s [\002By\002]: %s [\002Set\002]: %s ago [\002Expires\002]: %s [\002Reason\002]: %s",							j, it->first.c_str(), i->nick.c_str(), datebuf, expirebuf, i->reason.c_str());					}				}			}		}	}};class ChanStatBotinfo : public Command{ public:	ChanStatBotinfo() : Command("!BOTINFO", 1, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel && !params.empty())		{			if (params[0] == Config->Nick || params[0] == "ALL")			{				ci::string channels;				for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); ++it)				{					if (channels.empty())						channels = it->first;					else						channels += " " + it->first;				}				IRCdProto->Privmsg(target.c_str(), "(7BOTINFO) Currently on %d channels: %s", GetTotalChannelCount(), channels.c_str());			}		}	}};class ChanStatChancount : public Command{ public:	ChanStatChancount() : Command("!CHANCOUNT", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			IRCdProto->Privmsg(target.c_str(), "(7CHANCOUNT) Currently on %d channels", GetTotalChannelCount());		}	}};class ChanStatBLPart : public Command{ public:	ChanStatBLPart() : Command("!BLACKLISTPART", 1, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			IRCdProto->Part(assign(params[0]), "Your channel has been blacklisted. Join #ChanStat for more information");		}	}};class ChanStatAMSG : public Command{ public:	ChanStatAMSG() : Command("!AMSG", 1, 1) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			if (nick != "Adam" && nick != "Matt-")				return;			if (Config->Nick == "ChanStat")				IRCdProto->Privmsg("#chanstat", "(7 GLOBAL ) %s", params[0].c_str());			for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); ++it)			{				ci::string chname = it->first;				if (IsWhiteList(chname) && Config->Nick == "ChanStat") // These shouldn't be tracked anyway					continue;				IRCdProto->Privmsg(chname.c_str(), "(7 GLOBAL ) %s", params[0].c_str());			}		}	}};class ChanStatNick : public Command{ public:	ChanStatNick() : Command("!NICK", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			IRCSocket->Write("NICK %s", Config->Nick.c_str());		}	}};class ChanStatID : public Command{ public:	ChanStatID() : Command("!ID", 0, 0) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			IRCdProto->Privmsg("NickServ", "IDENTIFY %s", NSPW.c_str());		}	}};class ChanStatQuit : public Command{ public:	ChanStatQuit() : Command("!QUIT", 1, 2) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		if (target == BotChannel)		{			if ((nick == "Adam" || nick == "Matt-") && params[0] == "ALL")			{				SaveDatabase();				IRCSocket->Write("QUIT :%s", params.size() > 1 ? params[1].c_str() : "");				End = true;			}			else if (params[0] == Config->Nick)			{				SaveDatabase();				if (PersonalBot)					IRCSocket->Write("QUIT :This bot has been removed by a staff member, for more information join #ChanStat");				else					IRCSocket->Write("QUIT :%s", params.size() > 1 ? params[1].c_str() : "");				End = true;			}		}	}};class ChanStatHostManage : public Command{ public:	ChanStatHostManage() : Command("!HOSTS", 1, 3) { }	void Execute(const std::string &nick, const std::string &uhost, const ci::string &target, const std::vector<ci::string> &params)	{		std::vector<ci::string> realparams = params;		if (target == BotChannel)		{			realparams.erase(realparams.begin());			if (params[0] != Config->Nick && params[0] != "ALL")				return;			if (realparams.empty())				return;		}		else if (!CanUsePersonalCommands(nick + "!" + uhost))		{			IRCdProto->Notice(nick, "Access denied.");			return;		}		if (realparams[0] == "ADD" && realparams.size() > 1)		{			if (HostAllows.size() >= 10 && target != BotChannel)			{				IRCdProto->Notice(nick, "Sorry, your allowed hosts list is full.");				IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) Denied adding host %s from %s in %s because the allowed host list is full.", realparams[1].c_str(), nick.c_str(), target.c_str());			}			else			{				HostAllow *h = new HostAllow;				h->host = assign(realparams[1]);				h->creator = nick;				HostAllows.push_back(h);				IRCdProto->Notice(nick, "Host %s added to the allowed hosts list.", h->host.c_str());				IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) %s added host %s to allowed hosts list in %s", nick.c_str(), realparams[1].c_str(), target.c_str());			}		}		else if (realparams[0] == "DEL" && realparams.size() > 1)		{			int value = atoi(realparams[1].c_str());			if (value > 0)			{				unsigned valueu = value;				if (valueu <= HostAllows.size())				{					IRCdProto->Notice(nick, "Host %s removed from the allowed hosts list.", HostAllows[valueu - 1]->host.c_str());					IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) %s removed host %s from the allowed hosts list in %s", nick.c_str(), HostAllows[valueu - 1]->host.c_str(), target.c_str());					delete HostAllows[valueu - 1];					HostAllows.erase(HostAllows.begin() + valueu - 1);				}				else				{					IRCdProto->Notice(nick, "Unabled to find host %i on allowed hosts list.", value);				}			}			else			{				bool deleted = false;				for (std::vector<HostAllow *>::iterator it = HostAllows.begin(); it != HostAllows.end(); ++it)				{					if (realparams[1] == (*it)->host)					{						IRCdProto->Notice(nick, "Host %s removed from the allowed hosts list.", (*it)->host.c_str());						IRCdProto->Privmsg(BotChannel, "(\00312CUSTOMBOT\003) %s removed host %s from the allowed hosts list in %s", nick.c_str(), (*it)->host.c_str(), target.c_str());						delete *it;						HostAllows.erase(it);						deleted = true;						break;					}				}				if (!deleted)				{					IRCdProto->Notice(nick, "Unabled to find %s on allowed hosts list.", realparams[1].c_str());				}			}		}		else if (realparams[0] == "LIST")		{			if (HostAllows.empty())			{				IRCdProto->Notice(nick, "Allowed hosts list is empty.");			}			else			{				unsigned num = 0;				for (std::vector<HostAllow *>::iterator it = HostAllows.begin(); it != HostAllows.end(); ++it)				{					HostAllow *h = *it;					IRCdProto->Notice(nick, "%d. Host: %s Added by: %s", ++num, h->host.c_str(), h->creator.c_str());				}			}		}		else		{			IRCdProto->Notice(nick, "Usage: !HOSTS (ADD|DEL|LIST) [nick@host/number]");		}	}};EventReturn do_privmsg(const std::vector<std::string> &params){	if (params.size() < 3)		return EVENT_CONTINUE;	ci::string chname = assign(params[2]);	char mins[4], hours[4];	getTimestamp(hours, mins);	std::string nick, uhost;	GetNickHost(params[0], nick, uhost);	if (nick.empty())		return EVENT_CONTINUE;	if (params.size() > 2 && !params[2].empty() && params[2][0] != '#')	{		return EVENT_CONTINUE;	}	CSChannel *cs = getcs(chname);	if (!cs)		return EVENT_CONTINUE;	if (params.size() > 3 && !params[3].empty())	{		if ((params[3][0] == '\1') && (!strncmp(params[3].c_str(), "\1ACTION ", 8)))		{			std::string temp = params[3];			temp.erase(temp.length() - 1);			char *msgbuf = const_cast<char *>(temp.c_str()); //XXX ew?			msgbuf += 8;			cs->Write("[%s:%s] Action: %s %s", hours, mins, nick.c_str(), msgbuf);		} 		else			cs->Write("[%s:%s] <%s> %s", hours, mins, nick.c_str(), params[3].c_str());	}	return EVENT_CONTINUE;}EventReturn do_mode(const std::vector<std::string> &params){	ci::string chname = assign(params[2]);	char hour[4], min[4];	getTimestamp(hour, min);	CSChannel *c = getcs(chname);	if (!c)		return EVENT_CONTINUE;	std::string modes;	for (unsigned j = 3; j < params.size(); ++j)	{		if (modes.empty())			modes = params[j];		else			modes += " " + params[j];	}	c->Write("[%s:%s] %s: mode change '%s' by %s", hour, min, params[2].c_str(), modes.c_str(), params[0].c_str());	return EVENT_CONTINUE;}EventReturn do_join(const std::vector<std::string> &params){	std::string nick;	std::string host;	ci::string chname = assign(params[2]), cinick;	GetNickHost(params[0], nick, host);	/* A chanstat joined the bot channel, request its channels but ONLY not during a netsplit, otherwise the hub ends up floooding out	 * the other bots	 */	if (!netsplit && Config->Nick == "ChanStat" && chname == BotChannel && nick.find("ChanStat") != std::string::npos)	{		IRCdProto->Privmsg(BotChannel, "!channels %s", nick.c_str());	}	bool mejoining = (nick == Config->Nick ? false : true);	CSChannel *cs = getcs(chname, mejoining);	if (!cs)		return EVENT_CONTINUE;	cinick = assign(nick);	if (!nick.empty() && !host.empty())	{		/* If another chanstat joins the channel, we part		 * they must have "ChanStat@" in their ident, and have		 * ChanStat.info chanstat.info or 2001:470:1f0f:438: in their host		 */		if (nick != Config->Nick && host.find("ChanStat@") != std::string::npos && (host.find("ChanStat.info") != std::string::npos || host.find("chanstat.info") != std::string::npos || host.find("2001:470:1f0f:438:") != std::string::npos))		{			if (!ircnick.empty() && ircnick != Config->Nick)			{				/* This means that the bot has disconnected sometime and reconnected back on				 * a different nick. It would have parted here, resulting in the bot losing				 * all of the channels it was in (fake detection of another ChanStat)				 * A /NICK command was sent earlier in do_mynick, and may have not				 * been successful or we may have not got the responce yet.. Regardless				 * we should not part here				 */				IRCdProto->Privmsg(StaffChannel, "(04WARNING) %s joined %s, I should be %s but I have detected myself to be %s. Not parting.",					nick.c_str(), params[2].c_str(), Config->Nick.c_str(), ircnick.c_str());			}			/* Don't part the channel if this channel is whitelisted. */			else if (!IsWhiteList(chname))			{				IRCdProto->Part(params[2], "Another ChanStat was detected in your channel");				IRCdProto->Privmsg(StaffChannel, "(3PARTS) Parted %s because another bot was found in the channel (%s)",					params[2].c_str(), nick.c_str());			}			return EVENT_CONTINUE;		}		if (Config->Nick == nick)		{			/* The bot has joined the channel			 * Send a MODE #channel to the ircd so we can			 * check for retards doing stuff such as unreals +u mode			 * which would break the bot			*/			IRCSocket->Write("MODE %s", chname.c_str());		}	}	char hour[4], min[4];	getTimestamp(hour, min);	cs->Write("[%s:%s] %s (%s) joined %s.", hour, min, nick.c_str(), host.c_str(), params[2].c_str());	cs->AddUser(cinick);		if (!greetchannel.empty() && greetchannel == params[2])	{		// Matt is a retard who doesn't want the nick in the greet msg		IRCdProto->Privmsg(params[2], "12Thank you for inviting ChanStat, an IRC channel statistics bot. For immediate assistance go to14 http://chanstat.info12. For news, updates, and extra features visit our forums at14 http://forum.chanstat.info12. If you have any further questions, join14 #ChanStat12.");		greetchannel.clear();		IRCdProto->Privmsg(StaffChannel, "(3JOINS) Just joined %s", params[2].c_str());		if (!PersonalBot)			IRCdProto->Privmsg(BotChannel, "!mychannels %i", GetTotalChannelCount());	}	invitedby.clear();	return EVENT_CONTINUE;}EventReturn do_part(const std::vector<std::string> &params){	ci::string chname = assign(params[2]);	std::string nick, host;	GetNickHost(params[0], nick, host);	if (chname == BotChannel && Config->Nick == "ChanStat" && nick.find("ChanStat") != std::string::npos)	{		std::map<ci::string, int>::iterator it = BotsChanCount.find(assign(nick));		if (it != BotsChanCount.end())		{			BotsChanCount.erase(it);		}	}	CSChannel *cs = getcs(chname);	if (!cs)		return EVENT_CONTINUE;	if (nick.empty())		return EVENT_CONTINUE;	char hour[4], min[4];	getTimestamp(hour, min);	cs->Write("[%s:%s] %s (%s) left %s.", hour, min, nick.c_str(), host.c_str(), params[2].c_str());	if (nick == Config->Nick)	{		// We do -1 here because this channel isnt deleted yet		if (!PersonalBot)			IRCdProto->Privmsg(BotChannel, "!mychannels %d", GetTotalChannelCount() - 1);		delete cs;	}	else	{		cs->DelUser(nick);	}	return EVENT_CONTINUE;}EventReturn do_nick(const std::vector<std::string> &params){	std::string nick, uhost;	ci::string chname = assign(params[2]);	GetNickHost(params[0], nick, uhost);	if (nick.empty())		return EVENT_CONTINUE;	/* This is the bot being renamed, from the nick it detected itself to be at	 * the 001 raw to the nick in the Config	 */	if (!ircnick.empty() && nick == ircnick && params[2] == Config->Nick)	{		// Clear the user that the 001 said to make everything hopefully work again		ircnick.clear();		IRCdProto->Privmsg(StaffChannel, "(7NICK) I have detected changing back to %s (I should be %s)", params[2].c_str(), Config->Nick.c_str());		IRCSocket->Write("WHOIS %s", Config->Nick.c_str());	}	renameUser(assign(nick), assign(params[2]));	return EVENT_CONTINUE;}EventReturn do_quit(const std::vector<std::string> &params){	std::string nick, uhost;	GetNickHost(params[0], nick, uhost);	// This is a netsplit	if (match_wild(NSLine.c_str(), params[2].c_str()) && params[2].find(NSNLine) == std::string::npos)	{		// Reset the netsplit time		netsplit_time = time(NULL);		// Isn't already a netsplit		if (!netsplit)		{			// netsplit mode on			netsplit = true;			if (Config->Nick == "ChanStat")			{				IRCdProto->Privmsg(StaffChannel, "(04NETSPLIT) ChanStat has detected a netsplit, now in netsplit mode");			}		}	}	/* if this was a bot that quit, remove it */	/* However, dont if there is a netsplit */	if (!netsplit && Config->Nick == "ChanStat" && nick.find("ChanStat") != std::string::npos)	{		std::map<ci::string, int>::iterator it = BotsChanCount.find(assign(nick));		if (it != BotsChanCount.end())		{			BotsChanCount.erase(it);		}	}	removeUser(assign(nick), assign(uhost));	return EVENT_CONTINUE;}EventReturn do_names(const std::vector<std::string> &params){	if (params.size() <= 5)			return EVENT_CONTINUE;	std::stringstream ss(params[5]);	ci::string cname = assign(params[4]);	/* Don't rerequest names here, as we already have it */	CSChannel *c = getcs(cname, false);	if (!c)		return EVENT_CONTINUE;	std::string buf;	while (std::getline(ss, buf, ' '))	{		while (!buf.empty() && (buf[0] == '~' || buf[0] == '&' || buf[0] == '@' || buf[0] == '%' || buf[0] == '+'))			buf.erase(buf.begin());		if (!buf.empty())			c->AddUser(ci::string(buf.c_str()));	}	/* Im the only one in the channel? */	if (!c->users.empty() && c->users.size() == 1 && c->users[0] == Config->Nick)	{		IRCdProto->Part(assign(cname));		IRCdProto->Privmsg(StaffChannel, "(\00307PARTS\003) Just parted %s because it was empty", cname.c_str());	}	return EVENT_CONTINUE;}EventReturn do_kick(const std::vector<std::string> &params){	if (params[3] == Config->Nick && params.size() > 4 && !params[4].empty() && params[4] == "Fake direction")	{		/* This is to fix a SwiftIRC bug		 * There is a problem with their IRCd when a server splits		 * and reconnects, it will sometimes mass kick everyone for		 * "Fake direction". Unfortuantly, the administration of		 * SwiftIRC are incapable or unwilling to put forth the effort		 * to fix this, so we must rejoin and halt.		 * EDIT: This is supposidly fixed now.. will leave it here just incase for now.		 */		/* Pretend were in netsplit mode (or restart the netsplit mode counter)		 * to prevent parting for no users		 */		netsplit = true;		netsplit_time = time(NULL);		IRCdProto->Join(params[2]);		return EVENT_CONTINUE;	}	char hour[4], min[4];	getTimestamp(hour, min);	ci::string chname = assign(params[2]);	/* Remove this bot from the internal list */	if (Config->Nick == "ChanStat" && chname == BotChannel && params[3].find("ChanStat") != std::string::npos)	{		std::map<ci::string, int>::iterator it = BotsChanCount.find(assign(params[3]));		if (it != BotsChanCount.end())		{			BotsChanCount.erase(it);		}	}	CSChannel *cs = getcs(chname);	if (!cs)		return EVENT_CONTINUE;	std::string nick, uhost;	GetNickHost(params[0], nick, uhost);	cs->Write("[%s:%s] %s kicked from %s by %s: %s", hour, min, params[3].c_str(), params[2].c_str(), nick.c_str(), params[4].c_str());	if (Config->Nick == params[3])	{		IRCdProto->Privmsg(StaffChannel, "(03KICK) Kicked from %s by %s (%s)", params[2].c_str(), nick.c_str(), uhost.c_str());		// We do -1 here because this channel isn't deleted yet		if (!PersonalBot)			IRCdProto->Privmsg(BotChannel, "!mychannels %d", GetTotalChannelCount() - 1);		delete cs;	}	else	{		cs->DelUser(params[3]);	}	return EVENT_CONTINUE;}EventReturn do_invite(const std::vector<std::string> &params){	std::string nick, host;	ci::string chname = assign(params[3]);	char hour[4], min[4];	GetNickHost(params[0], nick, host);	getTimestamp(hour, min);	char *channel = const_cast<char *>(params[3].c_str());	if (IsWhiteList(chname))	{		IRCdProto->Privmsg(StaffChannel, "(03JOINS) Joined whitelisted channel %s because I was invited by %s (%s)",			params[3].c_str(), nick.c_str(), params[0].c_str());		IRCdProto->Join(params[3]);		return EVENT_CONTINUE;	}	else if (IsIgnored(params[0]))	{		IRCdProto->Privmsg(BotChannel, "(04IGNORES) Ignored invite to %s from ignored host %s", params[3].c_str(), params[0].c_str());		return EVENT_CONTINUE;	}	else if (Config->Nick != "ChanStat" && !PersonalBot)	{		IRCdProto->Privmsg(BotChannel, "(04INVITES) Denied invite to %s from %s because I'm not the hub", params[3].c_str(), params[0].c_str());		IRCdProto->Notice(nick, "You must invite the hub bot, /invite ChanStat %s", params[3].c_str());		return EVENT_CONTINUE;	}	else if (IsBlacklisted(params[3]))	{		IRCdProto->Privmsg(BotChannel, "(04BLACKLIST) Denied invite from %s because %s is blacklisted", params[0].c_str(), params[3].c_str());		IRCdProto->Notice(nick, "Your channel is blacklisted. Join #ChanStat for more information");		return EVENT_CONTINUE;	}	else if (strchr(channel, '/'))	{		IRCdProto->Notice(nick, "Sorry, channel names may not have a '/' in them.");		IRCdProto->Privmsg(StaffChannel, "(04INVITES) Denied invite from %s because of invalid channel name %s", params[0].c_str(), channel);		return EVENT_CONTINUE;	}	else if (PersonalBot)	{		if (!CanUsePersonalCommands(params[0]))		{			IRCdProto->Notice(nick, "This is a private bot. To invite ChanStat to your channel use /invite ChanStat %s", params[3].c_str());			IRCdProto->Privmsg(BotChannel, "(\00304INVITES\003) Denied invite from %s to %s because they aren't on the allowed hosts list.", params[0].c_str(), params[3].c_str());			return EVENT_STOP;		}	}	IRCdProto->Privmsg(StaffChannel, "(03INVITES) %s invited me to %s", params[0].c_str(), params[3].c_str());	AddCounter(params[0], INVITETHROTTLE);	unsigned numchan, minnumchan = MaxChannels;	ci::string botnick, minbotnick = "ChanStat-01";	if (!PersonalBot)	{		IRCdProto->Notice(nick, "Please wait while I search for an open bot...");		for (std::map<ci::string, int>::iterator it = BotsChanCount.begin(); it != BotsChanCount.end(); ++it)		{			botnick = it->first;			numchan = it->second;			if (numchan < minnumchan)			{				minbotnick = botnick;				minnumchan = numchan;			}		}	}	if (GetTotalChannelCount() < minnumchan)	{		IRCdProto->Join(params[3]);		greetchannel = assign(params[3]);		invitedby = nick;		return EVENT_CONTINUE;	}	else if (PersonalBot)	{		IRCdProto->Privmsg(StaffChannel, "(\00304INVITES\003) Denied invite from %s to %s because I am full", params[0].c_str(), params[3].c_str());		IRCdProto->Notice(nick, "This bot is currently full, for assistance join #ChanStat.");		return EVENT_CONTINUE;	}	if (minnumchan < MaxChannels)		IRCdProto->Privmsg(BotChannel, "!dojoin %s %s %s", minbotnick.c_str(), params[3].c_str(), nick.c_str());	else	{		if (BotsChanCount.empty())		{			IRCdProto->Privmsg(StaffChannel, "(\00304INVITES\003) Denied invite from %s to %s because we were busy", params[0].c_str(), params[3].c_str());			IRCdProto->Notice(nick, "ChanStat is currently busy, please try again later.");			IRCdProto->Privmsg(BotChannel, "!channels");		}		else		{			IRCdProto->Privmsg(StaffChannel, "(\00304INVITES\003) Denied invite from %s to %s because we are full", params[0].c_str(), params[3].c_str());			IRCdProto->Notice(nick, "ChanStat is currently full, for assistance join #ChanStat.");		}	}	return EVENT_CONTINUE;}EventReturn do_chmodeL(const std::vector<std::string> &params){	IRCdProto->Privmsg(StaffChannel, "(\00307JOINS\003) I was linked from %s to %s", params[4].c_str(), params[18].c_str());	CSChannel *cs = findcs(params[4]);	if (cs)	{		delete cs;		IRCdProto->Privmsg(StaffChannel, "(\00303REMOVED\003) Removed %s from internal tracking", params[4].c_str());	}		return EVENT_CONTINUE;}EventReturn do_swiftirc_faillink(const std::vector<std::string> &params){	IRCdProto->Privmsg(StaffChannel, "(\00304JOINS\003) %s", params[3].c_str());	std::vector<std::string> splitbuf;	std::string msg = params[3];	SplitBuffer(msg, splitbuf);	CSChannel *cs;	if (!splitbuf.empty() && (cs = findcs(splitbuf[0])))	{		delete cs;		IRCdProto->Privmsg(StaffChannel, "(\00303REMOVED\003) Removed %s from internal tracking", splitbuf[0].c_str());	}	return EVENT_CONTINUE;}EventReturn do_failjoin(const std::vector<std::string> &params){	IRCdProto->Privmsg(StaffChannel, "(4JOINS) Could not join %s (%s)", params[3].c_str(), params[4].c_str());	if (!invitedby.empty())		IRCdProto->Notice(invitedby, "(4ERROR) Unable to join \2%s\2 (%s)", params[3].c_str(), params[4].c_str());	invitedby.clear();	CSChannel *cs = findcs(params[3]);	if (cs)	{		IRCdProto->Privmsg(StaffChannel, "(\00303REMOVED\003) Removed %s from internal tracking", cs->name.c_str());		delete cs;	}	return EVENT_CONTINUE;}EventReturn do_failjoin2(const std::vector<std::string> &params){	IRCdProto->Privmsg(StaffChannel, "(4JOINS) Could not join, (%s)", params[3].c_str());	if (!invitedby.empty())		IRCdProto->Notice(invitedby, "(4ERROR) Unable to join (%s)", params[3].c_str());	invitedby.clear();	CSChannel *cs = findcs(params[3]);	if (cs)	{		IRCdProto->Privmsg(StaffChannel, "(\00303REMOVED\003) Removed %s from internal tracking", cs->name.c_str());		delete cs;	}	return EVENT_CONTINUE;}EventReturn do_whois(const std::vector<std::string> &params){	std::string buf;	std::stringstream ss(params[4]);	int i = 0;	ci::string cibuf;	while (std::getline(ss, buf, ' '))	{		if (!buf.empty() && (buf[0] == '!' || buf[0] == '~' || buf[0] == '&' || buf[0] == '@' || buf[0] == '%' || buf[0] == '+'))			buf.erase(buf.begin());		if (!buf.empty())		{			cibuf = assign(buf);			getcs(cibuf);			++i;		}	}	IRCdProto->Privmsg(BotChannel, "(07Loading) Successfully found %i channels", i);	return EVENT_CONTINUE;}EventReturn do_getmynick(const std::vector<std::string> &params){	std::vector<std::string> tok;	std::string nick, host, buf;	if (params.size() <= 3)	{		IRCdProto->Privmsg(StaffChannel, "!!!WARNING!!! got invalid do_getmynick");		return EVENT_CONTINUE;	}	buf = params[3];	SplitBuffer(buf, tok);	if (tok.size() <= Raw001Pos)	{			IRCdProto->Privmsg(StaffChannel, "!!!WARNING!!! RAW001POSITION is out of range (%d <= %d)", tok.size(), Raw001Pos);		// RAW001POSITION doens't exist.. too big		return EVENT_CONTINUE;	}	GetNickHost(tok[Raw001Pos], nick, host);	ircnick = nick;	if (!ircnick.empty() && Config->Nick != ircnick.c_str())	{		IRCdProto->Privmsg(StaffChannel, "(04WARNING) I have detected that I am %s, I should be %s", ircnick.c_str(), Config->Nick.c_str());		// Attempt to nick back to the proper nick, if this works it will catch it in do_nick		IRCSocket->Write("NICK %s", Config->Nick.c_str());	}	else	{		ircnick.clear();	}	return EVENT_CONTINUE;}EventReturn do_topic(const std::vector<std::string> &params){	CSChannel *cs;	char hour[4], min[4];	ci::string chname = assign(params[2]);	if (!(cs = getcs(chname)))		return EVENT_CONTINUE;	getTimestamp(hour, min);	cs->Write("[%s:%s] Topic changed on %s by %s: %s", hour, min, params[2].c_str(), params[0].c_str(), params[3].c_str());	return EVENT_CONTINUE;}EventReturn do_324mode(const std::vector<std::string> &params){	if (!AudMode.empty() && params.size() > 4 && params[4].find(AudMode) != std::string::npos)	{		/* Retarded channel, they are +u */		IRCdProto->Privmsg(StaffChannel, "(07WARNING) Channel %s has been detected as +u", params[3].c_str());		if (Config->Nick == "ChanStat")		{			Blacklist *b = new Blacklist;			b->nick = Config->Nick;			b->reason = "Automated blacklist for channel being +u";			time_t t = time(NULL);			b->date = t;			b->expires = t + 604800; //+u channels expire in a week			Blacklists.insert(std::make_pair(assign(params[3]), b));			IRCdProto->Privmsg(StaffChannel, "(04BLACKLIST) Automatically blacklisted channel %s for +u", params[3].c_str());			IRCdProto->Part(params[3]);		}		else		{			IRCdProto->Privmsg(BotChannel, "!blacklist add %s +d7 Automated blacklist for +u", params[3].c_str());		}	}	return EVENT_CONTINUE;}static void LoadDatabase(){	FILE *fd;	char filebuf[200];	std::vector<std::string> buf;	Blacklist *b;	Ignore *i;	std::string bbuf;	std::string dbname = "./data/" + Config->Nick + ".db";	if (!(fd = fopen(dbname.c_str(), "r")))		return;	while (!feof(fd))	{		memset(&filebuf, '\0', sizeof(filebuf));		fgets(filebuf, sizeof(filebuf), fd);		if (!filebuf || !*filebuf)			continue;				bbuf = filebuf;		while (bbuf[bbuf.length() - 1] == 10)			bbuf.erase(bbuf.length() - 1);		SplitBuffer(bbuf, buf);		if (buf.empty())			continue;		if (buf[0] == "channel")		{			ci::string chname = assign(buf[1]);			getcs(chname);		}		else if (buf[0] == "blacklist" && Config->Nick == "ChanStat")		{			b = new Blacklist;			b->nick = buf[2];			b->date = atol(buf[3].c_str());			b->expires = atol(buf[4].c_str());			b->reason = assign(buf[5]);			Blacklists.insert(std::make_pair(assign(buf[1]), b));		}		else if (buf[0] == "ignore")		{			i = new Ignore;			i->nick = buf[2];			i->date = atol(buf[3].c_str());			i->expires = atol(buf[4].c_str());			i->reason = assign(buf[5]);			IgnoredHosts.insert(std::make_pair(assign(buf[1]), i));		}		else if (buf[0] == "hostallow")		{			HostAllow *h = new HostAllow;			h->host = buf[1];			h->creator = buf[2];			HostAllows.push_back(h);		}	}	fclose(fd);}static void SaveDatabase(){	FILE *fd;	char filebuf[200];	std::string dbname = "./data/" + Config->Nick + ".db";	if (!(fd = fopen(dbname.c_str(), "w")))		return;	for (std::map<ci::string, CSChannel *>::iterator cit = Channels.begin(); cit != Channels.end(); ++cit)	{		CSChannel *c = cit->second;		snprintf(filebuf, sizeof(filebuf), "channel %s\n", c->name.c_str());		fputs(filebuf, fd);		/* We have no use for this, it is for the uptime.php page */		if (!c->users.empty())		{			snprintf(filebuf, sizeof(filebuf), "users %s %d\n", c->name.c_str(), c->users.size());			fputs(filebuf, fd);		}	}	for (std::map<ci::string, Ignore *>::iterator iit = IgnoredHosts.begin(); iit != IgnoredHosts.end(); ++iit)	{		Ignore *i = iit->second;		snprintf(filebuf, sizeof(filebuf), "ignore %s %s %ld %ld :%s\n", iit->first.c_str(), i->nick.c_str(), i->date, i->expires, i->reason.c_str());		fputs(filebuf, fd);	}	for (std::map<ci::string, Blacklist *>::iterator bit = Blacklists.begin(); bit != Blacklists.end(); ++bit)	{		Blacklist *b = bit->second;		snprintf(filebuf, sizeof(filebuf), "blacklist %s %s %ld %ld :%s\n", bit->first.c_str(), b->nick.c_str(), b->date, b->expires, b->reason.c_str());		fputs(filebuf, fd);	}	for (std::vector<HostAllow *>::iterator hostallow = HostAllows.begin(); hostallow != HostAllows.end(); ++hostallow)	{		HostAllow *h = *hostallow;		snprintf(filebuf, sizeof(filebuf), "hostallow %s %s\n", h->host.c_str(), h->creator.c_str());		fputs(filebuf, fd);	}	fclose(fd);}class DatabaseTimer : public Timer{ public:	DatabaseTimer() : Timer(300, time(NULL), true) { }	void Tick(time_t)	{		SaveDatabase();		/* We call getTimestamp here to cycle the logs if necessary (aka, this is a		 * VERY idle bot...)		 */		char hour[4], min[4];		getTimestamp(hour, min);	}} _DatabaseTimer;class ChanStat : public Module{ public:	ChanStat()	{		OnConfigRead();		this->AddCommand(new ChanStatNotice());		this->AddCommand(new ChanStatMessage());		this->AddCommand(new ChanStatMapNotice());		this->AddCommand(new ChanStatMapMessage());		this->AddCommand(new ChanStatChannel());		this->AddCommand(new ChanStatMyChannels());		this->AddCommand(new ChanStatDoJoin());		this->AddCommand(new ChanStatPart());		this->AddCommand(new ChanStatBlacklist());		this->AddCommand(new ChanStatIgnore());		this->AddCommand(new ChanStatBotinfo());		this->AddCommand(new ChanStatChancount());		this->AddCommand(new ChanStatBLPart());		this->AddCommand(new ChanStatAMSG());		this->AddCommand(new ChanStatNick());		this->AddCommand(new ChanStatID());		this->AddCommand(new ChanStatQuit());		if (PersonalBot)		{			this->AddCommand(new ChanStatHostManage());		}		this->AddMessage("PRIVMSG", do_privmsg);		this->AddMessage("MODE", do_mode);		this->AddMessage("JOIN", do_join);		this->AddMessage("PART", do_part);		this->AddMessage("NICK", do_nick);		this->AddMessage("QUIT", do_quit);		this->AddMessage("353", do_names);		this->AddMessage("KICK", do_kick);		this->AddMessage("INVITE", do_invite);		this->AddMessage("TOPIC", do_topic);		this->AddMessage("470", do_chmodeL);		this->AddMessage("538", do_swiftirc_faillink);		this->AddMessage("474", do_failjoin);		this->AddMessage("475", do_failjoin);		this->AddMessage("473", do_failjoin);		this->AddMessage("471", do_failjoin);		this->AddMessage("489", do_failjoin);		this->AddMessage("519", do_failjoin2);		this->AddMessage("520", do_failjoin2);		this->AddMessage("319", do_whois);		this->AddMessage("001", do_getmynick);		this->AddMessage("324", do_324mode);		Event e[] = { E_OnConnect, E_OnConfigRead };		this->AddEvent(e, 2);		LoadDatabase();		loadtime = time(NULL);	}	~ChanStat()	{		SaveDatabase();		for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); )		{			CSChannel *cs = it->second;			++it;			delete cs;		}		Channels.clear();		BotsChanCount.clear();		for (std::map<ci::string, Blacklist *>::iterator blklist = Blacklists.begin(); blklist != Blacklists.end(); ++blklist)		{			delete blklist->second;		}		Blacklists.clear();		for (std::map<ci::string, Throttle *>::iterator throttle = Throttles.begin(); throttle != Throttles.end(); ++throttle)		{			delete throttle->second;		}		Throttles.clear();		for (std::map<ci::string, Ignore *>::iterator ignore = IgnoredHosts.begin(); ignore != IgnoredHosts.end(); ++ignore)		{			delete ignore->second;		}		IgnoredHosts.clear();		for (std::vector<HostAllow *>::iterator hostallow = HostAllows.begin(); hostallow != HostAllows.end(); ++hostallow)		{			delete *hostallow;		}		HostAllows.clear();	}	void OnConnect(std::string &Server, std::string &BindIP, std::string &Pass, int Port, bool)	{		IRCdProto->Privmsg("NickServ", "IDENTIFY %s", NSPW.c_str());		if (!ircnick.empty())			IRCSocket->Write("MODE %s -x+pB", ircnick.c_str());		else			IRCSocket->Write("MODE %s -x+pB", Config->Nick.c_str());		for (unsigned i = 0; i < sizeof(WhiteList); ++i)		{			if (WhiteList[i].empty())				break;			/* Personal bots do not join #chanstat */			if (PersonalBot && WhiteList[i] == "#chanstat")				continue;			IRCdProto->Join(WhiteList[i]);		}		for (std::map<ci::string, CSChannel *>::iterator it = Channels.begin(); it != Channels.end(); ++it)		{			IRCdProto->Join(it->second->name);		}	}	void OnConfigRead()	{		ConfigReader Values[] = {			{"LogDir", &LogDir, DT_STRING},			{"NetsplitLine", &NSLine, DT_STRING},			{"NetsplitNotLine", &NSNLine, DT_STRING},			{"NickServPassword", &NSPW, DT_STRING},			{"AppendLink", &AppendLink, DT_STRING},			{"MaxChannels", &MaxChannels, DT_UNSIGNED},			{"AuditoriumMode", &AudMode, DT_STRING},			{"Raw001Position", &Raw001Pos, DT_UNSIGNED},			{"PersonalBot", &PersonalBot, DT_BOOL}		};					ReadConfig(Values, 9);	}};MODULE_INIT(ChanStat)