3660 lines
97 KiB
C++
3660 lines
97 KiB
C++
/**
|
|
* chanfix.cc
|
|
*
|
|
* Copyright (C) 2003 Reed Loden <reed@reedloden.com>
|
|
* Matthias Crauwels <ultimate_@wol.be>
|
|
* Jimmy Lipham <music0m@alltel.net>
|
|
* Neil Spierling <sirvulcan@gmail.com>
|
|
*
|
|
* Automatically and manually fix opless and taken over channels
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*
|
|
* $Id: chanfix.cc,v 1.16 2010/03/04 04:24:11 hidden1 Exp $
|
|
*/
|
|
|
|
#include <csignal>
|
|
#include <cstdarg>
|
|
#include <ctime>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <new>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include "dbHandle.h"
|
|
|
|
#include "gnuworld_config.h"
|
|
#include "client.h"
|
|
#include "EConfig.h"
|
|
#include "Network.h"
|
|
#include "server.h"
|
|
#include "StringTokenizer.h"
|
|
|
|
#include "chanfix.h"
|
|
#include "defs.h"
|
|
#include "chanfix_misc.h"
|
|
#include "chanfixCommands.h"
|
|
#include "responses.h"
|
|
#include "sqlChanOp.h"
|
|
#include "sqlChannel.h"
|
|
#include "sqlcfUser.h"
|
|
|
|
#ifdef CHANFIX_HAVE_BOOST_THREAD
|
|
#include <boost/thread/thread.hpp>
|
|
#endif /* CHANFIX_HAVE_BOOST_THREAD */
|
|
|
|
RCSTAG("$Id: chanfix.cc,v 1.16 2010/03/04 04:24:11 hidden1 Exp $");
|
|
|
|
namespace gnuworld
|
|
{
|
|
|
|
namespace cf
|
|
{
|
|
|
|
short currentDay;
|
|
|
|
/*
|
|
* Exported function used by moduleLoader to gain an
|
|
* instance of this module.
|
|
*/
|
|
|
|
extern "C"
|
|
{
|
|
xClient* _gnuwinit(const std::string& args)
|
|
{
|
|
return new chanfix( args );
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This constructor calls the base class constructor. The xClient
|
|
* constructor will open the configuration file given and retrieve
|
|
* basic client info (nick/user/host/etc).
|
|
* Any additional processing must be done here.
|
|
*/
|
|
chanfix::chanfix( const std::string& configFileName )
|
|
: xClient( configFileName )
|
|
{
|
|
/* Load the config file */
|
|
readConfigFile(configFileName);
|
|
|
|
/* Initial state */
|
|
currentState = INIT;
|
|
|
|
/* Initial finding of the channel service */
|
|
chanServLinked = false;
|
|
|
|
/* Initial update status */
|
|
updateInProgress = false;
|
|
|
|
std::string dbString = "host=" + sqlHost + " dbname=" + sqlDB
|
|
+ " port=" + sqlPort + " user=" + sqlcfUsername + " password=" + sqlPass;
|
|
|
|
theManager = sqlManager::getInstance(dbString);
|
|
|
|
/* Open our logfiles for writing */
|
|
adminLog.open(adminLogFile.c_str(), std::ios::out | std::ios::app);
|
|
debugLog.open(debugLogFile.c_str(), std::ios::out | std::ios::app);
|
|
|
|
/* Register the commands we want to use */
|
|
RegisterCommand(new ADDFLAGCommand(this, "ADDFLAG",
|
|
"<username> <flag>",
|
|
3,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new ADDHOSTCommand(this, "ADDHOST",
|
|
"<username> <nick!user@host>",
|
|
3,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new ADDNOTECommand(this, "ADDNOTE",
|
|
"<#channel> <reason>",
|
|
3,
|
|
sqlcfUser::F_COMMENT
|
|
));
|
|
RegisterCommand(new ADDUSERCommand(this, "ADDUSER",
|
|
"<username> [host]",
|
|
2,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new ALERTCommand(this, "ALERT",
|
|
"<#channel> <reason>",
|
|
3,
|
|
sqlcfUser::F_COMMENT
|
|
));
|
|
RegisterCommand(new BLOCKCommand(this, "BLOCK",
|
|
"<#channel> <reason>",
|
|
3,
|
|
sqlcfUser::F_BLOCK
|
|
));
|
|
RegisterCommand(new CANFIXCommand(this, "CANFIX",
|
|
"<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new CHANFIXCommand(this, "CHANFIX",
|
|
"<#channel> [override] [contact]",
|
|
2,
|
|
0 /* Set to 0 to allow all opers to access it, otherwise this should be sqlcfUser::F_CHANFIX */
|
|
));
|
|
RegisterCommand(new CHECKCommand(this, "CHECK",
|
|
"<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
#ifdef CHANFIX_DEBUG
|
|
RegisterCommand(new DEBUGCommand(this, "DEBUG",
|
|
"<ROTATE|UPDATE>",
|
|
2,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
#endif /* CHANFIX_DEBUG */
|
|
RegisterCommand(new DELFLAGCommand(this, "DELFLAG",
|
|
"<username> <flag>",
|
|
3,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new DELHOSTCommand(this, "DELHOST",
|
|
"<username> <nick!user@host>",
|
|
3,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new DELNOTECommand(this, "DELNOTE",
|
|
"<#channel> <note_id>",
|
|
3,
|
|
sqlcfUser::F_COMMENT
|
|
));
|
|
RegisterCommand(new DELUSERCommand(this, "DELUSER",
|
|
"<username>",
|
|
2,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
RegisterCommand(new HELPCommand(this, "HELP",
|
|
"[command]",
|
|
1,
|
|
0
|
|
));
|
|
RegisterCommand(new HISTORYCommand(this, "HISTORY",
|
|
"<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new INFOCommand(this, "INFO",
|
|
"<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new INVITECommand(this, "INVITE",
|
|
"",
|
|
1,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new LASTCOMCommand(this, "LASTCOM",
|
|
"[amount of commands] [days from]",
|
|
1,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new LISTBLOCKEDCommand(this, "LISTBLOCKED",
|
|
"",
|
|
1,
|
|
sqlcfUser::F_BLOCK
|
|
));
|
|
RegisterCommand(new LISTHOSTSCommand(this, "LISTHOSTS",
|
|
"[username]",
|
|
1,
|
|
sqlcfUser::F_LOGGEDIN
|
|
));
|
|
RegisterCommand(new OPLISTCommand(this, "OPLIST",
|
|
"<#channel> [-all] [-days]",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new OPNICKSCommand(this, "OPNICKS",
|
|
"<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
#ifdef ENABLE_QUOTE
|
|
RegisterCommand(new QUOTECommand(this, "QUOTE",
|
|
"<text>",
|
|
2,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
#endif /* ENABLE_QUOTE */
|
|
RegisterCommand(new REHASHCommand(this, "REHASH",
|
|
"",
|
|
1,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new RELOADCommand(this, "RELOAD",
|
|
"[reason]",
|
|
1,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new REQUESTOPCommand(this, "REQUESTOP",
|
|
isAllowingTopOpAlert() ? "<#channel> [contact]" : "<#channel>",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new SCORECommand(this, "SCORE",
|
|
"<#channel> [account|=nick]",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new SCORECommand(this, "CSCORE",
|
|
"<#channel> [account|=nick]",
|
|
2,
|
|
0
|
|
));
|
|
RegisterCommand(new SAYCommand(this, "SAY",
|
|
"<#channel> <text>",
|
|
3,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new SETCommand(this, "SET",
|
|
"<option> <value>",
|
|
3,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
#ifdef USERADMIN
|
|
RegisterCommand(new SETGROUPCommand(this, "SETGROUP",
|
|
"<username> <group>",
|
|
3,
|
|
sqlcfUser::F_USERMANAGER
|
|
));
|
|
#endif
|
|
RegisterCommand(new SIMULATECommand(this, "SIMULATE",
|
|
"<#channel> <auto/manual>",
|
|
3,
|
|
0 /* Set to 0 to allow all opers to access it, otherwise this should be sqlcfUser::F_CHANFIX */
|
|
));
|
|
RegisterCommand(new SHUTDOWNCommand(this, "SHUTDOWN",
|
|
"[reason]",
|
|
1,
|
|
sqlcfUser::F_OWNER
|
|
));
|
|
RegisterCommand(new STATUSCommand(this, "STATUS",
|
|
"",
|
|
1,
|
|
0
|
|
));
|
|
#ifdef USERADMIN
|
|
RegisterCommand(new SUSPENDCommand(this, "SUSPEND",
|
|
"<username>",
|
|
2,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
#endif
|
|
RegisterCommand(new UNALERTCommand(this, "UNALERT",
|
|
"<#channel>",
|
|
2,
|
|
sqlcfUser::F_COMMENT
|
|
));
|
|
RegisterCommand(new UNBLOCKCommand(this, "UNBLOCK",
|
|
"<#channel>",
|
|
2,
|
|
sqlcfUser::F_BLOCK
|
|
));
|
|
#ifdef USERADMIN
|
|
RegisterCommand(new UNSUSPENDCommand(this, "UNSUSPEND",
|
|
"<username>",
|
|
2,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
#endif
|
|
RegisterCommand(new USERSCORESCommand(this, "USERSCORES",
|
|
"<account>",
|
|
2,
|
|
sqlcfUser::F_LOGGEDIN
|
|
));
|
|
RegisterCommand(new USETCommand(this, "USET",
|
|
"[username] <option> <value>",
|
|
3,
|
|
sqlcfUser::F_LOGGEDIN
|
|
));
|
|
RegisterCommand(new WHOFLAGCommand(this, "WHOFLAG",
|
|
"<flag>",
|
|
2,
|
|
sqlcfUser::F_LOGGEDIN
|
|
));
|
|
#ifdef USERADMIN
|
|
RegisterCommand(new WHOGROUPCommand(this, "WHOGROUP",
|
|
"[group]",
|
|
1,
|
|
sqlcfUser::F_USERMANAGER | sqlcfUser::F_SERVERADMIN
|
|
));
|
|
#endif
|
|
RegisterCommand(new WHOISCommand(this, "WHOIS",
|
|
"<username|=nick|*> [-modif]",
|
|
2,
|
|
sqlcfUser::F_LOGGEDIN
|
|
));
|
|
|
|
/* Our global DB handler */
|
|
localDBHandle = theManager->getConnection();
|
|
|
|
/* Set our current day. */
|
|
setCurrentDay();
|
|
|
|
/* Precache our chanOps. */
|
|
precacheChanOps();
|
|
|
|
/* Precache our channels. */
|
|
precacheChannels();
|
|
|
|
/* Precache our staff. */
|
|
precacheUsers();
|
|
|
|
/* Load help messages. */
|
|
loadHelpTable();
|
|
|
|
/* Load our translation tables. */
|
|
loadTranslationTable();
|
|
|
|
/* Set up our timer. */
|
|
theTimer = new Timer();
|
|
|
|
}
|
|
|
|
chanfix::~chanfix()
|
|
{
|
|
}
|
|
|
|
/* Load (or reload) the configuration file */
|
|
void chanfix::readConfigFile(const std::string& configFileName)
|
|
{
|
|
chanfixConfig = new (std::nothrow) EConfig(configFileName);
|
|
assert(chanfixConfig != 0);
|
|
|
|
/* Config file processing */
|
|
consoleChan = chanfixConfig->Require("consoleChan")->second ;
|
|
consoleChanModes = chanfixConfig->Require("consoleChanModes")->second ;
|
|
sendConsoleNotices = atob(chanfixConfig->Require("sendConsoleNotices")->second) ;
|
|
joinChanModes = chanfixConfig->Require("joinChanModes")->second ;
|
|
enableAutoFix = atob(chanfixConfig->Require("enableAutoFix")->second) ;
|
|
enableChanFix = atob(chanfixConfig->Require("enableChanFix")->second) ;
|
|
enableChannelBlocking = atob(chanfixConfig->Require("enableChannelBlocking")->second) ;
|
|
autoFixNotice = atob(chanfixConfig->Require("autoFixNotice")->second) ;
|
|
manualFixNotice = atob(chanfixConfig->Require("manualFixNotice")->second) ;
|
|
joinChannels = atob(chanfixConfig->Require("joinChannels")->second) ;
|
|
stopAutoFixOnOp = atob(chanfixConfig->Require("stopAutoFixOnOp")->second) ;
|
|
stopChanFixOnOp = atob(chanfixConfig->Require("stopChanFixOnOp")->second) ;
|
|
allowTopOpFix = atob(chanfixConfig->Require("allowTopOpFix")->second) ;
|
|
allowTopOpAlert = atob(chanfixConfig->Require("allowTopOpAlert")->second) ;
|
|
topOpPercent = atoi((chanfixConfig->Require("topOpPercent")->second).c_str()) ;
|
|
minFixScore = atoi((chanfixConfig->Require("minFixScore")->second).c_str()) ;
|
|
minCanFixScore = atoi((chanfixConfig->Require("minCanFixScore")->second).c_str()) ;
|
|
minRequestOpTime = atoi((chanfixConfig->Require("minRequestOpTime")->second).c_str()) ;
|
|
version = atoi((chanfixConfig->Require("version")->second).c_str()) ;
|
|
useBurstToFix = atob(chanfixConfig->Require("useBurstToFix")->second) ;
|
|
numServers = atoi((chanfixConfig->Require("numServers")->second).c_str()) ;
|
|
minServersPresent = atoi((chanfixConfig->Require("minServersPresent")->second).c_str()) ;
|
|
chanServName = chanfixConfig->Require("chanServName")->second ;
|
|
numTopScores = atoi((chanfixConfig->Require("numTopScores")->second).c_str()) ;
|
|
minClients = atoi((chanfixConfig->Require("minClients")->second).c_str()) ;
|
|
clientNeedsIdent = atob(chanfixConfig->Require("clientNeedsIdent")->second) ;
|
|
connectCheckFreq = atoi((chanfixConfig->Require("connectCheckFreq")->second).c_str()) ;
|
|
adminLogFile = chanfixConfig->Require("adminLogFile")->second ;
|
|
debugLogFile = chanfixConfig->Require("debugLogFile")->second ;
|
|
|
|
/* Set up the channels that chanfix should join */
|
|
EConfig::const_iterator ptr = chanfixConfig->Find("joinChan");
|
|
while (ptr != chanfixConfig->end() && ptr->first == "joinChan") {
|
|
chansToJoin.push_back(ptr->second);
|
|
++ptr;
|
|
}
|
|
|
|
/* Make sure that the consoleChan is not a joinChan */
|
|
for (joinChansType::iterator ptr = chansToJoin.begin();
|
|
ptr != chansToJoin.end(); ++ptr) {
|
|
if (!strcasecmp((*ptr).c_str(), consoleChan.c_str())) {
|
|
/* Found, remove it from the list of chansToJoin */
|
|
chansToJoin.erase(ptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Database processing */
|
|
sqlHost = chanfixConfig->Require("sqlHost")->second;
|
|
sqlPort = chanfixConfig->Require("sqlPort")->second;
|
|
sqlDB = chanfixConfig->Require("sqlDB")->second;
|
|
sqlcfUsername = chanfixConfig->Require("sqlcfUser")->second;
|
|
sqlPass = chanfixConfig->Require("sqlPass")->second;
|
|
|
|
elog << "chanfix::readConfigFile> Configuration loaded!"
|
|
<< std::endl;
|
|
}
|
|
|
|
|
|
/* Register a new command */
|
|
bool chanfix::RegisterCommand( Command *theCommand )
|
|
{
|
|
UnRegisterCommand( theCommand->getName() ) ;
|
|
return commandMap.insert( commandMapType::value_type(theCommand->getName(), theCommand)).second;
|
|
}
|
|
|
|
/* UnRegister a command */
|
|
bool chanfix::UnRegisterCommand( const std::string& commName )
|
|
{
|
|
commandMapType::iterator ptr = commandMap.find( commName ) ;
|
|
if ( ptr == commandMap.end() ) {
|
|
return false ;
|
|
}
|
|
delete ptr->second ;
|
|
commandMap.erase( ptr ) ;
|
|
return true ;
|
|
}
|
|
|
|
/* OnShutdown */
|
|
void chanfix::OnShutdown(const std::string& reason)
|
|
{
|
|
MyUplink->UnloadClient(this, reason);
|
|
}
|
|
|
|
/* OnSignal */
|
|
void chanfix::OnSignal(int sig)
|
|
{
|
|
if (sig == SIGHUP) {
|
|
/* Close/reopen log files */
|
|
logAdminMessage("---------- End of log file. Rotating... ----------");
|
|
if (adminLog.is_open())
|
|
adminLog.close();
|
|
|
|
adminLog.open(adminLogFile.c_str(), std::ios::out | std::ios::app);
|
|
}
|
|
|
|
xClient::OnSignal(sig);
|
|
}
|
|
|
|
/* OnAttach */
|
|
void chanfix::OnAttach()
|
|
{
|
|
/**
|
|
* Don't send END_OF_BURST (EB) so we can stay in burst mode
|
|
* indefinitely in order to be able to do TS-1.
|
|
*/
|
|
if (useBurstToFix && version < 12) /* Not needed for u2.10.12+ */
|
|
MyUplink->setSendEB(false);
|
|
|
|
/**
|
|
* Set our uplink as our main server for our commands.
|
|
*/
|
|
for (commandMapType::iterator ptr = commandMap.begin(); ptr != commandMap.end(); ++ptr) {
|
|
ptr->second->setServer(MyUplink);
|
|
}
|
|
|
|
/**
|
|
* Register for global network events
|
|
*/
|
|
MyUplink->RegisterEvent( EVT_ACCOUNT, this );
|
|
MyUplink->RegisterEvent( EVT_KILL, this );
|
|
MyUplink->RegisterEvent( EVT_QUIT, this );
|
|
MyUplink->RegisterEvent( EVT_BURST_CMPLT, this );
|
|
MyUplink->RegisterEvent( EVT_NETJOIN, this );
|
|
MyUplink->RegisterEvent( EVT_NETBREAK, this );
|
|
MyUplink->RegisterEvent( EVT_NICK, this );
|
|
if (!MyUplink->RegisterEvent( EVT_SERVERMODE , this ))
|
|
elog << "FAILED to register chanfix.EVT_SERVERMODE" << std::endl;
|
|
else elog << "SUCCESSFULLY registered EVT_SERVERMODE" << std::endl;
|
|
|
|
/**
|
|
* Register for all channel events
|
|
*/
|
|
MyUplink->RegisterChannelEvent( xServer::CHANNEL_ALL, this );
|
|
|
|
xClient::OnAttach() ;
|
|
}
|
|
|
|
/**
|
|
* Thread class only used for score updates, updates on reload and shutdown are not threaded
|
|
*/
|
|
#ifdef CHANFIX_HAVE_BOOST_THREAD
|
|
class ClassUpdateDB {
|
|
public:
|
|
ClassUpdateDB(chanfix& cf) : cf_(cf) {}
|
|
void operator()() {
|
|
cf_.updateDB();
|
|
return;
|
|
}
|
|
|
|
private:
|
|
chanfix& cf_;
|
|
};
|
|
#endif /* CHANFIX_HAVE_BOOST_THREAD */
|
|
|
|
/* OnTimer */
|
|
void chanfix::OnTimer(const xServer::timerID& theTimer, void*)
|
|
{
|
|
time_t theTime;
|
|
if (theTimer == tidGivePoints) {
|
|
/* 5 min timer, loop through channels and give all ops a point! */
|
|
giveAllOpsPoints();
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + POINTS_UPDATE_TIME;
|
|
tidGivePoints = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
else if (theTimer == tidAutoFix) {
|
|
autoFix();
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + CHECK_CHANS_TIME;
|
|
tidAutoFix = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
else if (theTimer == tidCheckDB) {
|
|
/*checkDBConnection();*/
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + connectCheckFreq;
|
|
tidCheckDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
else if (theTimer == tidFixQ) {
|
|
processQueue();
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + PROCESS_QUEUE_TIME;
|
|
tidFixQ = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
setNextFix(currentTime() + PROCESS_QUEUE_TIME);
|
|
}
|
|
else if (theTimer == tidRotateDB) {
|
|
/* Clean-up the database if its 00 GMT */
|
|
rotateDB();
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + getSecsTilMidnight();
|
|
tidRotateDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
else if (theTimer == tidUpdateDB) {
|
|
/* Prepare to synchronize the database in a thread */
|
|
prepareUpdate(true);
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + SQL_UPDATE_TIME;
|
|
tidUpdateDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
else if (theTimer == tidTempBlocks) {
|
|
/* Remove expired temporary blocks */
|
|
expireTempBlocks();
|
|
|
|
/* Refresh Timer */
|
|
theTime = time(NULL) + TEMPBLOCKS_CHECK_TIME;
|
|
tidTempBlocks = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
}
|
|
|
|
/* OnDetach */
|
|
void chanfix::OnDetach( const std::string& reason )
|
|
{
|
|
/* Save our database */
|
|
prepareUpdate(false);
|
|
|
|
/* Delete our config */
|
|
delete chanfixConfig; chanfixConfig = 0;
|
|
|
|
/* Delete our timer */
|
|
delete theTimer; theTimer = 0;
|
|
|
|
/* Delete our commands */
|
|
for (commandMapType::iterator ptr = commandMap.begin();
|
|
ptr != commandMap.end(); ++ptr) {
|
|
delete ptr->second;
|
|
}
|
|
commandMap.clear();
|
|
|
|
/* Delete our sqlManager */
|
|
theManager->removeManager();
|
|
|
|
/* Finally, close our logs */
|
|
if (adminLog.is_open())
|
|
adminLog.close();
|
|
if (debugLog.is_open())
|
|
debugLog.close();
|
|
|
|
xClient::OnDetach( reason ) ;
|
|
}
|
|
|
|
/* OnConnect */
|
|
void chanfix::OnConnect()
|
|
{
|
|
/* If we have just reloaded, we won't be in BURST. */
|
|
if (currentState == INIT) {
|
|
findChannelService();
|
|
changeState(RUN);
|
|
}
|
|
|
|
xClient::OnConnect() ;
|
|
}
|
|
|
|
/* OnDisconnect */
|
|
void chanfix::OnDisconnect()
|
|
{
|
|
xClient::OnDisconnect() ;
|
|
}
|
|
|
|
void chanfix::OnPrivateMessage( iClient* theClient,
|
|
const std::string& Message, bool)
|
|
{
|
|
sqlcfUser* theUser = isAuthed(theClient->getAccount());
|
|
|
|
if (currentState == BURST) {
|
|
if (theUser)
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::no_commands_during_burst,
|
|
std::string("Sorry, I do not accept commands during a burst.")).c_str());
|
|
else
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::no_commands_during_burst_noper,
|
|
std::string("Sorry, I'm too busy at the moment. Please try again soon.")).c_str());
|
|
return;
|
|
}
|
|
|
|
StringTokenizer st(Message);
|
|
if (st.empty())
|
|
return;
|
|
|
|
const std::string Command = string_upper(st[0]);
|
|
|
|
if (!theClient->isOper()) {
|
|
if (Command != "CANFIX" && Command != "HELP" && Command != "REQUESTOP") {
|
|
if (!theUser || theUser->getNeedOper())
|
|
return;
|
|
}
|
|
}
|
|
|
|
commandMapType::iterator commHandler = commandMap.find(Command);
|
|
if (commHandler == commandMap.end()) {
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::unknown_command,
|
|
std::string("Unknown command.")).c_str());
|
|
return;
|
|
}
|
|
|
|
if (st.size() < commHandler->second->getNumParams()) {
|
|
commHandler->second->Usage(theClient);
|
|
return;
|
|
}
|
|
|
|
/* If you change this code, remember to change it in HELPCommand.cc */
|
|
sqlcfUser::flagType requiredFlags = commHandler->second->getRequiredFlags();
|
|
if (requiredFlags) {
|
|
if (!theUser) {
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::need_to_auth,
|
|
std::string("You need to authenticate to use this command.")).c_str());
|
|
return;
|
|
}
|
|
|
|
if (theUser->getIsSuspended()) {
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::access_suspended,
|
|
std::string("Your access to this service is suspended.")).c_str());
|
|
return;
|
|
}
|
|
#ifdef ENABLE_HOST_CHECKING
|
|
if (!theUser->matchHost(theClient->getRealNickUserHost().c_str())) {
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::host_not_matching,
|
|
std::string("Your current host does not match any registered hosts for your username.")).c_str());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (requiredFlags != sqlcfUser::F_LOGGEDIN &&
|
|
!theUser->getFlag(requiredFlags)) {
|
|
if (getFlagChar(requiredFlags) != ' ')
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::requires_flag,
|
|
std::string("This command requires flag '%c'.")).c_str(),
|
|
getFlagChar(requiredFlags));
|
|
else
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::requires_flags,
|
|
std::string("This command requires one of these flags: \"%s\".")).c_str(),
|
|
getFlagsString(requiredFlags).c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (theUser) {
|
|
theUser->setLastSeen(currentTime());
|
|
theUser->commit(localDBHandle);
|
|
}
|
|
|
|
commHandler->second->Exec(theClient, theUser ? theUser : NULL, Message);
|
|
|
|
xClient::OnPrivateMessage(theClient, Message);
|
|
}
|
|
|
|
void chanfix::OnCTCP( iClient* theClient, const std::string& CTCP,
|
|
const std::string& Message, bool Secure )
|
|
{
|
|
StringTokenizer st(CTCP);
|
|
|
|
if (st.empty()) return;
|
|
|
|
const std::string Command = string_upper(st[0]);
|
|
|
|
if (Command == "DCC") {
|
|
DoCTCP(theClient, CTCP, "REJECT");
|
|
} else if (Command == "PING" || Command == "ECHO") {
|
|
DoCTCP(theClient, CTCP, Message);
|
|
} else if (Command == "VERSION") {
|
|
DoCTCP(theClient, CTCP, "Evilnet Development -- mod.openchanfix v2.0 [compiled "__DATE__" "__TIME__"]");
|
|
} else if (Command == "WHODUNIT?") {
|
|
DoCTCP(theClient, CTCP, "reed, ULtimaTe_, Compy, SiRVulcaN");
|
|
} else if (Command == "SUBVERSION") {
|
|
DoCTCP(theClient, CTCP, rcsId);
|
|
} else if (Command == "GENDER") {
|
|
DoCTCP(theClient, CTCP, "Gender pending vote, but for now I'll be whatever you want!");
|
|
}
|
|
|
|
xClient::OnCTCP(theClient, CTCP, Message, Secure);
|
|
}
|
|
|
|
// Burst any channels.
|
|
void chanfix::BurstChannels()
|
|
{
|
|
xClient::BurstChannels();
|
|
|
|
MyUplink->JoinChannel(this, consoleChan, consoleChanModes);
|
|
|
|
for (joinChansType::size_type i = 0; i < chansToJoin.size(); i++) {
|
|
/* Burst our channels */
|
|
MyUplink->JoinChannel(this, chansToJoin[i], joinChanModes);
|
|
}
|
|
|
|
/* Start our timers */
|
|
startTimers();
|
|
}
|
|
|
|
/* OnChannelEvent */
|
|
void chanfix::OnChannelEvent( const channelEventType& whichEvent,
|
|
Channel* theChan,
|
|
void* data1, void* data2, void* data3, void* data4 )
|
|
{
|
|
iClient* theClient = 0;
|
|
iServer* theServer = 0;
|
|
|
|
/* If we are not running, we don't want to be giving points. */
|
|
if (currentState != RUN) return;
|
|
|
|
/* If this channel is too small, don't worry about it. */
|
|
if (theChan->size() < minClients) return;
|
|
|
|
switch( whichEvent )
|
|
{
|
|
case EVT_JOIN:
|
|
{
|
|
/* If this is the operChan or supportChan, op opers on join */
|
|
theClient = static_cast< iClient* >( data1 );
|
|
if (theClient->isOper()) {
|
|
for (joinChansType::iterator ptr = chansToJoin.begin();
|
|
ptr != chansToJoin.end(); ++ptr) {
|
|
if (!strcasecmp((*ptr).c_str(), theChan->getName().c_str())) {
|
|
/* Found, op the user */
|
|
Op(theChan, theClient);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
}
|
|
case EVT_KICK:
|
|
case EVT_PART:
|
|
{
|
|
theClient = static_cast< iClient* >( data1 );
|
|
lostOp(theChan->getName(), theClient, NULL);
|
|
break ;
|
|
}
|
|
case EVT_SERVERMODE:
|
|
{
|
|
theServer = static_cast< iServer* >( data1 );
|
|
if (theServer && theServer != MyUplink->getUplink() && theServer->isService()) {
|
|
if (!isTempBlocked(theChan->getName()))
|
|
tempBlockList.insert(tempBlockType::value_type(theChan->getName(), currentTime()));
|
|
}
|
|
elog << "chanfix: GOT SERVER MODE EVENT!" << std::endl;
|
|
break ;
|
|
}
|
|
default:
|
|
break ;
|
|
}
|
|
|
|
xClient::OnChannelEvent( whichEvent, theChan,
|
|
data1, data2, data3, data4 ) ;
|
|
}
|
|
|
|
void chanfix::OnChannelModeO( Channel* theChan, ChannelUser* theUser,
|
|
const xServer::opVectorType& theTargets)
|
|
{
|
|
/* if (currentState != RUN) return; */
|
|
|
|
if (theUser) {
|
|
iServer* theServer = Network->findServer(theUser->getClient()->getIntYY());
|
|
if (theServer && theServer != MyUplink->getUplink() && theServer->isService()) {
|
|
if (!isTempBlocked(theChan->getName()))
|
|
tempBlockList.insert(tempBlockType::value_type(theChan->getName(), currentTime()));
|
|
}
|
|
}
|
|
|
|
|
|
if (theChan->size() < minClients)
|
|
return;
|
|
|
|
if (!canScoreChan(theChan))
|
|
return;
|
|
|
|
for (xServer::opVectorType::const_iterator ptr = theTargets.begin();
|
|
ptr != theTargets.end(); ++ptr) {
|
|
ChannelUser* tmpUser = ptr->second;
|
|
|
|
if (tmpUser->getClient()->getAccount() == "")
|
|
continue;
|
|
|
|
bool polarity = ptr->first;
|
|
if (polarity) {
|
|
// Someone is opped
|
|
gotOpped(theChan, tmpUser->getClient());
|
|
|
|
// If the channel is being fixed and the op is done by a user,
|
|
// cancel the fix, as there is an awake op
|
|
if (theUser)
|
|
stopFixingChan(theChan, false);
|
|
} else {
|
|
// Someone is deopped
|
|
lostOp(theChan->getName(), tmpUser->getClient(), NULL);
|
|
} // if
|
|
} // for
|
|
}
|
|
|
|
bool chanfix::logLastComMessage(iClient* theClient, const std::string& Message)
|
|
{
|
|
StringTokenizer st(Message);
|
|
if (st.empty())
|
|
return false;
|
|
|
|
const std::string Command = string_upper(st[0]);
|
|
|
|
const std::string username = (theClient->getAccount() != "") ? theClient->getAccount() : theClient->getNickName();
|
|
|
|
std::string log;
|
|
dbHandle* cacheCon = getLocalDBHandle();
|
|
|
|
static const char *Main = "INSERT into comlog (ts,user_name,command) VALUES (date_part('epoch', CURRENT_TIMESTAMP)::int,'";
|
|
|
|
std::stringstream insertString;
|
|
insertString << Main
|
|
<< username
|
|
<< "','"
|
|
<< Command
|
|
<< " ";
|
|
|
|
unsigned int pos = 1;
|
|
while (pos < st.size()) {
|
|
insertString << st[pos]
|
|
<< " ";
|
|
pos++;
|
|
}
|
|
|
|
insertString << "')"
|
|
<< std::ends;
|
|
|
|
if (!cacheCon->Exec(insertString.str())) {
|
|
elog << "sqlChannel::Insert> Something went wrong: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
}
|
|
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* msgTopOps */
|
|
bool chanfix::msgTopOps(Channel* netChan) {
|
|
|
|
int opCount = 0;
|
|
sqlChanOp* curOp = 0;
|
|
bool inChan = true;
|
|
|
|
chanOpsType myOps = getMyOps(netChan);
|
|
|
|
for (chanOpsType::iterator opPtr = myOps.begin();
|
|
opPtr != myOps.end() && (opCount < OPCOUNT); opPtr++) {
|
|
|
|
curOp = *opPtr;
|
|
opCount++;
|
|
|
|
inChan = accountIsOnChan(netChan->getName(), curOp->getAccount());
|
|
if (!inChan) {
|
|
typedef std::list < iClient* > accountListType;
|
|
const iClient* curAccount;
|
|
accountListType accountList;
|
|
authMapType::iterator cAuth = authMap.find(curOp->getAccount());
|
|
if (cAuth == authMap.end())
|
|
break;
|
|
|
|
accountList = cAuth->second;
|
|
|
|
if (accountList.empty())
|
|
break;
|
|
|
|
for (accountListType::iterator cptr = accountList.begin(); (cptr != accountList.end()); cptr++)
|
|
{
|
|
curAccount = *cptr;
|
|
|
|
MyUplink->Write("%s P %s :%s channel modes have been removed to allow you to return. Please return so that I can op you during the channel fixing process\r\n",
|
|
getCharYYXXX().c_str(), curAccount->getCharYYXXX().c_str(), netChan->getName().c_str());
|
|
|
|
MyUplink->Write("%s P %s :\002DO NOT REPLY TO THIS MESSAGE\002\r\n",
|
|
getCharYYXXX().c_str(), curAccount->getCharYYXXX().c_str());
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* OnEvent */
|
|
void chanfix::OnEvent( const eventType& whichEvent,
|
|
void* data1, void* data2, void* data3, void* data4 )
|
|
{
|
|
switch(whichEvent)
|
|
{
|
|
case EVT_ACCOUNT:
|
|
{
|
|
iClient* tmpUser = static_cast< iClient* >( data1 ) ;
|
|
authMapType::iterator ptr = authMap.find(tmpUser->getAccount());
|
|
if (ptr != authMap.end()) {
|
|
/* This user is already logged in, just add the iClient
|
|
* (if they aren't in the list already)
|
|
*/
|
|
authMapType::mapped_type::iterator listPtr = std::find(ptr->second.begin(),
|
|
ptr->second.end(),
|
|
tmpUser);
|
|
if (listPtr == ptr->second.end())
|
|
ptr->second.push_back(tmpUser);
|
|
} else {
|
|
/* Add the map entry AND the initial list entry */
|
|
authMapType::mapped_type theList;
|
|
theList.push_back(tmpUser);
|
|
authMap.insert(authMapType::value_type(tmpUser->getAccount(), theList));
|
|
}
|
|
break;
|
|
}
|
|
case EVT_BURST_CMPLT:
|
|
{
|
|
if (currentState != SPLIT)
|
|
changeState(RUN);
|
|
break;
|
|
}
|
|
case EVT_NETJOIN:
|
|
case EVT_NETBREAK:
|
|
{
|
|
iServer* theServer = static_cast< iServer* >(data1);
|
|
checkChannelServiceLink(theServer, whichEvent);
|
|
checkNetwork();
|
|
break;
|
|
}
|
|
case EVT_KILL:
|
|
case EVT_QUIT:
|
|
{
|
|
iClient* theClient = static_cast< iClient* >
|
|
((whichEvent == EVT_QUIT) ?
|
|
data1 :
|
|
data2 );
|
|
|
|
clientOpsType* myOps = findMyOps(theClient);
|
|
if (!myOps->empty()) {
|
|
for (clientOpsType::iterator ptr = myOps->begin(); ptr != myOps->end();)
|
|
lostOp(*ptr++, theClient, myOps);
|
|
} else {
|
|
delete myOps;
|
|
}
|
|
|
|
if (!theClient->getAccount().empty())
|
|
{
|
|
/* Now we need to remove this iClient from the auth map */
|
|
authMapType::iterator ptr = authMap.find(theClient->getAccount());
|
|
|
|
if (ptr != authMap.end()) {
|
|
ptr->second.erase(std::find(ptr->second.begin(), ptr->second.end(), theClient));
|
|
|
|
/* If the list is empty, remove the map entry */
|
|
if (ptr->second.empty())
|
|
authMap.erase(theClient->getAccount());
|
|
}
|
|
}
|
|
//Cleanup
|
|
theClient->removeCustomData(this);
|
|
break;
|
|
}
|
|
case EVT_NICK:
|
|
{
|
|
iClient* tmpUser = static_cast< iClient* >( data1 );
|
|
if (tmpUser->isModeR()) {
|
|
/* Check to see if this current account is already mapped,
|
|
* Then check to see if this nick is already there
|
|
*/
|
|
authMapType::iterator ptr = authMap.find(tmpUser->getAccount());
|
|
if (ptr != authMap.end()) {
|
|
/* Already have an entry, just add the iClient if not there already */
|
|
authMapType::mapped_type::iterator listPtr = std::find(ptr->second.begin(),
|
|
ptr->second.end(),
|
|
tmpUser);
|
|
if (listPtr == ptr->second.end())
|
|
ptr->second.push_back(tmpUser);
|
|
|
|
} else {
|
|
authMapType::mapped_type theList;
|
|
theList.push_back(tmpUser);
|
|
authMap.insert(authMapType::value_type(tmpUser->getAccount(), theList));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
xClient::OnEvent( whichEvent, data1, data2, data3, data4 ) ;
|
|
}
|
|
|
|
bool chanfix::serverNotice( Channel* theChannel, const char* format, ... )
|
|
{
|
|
char buf[ 1024 ] = { 0 } ;
|
|
va_list _list ;
|
|
va_start( _list, format ) ;
|
|
vsnprintf( buf, 1024, format, _list ) ;
|
|
va_end( _list ) ;
|
|
|
|
std::stringstream s;
|
|
s << MyUplink->getCharYY()
|
|
<< " O "
|
|
<< theChannel->getName()
|
|
<< " :"
|
|
<< buf
|
|
<< std::ends;
|
|
|
|
Write( s );
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Send a notice to a channel from the server.
|
|
* TODO: Move this method to xServer.
|
|
*/
|
|
bool chanfix::serverNotice( Channel* theChannel, const std::string& Message)
|
|
{
|
|
std::stringstream s;
|
|
s << MyUplink->getCharYY()
|
|
<< " O "
|
|
<< theChannel->getName()
|
|
<< " :"
|
|
<< Message
|
|
<< std::ends;
|
|
|
|
Write( s );
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::logAdminMessage(const char* format, ... )
|
|
{
|
|
char buf[ 1024 ] = { 0 } ;
|
|
va_list _list ;
|
|
va_start( _list, format ) ;
|
|
vsnprintf( buf, 1024, format, _list ) ;
|
|
va_end( _list ) ;
|
|
|
|
// Try and locate the relay channel.
|
|
if (sendConsoleNotices) {
|
|
Channel* tmpChan = Network->findChannel(consoleChan);
|
|
if (!tmpChan)
|
|
return false;
|
|
|
|
std::string message = std::string( "[" ) + nickName + "] " + buf ;
|
|
serverNotice(tmpChan, message);
|
|
}
|
|
|
|
/* Everything sent here is also logged to a file on disk */
|
|
if (adminLog.is_open()) {
|
|
std::string theLog = std::string( "[" ) + tsToDateTime(currentTime(), true) + "] " + buf ;
|
|
adminLog << theLog << std::endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool chanfix::logDebugMessage(const char* format, ... )
|
|
{
|
|
char buf[ 1024 ] = { 0 } ;
|
|
va_list _list ;
|
|
va_start( _list, format ) ;
|
|
vsnprintf( buf, 1024, format, _list ) ;
|
|
va_end( _list ) ;
|
|
|
|
// Try and locate the relay channel.
|
|
Channel* tmpChan = Network->findChannel(consoleChan);
|
|
if (!tmpChan)
|
|
return false;
|
|
|
|
std::string message = std::string( "[" ) + nickName + "] " + buf ;
|
|
serverNotice(tmpChan, message);
|
|
|
|
/* Everything sent here is also logged to a file on disk */
|
|
if (debugLog.is_open()) {
|
|
std::string theLog = std::string( "[" ) + tsToDateTime(currentTime(), true) + "] " + buf ;
|
|
debugLog << theLog << std::endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void chanfix::SendTo(iClient* theClient, const std::string& theMessage)
|
|
{
|
|
sqlcfUser* theUser = isAuthed(theClient->getAccount());
|
|
|
|
if (theUser && !theUser->getUseNotice())
|
|
Message(theClient, theMessage);
|
|
else
|
|
Notice(theClient, theMessage);
|
|
}
|
|
|
|
char *chanfix::convertToAscTime(time_t NOW)
|
|
{
|
|
time_t *tNow = &NOW;
|
|
struct tm* Now = gmtime(tNow);
|
|
char *ATime = asctime(Now);
|
|
ATime[strlen(ATime)-1] = '\0';
|
|
return ATime;
|
|
}
|
|
|
|
void chanfix::SendFmtTo(iClient* theClient, const std::string& theMessage)
|
|
{
|
|
char buffer[512] = { 0 };
|
|
char *b = buffer ;
|
|
const char *m = 0 ;
|
|
|
|
sqlcfUser* theUser = isAuthed(theClient->getAccount());
|
|
|
|
for (m = theMessage.c_str(); *m != 0; m++) {
|
|
if (*m == '\n' || *m == '\r') {
|
|
*b='\0';
|
|
if (theUser) {
|
|
if (!theUser->getUseNotice())
|
|
MyUplink->Write("%s P %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
theClient->getCharYYXXX().c_str(),
|
|
buffer);
|
|
else
|
|
MyUplink->Write("%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
theClient->getCharYYXXX().c_str(),
|
|
buffer);
|
|
}
|
|
b=buffer;
|
|
}
|
|
else if (b < buffer + 509)
|
|
*(b++)=*m;
|
|
}
|
|
|
|
*b='\0'; // What's this for? 'b' isn't used anymore.
|
|
|
|
if (theUser) {
|
|
if (!theUser->getUseNotice())
|
|
MyUplink->Write("%s P %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
theClient->getCharYYXXX().c_str(),
|
|
buffer);
|
|
else
|
|
MyUplink->Write("%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
theClient->getCharYYXXX().c_str(),
|
|
buffer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::SendTo(iClient* theClient, const char *Msg, ...)
|
|
{
|
|
char buffer[ 1024 ] = { 0 } ;
|
|
va_list list;
|
|
|
|
va_start( list, Msg ) ;
|
|
vsprintf( buffer, Msg, list ) ;
|
|
va_end( list ) ;
|
|
|
|
sqlcfUser* theUser = isAuthed(theClient->getAccount());
|
|
|
|
if (theUser && !theUser->getUseNotice())
|
|
Message(theClient, "%s", buffer);
|
|
else
|
|
Notice(theClient, "%s", buffer);
|
|
}
|
|
|
|
void chanfix::doSqlError(const std::string& theQuery, const std::string& theError)
|
|
{
|
|
/* First, log it to error out */
|
|
elog << "SQL> Whilst executing: "
|
|
<< theQuery
|
|
<< std::endl;
|
|
elog << "SQL> "
|
|
<< theError
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
void chanfix::precacheChanOps()
|
|
{
|
|
elog << "*** [chanfix::precacheChanOps] Precaching chanops." << std::endl;
|
|
|
|
/* Get a connection instance to our backend */
|
|
//dbHandle* cacheCon = theManager->getConnection();
|
|
|
|
/* Check for the backup table. If it exists, something went wrong. */
|
|
/* SELECT count(*) FROM pg_tables WHERE tablename = 'chanOpsBackup' */
|
|
/* SELECT chanOpsBackup FROM information_schema.tables */
|
|
if (!localDBHandle->Exec("SELECT count(*) FROM pg_tables WHERE tablename = 'chanOpsBackup'",true)) {
|
|
elog << "*** [chanfix::precacheChanOps]: Error checking for backup table presence: "
|
|
<< localDBHandle->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
if (localDBHandle->Tuples() && atoi(localDBHandle->GetValue(0, 0))) {
|
|
elog << "*** [chanfix::precacheChanOps]: Backup table still exists! "
|
|
<< "Something must have gone wrong on the last update. Exiting..."
|
|
<< std::endl;
|
|
::exit(0);
|
|
}
|
|
|
|
/* Retrieve the list of chanops */
|
|
std::stringstream theQuery;
|
|
std::string lastChan = "";
|
|
theQuery << "SELECT channel,account,last_seen_as,ts_firstopped,ts_lastopped,day0,day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13 FROM chanOps ORDER BY channel ASC, ts_firstopped ASC"
|
|
;
|
|
|
|
elog << "*** [chanfix::precacheChanOps]: Loading chanOps and their points ..."
|
|
<< std::endl;
|
|
|
|
if (localDBHandle->Exec(theQuery.str(),true)) {
|
|
for (unsigned int i = 0 ; i < localDBHandle->Tuples(); i++) {
|
|
sqlChanOp* newOp = new (std::nothrow) sqlChanOp(theManager);
|
|
assert( newOp != 0 ) ;
|
|
|
|
newOp->setAllMembers(localDBHandle, i);
|
|
if (newOp->getChannel().c_str() != lastChan.c_str()) {
|
|
lastChan = newOp->getChannel();
|
|
newOp->setIsOldestOp(true);
|
|
}
|
|
sqlChanOpsType::iterator ptr = sqlChanOps.find(newOp->getChannel());
|
|
if (ptr != sqlChanOps.end()) {
|
|
ptr->second.insert(sqlChanOpsType::mapped_type::value_type(newOp->getAccount(),newOp));
|
|
} else {
|
|
sqlChanOpsType::mapped_type theMap;
|
|
theMap.insert(sqlChanOpsType::mapped_type::value_type(newOp->getAccount(), newOp));
|
|
sqlChanOps.insert(sqlChanOpsType::value_type(newOp->getChannel(), theMap));
|
|
}
|
|
}
|
|
} else {
|
|
elog << "*** [chanfix::precacheChanOps] Error executing query: "
|
|
<< localDBHandle->ErrorMessage()
|
|
<< std::endl;
|
|
::exit(0);
|
|
}
|
|
|
|
|
|
elog << "*** [chanfix::precacheChanOps]: Done. Loaded "
|
|
<< sqlChanOps.size()
|
|
<< " chanops."
|
|
<< std::endl;
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::precacheChannels()
|
|
{
|
|
elog << "*** [chanfix::precacheChannels] Precaching channels." << std::endl;
|
|
|
|
/* Get a connection instance to our backend */
|
|
dbHandle* cacheCon = localDBHandle;
|
|
|
|
/* Retrieve the list of channels */
|
|
std::stringstream theQuery;
|
|
theQuery << "SELECT id, channel, flags FROM channels"
|
|
;
|
|
|
|
elog << "*** [chanfix::precacheChannels]: Loading channels ..."
|
|
<< std::endl;
|
|
|
|
if (cacheCon->Exec(theQuery.str(),true)) {
|
|
for (unsigned int i = 0; i < cacheCon->Tuples(); i++) {
|
|
sqlChannel* newChan = new (std::nothrow) sqlChannel(theManager);
|
|
assert( newChan != 0 ) ;
|
|
|
|
newChan->setAllMembers(cacheCon, i);
|
|
sqlChanCache.insert(sqlChannelCacheType::value_type(newChan->getChannel(), newChan));
|
|
}
|
|
} else {
|
|
elog << "*** [chanfix::precacheChannels] Error executing query: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
::exit(0);
|
|
}
|
|
|
|
elog << "*** [chanfix::precacheChannels]: Done. Loaded "
|
|
<< sqlChanCache.size()
|
|
<< " channels."
|
|
<< std::endl;
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::precacheUsers()
|
|
{
|
|
elog << "*** [chanfix::precacheUsers] Precaching users." << std::endl;
|
|
|
|
/* Get a connection instance to our backend */
|
|
dbHandle* cacheCon = localDBHandle;
|
|
|
|
if (!cacheCon)
|
|
elog << "*** [chanfix::precacheUsers]: Error getting a new SQL connection through the manager."
|
|
<< std::endl;
|
|
|
|
/* Retrieve the list of chanops */
|
|
std::stringstream myQuery;
|
|
|
|
|
|
myQuery << "SELECT id, user_name, created, last_seen, last_updated, last_updated_by, language_id, faction, flags, issuspended, usenotice, needoper "
|
|
<< "FROM users"
|
|
;
|
|
|
|
|
|
if (cacheCon->Exec(myQuery.str(),true)) {
|
|
|
|
/* First we need to clear the current cache. */
|
|
for (usersMapType::iterator itr = usersMap.begin();
|
|
itr != usersMap.end(); ++itr) {
|
|
delete itr->second;
|
|
}
|
|
usersMap.clear();
|
|
|
|
for (unsigned int i = 0; i < cacheCon->Tuples(); ++i) {
|
|
sqlcfUser *newUser = new sqlcfUser(theManager);
|
|
assert(newUser != 0);
|
|
|
|
newUser->setAllMembers(cacheCon, i);
|
|
usersMap.insert(usersMapType::value_type(newUser->getUserName(), newUser));
|
|
}
|
|
|
|
|
|
} else {
|
|
elog << "*** [chanfix::precacheUsers] Error executing query: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
::exit(0);
|
|
}
|
|
|
|
/* Load up the host cache */
|
|
for (usersMapType::iterator itr = usersMap.begin();
|
|
itr != usersMap.end(); ++itr) {
|
|
itr->second->loadHostList(cacheCon);
|
|
|
|
}
|
|
|
|
elog << "chanfix::precacheUsers> Loaded "
|
|
<< usersMap.size()
|
|
<< " users."
|
|
<< std::endl;
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::changeState(STATE newState)
|
|
{
|
|
if (currentState == newState) return;
|
|
|
|
/* Start our timer. */
|
|
Timer stateTimer;
|
|
stateTimer.Start();
|
|
|
|
/* First, do what we need to exit our current state */
|
|
switch( currentState ) {
|
|
case BURST:
|
|
{
|
|
elog << "chanfix::changeState> Exiting state BURST"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case RUN:
|
|
{
|
|
elog << "chanfix::changeState> Exiting state RUN"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case SPLIT:
|
|
{
|
|
elog << "chanfix::changeState> Exiting state SPLIT"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case INIT:
|
|
{
|
|
elog << "chanfix::changeState> Exiting state INIT"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
currentState = newState;
|
|
|
|
switch( currentState ) {
|
|
case BURST:
|
|
{
|
|
elog << "chanfix::changeState> Entering state BURST"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case RUN:
|
|
{
|
|
elog << "chanfix::changeState> Entering state RUN"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case SPLIT:
|
|
{
|
|
elog << "chanfix::changeState> Entering state SPLIT"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
case INIT:
|
|
{
|
|
elog << "chanfix::changeState> Entering state INIT"
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
elog << "Changed state in: "
|
|
<< stateTimer.stopTimeMS()
|
|
<< "ms"
|
|
<< std::endl;
|
|
}
|
|
|
|
sqlChanOp* chanfix::findChanOp(const std::string& channel, const std::string& account)
|
|
{
|
|
sqlChanOpsType::iterator ptr = sqlChanOps.find(channel);
|
|
if (ptr != sqlChanOps.end()) {
|
|
sqlChanOpsType::mapped_type::iterator chanOp = ptr->second.find(account);
|
|
if (chanOp != ptr->second.end())
|
|
/* elog << "chanfix::findChanOp> DEBUG: We've got a winner: "
|
|
* << chanOp->second->getAccount() << " on " << chanOp->second->getChannel() << "!!" << std::endl;
|
|
*/
|
|
return chanOp->second ;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sqlChanOp* chanfix::newChanOp(const std::string& channel, const std::string& account)
|
|
{
|
|
sqlChanOp* newOp = new (std::nothrow) sqlChanOp(theManager);
|
|
assert( newOp != 0 ) ;
|
|
|
|
/* elog << "chanfix::newChanOp> DEBUG: Added new operator: " << account << " on " << channel << "!!" << std::endl; */
|
|
|
|
newOp->setChannel(channel);
|
|
newOp->setAccount(account);
|
|
newOp->setTimeFirstOpped(currentTime());
|
|
newOp->setTimeLastOpped(currentTime());
|
|
|
|
sqlChanOpsType::iterator ptr = sqlChanOps.find(channel);
|
|
if (ptr != sqlChanOps.end()) {
|
|
ptr->second.insert(sqlChanOpsType::mapped_type::value_type(account, newOp));
|
|
} else {
|
|
sqlChanOpsType::mapped_type theMap;
|
|
theMap.insert(sqlChanOpsType::mapped_type::value_type(account, newOp));
|
|
sqlChanOps.insert(sqlChanOpsType::value_type(channel, theMap));
|
|
}
|
|
|
|
return newOp;
|
|
}
|
|
|
|
sqlChanOp* chanfix::findChanOp(Channel* theChan, iClient* theClient)
|
|
{
|
|
return findChanOp(theChan->getName(), theClient->getAccount());
|
|
}
|
|
|
|
sqlChanOp* chanfix::newChanOp(Channel* theChan, iClient* theClient)
|
|
{
|
|
return newChanOp(theChan->getName(), theClient->getAccount());
|
|
}
|
|
|
|
chanfix::chanOpsType chanfix::getMyOps(const std::string &channel, bool newScore)
|
|
{
|
|
chanOpsType myOps;
|
|
time_t oldestTS = currentTime();
|
|
|
|
sqlChanOpsType::iterator ptr = sqlChanOps.find(channel);
|
|
|
|
if (ptr != sqlChanOps.end()) {
|
|
|
|
#ifdef ENABLE_NEWSCORES
|
|
for (sqlChanOpsType::mapped_type::iterator chOp = ptr->second.begin();
|
|
chOp != ptr->second.end(); chOp++) {
|
|
if (chOp->second->getTimeFirstOpped() < oldestTS)
|
|
oldestTS = chOp->second->getTimeFirstOpped();
|
|
}
|
|
#endif
|
|
|
|
for (sqlChanOpsType::mapped_type::iterator chanOp = ptr->second.begin();
|
|
chanOp != ptr->second.end(); chanOp++) {
|
|
/* elog << "chanfix::findChanOp> DEBUG: We've got a winner: "
|
|
* << chanOp->second->getAccount() << " on " << chanOp->second->getChannel() << "!!" << std::endl;
|
|
*/
|
|
#ifdef ENABLE_NEWSCORES
|
|
if (chanOp->second->getTimeFirstOpped() == oldestTS) {
|
|
chanOp->second->setIsOldestOp(true);
|
|
} else {
|
|
chanOp->second->setIsOldestOp(false);
|
|
}
|
|
chanOp->second->setBonus(getNewScore(chanOp->second,oldestTS));
|
|
#endif
|
|
|
|
myOps.push_back(chanOp->second);
|
|
}
|
|
}
|
|
#ifdef ENABLE_NEWSCORES
|
|
if (newScore)
|
|
myOps.sort(compare_points_new);
|
|
else
|
|
myOps.sort(compare_points);
|
|
#else
|
|
myOps.sort(compare_points);
|
|
#endif
|
|
|
|
return myOps;
|
|
}
|
|
|
|
chanfix::chanOpsType chanfix::getMyOps(Channel* theChan)
|
|
{
|
|
return getMyOps(theChan->getName(), true);
|
|
}
|
|
|
|
chanfix::chanOpsType chanfix::getMyOps(const std::string &channel) {
|
|
return getMyOps(channel, true);
|
|
}
|
|
|
|
size_t chanfix::countMyOps(const std::string& channel)
|
|
{
|
|
sqlChanOpsType::iterator ptr = sqlChanOps.find(channel);
|
|
if (ptr != sqlChanOps.end())
|
|
return ptr->second.size();
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t chanfix::countMyOps(Channel* theChan)
|
|
{
|
|
return countMyOps(theChan->getName());
|
|
}
|
|
|
|
const std::string chanfix::getHostList( sqlcfUser* User)
|
|
{
|
|
/* Get a connection instance to our backend */
|
|
dbHandle* cacheCon = localDBHandle;
|
|
|
|
/* Grab the user's host list */
|
|
static const char* queryHeader
|
|
= "SELECT host FROM hosts WHERE user_id = ";
|
|
|
|
std::stringstream theQuery;
|
|
theQuery << queryHeader
|
|
<< User->getID()
|
|
;
|
|
|
|
if (!cacheCon->Exec(theQuery.str(),true)) {
|
|
elog << "chanfix::getHostList> SQL Error: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return 0;
|
|
}
|
|
|
|
// SQL Query succeeded
|
|
std::stringstream hostlist;
|
|
for (unsigned int i = 0 ; i < cacheCon->Tuples(); i++)
|
|
{
|
|
if (!i)
|
|
hostlist << cacheCon->GetValue(i, 0);
|
|
else
|
|
hostlist << ", " << cacheCon->GetValue(i, 0);
|
|
}
|
|
if (hostlist.str() == "") hostlist << "None.";
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return hostlist.str();
|
|
}
|
|
|
|
const std::string escapeSQLChars(const std::string& theString)
|
|
{
|
|
std::string retMe ;
|
|
|
|
for( std::string::const_iterator ptr = theString.begin() ;
|
|
ptr != theString.end() ; ++ptr )
|
|
{
|
|
if( *ptr == '\'' )
|
|
{
|
|
retMe += "\\\047" ;
|
|
}
|
|
else if ( *ptr == '\\' )
|
|
{
|
|
retMe += "\\\134" ;
|
|
}
|
|
else
|
|
{
|
|
retMe += *ptr ;
|
|
}
|
|
}
|
|
return retMe ;
|
|
}
|
|
|
|
bool atob( std::string str )
|
|
{
|
|
str = string_lower(str);
|
|
if (str == "y" || str == "true" || str == "yes")
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void chanfix::givePoints(Channel* theChan, iClient* theClient)
|
|
{
|
|
//No points for unidented clients
|
|
if (clientNeedsIdent && !hasIdent(theClient))
|
|
return;
|
|
|
|
sqlChanOp* thisOp = findChanOp(theChan, theClient);
|
|
if (!thisOp) {
|
|
size_t numMyOps = countMyOps(theChan);
|
|
if ((numMyOps >= MAXOPCOUNT) || (!numMyOps && !chanServLinked))
|
|
return; /* No room for new ops or channel service not linked */
|
|
thisOp = newChanOp(theChan, theClient);
|
|
gotOpped(theChan, theClient);
|
|
}
|
|
|
|
thisOp->addPoint();
|
|
thisOp->setTimeLastOpped(currentTime()); //Update the time they were last opped
|
|
|
|
/* elog << "chanfix::givePoints> DEBUG: Gave " << thisOp->getAccount()
|
|
* << " on " << thisOp->getChannel() << " a point."
|
|
* << std::endl;
|
|
*/
|
|
}
|
|
|
|
void chanfix::gotOpped(Channel* thisChan, iClient* thisClient)
|
|
{
|
|
//No tracking for unidented clients
|
|
if (clientNeedsIdent && !hasIdent(thisClient))
|
|
return;
|
|
|
|
/* elog << "chanfix::gotOpped> DEBUG: " << thisClient->getAccount()
|
|
* << " got opped on " << thisChan->getName()
|
|
* << std::endl;
|
|
*/
|
|
sqlChanOp* thisOp = findChanOp(thisChan, thisClient);
|
|
if (!thisOp) {
|
|
size_t numMyOps = countMyOps(thisChan);
|
|
if ((numMyOps >= MAXOPCOUNT) || (!numMyOps && !chanServLinked))
|
|
return; /* No room for new ops or channel service not linked */
|
|
thisOp = newChanOp(thisChan, thisClient);
|
|
} else {
|
|
thisOp->setTimeLastOpped(currentTime());
|
|
}
|
|
|
|
thisOp->setLastSeenAs(thisClient->getRealNickUserHost());
|
|
|
|
clientOpsType* myOps = findMyOps(thisClient);
|
|
if (myOps != NULL && !myOps->empty()) {
|
|
clientOpsType::iterator ptr = myOps->find(thisChan->getName());
|
|
if (ptr != myOps->end()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
myOps->insert(clientOpsType::value_type(thisChan->getName()));
|
|
thisClient->removeCustomData(this);
|
|
thisClient->setCustomData(this, static_cast< void*>(myOps));
|
|
return;
|
|
}
|
|
|
|
void chanfix::lostOp(const std::string& channel, iClient* theClient, clientOpsType* myOps)
|
|
{
|
|
if (myOps == NULL)
|
|
myOps = findMyOps(theClient);
|
|
|
|
if (myOps->empty())
|
|
return;
|
|
|
|
clientOpsType::iterator ptr = myOps->find(channel);
|
|
if (ptr != myOps->end()) {
|
|
myOps->erase(ptr);
|
|
theClient->removeCustomData(this);
|
|
if (!myOps->empty())
|
|
theClient->setCustomData(this, static_cast< void*>(myOps));
|
|
}
|
|
}
|
|
|
|
bool chanfix::hasIdent(iClient* theClient)
|
|
{
|
|
std::string userName = theClient->getUserName();
|
|
if (userName[0] == '~')
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void chanfix::checkNetwork()
|
|
{
|
|
if (100 * Network->serverList_size() < numServers * minServersPresent) {
|
|
if (currentState != SPLIT) {
|
|
elog << "chanfix::checkNetwork> DEBUG: Not enough servers linked! Going to SPLIT-state" << std::endl;
|
|
changeState(SPLIT);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (currentState == SPLIT) {
|
|
elog << "chanfix::checkNetwork> DEBUG: Enough servers linked! Going to BURST-state" << std::endl;
|
|
changeState(BURST);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void chanfix::checkChannelServiceLink(iServer* theServer, const eventType& theEvent)
|
|
{
|
|
if (string_lower(theServer->getName()) == string_lower(chanServName)) {
|
|
if (theEvent == EVT_NETJOIN)
|
|
chanServLinked = true;
|
|
else if (theEvent == EVT_NETBREAK)
|
|
chanServLinked = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void chanfix::JoinChan(Channel* theChan)
|
|
{
|
|
MyUplink->JoinChannel(this, theChan->getName());
|
|
}
|
|
|
|
void chanfix::PartChan(Channel* theChan)
|
|
{
|
|
MyUplink->PartChannel(this, theChan->getName(), "");
|
|
}
|
|
|
|
/* Check for the channel service server to see if it is linked. */
|
|
void chanfix::findChannelService()
|
|
{
|
|
iServer* curServer = 0;
|
|
for (xNetwork::serverIterator ptr = Network->servers_begin();
|
|
ptr != Network->servers_end(); ptr++) {
|
|
curServer = ptr->second;
|
|
if ((curServer == NULL) || (Network->findFakeServer(curServer) != 0))
|
|
continue;
|
|
if (string_lower(curServer->getName()) == string_lower(chanServName)) {
|
|
chanServLinked = true;
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
const int chanfix::getLastFix(sqlChannel* theChan)
|
|
{
|
|
/* Get a connection instance to our backend */
|
|
dbHandle* cacheCon = localDBHandle;
|
|
|
|
/* Grab the user's host list */
|
|
static const char* queryHeader
|
|
= "SELECT ts,event FROM notes WHERE channelID = ";
|
|
|
|
std::stringstream theQuery;
|
|
theQuery << queryHeader
|
|
<< theChan->getID()
|
|
;
|
|
|
|
if (!cacheCon->Exec(theQuery.str(),true)) {
|
|
elog << "chanfix::getHostList> SQL Error: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return 0;
|
|
}
|
|
|
|
int max_ts = 0;
|
|
int ts = 0;
|
|
int event;
|
|
|
|
// SQL Query succeeded
|
|
std::stringstream notes;
|
|
for (unsigned int i = 0 ; i < cacheCon->Tuples(); i++)
|
|
{
|
|
ts = atoi(cacheCon->GetValue(i, 0));
|
|
event = atoi(cacheCon->GetValue(i, 1));
|
|
|
|
if ((event == sqlChannel::EV_CHANFIX) || (event == sqlChannel::EV_REQUESTOP)) {
|
|
if (ts > max_ts)
|
|
max_ts = ts;
|
|
}
|
|
}
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return max_ts;
|
|
}
|
|
|
|
void chanfix::autoFix()
|
|
{
|
|
/* If autofixing has been disabled, well, forget it. */
|
|
if (!enableAutoFix) {
|
|
//elog << "chanfix::autoFix> DEBUG: AutoFix not enabled." << std::endl;
|
|
return;
|
|
}
|
|
|
|
/* If there are too many servers split, don't autofix. */
|
|
if (currentState != RUN) {
|
|
//elog << "chanfix::autoFix> DEBUG: currentState != RUN" << std::endl;
|
|
return;
|
|
}
|
|
|
|
/* Start our timer. */
|
|
Timer autoFixTimer;
|
|
autoFixTimer.Start();
|
|
|
|
/* Now walk through all channels to find the opless ones. */
|
|
Channel* thisChan;
|
|
ChannelUser* curUser;
|
|
int numOpLess = 0;
|
|
tempBlockType::iterator tbPtr;
|
|
for (xNetwork::channelIterator ptr = Network->channels_begin(); ptr != Network->channels_end(); ptr++) {
|
|
thisChan = ptr->second;
|
|
bool opLess = true;
|
|
bool hasService = false;
|
|
|
|
// Do not autofix +R channels
|
|
if (thisChan->getMode(Channel::MODE_REG))
|
|
continue;
|
|
|
|
if (thisChan->size() >= minClients && !isBeingFixed(thisChan)) {
|
|
/* Don't autofix if the chan is temp blocked */
|
|
tbPtr = tempBlockList.find(thisChan->getName());
|
|
if (tbPtr != tempBlockList.end()) continue;
|
|
|
|
/* Loop through the channel list to check users for op and umode +k */
|
|
for (Channel::userIterator ptr = thisChan->userList_begin(); ptr != thisChan->userList_end(); ptr++) {
|
|
curUser = ptr->second;
|
|
if (curUser->getClient()->getMode(iClient::MODE_SERVICES)) {
|
|
hasService = true;
|
|
break;
|
|
}
|
|
if (curUser->isModeO())
|
|
opLess = false;
|
|
}
|
|
if (opLess && !hasService) {
|
|
chanOpsType myOps = getMyOps(thisChan);
|
|
if (myOps.empty())
|
|
continue;
|
|
|
|
sqlChannel* sqlChan = getChannelRecord(thisChan);
|
|
if (!sqlChan) sqlChan = newChannelRecord(thisChan);
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
if ((sqlChan->getMaxScore() >
|
|
static_cast<int>(static_cast<float>(FIX_MIN_ABS_SCORE_END)
|
|
* MAX_SCORE)) && !sqlChan->getFlag(sqlChannel::F_BLOCKED) &&
|
|
!isTempBlocked(thisChan->getName())) {
|
|
elog << "chanfix::autoFix> DEBUG: " << thisChan->getName() << " is opless, fixing." << std::endl;
|
|
autoFixQ.insert(fixQueueType::value_type(thisChan->getName(), currentTime()));
|
|
numOpLess++;
|
|
|
|
if (doJoinChannels() && shouldCJoin(sqlChan, true))
|
|
JoinChan(thisChan);
|
|
if (doAutoFixNotice() && shouldCJoin(sqlChan, true))
|
|
Message(thisChan, "Automatic channel fix in progress, please stand by.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//elog << "chanfix::autoFix> DEBUG: Found " << numOpLess << " of "
|
|
// << Network->channelList_size() << " channels in "
|
|
// << autoFixTimer.stopTimeMS() << " ms." << std::endl;
|
|
}
|
|
|
|
void chanfix::manualFix(Channel* thisChan)
|
|
{
|
|
/* If the channel doesn't exist (anymore), don't try to fix it. */
|
|
if (!thisChan) return;
|
|
|
|
elog << "chanfix::manualFix> DEBUG: Manual fix " << thisChan->getName() << "!" << std::endl;
|
|
|
|
if (useBurstToFix && thisChan->getCreationTime() > 1) {
|
|
if (version >= 12) /* temporary fix until GNUWorld is fixed */
|
|
MyUplink->setBursting(true);
|
|
|
|
xServer::modeVectorType modeVector;
|
|
if (thisChan->getMode(Channel::MODE_I))
|
|
modeVector.push_back(std::make_pair(false, Channel::MODE_I));
|
|
if (thisChan->getMode(Channel::MODE_K))
|
|
MyUplink->OnChannelModeK(thisChan, false, 0, std::string());
|
|
if (thisChan->getMode(Channel::MODE_L))
|
|
MyUplink->OnChannelModeL(thisChan, false, 0, 0);
|
|
if (thisChan->getMode(Channel::MODE_R))
|
|
modeVector.push_back(std::make_pair(false, Channel::MODE_R));
|
|
if (thisChan->getMode(Channel::MODE_D))
|
|
modeVector.push_back(std::make_pair(false, Channel::MODE_D));
|
|
/* Due to a bug in .11, we need to set at least one mode. */
|
|
if (version < 12) {
|
|
if (!thisChan->getMode(Channel::MODE_N))
|
|
modeVector.push_back(std::make_pair(true, Channel::MODE_N));
|
|
if (!thisChan->getMode(Channel::MODE_T))
|
|
modeVector.push_back(std::make_pair(true, Channel::MODE_T));
|
|
}
|
|
if (!modeVector.empty())
|
|
MyUplink->OnChannelMode(thisChan, 0, modeVector);
|
|
|
|
MyUplink->BurstChannel(thisChan->getName(), thisChan->getModeString(),
|
|
thisChan->getCreationTime() - 1);
|
|
|
|
if (version >= 12)
|
|
MyUplink->setBursting(false);
|
|
} else {
|
|
ClearMode(thisChan, "obiklrD", true);
|
|
}
|
|
|
|
sqlChannel* sqlChan = getChannelRecord(thisChan);
|
|
if (!sqlChan) sqlChan = newChannelRecord(thisChan);
|
|
|
|
if (doJoinChannels() && shouldCJoin(sqlChan, false))
|
|
JoinChan(thisChan);
|
|
if (doManualFixNotice() && shouldCJoin(sqlChan, false))
|
|
Message(thisChan, "Channel fix in progress, please stand by.");
|
|
|
|
manFixQ.insert(fixQueueType::value_type(thisChan->getName(), currentTime() + CHANFIX_DELAY));
|
|
}
|
|
|
|
|
|
void chanfix::insertop(sqlChanOp* curOp, sqlChannel* sqlChan)
|
|
{
|
|
simOppedStruct curStruct;
|
|
|
|
curStruct.account = curOp->getAccount();
|
|
curStruct.channel = sqlChan->getChannel();
|
|
|
|
simMap.insert(SimMapType::value_type(sqlChan->getChannel(), curStruct));
|
|
|
|
return;
|
|
}
|
|
|
|
bool chanfix::findop(sqlChanOp* curOp, sqlChannel* sqlChan)
|
|
{
|
|
for (SimMapType::iterator ptr = simMap.begin();
|
|
ptr != simMap.end(); ptr++) {
|
|
|
|
if ((ptr->first == sqlChan->getChannel()) && (ptr->second.account == curOp->getAccount()))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void chanfix::removechan(sqlChannel* sqlChan)
|
|
{
|
|
simMap.erase(sqlChan->getChannel());
|
|
return;
|
|
}
|
|
|
|
bool chanfix::simFix(sqlChannel* sqlChan, bool autofix, time_t c_Time, iClient* theClient, sqlcfUser* theUser)
|
|
{
|
|
if (sqlChan->getSimStart() == 0) sqlChan->setSimStart(c_Time);
|
|
sqlChan->setLastSimAttempt(c_Time);
|
|
|
|
Channel* netChan = Network->findChannel(sqlChan->getChannel());
|
|
if (!netChan) return true;
|
|
|
|
chanOpsType myOps = getMyOps(netChan);
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setTMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
int maxScore = sqlChan->getTMaxScore();
|
|
if (maxScore <= FIX_MIN_ABS_SCORE_END * MAX_SCORE)
|
|
return false;
|
|
|
|
unsigned int maxOpped = (autofix ? AUTOFIX_NUM_OPPED : CHANFIX_NUM_OPPED);
|
|
|
|
unsigned int currentOps = sqlChan->getAmountSimOpped();
|
|
if (currentOps >= maxOpped)
|
|
return true;
|
|
|
|
int time_since_start;
|
|
if (autofix)
|
|
time_since_start = c_Time - sqlChan->getSimStart();
|
|
else
|
|
time_since_start = c_Time - (sqlChan->getSimStart() + CHANFIX_DELAY);
|
|
|
|
int max_time = (autofix ? AUTOFIX_MAXIMUM : CHANFIX_MAXIMUM);
|
|
int min_score_abs = static_cast<int>((MAX_SCORE *
|
|
static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(MAX_SCORE * static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_ABS_SCORE_END) * MAX_SCORE));
|
|
|
|
int min_score_rel = static_cast<int>((maxScore *
|
|
static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(maxScore * static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_REL_SCORE_END) * maxScore));
|
|
|
|
int min_score = min_score_abs;
|
|
if (min_score_rel > min_score)
|
|
min_score = min_score_rel;
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setTMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
if (sqlChan->getTMaxScore() < min_score)
|
|
min_score = sqlChan->getTMaxScore();
|
|
|
|
unsigned int amtopped;
|
|
iClient* curClient = 0;
|
|
sqlChanOp* curOp = 0;
|
|
acctListType acctToOp;
|
|
std::string modes = "+";
|
|
std::string args;
|
|
unsigned int numClientsToOp = 0;
|
|
bool cntMaxedOut = false;
|
|
for (chanOpsType::iterator opPtr = myOps.begin(); opPtr != myOps.end();
|
|
opPtr++) {
|
|
curOp = *opPtr;
|
|
if ((curOp->getPoints() + curOp->getBonus()) >= min_score) {
|
|
acctToOp = findAccount(netChan, curOp->getAccount());
|
|
std::vector< iClient* >::const_iterator acctPtr = acctToOp.begin(),
|
|
end = acctToOp.end();
|
|
while (acctPtr != end) {
|
|
curClient = *acctPtr;
|
|
if (curClient && !findop(curOp, sqlChan)) {
|
|
amtopped = sqlChan->getAmountSimOpped();
|
|
amtopped++;
|
|
sqlChan->setAmountSimOpped(amtopped);
|
|
|
|
insertop(curOp, sqlChan);
|
|
if (!args.empty())
|
|
args += " ";
|
|
args += curClient->getNickName();
|
|
|
|
if ((++numClientsToOp + currentOps) >= maxOpped) {
|
|
cntMaxedOut = true;
|
|
break;
|
|
}
|
|
}
|
|
++acctPtr;
|
|
}
|
|
acctToOp.clear();
|
|
if (cntMaxedOut)
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::stringstream chanStatus;
|
|
std::stringstream chanModes;
|
|
|
|
if (netChan->getMode(Channel::MODE_I)) chanModes << "i";
|
|
if (netChan->getMode(Channel::MODE_K)) chanModes << "k";
|
|
if (netChan->getMode(Channel::MODE_L)) chanModes << "l";
|
|
if (netChan->getMode(Channel::MODE_R)) chanModes << "r";
|
|
if (netChan->getMode(Channel::MODE_D)) chanModes << "d";
|
|
|
|
|
|
chanStatus << "* Channel Status for "
|
|
<< netChan->getName() << ": "
|
|
<< netChan->banList_size()
|
|
<< " bans -- Restrictive Modes: +"
|
|
<< chanModes.str()
|
|
;
|
|
|
|
SendTo(theClient, chanStatus.str().c_str());
|
|
|
|
if ((!numClientsToOp || maxScore < min_score) &&
|
|
(!autofix || !(numClientsToOp + currentOps))) {
|
|
if (autofix && !sqlChan->getSimModesRemoved()) {
|
|
|
|
if (netChan->banList_size() ||
|
|
netChan->getMode(Channel::MODE_I) ||
|
|
netChan->getMode(Channel::MODE_K) ||
|
|
netChan->getMode(Channel::MODE_L) ||
|
|
netChan->getMode(Channel::MODE_R) ||
|
|
netChan->getMode(Channel::MODE_D)) {
|
|
|
|
sqlChan->setSimModesRemoved(true);
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::sim_modes_removed,
|
|
std::string("(%s) Channel modes have been removed.")).c_str(),
|
|
tsToDateTime(c_Time, true).c_str());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
if (numClientsToOp) {
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::sim_opping,
|
|
std::string("(%s) Opping: %s (%d Clients)")).c_str(),
|
|
tsToDateTime(c_Time, true).c_str(), args.c_str(), numClientsToOp);
|
|
}
|
|
|
|
if (numClientsToOp + currentOps >= netChan->size() ||
|
|
numClientsToOp + currentOps >= maxOpped)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::simulateFix(sqlChannel* sqlChan, bool autofix, iClient* theClient, sqlcfUser* theUser)
|
|
{
|
|
bool isFixed = false;
|
|
time_t t = getNextFix();
|
|
time_t end_fix = t + 86400; /* 1 Day */
|
|
time_t next_fix = t + PROCESS_QUEUE_TIME;
|
|
|
|
sqlChan->setSimStart(0);
|
|
sqlChan->setLastSimAttempt(0);
|
|
sqlChan->setAmountSimOpped(0);
|
|
sqlChan->setSimModesRemoved(false);
|
|
|
|
/* Modes are always removed straight when a CHANFIX command is issued
|
|
for a channel (manual fix). */
|
|
if (autofix == false)
|
|
SendTo(theClient,
|
|
getResponse(theUser,
|
|
language::sim_modes_removed,
|
|
std::string("(%s) Channel modes have been removed.")).c_str(),
|
|
tsToDateTime(currentTime(), true).c_str());
|
|
|
|
while (t) {
|
|
if (next_fix == t) {
|
|
if (autofix) {
|
|
if (t - sqlChan->getSimStart() > AUTOFIX_MAXIMUM) {
|
|
isFixed = simFix(sqlChan, autofix, t, theClient, theUser);
|
|
}
|
|
} else {
|
|
if (t - sqlChan->getSimStart() > CHANFIX_MAXIMUM) {
|
|
isFixed = simFix(sqlChan, autofix, t, theClient, theUser);
|
|
}
|
|
}
|
|
|
|
next_fix = t + PROCESS_QUEUE_TIME;
|
|
}
|
|
|
|
if (isFixed || (t == end_fix))
|
|
break;
|
|
else
|
|
t++;
|
|
}
|
|
|
|
/* Cleanup */
|
|
removechan(sqlChan);
|
|
|
|
sqlChan->setSimStart(0);
|
|
sqlChan->setLastSimAttempt(0);
|
|
sqlChan->setAmountSimOpped(0);
|
|
sqlChan->setSimModesRemoved(false);
|
|
|
|
return isFixed;
|
|
}
|
|
|
|
|
|
|
|
bool chanfix::shouldCJoin(sqlChannel* sqlChan, bool autofix)
|
|
{
|
|
bool joinchan = false;
|
|
int maxScore;
|
|
time_t lastattempt;
|
|
time_t fixstart;
|
|
|
|
/* coder notes (mostly so i dont forget -sirv)
|
|
* if we use this function for simulation then fix start is the both
|
|
* these times will need to be set as temp vars in sqlChan
|
|
*/
|
|
fixstart = currentTime();
|
|
lastattempt = currentTime();
|
|
|
|
Channel* netChan = Network->findChannel(sqlChan->getChannel());
|
|
if (!netChan) return joinchan;
|
|
|
|
chanOpsType myOps = getMyOps(netChan);
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setTMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
maxScore = sqlChan->getTMaxScore();
|
|
|
|
if (maxScore <= FIX_MIN_ABS_SCORE_END * MAX_SCORE)
|
|
return joinchan;
|
|
|
|
unsigned int maxOpped = (autofix ? AUTOFIX_NUM_OPPED : CHANFIX_NUM_OPPED);
|
|
unsigned int currentOps = countChanOps(netChan);
|
|
|
|
if (currentOps >= maxOpped) {
|
|
return joinchan;
|
|
}
|
|
|
|
int time_since_start;
|
|
if (autofix)
|
|
time_since_start = currentTime() - fixstart;
|
|
else
|
|
time_since_start = currentTime() - (fixstart + CHANFIX_DELAY);
|
|
|
|
int max_time = (autofix ? AUTOFIX_MAXIMUM : CHANFIX_MAXIMUM);
|
|
|
|
int min_score_abs = static_cast<int>((MAX_SCORE *
|
|
static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(MAX_SCORE * static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_ABS_SCORE_END) * MAX_SCORE));
|
|
|
|
int min_score_rel = static_cast<int>((maxScore *
|
|
static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(maxScore * static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_REL_SCORE_END) * maxScore));
|
|
|
|
int min_score = min_score_abs;
|
|
if (min_score_rel > min_score)
|
|
min_score = min_score_rel;
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setTMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
if (sqlChan->getTMaxScore() < min_score)
|
|
min_score = sqlChan->getTMaxScore();
|
|
|
|
iClient* curClient = 0;
|
|
sqlChanOp* curOp = 0;
|
|
acctListType acctToOp;
|
|
unsigned int numClientsToOp = 0;
|
|
bool cntMaxedOut = false;
|
|
for (chanOpsType::iterator opPtr = myOps.begin(); opPtr != myOps.end();
|
|
opPtr++) {
|
|
curOp = *opPtr;
|
|
if ((curOp->getPoints() + curOp->getBonus()) >= min_score) {
|
|
acctToOp = findAccount(netChan, curOp->getAccount());
|
|
std::vector< iClient* >::const_iterator acctPtr = acctToOp.begin(),
|
|
end = acctToOp.end();
|
|
while (acctPtr != end) {
|
|
curClient = *acctPtr;
|
|
if (curClient && !netChan->findUser(curClient)->isModeO()) {
|
|
if ((++numClientsToOp + currentOps) >= maxOpped) {
|
|
joinchan = true;
|
|
cntMaxedOut = true;
|
|
break;
|
|
}
|
|
}
|
|
++acctPtr;
|
|
}
|
|
acctToOp.clear();
|
|
if (cntMaxedOut)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return joinchan;
|
|
}
|
|
|
|
bool chanfix::fixChan(sqlChannel* sqlChan, bool autofix)
|
|
{
|
|
/* First update the time of the previous attempt to now. */
|
|
if (sqlChan->getFixStart() == 0) sqlChan->setFixStart(currentTime());
|
|
sqlChan->setLastAttempt(currentTime());
|
|
|
|
/* If the channel doesn't exist (anymore), the fix is successful. */
|
|
Channel* netChan = Network->findChannel(sqlChan->getChannel());
|
|
if (!netChan) return true;
|
|
|
|
chanOpsType myOps = getMyOps(netChan);
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
int maxScore = sqlChan->getMaxScore();
|
|
|
|
/* If the max score of the channel is lower than the absolute minimum
|
|
* score required, don't even bother trying. */
|
|
if (maxScore <= FIX_MIN_ABS_SCORE_END * MAX_SCORE)
|
|
return false;
|
|
|
|
/* Get the number of clients that should have ops */
|
|
unsigned int maxOpped = (autofix ? AUTOFIX_NUM_OPPED : CHANFIX_NUM_OPPED);
|
|
|
|
/* If the channel has enough ops, abort & return. */
|
|
unsigned int currentOps = countChanOps(netChan);
|
|
if (currentOps >= maxOpped) {
|
|
elog << "chanfix::fixChan> DEBUG: Enough clients opped on " << netChan->getName() << std::endl;
|
|
return true;
|
|
}
|
|
|
|
int time_since_start;
|
|
if (autofix)
|
|
time_since_start = currentTime() - sqlChan->getFixStart();
|
|
else
|
|
time_since_start = currentTime() - (sqlChan->getFixStart() + CHANFIX_DELAY);
|
|
|
|
int max_time = (autofix ? AUTOFIX_MAXIMUM : CHANFIX_MAXIMUM);
|
|
|
|
/* Determine minimum score required for this time. */
|
|
|
|
/* Linear interpolation of (0, fraction_abs_max * max_score) ->
|
|
* (max_time, fraction_abs_min * max_score)
|
|
* at time t between 0 and max_time. */
|
|
int min_score_abs = static_cast<int>((MAX_SCORE *
|
|
static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(MAX_SCORE * static_cast<float>(FIX_MIN_ABS_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_ABS_SCORE_END) * MAX_SCORE));
|
|
|
|
elog << "chanfix::fixChan> [" << netChan->getName() << "] max "
|
|
<< MAX_SCORE << ", begin " << FIX_MIN_ABS_SCORE_BEGIN
|
|
<< ", end " << FIX_MIN_ABS_SCORE_END << ", time "
|
|
<< time_since_start << ", maxtime " << max_time << "."
|
|
<< std::endl;
|
|
|
|
/* Linear interpolation of (0, fraction_rel_max * max_score_channel) ->
|
|
* (max_time, fraction_rel_min * max_score_channel)
|
|
* at time t between 0 and max_time. */
|
|
int min_score_rel = static_cast<int>((maxScore *
|
|
static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)) -
|
|
static_cast<float>(time_since_start) /
|
|
static_cast<float>(max_time) *
|
|
(maxScore * static_cast<float>(FIX_MIN_REL_SCORE_BEGIN)
|
|
- static_cast<float>(FIX_MIN_REL_SCORE_END) * maxScore));
|
|
|
|
/* The minimum score needed for ops is the HIGHER of these two
|
|
* scores. */
|
|
int min_score = min_score_abs;
|
|
if (min_score_rel > min_score)
|
|
min_score = min_score_rel;
|
|
|
|
/* We need to check to see if the highest scoring ops score for the chan
|
|
* is less than min_score, if so, adjust min_score so we wont have
|
|
* to wait forever for the first op! */
|
|
|
|
if (myOps.begin() != myOps.end())
|
|
sqlChan->setMaxScore((*myOps.begin())->getPoints() + (*myOps.begin())->getBonus());
|
|
|
|
if (sqlChan->getMaxScore() < min_score)
|
|
min_score = sqlChan->getMaxScore();
|
|
|
|
elog << "chanfix::fixChan> [" << netChan->getName() << "] start "
|
|
<< sqlChan->getFixStart() << ", delta " << time_since_start
|
|
<< ", max " << maxScore << ", minabs " << min_score_abs
|
|
<< ", minrel " << min_score_rel << "." << std::endl;
|
|
|
|
/**
|
|
* Get the scores of the accounts of the non-opped clients.
|
|
* Find out which clients need to be opped.
|
|
*/
|
|
iClient* curClient = 0;
|
|
sqlChanOp* curOp = 0;
|
|
acctListType acctToOp;
|
|
std::string modes = "+";
|
|
std::string args;
|
|
unsigned int numClientsToOp = 0;
|
|
bool cntMaxedOut = false;
|
|
for (chanOpsType::iterator opPtr = myOps.begin(); opPtr != myOps.end();
|
|
opPtr++) {
|
|
curOp = *opPtr;
|
|
// elog << "chanfix::fixChan> DEBUG: "
|
|
// << curOp->getPoints() << " >= " << min_score << std::endl;
|
|
if ((curOp->getPoints() + curOp->getBonus()) >= min_score) {
|
|
acctToOp = findAccount(netChan, curOp->getAccount());
|
|
std::vector< iClient* >::const_iterator acctPtr = acctToOp.begin(),
|
|
end = acctToOp.end();
|
|
while (acctPtr != end) {
|
|
curClient = *acctPtr;
|
|
if (curClient && !netChan->findUser(curClient)->isModeO()) {
|
|
elog << "chanfix::fixChan> DEBUG: Decided to op: "
|
|
<< curClient->getNickName() << " on "
|
|
<< netChan->getName() << ". Client has "
|
|
<< (curOp->getPoints() + curOp->getBonus()) << " points. ABS_MIN = "
|
|
<< min_score_abs << " and REL_MIN = " << min_score_rel
|
|
<< std::endl;
|
|
modes += "o";
|
|
if (!args.empty())
|
|
args += " ";
|
|
args += curClient->getNickName();
|
|
if ((++numClientsToOp + currentOps) >= maxOpped) {
|
|
elog << "chanfix::fixChan> DEBUG: Enough clients are to be "
|
|
<< "opped (" << numClientsToOp << "); breaking the loop."
|
|
<< std::endl;
|
|
cntMaxedOut = true;
|
|
break;
|
|
}
|
|
}
|
|
++acctPtr;
|
|
}
|
|
acctToOp.clear();
|
|
if (cntMaxedOut)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If no scores are high enough, return. */
|
|
if ((!numClientsToOp || maxScore < min_score) &&
|
|
(!autofix || !(numClientsToOp + currentOps))) {
|
|
if (autofix && !sqlChan->getModesRemoved() &&
|
|
needsModesRemoved(netChan)) {
|
|
ClearMode(netChan, "biklrD", true);
|
|
sqlChan->setModesRemoved(true);
|
|
Message(netChan, "Channel modes have been removed.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* If we need to op at least one client, op him/her. */
|
|
if (numClientsToOp) {
|
|
Mode(netChan, modes, args, true);
|
|
|
|
if (numClientsToOp == 1)
|
|
Message(netChan, "1 client should have been opped.");
|
|
else
|
|
Message(netChan, "%d clients should have been opped.",
|
|
numClientsToOp);
|
|
}
|
|
|
|
/* Now see if there are enough ops; if so, the fix is complete. */
|
|
if (numClientsToOp + currentOps >= netChan->size() ||
|
|
numClientsToOp + currentOps >= maxOpped)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void chanfix::stopFixingChan(Channel* theChan, bool force)
|
|
{
|
|
/* If the channel doesn't exist (anymore), don't try to end the fix. */
|
|
if (!theChan) return;
|
|
|
|
bool inFix = false;
|
|
bool isAutoFix = false; /* false = manual - true = auto */
|
|
|
|
if ((stopAutoFixOnOp || force) && isBeingAutoFixed(theChan)) {
|
|
isAutoFix = true;
|
|
inFix = true;
|
|
removeFromAutoQ(theChan);
|
|
}
|
|
if ((stopChanFixOnOp || force) && isBeingChanFixed(theChan)) {
|
|
inFix = true;
|
|
removeFromManQ(theChan);
|
|
}
|
|
|
|
if (inFix) {
|
|
sqlChannel* sqlChan = getChannelRecord(theChan);
|
|
if (sqlChan) {
|
|
sqlChan->setFixStart(0);
|
|
sqlChan->setLastAttempt(0);
|
|
}
|
|
}
|
|
|
|
if (doJoinChannels())
|
|
PartChan(theChan);
|
|
|
|
return;
|
|
}
|
|
|
|
chanfix::acctListType chanfix::findAccount(Channel* theChan, const std::string& account)
|
|
{
|
|
acctListType chanAccts;
|
|
for (Channel::userIterator ptr = theChan->userList_begin();
|
|
ptr != theChan->userList_end(); ptr++) {
|
|
if (account == ptr->second->getClient()->getAccount())
|
|
chanAccts.push_back(ptr->second->getClient());
|
|
}
|
|
|
|
return chanAccts;
|
|
}
|
|
|
|
bool chanfix::accountIsOnChan(const std::string& channel, const std::string& account)
|
|
{
|
|
Channel* tmpChan = Network->findChannel(channel);
|
|
if (!tmpChan) return false;
|
|
|
|
for (Channel::userIterator ptr = tmpChan->userList_begin();
|
|
ptr != tmpChan->userList_end(); ptr++) {
|
|
if (account == ptr->second->getClient()->getAccount())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const std::string chanfix::getChanNickName(const std::string& channel, const std::string& account)
|
|
{
|
|
std::string theNick = "";
|
|
Channel* tmpChan = Network->findChannel(channel);
|
|
if (!tmpChan) return "";
|
|
|
|
for (Channel::userIterator ptr = tmpChan->userList_begin();
|
|
ptr != tmpChan->userList_end(); ptr++) {
|
|
if (account == ptr->second->getClient()->getAccount()) {
|
|
if (ptr->second->isModeO()) {
|
|
theNick = "@";
|
|
theNick += ptr->second->getClient()->getNickName();
|
|
return theNick;
|
|
}
|
|
else if (ptr->second->isModeV()) {
|
|
theNick = "+";
|
|
theNick += ptr->second->getClient()->getNickName();
|
|
return theNick;
|
|
}
|
|
else {
|
|
theNick = ptr->second->getClient()->getNickName();
|
|
return theNick;
|
|
}
|
|
}
|
|
}
|
|
return theNick;
|
|
}
|
|
|
|
sqlChannel* chanfix::getChannelRecord(const std::string& Channel)
|
|
{
|
|
sqlChannelCacheType::iterator ptr = sqlChanCache.find(Channel);
|
|
if (ptr != sqlChanCache.end()) {
|
|
//elog << "chanfix::getChannelRecord> DEBUG: cached channel " << Channel << " found" << std::endl;
|
|
return ptr->second;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sqlChannel* chanfix::getChannelRecord(Channel* theChan)
|
|
{
|
|
return getChannelRecord(theChan->getName());
|
|
}
|
|
|
|
sqlChannel* chanfix::newChannelRecord(const std::string& Channel)
|
|
{
|
|
sqlChannel* newChan = new (std::nothrow) sqlChannel(theManager);
|
|
assert( newChan != 0 ) ;
|
|
|
|
newChan->setChannel(Channel);
|
|
newChan->setFixStart(0);
|
|
newChan->setLastAttempt(0);
|
|
|
|
sqlChanCache.insert(sqlChannelCacheType::value_type(Channel, newChan));
|
|
elog << "chanfix::newChannelRecord> DEBUG: Added new channel: " << Channel << std::endl;
|
|
|
|
return newChan;
|
|
}
|
|
|
|
sqlChannel* chanfix::newChannelRecord(Channel* theChan)
|
|
{
|
|
return newChannelRecord(theChan->getName());
|
|
}
|
|
|
|
bool chanfix::deleteChannelRecord(sqlChannel* sqlChan)
|
|
{
|
|
if (sqlChan->useSQL() && sqlChan->Delete(localDBHandle))
|
|
return false;
|
|
sqlChanCache.erase(sqlChan->getChannel());
|
|
delete sqlChan; sqlChan = 0;
|
|
|
|
return !sqlChan;
|
|
}
|
|
|
|
size_t chanfix::countChanOps(const Channel* theChan)
|
|
{
|
|
if (!theChan) {
|
|
/* Don't try this on a null channel. */
|
|
return 0;
|
|
}
|
|
|
|
size_t chanOps = 0;
|
|
|
|
for (Channel::const_userIterator ptr = theChan->userList_begin();
|
|
ptr != theChan->userList_end(); ++ptr)
|
|
if (ptr->second->isModeO())
|
|
chanOps++;
|
|
|
|
return chanOps;
|
|
}
|
|
|
|
bool chanfix::needsModesRemoved(Channel* theChan)
|
|
{
|
|
/* Modes need to be removed if +b/i/k/l/r/D is set.
|
|
* This check should actually be more specific (checking each ban
|
|
* to see if it matches a high scored hostmask) but this will do
|
|
* for now.
|
|
*/
|
|
if (theChan->banList_size() ||
|
|
theChan->getMode(Channel::MODE_I) ||
|
|
theChan->getMode(Channel::MODE_K) ||
|
|
theChan->getMode(Channel::MODE_L) ||
|
|
theChan->getMode(Channel::MODE_R) ||
|
|
theChan->getMode(Channel::MODE_D))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::canScoreChan(Channel* theChan)
|
|
{
|
|
// Lets leave this and just return 'true' since we're allowing +R channels to be scored
|
|
/*
|
|
* for (Channel::const_userIterator ptr = theChan->userList_begin();
|
|
* ptr != theChan->userList_end(); ++ptr)
|
|
*if (ptr->second->getClient()->getMode(iClient::MODE_SERVICES))
|
|
* return false;
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
|
|
void chanfix::processQueue()
|
|
{
|
|
/* If there are too many servers split, don't process queue. */
|
|
if (currentState != RUN) {
|
|
//elog << "chanfix::processQueue> DEBUG: currentState != RUN" << std::endl;
|
|
return;
|
|
}
|
|
|
|
for (fixQueueType::iterator ptr = autoFixQ.begin(); ptr != autoFixQ.end(); ) {
|
|
//elog << "chanfix::processQueue> DEBUG: Processing " << ptr->first << " in autoFixQ ..." << std::endl;
|
|
if (ptr->second <= currentTime()) {
|
|
sqlChannel* sqlChan = getChannelRecord(ptr->first);
|
|
if (!sqlChan) sqlChan = newChannelRecord(ptr->first);
|
|
bool isFixed = false;
|
|
|
|
if (currentTime() - sqlChan->getLastAttempt() >= AUTOFIX_INTERVAL)
|
|
isFixed = fixChan(sqlChan, true);
|
|
|
|
/**
|
|
* If the channel has been fixed, or the fixing time window
|
|
* has passed, remove it from the list
|
|
*/
|
|
if (isFixed || currentTime() - sqlChan->getFixStart() > AUTOFIX_MAXIMUM) {
|
|
Channel* theChan = Network->findChannel(sqlChan->getChannel());
|
|
|
|
if (!theChan) {
|
|
//Object no longer exists
|
|
autoFixQ.erase(ptr++);
|
|
continue;
|
|
}
|
|
|
|
if (doJoinChannels())
|
|
PartChan(theChan);
|
|
autoFixQ.erase(ptr++);
|
|
sqlChan->setFixStart(0);
|
|
sqlChan->setLastAttempt(0);
|
|
elog << "chanfix::processQueue> DEBUG: Channel " << sqlChan->getChannel() << " done!" << std::endl;
|
|
} else {
|
|
ptr->second = currentTime() + AUTOFIX_INTERVAL;
|
|
ptr++;
|
|
elog << "chanfix::processQueue> DEBUG: Channel " << sqlChan->getChannel() << " not done yet ..." << std::endl;
|
|
}
|
|
} else ptr++;
|
|
}
|
|
|
|
for (fixQueueType::iterator ptr = manFixQ.begin(); ptr != manFixQ.end(); ) {
|
|
//elog << "chanfix::processQueue> DEBUG: Processing " << ptr->first << " in manFixQ ..." << std::endl;
|
|
if (ptr->second <= currentTime()) {
|
|
sqlChannel* sqlChan = getChannelRecord(ptr->first);
|
|
if (!sqlChan) sqlChan = newChannelRecord(ptr->first);
|
|
bool isFixed = false;
|
|
|
|
if (currentTime() - sqlChan->getLastAttempt() >= CHANFIX_INTERVAL)
|
|
isFixed = fixChan(sqlChan, false);
|
|
|
|
/**
|
|
* If the channel has been fixed, or the fixing time window
|
|
* has passed, remove it from the list
|
|
*/
|
|
if (isFixed || currentTime() - sqlChan->getFixStart() > CHANFIX_MAXIMUM + CHANFIX_DELAY) {
|
|
Channel* theChan = Network->findChannel(sqlChan->getChannel());
|
|
|
|
if (!theChan) {
|
|
//Object no longer exists
|
|
manFixQ.erase(ptr++);
|
|
continue;
|
|
}
|
|
|
|
if (doJoinChannels())
|
|
PartChan(theChan);
|
|
manFixQ.erase(ptr++);
|
|
sqlChan->setFixStart(0);
|
|
sqlChan->setLastAttempt(0);
|
|
elog << "chanfix::processQueue> DEBUG: Channel " << sqlChan->getChannel() << " done!" << std::endl;
|
|
} else {
|
|
ptr->second = currentTime() + CHANFIX_INTERVAL;
|
|
ptr++;
|
|
elog << "chanfix::processQueue> DEBUG: Channel " << sqlChan->getChannel() << " not done yet ..." << std::endl;
|
|
}
|
|
} else ptr++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool chanfix::isBeingFixed(Channel* theChan)
|
|
{
|
|
return (isBeingAutoFixed(theChan) || isBeingChanFixed(theChan));
|
|
}
|
|
|
|
bool chanfix::isBeingAutoFixed(Channel* theChan)
|
|
{
|
|
fixQueueType::iterator ptr = autoFixQ.find(theChan->getName());
|
|
if (ptr != autoFixQ.end())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::isBeingChanFixed(Channel* theChan)
|
|
{
|
|
fixQueueType::iterator ptr = manFixQ.find(theChan->getName());
|
|
if (ptr != manFixQ.end())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::removeFromAutoQ(Channel* theChan)
|
|
{
|
|
fixQueueType::iterator ptr = autoFixQ.find(theChan->getName());
|
|
if (ptr != autoFixQ.end()) {
|
|
autoFixQ.erase(ptr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool chanfix::removeFromManQ(Channel* theChan)
|
|
{
|
|
fixQueueType::iterator ptr = manFixQ.find(theChan->getName());
|
|
if (ptr != manFixQ.end()) {
|
|
manFixQ.erase(ptr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const std::string chanfix::prettyDuration( int duration )
|
|
{
|
|
|
|
// Pretty format a 'duration' in seconds to
|
|
// x day(s), xx:xx:xx.
|
|
|
|
char tmpBuf[ 64 ] = {0};
|
|
|
|
int res = currentTime() - duration,
|
|
secs = res % 60,
|
|
mins = (res / 60) % 60,
|
|
hours = (res / 3600) % 24,
|
|
days = (res / 86400) ;
|
|
|
|
sprintf(tmpBuf, "%i day%s, %02d:%02d:%02d",
|
|
days,
|
|
(days == 1 ? "" : "s"),
|
|
hours,
|
|
mins,
|
|
secs );
|
|
|
|
return std::string( tmpBuf ) ;
|
|
}
|
|
|
|
const std::string chanfix::tsToDateTime(time_t timestamp, bool time)
|
|
{
|
|
char datetimestring[ 20 ] = {0};
|
|
struct tm *stm;
|
|
|
|
stm = localtime(×tamp);
|
|
memset(datetimestring, 0, sizeof(datetimestring));
|
|
|
|
if (time)
|
|
strftime(datetimestring, sizeof(datetimestring), "%Y-%m-%d %H:%M:%S", stm);
|
|
else
|
|
strftime(datetimestring, sizeof(datetimestring), "%Y-%m-%d", stm);
|
|
|
|
return std::string(datetimestring);
|
|
}
|
|
|
|
const int chanfix::getCurrentGMTHour()
|
|
{
|
|
time_t rawtime;
|
|
tm * ptm;
|
|
|
|
time ( &rawtime );
|
|
ptm = gmtime ( &rawtime );
|
|
|
|
return ptm->tm_hour;
|
|
}
|
|
|
|
sqlcfUser* chanfix::isAuthed(const std::string Name)
|
|
{
|
|
//Name = escapeSQLChars(Name);
|
|
sqlcfUser* tempUser = usersMap[Name];
|
|
if (!tempUser)
|
|
usersMap.erase(usersMap.find(Name));
|
|
return tempUser;
|
|
}
|
|
|
|
chanfix::clientOpsType* chanfix::findMyOps(iClient* theClient)
|
|
{
|
|
clientOpsType* myOps = static_cast< clientOpsType* >(theClient->getCustomData(this) );
|
|
|
|
if (myOps == NULL) myOps = new clientOpsType;
|
|
|
|
return myOps;
|
|
}
|
|
|
|
/*
|
|
* Start timers
|
|
*/
|
|
void chanfix::startTimers()
|
|
{
|
|
time_t theTime;
|
|
theTime = time(NULL) + connectCheckFreq;
|
|
tidCheckDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + CHECK_CHANS_TIME;
|
|
tidAutoFix = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + PROCESS_QUEUE_TIME;
|
|
tidFixQ = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + POINTS_UPDATE_TIME;
|
|
tidGivePoints = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + getSecsTilMidnight();
|
|
tidRotateDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + SQL_UPDATE_TIME;
|
|
tidUpdateDB = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
theTime = time(NULL) + TEMPBLOCKS_CHECK_TIME;
|
|
tidTempBlocks = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
elog << "chanfix::startTimers> Started all timers."
|
|
<< std::endl;
|
|
}
|
|
|
|
/**
|
|
* prepareUpdate - Copies the sqlChanOp map to a temporary multimap
|
|
*/
|
|
#ifdef CHANFIX_HAVE_BOOST_THREAD
|
|
void chanfix::prepareUpdate(bool threaded)
|
|
#else
|
|
void chanfix::prepareUpdate(bool)
|
|
#endif /* CHANFIX_HAVE_BOOST_THREAD */
|
|
{
|
|
if (updateInProgress) {
|
|
elog << "*** [chanfix::prepareUpdate] Update already in progress; not starting."
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
elog << "*** [chanfix::prepareUpdate] Updating the SQL database "
|
|
#ifdef CHANFIX_HAVE_BOOST_THREAD
|
|
<< (threaded ? "(threaded)." : "(unthreaded).")
|
|
#else
|
|
<< "(unthreaded [no boost])."
|
|
#endif /* CHANFIX_HAVE_BOOST_THREAD */
|
|
<< std::endl;
|
|
logDebugMessage("Starting to update the SQL database.");
|
|
|
|
/**
|
|
* Set updateInProgress boolean to true so that any other updates
|
|
* and/or requests for shutdown/reload will be denied.
|
|
*/
|
|
updateInProgress = true;
|
|
|
|
/* Start our timer */
|
|
Timer snapShotTimer;
|
|
snapShotTimer.Start();
|
|
|
|
/* Clear the snapShot map */
|
|
snapShot.clear();
|
|
|
|
//snapShotStruct* curStruct = new (std::nothrow) snapShotStruct;
|
|
snapShotStruct curStruct;
|
|
|
|
sqlChanOp* curOp;
|
|
std::string curChan;
|
|
int i = 0;
|
|
|
|
for (sqlChanOpsType::iterator ptr = sqlChanOps.begin();
|
|
ptr != sqlChanOps.end(); ptr++) {
|
|
curChan = ptr->first;
|
|
for (sqlChanOpsType::mapped_type::iterator chanOp = ptr->second.begin();
|
|
chanOp != ptr->second.end(); chanOp++) {
|
|
curOp = chanOp->second;
|
|
|
|
/* Fill the structures and maps */
|
|
curStruct.account = curOp->getAccount();
|
|
curStruct.lastSeenAs = curOp->getLastSeenAs();
|
|
curStruct.firstOpped = curOp->getTimeFirstOpped();
|
|
curStruct.lastOpped = curOp->getTimeLastOpped();
|
|
|
|
for (i = 0; i < DAYSAMPLES; i++) {
|
|
curStruct.day[i] = curOp->getDay(i);
|
|
}
|
|
|
|
snapShot.insert(DBMapType::value_type(curChan, curStruct));
|
|
}
|
|
}
|
|
|
|
logDebugMessage("Created snapshot map in %u ms.",
|
|
snapShotTimer.stopTimeMS());
|
|
|
|
#ifdef CHANFIX_HAVE_BOOST_THREAD
|
|
if (threaded) {
|
|
ClassUpdateDB updateDB(*this);
|
|
boost::thread pthrd(updateDB);
|
|
pthrd.join();
|
|
} else
|
|
#endif /* CHANFIX_HAVE_BOOST_THREAD */
|
|
updateDB();
|
|
printResourceStats();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* updateDB - Copies the contents of sqlChanOps to the SQL database
|
|
* Note: Only threaded if called via the ClassUpdateDB function with
|
|
* boost::thread
|
|
*/
|
|
void chanfix::updateDB()
|
|
{
|
|
/* Start our timer */
|
|
Timer updateDBTimer;
|
|
updateDBTimer.Start();
|
|
|
|
/* Get a connection instance to our backend */
|
|
dbHandle* cacheCon = localDBHandle;
|
|
|
|
/* Check for the backup table. If it exists, something went wrong. */
|
|
/* SELECT count(*) FROM pg_tables WHERE tablename = 'chanOpsBackup' */
|
|
/* SELECT chanOpsBackup FROM information_schema.tables */
|
|
if (!cacheCon->Exec("SELECT count(*) FROM pg_tables WHERE tablename = 'chanOpsBackup'",true)) {
|
|
elog << "*** [chanfix::updateDB]: Error checking for backup table presence: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
if (cacheCon->Tuples() && atoi(cacheCon->GetValue(0, 0))) {
|
|
/* Drop the backup table. */
|
|
if (!cacheCon->Exec("DROP TABLE chanOpsBackup")) {
|
|
elog << "*** [chanfix::updateDB]: Error dropping backup table: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Copy all data from the main table to the backup table. */
|
|
if (!cacheCon->Exec("CREATE TABLE chanOpsBackup AS SELECT * FROM chanOps")) {
|
|
elog << "*** [chanfix::updateDB]: Error creating backup table: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
/* Truncate the current chanOps table. */
|
|
if (!cacheCon->Exec("TRUNCATE TABLE chanOps")) {
|
|
elog << "*** [chanfix::updateDB]: Error truncating current chanOps table: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
/* Copy the current chanOps to SQL. */
|
|
if( !cacheCon->Exec("COPY chanOps FROM stdin") )
|
|
{
|
|
elog << "*** [chanfix::updateDB]: Error starting copy of chanOps table."
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
std::stringstream theLine;
|
|
int chanOpsProcessed = 0;
|
|
int i = 0;
|
|
|
|
for (DBMapType::iterator ptr = snapShot.begin();
|
|
ptr != snapShot.end(); ptr++) {
|
|
theLine.str("");
|
|
theLine << escapeSQLChars(ptr->first) << "\t"
|
|
<< escapeSQLChars(ptr->second.account) << "\t"
|
|
<< escapeSQLChars(ptr->second.lastSeenAs) << "\t"
|
|
<< ptr->second.firstOpped << "\t"
|
|
<< ptr->second.lastOpped
|
|
;
|
|
|
|
for (i = 0; i < DAYSAMPLES; i++) {
|
|
theLine << "\t" << ptr->second.day[i]
|
|
;
|
|
}
|
|
|
|
theLine << "\n"
|
|
;
|
|
|
|
cacheCon->PutLine(theLine.str());
|
|
chanOpsProcessed++;
|
|
}
|
|
|
|
/* Send completion string for the end of the data. */
|
|
cacheCon->PutLine("\\.\n");
|
|
|
|
/**
|
|
* Synchronize with the backend.
|
|
* Returns 0 on success, 1 on failure
|
|
*/
|
|
if( !cacheCon->StopCopyIn() )
|
|
// if (copyStatus != 0) {
|
|
{
|
|
elog << "*** [chanfix::updateDB] Error ending copy!"
|
|
// << copyStatus << " instead (should be 0 for success)."
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
/* Count the rows to see if the cache == number of rows in table. */
|
|
if (!cacheCon->Exec("SELECT count(*) FROM chanOps",true)) {
|
|
elog << "*** [chanfix::updateDB]: Error counting rows in chanOps table: "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
int actualChanOpsProcessed = atoi(cacheCon->GetValue(0, 0));
|
|
if (actualChanOpsProcessed != chanOpsProcessed) {
|
|
elog << "*** [chanfix::updateDB] Error updating chanOps! "
|
|
<< "Only " << actualChanOpsProcessed << " of "
|
|
<< chanOpsProcessed << " chanops were copied to the SQL database."
|
|
<< std::endl;
|
|
logDebugMessage("ERROR: Only %d of %d chanops were updated in %u ms.",
|
|
actualChanOpsProcessed, chanOpsProcessed,
|
|
updateDBTimer.stopTimeMS());
|
|
} else {
|
|
elog << "*** [chanfix::updateDB]: Done. Copied "
|
|
<< actualChanOpsProcessed
|
|
<< " chanops to the SQL database."
|
|
<< std::endl;
|
|
logDebugMessage("Synched %d members to the SQL database in %u ms.",
|
|
actualChanOpsProcessed, updateDBTimer.stopTimeMS());
|
|
}
|
|
|
|
/* Drop the backup table. */
|
|
if (!cacheCon->Exec("DROP TABLE chanOpsBackup")) {
|
|
elog << "*** [chanfix::updateDB]: Error dropping backup table (after completion): "
|
|
<< cacheCon->ErrorMessage()
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
/* Clean-up after ourselves and allow new updates to be started */
|
|
snapShot.clear();
|
|
updateInProgress = false;
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::printResourceStats()
|
|
{
|
|
int who = RUSAGE_SELF;
|
|
struct rusage usage;
|
|
int ret;
|
|
ret = getrusage(who, &usage);
|
|
logDebugMessage("Max. resident size used by chanfix (kB): %ld", usage.ru_maxrss);
|
|
}
|
|
|
|
bool chanfix::isTempBlocked(const std::string& theChan)
|
|
{
|
|
tempBlockType::iterator ptr = tempBlockList.find(theChan);
|
|
if (ptr == tempBlockList.end())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void chanfix::expireTempBlocks()
|
|
{
|
|
time_t unixTime = currentTime();
|
|
std::list<std::string> remList;
|
|
std::list<std::string>::iterator remIterator;
|
|
|
|
for (tempBlockType::iterator ptr = tempBlockList.begin();
|
|
ptr != tempBlockList.end(); ptr++) {
|
|
if ((unixTime - ptr->second) >= TEMPBLOCK_DURATION_TIME)
|
|
remList.push_back(ptr->first);
|
|
}
|
|
|
|
for(remIterator = remList.begin();remIterator != remList.end();)
|
|
{
|
|
tempBlockList.erase(*remIterator);
|
|
remIterator = remList.erase(remIterator);
|
|
}
|
|
}
|
|
|
|
void chanfix::rotateDB()
|
|
{
|
|
/*
|
|
* CODER NOTES:
|
|
* go through everybody and remove the oldest day (set it to 0)
|
|
* then loop back through and pick out the users with 0 points total and check their ts_firstopped
|
|
* if it is older than 1 day, delete the user
|
|
* cache: acct,chan
|
|
*/
|
|
|
|
logDebugMessage("Beginning database rotation.");
|
|
|
|
/* Start our timer */
|
|
Timer rotateDBTimer;
|
|
rotateDBTimer.Start();
|
|
|
|
int deleteCount = 0;
|
|
int errorCount = 0;
|
|
|
|
short nextDay = currentDay;
|
|
setCurrentDay();
|
|
if (nextDay >= (DAYSAMPLES - 1))
|
|
nextDay = 0;
|
|
else
|
|
nextDay++;
|
|
|
|
/**
|
|
* Basically, we need to make sure we do not delete ops that were
|
|
* just added. To do this, we take the currentTime(), subtract
|
|
* POINTS_UPDATE_TIME (plus 10 seconds to make sure), and then
|
|
* check it against the time that he/she was first opped.
|
|
*/
|
|
time_t maxFirstOppedTS = currentTime() - (POINTS_UPDATE_TIME + 10);
|
|
time_t maxLastOppedTS = currentTime() - (DAYSAMPLES * 86400);
|
|
sqlChanOp* curOp;
|
|
std::string curChan;
|
|
|
|
for (sqlChanOpsType::iterator ptr = sqlChanOps.begin();
|
|
ptr != sqlChanOps.end(); ptr++) {
|
|
for (sqlChanOpsType::mapped_type::iterator chanOp = ptr->second.begin();
|
|
chanOp != ptr->second.end();) {
|
|
curOp = chanOp->second;
|
|
curOp->setDay(nextDay, 0);
|
|
curOp->calcTotalPoints();
|
|
if (((curOp->getPoints() <= 0) &&
|
|
(maxFirstOppedTS > curOp->getTimeFirstOpped()))
|
|
|| (maxLastOppedTS > curOp->getTimeLastOpped())) {
|
|
ptr->second.erase(chanOp++);
|
|
delete curOp; curOp = 0;
|
|
} else {
|
|
chanOp++;
|
|
}
|
|
}
|
|
if (!ptr->second.size()) {
|
|
/* Empty channel, start deleting info */
|
|
curChan = ptr->first;
|
|
sqlChannel* sqlChan = getChannelRecord(curChan);
|
|
if (!sqlChan)
|
|
continue;
|
|
|
|
#ifndef REMEMBER_CHANNELS_WITH_NOTES_OR_FLAGS
|
|
if (!sqlChan->deleteAllNotes()) {
|
|
elog << "chanfix::rotateDB> Error: could not delete all the notes of channel "
|
|
<< curChan.c_str()
|
|
<< std::endl;
|
|
}
|
|
|
|
#else
|
|
if (!sqlChan->getFlags()) {
|
|
#endif
|
|
if (!deleteChannelRecord(sqlChan)) {
|
|
elog << "chanfix::rotateDB> Error: could not delete channel "
|
|
<< curChan.c_str()
|
|
<< std::endl;
|
|
errorCount++;
|
|
} else {
|
|
deleteCount++;
|
|
}
|
|
#ifdef REMEMBER_CHANNELS_WITH_NOTES_OR_FLAGS
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
logDebugMessage("Completed database rotation in %u ms.",
|
|
rotateDBTimer.stopTimeMS());
|
|
|
|
logDebugMessage("%i channels were deleted. %i channels errored out while deleting.",
|
|
deleteCount,
|
|
errorCount);
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::giveAllOpsPoints()
|
|
{
|
|
typedef std::list <std::string> ScoredOpsListType;
|
|
|
|
Channel* thisChan;
|
|
ScoredOpsListType scoredOpsList;
|
|
ScoredOpsListType::iterator scOpIter;
|
|
for (xNetwork::channelIterator ptr = Network->channels_begin();
|
|
ptr != Network->channels_end(); ptr++) {
|
|
thisChan = ptr->second;
|
|
if (!canScoreChan(thisChan))
|
|
continue; // Exit the loop and go to the next chan
|
|
if (thisChan->size() >= minClients && !isBeingFixed(thisChan)) {
|
|
scoredOpsList.clear();
|
|
for (Channel::userIterator ptr = thisChan->userList_begin();
|
|
ptr != thisChan->userList_end(); ptr++) {
|
|
ChannelUser* curUser = ptr->second;
|
|
if (curUser->isModeO() && curUser->getClient()->getAccount() != "") {
|
|
//Ok hes an op
|
|
//Grab an iClient for curUser
|
|
scOpIter = std::find(scoredOpsList.begin(), scoredOpsList.end(), curUser->getClient()->getAccount());
|
|
if (scOpIter == scoredOpsList.end()) {
|
|
givePoints(thisChan, curUser->getClient());
|
|
scoredOpsList.push_back(ScoredOpsListType::value_type(curUser->getClient()->getAccount()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
} //giveAllOpsPoints
|
|
|
|
char chanfix::getFlagChar(const sqlcfUser::flagType& whichFlag)
|
|
{
|
|
if (whichFlag == sqlcfUser::F_SERVERADMIN)
|
|
return 'a';
|
|
else if (whichFlag == sqlcfUser::F_BLOCK)
|
|
return 'b';
|
|
else if (whichFlag == sqlcfUser::F_COMMENT)
|
|
return 'c';
|
|
else if (whichFlag == sqlcfUser::F_CHANFIX)
|
|
return 'f';
|
|
else if (whichFlag == sqlcfUser::F_OWNER)
|
|
return 'o';
|
|
else if (whichFlag == sqlcfUser::F_USERMANAGER)
|
|
return 'u';
|
|
else if (whichFlag == sqlcfUser::F_PERMBLOCKER)
|
|
return 'p';
|
|
else
|
|
return ' ';
|
|
}
|
|
|
|
const std::string chanfix::getFlagsString(const sqlcfUser::flagType& whichFlags)
|
|
{
|
|
std::string flagstr;
|
|
if (whichFlags & sqlcfUser::F_SERVERADMIN)
|
|
flagstr += "a";
|
|
if (whichFlags & sqlcfUser::F_BLOCK)
|
|
flagstr += "b";
|
|
if (whichFlags & sqlcfUser::F_COMMENT)
|
|
flagstr += "c";
|
|
if (whichFlags & sqlcfUser::F_CHANFIX)
|
|
flagstr += "f";
|
|
if (whichFlags & sqlcfUser::F_OWNER)
|
|
flagstr += "o";
|
|
if (whichFlags & sqlcfUser::F_USERMANAGER)
|
|
flagstr += "u";
|
|
if (whichFlags & sqlcfUser::F_PERMBLOCKER)
|
|
flagstr += "p";
|
|
|
|
return flagstr;
|
|
}
|
|
|
|
sqlcfUser::flagType chanfix::getFlagType(const char whichChar)
|
|
{
|
|
switch (whichChar) {
|
|
case 'a': return sqlcfUser::F_SERVERADMIN;
|
|
case 'b': return sqlcfUser::F_BLOCK;
|
|
case 'c': return sqlcfUser::F_COMMENT;
|
|
case 'f': return sqlcfUser::F_CHANFIX;
|
|
case 'o': return sqlcfUser::F_OWNER;
|
|
case 'u': return sqlcfUser::F_USERMANAGER;
|
|
case 'p': return sqlcfUser::F_PERMBLOCKER;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const std::string chanfix::getEventName(const int whichEvent)
|
|
{
|
|
if (whichEvent == sqlChannel::EV_MISC)
|
|
return "MISC";
|
|
else if (whichEvent == sqlChannel::EV_NOTE)
|
|
return "NOTE";
|
|
else if (whichEvent == sqlChannel::EV_CHANFIX)
|
|
return "CHANFIX";
|
|
else if (whichEvent == sqlChannel::EV_SIMULATE)
|
|
return "SIMULATE";
|
|
else if (whichEvent == sqlChannel::EV_REQUESTOP)
|
|
return "REQUESTOP";
|
|
else if (whichEvent == sqlChannel::EV_BLOCK)
|
|
return "BLOCK";
|
|
else if (whichEvent == sqlChannel::EV_TEMPBLOCK)
|
|
return "TEMPBLOCK";
|
|
else if (whichEvent == sqlChannel::EV_UNTEMPBLOCK)
|
|
return "UNTEMPBLOCK";
|
|
else if (whichEvent == sqlChannel::EV_UNBLOCK)
|
|
return "UNBLOCK";
|
|
else if (whichEvent == sqlChannel::EV_ALERT)
|
|
return "ALERT";
|
|
else if (whichEvent == sqlChannel::EV_UNALERT)
|
|
return "UNALERT";
|
|
else
|
|
return "";
|
|
}
|
|
|
|
const std::string chanfix::getHelpMessage(sqlcfUser* theUser, std::string topic)
|
|
{
|
|
int lang_id = 1;
|
|
|
|
if (theUser)
|
|
lang_id = theUser->getLanguageId();
|
|
|
|
std::pair <int, std::string> thePair(lang_id, topic);
|
|
helpTableType::iterator ptr = helpTable.find(thePair);
|
|
if (ptr != helpTable.end())
|
|
return ptr->second;
|
|
|
|
if (lang_id != 1)
|
|
return getHelpMessage(theUser, topic);
|
|
|
|
return std::string("");
|
|
}
|
|
|
|
void chanfix::loadHelpTable()
|
|
{
|
|
/* Get a connection instance to our backend */
|
|
//dbHandle* cacheCon = theManager->getConnection();
|
|
|
|
/* Grab the help table */
|
|
std::stringstream theQuery;
|
|
theQuery << "SELECT language_id,topic,contents FROM help"
|
|
;
|
|
if (localDBHandle->Exec("SELECT language_id,topic,contents FROM help",true))
|
|
for (unsigned int i = 0; i < localDBHandle->Tuples(); i++)
|
|
helpTable.insert(helpTableType::value_type(
|
|
std::make_pair(atoi(localDBHandle->GetValue(i, 0)),
|
|
localDBHandle->GetValue(i, 1)),
|
|
localDBHandle->GetValue(i, 2)));
|
|
|
|
elog << "*** [chanfix::loadHelpTable]: Loaded "
|
|
<< helpTable.size()
|
|
<< " help messages."
|
|
<< std::endl;
|
|
|
|
/* Dispose of our connection instance */
|
|
//theManager->removeConnection(cacheCon);
|
|
|
|
return;
|
|
}
|
|
|
|
const std::string chanfix::getResponse( sqlcfUser* theUser,
|
|
int response_id, std::string msg )
|
|
{
|
|
|
|
// Language defaults to English
|
|
int lang_id = 1;
|
|
|
|
if (theUser)
|
|
lang_id = theUser->getLanguageId();
|
|
|
|
std::pair<int, int> thePair( lang_id, response_id );
|
|
|
|
translationTableType::iterator ptr = translationTable.find(thePair);
|
|
if (ptr != translationTable.end()) {
|
|
/* Found something! */
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* Can't find this response Id within a valid language.
|
|
* Realistically we should bomb here, however it might be wise
|
|
* to 'fallback' to a lower language ID and try again, only bombing if we
|
|
* can't find an english variant. (Carrying on here could corrupt
|
|
* numerous varg lists, and will most likely segfault anyway).
|
|
*/
|
|
if (lang_id != 1) {
|
|
std::pair<int, int> thePair( 1, response_id );
|
|
translationTableType::iterator ptr = translationTable.find(thePair);
|
|
if (ptr != translationTable.end())
|
|
return ptr->second ;
|
|
}
|
|
|
|
if (!msg.empty())
|
|
return msg;
|
|
|
|
return std::string( "Unable to retrieve response. Please contact a chanfix "
|
|
"administrator." ) ;
|
|
}
|
|
|
|
void chanfix::loadTranslationTable()
|
|
{
|
|
/* Get a connection instance to our backend */
|
|
/* dbHandle* cacheCon = theManager->getConnection(); */
|
|
|
|
/* Grab the languages table */
|
|
std::stringstream langQuery;
|
|
langQuery << "SELECT id,code,name FROM languages"
|
|
;
|
|
|
|
if (localDBHandle->Exec(langQuery.str(),true))
|
|
for (unsigned int i = 0; i < localDBHandle->Tuples(); i++)
|
|
languageTable.insert(languageTableType::value_type(localDBHandle->GetValue(i, 1),
|
|
std::make_pair(atoi(localDBHandle->GetValue(i, 0)),
|
|
localDBHandle->GetValue(i, 2))));
|
|
|
|
elog << "*** [chanfix::loadTranslationTable]: Loaded "
|
|
<< languageTable.size()
|
|
<< " language" << ((languageTable.size() != 1) ? "s." : ".")
|
|
<< std::endl;
|
|
|
|
/* Grab the translations table */
|
|
std::stringstream transQuery;
|
|
transQuery << "SELECT language_id,response_id,text FROM translations"
|
|
;
|
|
|
|
if (localDBHandle->Exec(transQuery.str(),true)) {
|
|
for (unsigned int i = 0 ; i < localDBHandle->Tuples(); i++) {
|
|
/*
|
|
* Add to our translations table.
|
|
*/
|
|
|
|
int lang_id = atoi(localDBHandle->GetValue( i, 0 ));
|
|
int resp_id = atoi(localDBHandle->GetValue( i, 1 ));
|
|
|
|
std::pair<int, int> thePair( lang_id, resp_id ) ;
|
|
|
|
translationTable.insert(
|
|
translationTableType::value_type(
|
|
thePair, localDBHandle->GetValue( i, 2 )) );
|
|
}
|
|
}
|
|
|
|
elog << "*** [chanfix::loadTranslationTable]: Loaded "
|
|
<< translationTable.size()
|
|
<< " translations."
|
|
<< std::endl;
|
|
|
|
/* Dispose of our connection instance */
|
|
/* theManager->removeConnection(cacheCon); */
|
|
|
|
return;
|
|
}
|
|
|
|
void chanfix::updatePoints()
|
|
{
|
|
/*
|
|
sqlChanOp* curOp;
|
|
|
|
for(chanOpsType::iterator ptr = opList.begin(); ptr != opList.end(); ptr++) {
|
|
curOp = *ptr;
|
|
givePoints(curOp);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*void chanfix::checkDBConnection()
|
|
{
|
|
if (cacheCon->Status() == CONNECTION_BAD) { //Check if the connection has died
|
|
delete(cacheCon);
|
|
dbConnected = false;
|
|
updateSQLDb(NULL);
|
|
MsgChanLog("PANIC! - The Connection With The Db Was Lost\n");
|
|
MsgChanLog("Attempting to reconnect, Attempt %d out of %d\n",
|
|
connectCount+1,connectRetry+1);
|
|
std::string Query = "host=" + sqlHost + " dbname=" + sqlDB + " port=" + sqlPort;
|
|
if (strcasecmp(sqlcfUser,"''"))
|
|
Query += (" user=" + sqlcfUser);
|
|
if (strcasecmp(sqlPass,"''"))
|
|
Query += (" password=" + sqlPass);
|
|
theManager = new (std::nothrow) cmDatabase(Query.c_str());
|
|
assert(theManager != NULL);
|
|
|
|
if (theManager->ConnectionBad()) {
|
|
++connectCount;
|
|
if (connectCount > connectRetry) {
|
|
MsgChanLog("Cant connect to the database, quiting\n");
|
|
::exit(1);
|
|
} else {
|
|
MsgChanLog("Attempt failed\n");
|
|
}
|
|
} else {
|
|
dbConnected = true;
|
|
MsgChanLog("The PANIC is over, db connection restored\n");
|
|
updateSqldb(theManager);
|
|
connectCount = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void chanfix::updateSQLDb(dbHandle* _SQLDb)
|
|
{
|
|
for(glineIterator ptr = glineList.begin();ptr != glineList.end();++ptr)
|
|
{
|
|
(ptr->second)->setSqldb(_SQLDb);
|
|
}
|
|
|
|
for(glineIterator ptr = rnGlineList.begin();ptr != rnGlineList.end();++ptr)
|
|
{
|
|
(ptr->second)->setSqldb(_SQLDb);
|
|
}
|
|
|
|
for(exceptionIterator ptr = exception_begin();ptr != exception_end();++ptr)
|
|
{
|
|
(*ptr)->setSqldb(_SQLDb);
|
|
}
|
|
|
|
for(usersIterator ptr = usersMap.begin();ptr != usersMap.end();++ptr)
|
|
{
|
|
ptr->second->setSqldb(_SQLDb);
|
|
}
|
|
|
|
for(serversIterator ptr = serversMap.begin();ptr != serversMap.end();++ptr)
|
|
{
|
|
ptr->second->setSqldb(_SQLDb);
|
|
}
|
|
}*/
|
|
|
|
void Command::Usage( iClient* theClient )
|
|
{
|
|
sqlcfUser* theUser = bot->isAuthed(theClient->getAccount());
|
|
bot->SendTo(theClient,
|
|
bot->getResponse(theUser,
|
|
language::syntax,
|
|
std::string("SYNTAX: ")).c_str() + getInfo() );
|
|
}
|
|
|
|
/* THIS IS FOR CALCULATING NEW SCORES */
|
|
int chanfix::getNewScore( sqlChanOp* chOp, time_t oldestTS )
|
|
{
|
|
int daysSinceFirstOpOnChan = (currentTime() - oldestTS) / 86400;
|
|
int daysSinceFirstOp = (currentTime() - chOp->getTimeFirstOpped()) / 86400;
|
|
|
|
int x = 0;
|
|
|
|
if (daysSinceFirstOpOnChan < 30)
|
|
x = 100;
|
|
else
|
|
x = 3000 / daysSinceFirstOpOnChan;
|
|
|
|
/* GET NEW SCORE */
|
|
int newScore = (x * daysSinceFirstOp);
|
|
|
|
elog << "chanfix::getNewScore> daysSinceFirstOpOnChan: "
|
|
<< daysSinceFirstOpOnChan <<
|
|
" daysSinceFirstOp: "
|
|
<< daysSinceFirstOp
|
|
<< " x = "
|
|
<< x
|
|
<< " newScore = (x * daysSinceFirstOp) = "
|
|
<< newScore
|
|
<< " oldestTS = "
|
|
<< oldestTS
|
|
<< std::endl;
|
|
|
|
return newScore;
|
|
}
|
|
|
|
} // namespace cf
|
|
|
|
} // namespace gnuworld
|
|
|