8556 lines
227 KiB
C++
8556 lines
227 KiB
C++
/**
|
|
* cservice.cc
|
|
* Author: Greg Sikorski
|
|
* Purpose: Overall control client.
|
|
*
|
|
* 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: cservice.cc,v 1.300 2012/06/03 16:14:46 Seven Exp $
|
|
*/
|
|
|
|
#include <new>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <iomanip>
|
|
#include <utility>
|
|
|
|
#include <ctime>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdarg>
|
|
|
|
#include "client.h"
|
|
#include "cservice.h"
|
|
#include "EConfig.h"
|
|
#include "events.h"
|
|
#include "ip.h"
|
|
#include "Network.h"
|
|
#include "StringTokenizer.h"
|
|
#include "misc.h"
|
|
#include "ELog.h"
|
|
#include "dbHandle.h"
|
|
#include "constants.h"
|
|
#include "networkData.h"
|
|
#include "levels.h"
|
|
#include "cservice_config.h"
|
|
#include "match.h"
|
|
#include "md5hash.h"
|
|
#include "responses.h"
|
|
#include "banMatcher.h"
|
|
|
|
namespace gnuworld
|
|
{
|
|
|
|
using std::pair ;
|
|
using std::vector ;
|
|
using std::endl ;
|
|
using std::stringstream ;
|
|
using std::ends ;
|
|
using std::string ;
|
|
using std::clog ;
|
|
|
|
/*
|
|
* Exported function used by moduleLoader to gain an
|
|
* instance of this module.
|
|
*/
|
|
|
|
extern "C"
|
|
{
|
|
xClient* _gnuwinit(const string& args)
|
|
{
|
|
return new cservice( args );
|
|
}
|
|
}
|
|
|
|
bool cservice::RegisterCommand( Command* newComm )
|
|
{
|
|
UnRegisterCommand( newComm->getName() ) ;
|
|
return commandMap.insert( pairType( newComm->getName(), newComm ) ).second ;
|
|
}
|
|
|
|
bool cservice::UnRegisterCommand( const string& commName )
|
|
{
|
|
commandMapType::iterator ptr = commandMap.find( commName ) ;
|
|
if( ptr == commandMap.end() )
|
|
{
|
|
return false ;
|
|
}
|
|
delete ptr->second ;
|
|
commandMap.erase( ptr ) ;
|
|
return true ;
|
|
}
|
|
|
|
void cservice::OnAttach()
|
|
{
|
|
for( commandMapType::iterator ptr = commandMap.begin() ;
|
|
ptr != commandMap.end() ; ++ptr )
|
|
{
|
|
ptr->second->setServer( MyUplink ) ;
|
|
}
|
|
|
|
// Start the Db checker timer rolling.
|
|
time_t theTime = time(NULL) + connectCheckFreq;
|
|
dBconnection_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the Db update/Reop timer rolling.
|
|
theTime = time(NULL) + updateInterval;
|
|
update_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the ban suspend/expire timer rolling.
|
|
theTime = time(NULL) + expireInterval;
|
|
expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the users DB expire timer rolling.
|
|
theTime = time(NULL) + expireDBUserPeriod;
|
|
users_expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the channels DB expire timer rolling.
|
|
theTime = time(NULL) + expireDBChannelPeriod;
|
|
channels_expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the cache expire timer rolling.
|
|
theTime = time(NULL) + cacheInterval;
|
|
cache_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the pending chan timer rolling.
|
|
theTime = time(NULL) + pendingChanPeriod;
|
|
pending_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
// Start the floating Limit timer rolling.
|
|
theTime = time(NULL) + limitCheckPeriod;
|
|
limit_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
|
|
/* Start the web relay timer rolling.
|
|
* First, empty out any old notices that may be present.
|
|
*/
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::OnAttach::sqlQuery> DELETE FROM "
|
|
<< "webnotices WHERE created_ts < "
|
|
<< "date_part('epoch', CURRENT_TIMESTAMP)::int - 600)"
|
|
<< endl;
|
|
#endif
|
|
if (SQLDb->Exec("DELETE FROM webnotices WHERE created_ts < (date_part('epoch', CURRENT_TIMESTAMP)::int - 600)"))
|
|
{
|
|
/* only register the timer if the query is ok.
|
|
* if the query fails, we most likely don't have
|
|
* the table setup, so pointless checking it.
|
|
*/
|
|
theTime = time(NULL) + webrelayPeriod;
|
|
webrelay_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
} else {
|
|
/* log the error */
|
|
elog << "cservice::OnAttach> Unable to empty webnotices "
|
|
<< "table, not checking webnotices."
|
|
<< endl;
|
|
}
|
|
|
|
if (SQLDb->Exec("SELECT date_part('epoch', CURRENT_TIMESTAMP)::int;",true))
|
|
{
|
|
// Set our "Last Refresh" timers to the current database system time.
|
|
time_t serverTime = atoi(SQLDb->GetValue(0,0).c_str());
|
|
lastChannelRefresh = serverTime;
|
|
lastUserRefresh = serverTime;
|
|
lastLevelRefresh = serverTime;
|
|
lastBanRefresh = serverTime;
|
|
|
|
/*
|
|
* Calculate the current time offset from the DB server.
|
|
* We always want to talk in DB server time.
|
|
*/
|
|
|
|
dbTimeOffset = serverTime - ::time(NULL);
|
|
elog << "*** [CMaster::ImplementServer]: Current DB server time: "
|
|
<< currentTime()
|
|
<< endl;
|
|
}
|
|
else
|
|
{
|
|
elog << "Unable to retrieve time from postgres server!"
|
|
<< endl;
|
|
::exit(0);
|
|
}
|
|
|
|
/* Register our interest in recieving some Network events from gnuworld. */
|
|
|
|
MyUplink->RegisterEvent( EVT_KILL, this );
|
|
MyUplink->RegisterEvent( EVT_QUIT, this );
|
|
MyUplink->RegisterEvent( EVT_NICK, this );
|
|
MyUplink->RegisterEvent( EVT_CHNICK, this );
|
|
MyUplink->RegisterEvent( EVT_ACCOUNT, this );
|
|
MyUplink->RegisterEvent( EVT_BURST_ACK, this );
|
|
MyUplink->RegisterEvent( EVT_XQUERY, this );
|
|
MyUplink->RegisterEvent( EVT_GLINE , this );
|
|
MyUplink->RegisterEvent( EVT_REMGLINE , this );
|
|
MyUplink->RegisterEvent( EVT_SERVERMODE , this );
|
|
|
|
xClient::OnAttach() ;
|
|
}
|
|
|
|
void cservice::OnShutdown(const std::string& reason)
|
|
{
|
|
/* handle client shutdown */
|
|
MyUplink->UnloadClient(this, reason);
|
|
}
|
|
|
|
cservice::cservice(const string& args)
|
|
: xClient( args )
|
|
{
|
|
|
|
#ifdef USE_COMMAND_LOG
|
|
commandLog.open(commandlogPath.c_str());
|
|
#endif
|
|
/*
|
|
* Register command handlers.
|
|
*/
|
|
|
|
RegisterCommand(new SHOWCOMMANDSCommand(this, "SHOWCOMMANDS", "[#channel]", 3));
|
|
RegisterCommand(new LOGINCommand(this, "LOGIN", "<username> <password>", 30));
|
|
RegisterCommand(new ACCESSCommand(this, "ACCESS", "<channel> <username> [-min n] [-max n] [-op] [-voice] [-none] [-modif]", 5));
|
|
RegisterCommand(new CHANINFOCommand(this, "CHANINFO", "<#channel>", 3));
|
|
RegisterCommand(new CHANINFOCommand(this, "INFO", "<username>", 3));
|
|
RegisterCommand(new ISREGCommand(this, "ISREG", "<#channel>", 4));
|
|
RegisterCommand(new VERIFYCommand(this, "VERIFY", "<nick>", 3));
|
|
//RegisterCommand(new SEARCHCommand(this, "SEARCH", "[-min <amount>] <keywords>", 5));
|
|
RegisterCommand(new MOTDCommand(this, "MOTD", "", 4));
|
|
RegisterCommand(new HELPCommand(this, "HELP", "[command]", 4));
|
|
RegisterCommand(new RANDOMCommand(this, "RANDOM", "", 4));
|
|
RegisterCommand(new SHOWIGNORECommand(this, "SHOWIGNORE", "", 3));
|
|
RegisterCommand(new SUPPORTCommand(this, "SUPPORT", "#channel <YES|NO>", 15));
|
|
RegisterCommand(new NOTECommand(this, "NOTE", "send <username> <message>, read all, erase <all|message id>", 10));
|
|
RegisterCommand(new NOTECommand(this, "NOTES", "send <username> <message>, read all, erase <all|message id>", 10));
|
|
RegisterCommand(new OBJECTCommand(this, "OBJECT", "<#channel> <objection>", 8));
|
|
RegisterCommand(new CANCELCommand(this, "CANCEL", "<#channel> <YES|NO>", 8));
|
|
|
|
RegisterCommand(new OPCommand(this, "OP", "<#channel> [nick] [nick] ..", 3));
|
|
RegisterCommand(new DEOPCommand(this, "DEOP", "<#channel> [nick] [nick] ..", 3));
|
|
#ifdef USING_NEFARIOUS
|
|
#ifdef USE_HALFOPS
|
|
RegisterCommand(new HALFOPCommand(this, "HALFOP", "<#channel> [nick] [nick] ..", 3));
|
|
RegisterCommand(new DEHALFOPCommand(this, "DEHALFOP", "<#channel> [nick] [nick] ..", 3));
|
|
#endif
|
|
#endif
|
|
RegisterCommand(new VOICECommand(this, "VOICE", "<#channel> [nick] [nick] ..", 3));
|
|
RegisterCommand(new DEVOICECommand(this, "DEVOICE", "<#channel> [nick] [nick] ..", 3));
|
|
RegisterCommand(new ADDUSERCommand(this, "ADDUSER", "<#channel> <username> <access>", 8));
|
|
RegisterCommand(new REMUSERCommand(this, "REMUSER", "<#channel> <username>", 4));
|
|
RegisterCommand(new MODINFOCommand(this, "MODINFO", "<#channel> [ACCESS <username> <level>] [AUTOMODE <username> <NONE|OP|VOICE>] [INVITE <ON|OFF>]", 6));
|
|
RegisterCommand(new SETCommand(this, "SET", "[#channel] <variable> <value> or, SET <invisible> <ON|OFF> or, SET LANG <language> or, SET MAXLOGINS <max-logins>.", 6));
|
|
RegisterCommand(new INVITECommand(this, "INVITE", "<#channel> <#channel> <#channel> ... ", 2));
|
|
RegisterCommand(new TOPICCommand(this, "TOPIC", "<#channel> <topic>", 4));
|
|
RegisterCommand(new BANLISTCommand(this, "BANLIST", "<#channel>", 3));
|
|
RegisterCommand(new KICKCommand(this, "KICK", "<#channel> <nick> [reason]", 4));
|
|
RegisterCommand(new STATUSCommand(this, "STATUS", "<#channel>", 4));
|
|
RegisterCommand(new SUSPENDCommand(this, "SUSPEND", "<#channel> <username> [duration] [level] [reason]", 5));
|
|
RegisterCommand(new UNSUSPENDCommand(this, "UNSUSPEND", "<#channel> <username> [reason]", 5));
|
|
RegisterCommand(new BANCommand(this, "BAN", "<#channel> <nick | *!*user@*.host> [duration] [level] [reason]", 5));
|
|
RegisterCommand(new UNBANCommand(this, "UNBAN", "<#channel> <*!*user@*.host>", 5));
|
|
RegisterCommand(new LBANLISTCommand(this, "LBANLIST", "<#channel> <banmask>", 5));
|
|
RegisterCommand(new NEWPASSCommand(this, "NEWPASS", "<new passphrase|username>", 8));
|
|
RegisterCommand(new JOINCommand(this, "JOIN", "<#channel>", 8));
|
|
RegisterCommand(new PARTCommand(this, "PART", "<#channel>", 8));
|
|
RegisterCommand(new OPERJOINCommand(this, "OPERJOIN", "<#channel>", 8));
|
|
RegisterCommand(new OPERPARTCommand(this, "OPERPART", "<#channel>", 8));
|
|
RegisterCommand(new CLEARMODECommand(this, "CLEARMODE", "<#channel>", 4));
|
|
RegisterCommand(new SUSPENDMECommand(this, "SUSPENDME", "<password>", 15));
|
|
|
|
RegisterCommand(new WHITELISTCommand(this, "WHITELIST", "<ADD|REM|VIEW> <IP> [duration] [reason]", 10));
|
|
RegisterCommand(new SCANCommand(this, "SCAN", "NICK|NICKNAME <nick>", 10));
|
|
RegisterCommand(new SCANHOSTCommand(this, "SCANHOST", "<mask> [-all]", 10));
|
|
RegisterCommand(new SCANUNAMECommand(this, "SCANUNAME", "<mask> [-all]", 10));
|
|
RegisterCommand(new SCANEMAILCommand(this, "SCANEMAIL", "<mask> [-all]", 10));
|
|
RegisterCommand(new REMIGNORECommand(this, "REMIGNORE", "<mask>", 5));
|
|
RegisterCommand(new REGISTERCommand(this, "REGISTER", "<#channel>", 8));
|
|
RegisterCommand(new REMOVEALLCommand(this, "REMOVEALL", "<#channel>", 15));
|
|
RegisterCommand(new PURGECommand(this, "PURGE", "<username | #channel> [-noop] <reason>", 8));
|
|
RegisterCommand(new ACCEPTCommand(this, "ACCEPT", "<#channel> <decision>", 8));
|
|
RegisterCommand(new REJECTCommand(this, "REJECT", "<#channel> <decision>", 8));
|
|
RegisterCommand(new RENAMECommand(this, "RENAME", "<old_username> <new_username>", 5));
|
|
RegisterCommand(new FORCECommand(this, "FORCE", "<#channel>", 8));
|
|
RegisterCommand(new UNFORCECommand(this, "UNFORCE", "<#channel>", 8));
|
|
RegisterCommand(new SERVNOTICECommand(this, "SERVNOTICE", "<#channel> <text>", 5));
|
|
RegisterCommand(new SAYCommand(this, "SAY", "<#channel> <text>", 5));
|
|
RegisterCommand(new SAYCommand(this, "DO", "<#channel> <text>", 5));
|
|
RegisterCommand(new QUOTECommand(this, "QUOTE", "<text>", 5));
|
|
RegisterCommand(new REHASHCommand(this, "REHASH", "[translations | help | config | motd]", 5));
|
|
RegisterCommand(new STATSCommand(this, "STATS", "", 8));
|
|
RegisterCommand(new ADDCOMMENTCommand(this, "ADDCOMMENT", "<username | #channel> <comment>", 10));
|
|
RegisterCommand(new SHUTDOWNCommand(this, "SHUTDOWN", "<reason>", 10));
|
|
|
|
#ifdef ALLOW_HELLO
|
|
RegisterCommand( new HELLOCommand( this,
|
|
"HELLO", "<username> <email> <email> <1-3> <verification answer>", 10 ) ) ;
|
|
#endif // ALLOW_HELLO
|
|
|
|
cserviceConfig = new (std::nothrow) EConfig( args ) ;
|
|
assert( cserviceConfig != 0 ) ;
|
|
|
|
confSqlHost = cserviceConfig->Require( "sql_host" )->second;
|
|
confSqlPass = cserviceConfig->Require( "sql_pass" )->second;
|
|
confSqlDb = cserviceConfig->Require( "sql_db" )->second;
|
|
confSqlPort = cserviceConfig->Require( "sql_port" )->second;
|
|
confSqlUser = cserviceConfig->Require( "sql_user" )->second;
|
|
|
|
string Query = "host=" + confSqlHost + " dbname=" + confSqlDb + " port=" + confSqlPort + " user=" + confSqlUser
|
|
+ " password=" + confSqlPass;
|
|
|
|
elog << "*** [CMaster]: Attempting to make PostgreSQL connection to: "
|
|
<< confSqlHost
|
|
<< "; Database Name: "
|
|
<< confSqlDb
|
|
<< endl;
|
|
|
|
//SQLDb = new (std::nothrow) cmDatabase( Query.c_str() ) ;
|
|
SQLDb = new (std::nothrow) dbHandle( confSqlHost,
|
|
atoi( confSqlPort.c_str() ),
|
|
confSqlDb,
|
|
confSqlUser,
|
|
confSqlPass ) ;
|
|
assert( SQLDb != 0 ) ;
|
|
|
|
if (SQLDb->ConnectionBad ())
|
|
{
|
|
elog << "*** [CMaster]: Unable to connect to SQL server."
|
|
<< endl
|
|
<< "*** [CMaster]: PostgreSQL error message: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
|
|
::exit( 0 ) ;
|
|
}
|
|
else
|
|
{
|
|
elog << "*** [CMaster]: Connection established to SQL server. "
|
|
<< endl ;
|
|
}
|
|
|
|
// The program will exit if these variables are not defined in the
|
|
// configuration file.
|
|
relayChan = cserviceConfig->Require( "relay_channel" )->second ;
|
|
privrelayChan = cserviceConfig->Require( "priv_relay_channel" )->second ;
|
|
debugChan = cserviceConfig->Require( "debug_channel" )->second ;
|
|
coderChan = cserviceConfig->Require( "coder_channel" )->second ;
|
|
pendingPageURL = cserviceConfig->Require( "pending_page_url" )->second ;
|
|
updateInterval = atoi((cserviceConfig->Require( "update_interval" )->second).c_str());
|
|
expireInterval = atoi((cserviceConfig->Require( "expire_interval" )->second).c_str());
|
|
cacheInterval = atoi((cserviceConfig->Require( "cache_interval" )->second).c_str());
|
|
webrelayPeriod = atoi((cserviceConfig->Require( "webrelay_interval" )->second).c_str());
|
|
input_flood = atoi((cserviceConfig->Require( "input_flood" )->second).c_str());
|
|
output_flood = atoi((cserviceConfig->Require( "output_flood" )->second).c_str());
|
|
flood_duration = atoi((cserviceConfig->Require( "flood_duration" )->second).c_str());
|
|
topic_duration = atoi((cserviceConfig->Require( "topic_duration" )->second).c_str());
|
|
pendingChanPeriod = atoi((cserviceConfig->Require( "pending_duration" )->second).c_str());
|
|
connectCheckFreq = atoi((cserviceConfig->Require( "connection_check_frequency" )->second).c_str());
|
|
connectRetry = atoi((cserviceConfig->Require( "connection_retry_total" )->second).c_str());
|
|
limitCheckPeriod = atoi((cserviceConfig->Require( "limit_check" )->second).c_str());
|
|
loginDelay = atoi((cserviceConfig->Require( "login_delay" )->second).c_str());
|
|
noteDuration = atoi((cserviceConfig->Require( "note_duration" )->second).c_str());
|
|
noteLimit = atoi((cserviceConfig->Require( "note_limit" )->second).c_str());
|
|
preloadUserDays = atoi((cserviceConfig->Require( "preload_user_days" )->second).c_str());
|
|
partIdleChan = atoi((cserviceConfig->Require( "part_idle_chan" )->second).c_str());
|
|
expireDBUserPeriod = atoi((cserviceConfig->Require( "users_db_idle" )->second).c_str());
|
|
expireDBChannelPeriod = atoi((cserviceConfig->Require( "channels_db_idle" )->second).c_str());
|
|
UsersExpireDBDays = atoi((cserviceConfig->Require( "users_expire_days" )->second).c_str());
|
|
startMIA = atoi((cserviceConfig->Require( "MIA_start_days" )->second).c_str());
|
|
endMIA = atoi((cserviceConfig->Require( "MIA_end_days" )->second).c_str());
|
|
hourSeconds = atoi((cserviceConfig->Require( "hour_seconds" )->second).c_str());
|
|
daySeconds = atoi((cserviceConfig->Require( "day_seconds" )->second).c_str());
|
|
MIAStartDesc = cserviceConfig->Require( "MIA_start_desc" )->second;
|
|
MIAURL = cserviceConfig->Require( "MIA_URL" )->second;
|
|
MIAEndDesc = cserviceConfig->Require( "MIA_end_desc" )->second;
|
|
MAXnotes = atoi((cserviceConfig->Require( "max_notes" )->second).c_str());
|
|
RequiredSupporters = atoi((cserviceConfig->Require( "required_supporters" )->second).c_str());
|
|
JudgeDaySeconds = atoi((cserviceConfig->Require( "judge_day_seconds" )->second).c_str());
|
|
MinDaysBeforeReg = atoi((cserviceConfig->Require( "min_days_before_reg" )->second).c_str());
|
|
MinDaysBeforeSupport = atoi((cserviceConfig->Require( "min_days_before_support" )->second).c_str());
|
|
MaxConcurrentSupports = atoi((cserviceConfig->Require( "max_concurrent_supports" )->second).c_str());
|
|
NoRegDaysOnNOSupport = atoi((cserviceConfig->Require( "noreg_days_on_nosupport" )->second).c_str());
|
|
RejectAppOnUserFraud = atoi((cserviceConfig->Require( "reject_app_on_userfraud" )->second).c_str());
|
|
//int AcceptOnTrafficPass = 1;
|
|
RewievOnObject = atoi((cserviceConfig->Require( "rewiev_on_object" )->second).c_str());
|
|
RewievsExpireTime = atoi((cserviceConfig->Require( "rewievs_expire_time" )->second).c_str());
|
|
PendingsExpireTime = atoi((cserviceConfig->Require( "pendings_expire_time" )->second).c_str());
|
|
MaxDays = atoi((cserviceConfig->Require( "max_days" )->second).c_str());
|
|
Joins = atoi((cserviceConfig->Require( "joins" )->second).c_str());
|
|
UniqueJoins = atoi((cserviceConfig->Require( "unique_joins" )->second).c_str());
|
|
MinSupporters = atoi((cserviceConfig->Require( "min_supporters" )->second).c_str());
|
|
MinSupportersJoin = atoi((cserviceConfig->Require( "min_supporters_joins" )->second).c_str());
|
|
NotifyDays = atoi((cserviceConfig->Require( "notify_days" )->second).c_str());
|
|
SupportDays = atoi((cserviceConfig->Require( "support_days" )->second).c_str());
|
|
ReviewerId = atoi((cserviceConfig->Require( "reviewer_id" )->second).c_str());
|
|
|
|
welcomeNewChanMessage = cserviceConfig->Require("welcome_newchan_message")->second ;
|
|
welcomeNewChanTopic = cserviceConfig->Require("welcome_newchan_topic")->second ;
|
|
|
|
#ifdef USE_COMMAND_LOG
|
|
commandlogPath = cserviceConfig->Require( "command_logfile" )->second ;
|
|
#endif
|
|
/* adminlogPath = cserviceConfig->Require( "admin_logfile" )->second ; */
|
|
|
|
#ifdef ALLOW_HELLO
|
|
helloBlockPeriod = atoi(cserviceConfig->Require("hello_block_period")->second.c_str());
|
|
helloSendmailEnabled = atoi((cserviceConfig->Require("hello_sendmail_enabled")->second).c_str()) == 1;
|
|
#endif // ALLOW_HELLO
|
|
|
|
sendmailFrom = cserviceConfig->Require("sendmail_from")->second;
|
|
|
|
#ifdef TOTP_AUTH_ENABLED
|
|
totpAuthEnabled = atoi((cserviceConfig->Require( "enable_totp" )->second).c_str()) == 1;
|
|
#endif
|
|
|
|
reservedHostsList.push_back(hostName);
|
|
reservedHostsList.push_back("*" + iClient::getHiddenHostSuffix());
|
|
|
|
EConfig::const_iterator ptr = cserviceConfig->Find("reservedHost");
|
|
while (ptr != cserviceConfig->end() && ptr->first == "reservedHost")
|
|
{
|
|
reservedHostsList.push_back(ptr->second);
|
|
ptr++;
|
|
}
|
|
|
|
loadConfigData();
|
|
|
|
userHits = 0;
|
|
userCacheHits = 0;
|
|
channelHits = 0;
|
|
channelCacheHits = 0;
|
|
levelHits = 0;
|
|
levelCacheHits = 0;
|
|
banHits = 0;
|
|
banCacheHits = 0;
|
|
dbErrors = 0;
|
|
joinCount = 0;
|
|
connectRetries = 0;
|
|
totalCommands = 0;
|
|
if (hourSeconds < 1) hourSeconds = 1;
|
|
if (daySeconds < 1) daySeconds = 1;
|
|
if (expireDBUserPeriod < 1) expireDBUserPeriod = 8;
|
|
if (expireDBChannelPeriod < 1) expireDBChannelPeriod = 3;
|
|
if (MAXnotes == 0) MAXnotes = 3;
|
|
expireDBUserPeriod *= hourSeconds;
|
|
expireDBChannelPeriod *= hourSeconds;
|
|
UsersExpireDBDays *= daySeconds;
|
|
startMIA *= daySeconds;
|
|
endMIA *= daySeconds;
|
|
|
|
/* Load our translation tables. */
|
|
loadTranslationTable();
|
|
|
|
/* Load help messages */
|
|
loadHelpTable();
|
|
|
|
/* Preload the Channel Cache */
|
|
preloadChannelCache();
|
|
|
|
/* Preload the Ban Cache */
|
|
preloadBanCache();
|
|
|
|
/* Preload the Level cache */
|
|
preloadLevelsCache();
|
|
|
|
/* Preload any user accounts we want to */
|
|
preloadUserCache();
|
|
|
|
/* Load the glines from db */
|
|
loadGlines();
|
|
|
|
/*
|
|
* Init the admin log.
|
|
*/
|
|
|
|
/* adminLog.open( adminlogPath.c_str() ) ;
|
|
if( !adminLog.is_open() )
|
|
{
|
|
clog << "*** Unable to open CMaster admin log file: "
|
|
<< adminlogPath
|
|
<< endl ;
|
|
::exit( 0 ) ;
|
|
} */
|
|
|
|
loadIncompleteChanRegs();
|
|
|
|
}
|
|
|
|
cservice::~cservice()
|
|
{
|
|
delete cserviceConfig ; cserviceConfig = 0 ;
|
|
delete SQLDb ; SQLDb = 0 ;
|
|
|
|
for( commandMapType::iterator ptr = commandMap.begin() ;
|
|
ptr != commandMap.end() ; ++ptr )
|
|
{
|
|
delete ptr->second ;
|
|
}
|
|
|
|
commandMap.clear() ;
|
|
|
|
for (incompleteChanRegsType::iterator ptr = incompleteChanRegs.begin();
|
|
ptr != incompleteChanRegs.end(); ++ptr )
|
|
{
|
|
delete ptr->second;
|
|
}
|
|
incompleteChanRegs.clear();
|
|
reservedHostsList.clear();
|
|
}
|
|
|
|
void cservice::BurstChannels()
|
|
{
|
|
/*
|
|
* Need to join every channel with AUTOJOIN set. (But not * ;))
|
|
* Various other things must be done, such as setting the topic if AutoTopic
|
|
* is on.
|
|
*/
|
|
|
|
Channel* tmpChan;
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.begin();
|
|
while (ptr != sqlChannelCache.end())
|
|
{
|
|
sqlChannel* theChan = (ptr)->second;
|
|
|
|
/* we're now interested in registered channels even if we're not in it... */
|
|
if(theChan->getName() == "*")
|
|
{ //Do not do anything for the admins channel
|
|
++ptr;
|
|
continue;
|
|
}
|
|
|
|
MyUplink->RegisterChannelEvent( theChan->getName(), this ) ;
|
|
|
|
tmpChan = Network->findChannel(theChan->getName());
|
|
|
|
if (tmpChan && tmpChan->getCreationTime() < theChan->getChannelTS())
|
|
theChan->setChannelTS(tmpChan->getCreationTime());
|
|
|
|
if (theChan->getFlag(sqlChannel::F_AUTOJOIN))
|
|
{
|
|
string tempModes = theChan->getChannelMode();
|
|
if (tempModes.empty())
|
|
tempModes = "+";
|
|
if (tempModes.find('R') == string::npos)
|
|
tempModes += 'R';
|
|
MyUplink->JoinChannel( this,
|
|
theChan->getName(),
|
|
tempModes,
|
|
theChan->getChannelTS(),
|
|
true );
|
|
|
|
theChan->setInChan(true);
|
|
DoTheRightThing(tmpChan);
|
|
joinCount++;
|
|
|
|
if (getConfigVar("BAN_CHECK_ON_BURST")->asInt() == 1)
|
|
{
|
|
/* check current inhabitants of the channel against our banlist */
|
|
tmpChan = Network->findChannel(theChan->getName());
|
|
for (Channel::userIterator chanUsers = tmpChan->userList_begin();
|
|
chanUsers != tmpChan->userList_end(); ++chanUsers)
|
|
{
|
|
ChannelUser* tmpUser = chanUsers->second;
|
|
/* check if this user is banned */
|
|
(void)checkBansOnJoin(tmpChan, theChan, tmpUser->getClient());
|
|
}
|
|
}
|
|
} else {
|
|
/* although AUTOJOIN isn't set, set the channel to +R if
|
|
* it exists on the Network.
|
|
*/
|
|
if (tmpChan)
|
|
{
|
|
stringstream tmpTS;
|
|
tmpTS << theChan->getChannelTS();
|
|
string channelTS = tmpTS.str();
|
|
MyUplink->Mode(NULL, tmpChan, string("+R"), channelTS );
|
|
}
|
|
}
|
|
++ptr;
|
|
}
|
|
|
|
logDebugMessage("Channel join complete.");
|
|
|
|
return xClient::BurstChannels();
|
|
}
|
|
|
|
void cservice::OnConnect()
|
|
{
|
|
// TODO: I changed this from return 0
|
|
xClient::OnConnect() ;
|
|
}
|
|
|
|
unsigned short cservice::getFloodPoints(iClient* theClient)
|
|
{
|
|
/*
|
|
* This function gets an iClient's flood points.
|
|
*/
|
|
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return 0;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
return tmpData->flood_points;
|
|
}
|
|
|
|
void cservice::setFloodPoints(iClient* theClient, unsigned short amount)
|
|
{
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if (!tmpData)
|
|
{
|
|
return;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
tmpData->flood_points = amount;
|
|
}
|
|
|
|
/**
|
|
* This method sets a timestamp for when we last recieved
|
|
* a message from this iClient.
|
|
*/
|
|
void cservice::setLastRecieved(iClient* theClient, time_t last_recieved)
|
|
{
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
tmpData->messageTime = last_recieved;
|
|
}
|
|
|
|
/**
|
|
* This method gets a timestamp from this iClient
|
|
* for flood control.
|
|
*/
|
|
time_t cservice::getLastRecieved(iClient* theClient)
|
|
{
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return 0;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
return tmpData->messageTime;
|
|
}
|
|
|
|
bool cservice::isIgnored(iClient* theClient)
|
|
{
|
|
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return tmpData->ignored;
|
|
}
|
|
|
|
void cservice::setIgnored(iClient* theClient, bool _ignored)
|
|
{
|
|
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tmpData->ignored = _ignored;
|
|
}
|
|
|
|
bool cservice::hasFlooded(iClient* theClient, const string& type)
|
|
{
|
|
if( (getLastRecieved(theClient) + flood_duration) <= ::time(NULL) )
|
|
{
|
|
/*
|
|
* Reset a few things, they're out of the flood period now.
|
|
* Or, this is the first message from them.
|
|
*/
|
|
|
|
setFloodPoints(theClient, 0);
|
|
setOutputTotal(theClient, 0);
|
|
setLastRecieved(theClient, ::time(NULL));
|
|
ipFloodMap[theClient->getIP()]=0;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Inside the flood period, check their points..
|
|
*/
|
|
|
|
if(getFloodPoints(theClient) > input_flood)
|
|
{
|
|
/*
|
|
* Check admin access, if present then
|
|
* don't trigger.
|
|
*/
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser && getAdminAccessLevel(theUser))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Bad boy!
|
|
setFloodPoints(theClient, 0);
|
|
setLastRecieved(theClient, ::time(NULL));
|
|
Notice(theClient,
|
|
"Flood me will you? I'm not going to listen to "
|
|
"you anymore.");
|
|
|
|
// Send a silence numeric target, and mask to ignore
|
|
// messages from this user.
|
|
string silenceMask = string( "*!*" )
|
|
+ theClient->getUserName()
|
|
+ "@"
|
|
+ theClient->getInsecureHost();
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " SILENCE "
|
|
<< theClient->getCharYYXXX()
|
|
<< " "
|
|
<< silenceMask
|
|
<< ends;
|
|
Write( s );
|
|
|
|
time_t expireTime = currentTime() + 3600;
|
|
silenceList.insert(silenceListType::value_type(silenceMask,
|
|
std::make_pair(expireTime, theClient->getCharYYXXX())));
|
|
|
|
setIgnored(theClient, true);
|
|
|
|
string floodComment;
|
|
StringTokenizer st(type) ;
|
|
|
|
if( st.size() >= 2 )
|
|
{
|
|
floodComment = st[0] + ' ' + st[1];
|
|
} else {
|
|
floodComment = st[0];
|
|
}
|
|
|
|
if (getConfigVar("FLOOD_MESSAGES")->asInt()==1)
|
|
{
|
|
logAdminMessage("MSG-FLOOD from %s (%s)",
|
|
theClient->getNickUserHost().c_str(),
|
|
floodComment.c_str());
|
|
}
|
|
return true;
|
|
} // if()
|
|
|
|
if (ipFloodMap[theClient->getIP()]>input_flood*5)
|
|
{
|
|
setLastRecieved(theClient, ::time(NULL));
|
|
|
|
string silenceMask = string( "*!*" )
|
|
+ theClient->getUserName()
|
|
+ "@"
|
|
+ theClient->getInsecureHost();
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " SILENCE "
|
|
<< theClient->getCharYYXXX()
|
|
<< " "
|
|
<< silenceMask
|
|
<< ends;
|
|
Write( s );
|
|
|
|
Notice(theClient,
|
|
"Flood me will you? I'm not going to listen to "
|
|
"you or your friends anymore.");
|
|
|
|
time_t expireTime = currentTime() + 3600;
|
|
silenceList.insert(silenceListType::value_type(silenceMask,
|
|
std::make_pair(expireTime, theClient->getCharYYXXX())));
|
|
|
|
setIgnored(theClient, true);
|
|
|
|
string floodComment;
|
|
StringTokenizer st(type);
|
|
|
|
if (st.size() >= 2)
|
|
{
|
|
floodComment = st[0] + ' ' + st[1];
|
|
} else {
|
|
floodComment = st[0];
|
|
}
|
|
|
|
if (getConfigVar("FLOOD_MESSAGES")->asInt()==1)
|
|
{
|
|
logAdminMessage("IP-FLOOD from %s (%s)",
|
|
theClient->getNickUserHost().c_str(),
|
|
floodComment.c_str());
|
|
}
|
|
return true;
|
|
}
|
|
} // else()
|
|
|
|
return false;
|
|
}
|
|
|
|
string cservice::NickIsRegisteredTo(const string& Nick)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT user_name FROM users WHERE lower(nickname) = '"
|
|
<< escapeSQLChars(string_lower(Nick))
|
|
<< "'"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on cservice::NickIsRegisteredTo::nicknameQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "cservice::NickIsRegisteredTo::nicknameQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return string();
|
|
}
|
|
else if (SQLDb->Tuples() > 0)
|
|
return (string)SQLDb->GetValue(0,0);
|
|
return string();
|
|
}
|
|
|
|
void cservice::generateNickName(iClient* theClient)
|
|
{
|
|
string genNick = theClient->getNickName();
|
|
string baseNick = genNick;
|
|
if (baseNick.length() > 25)
|
|
baseNick = baseNick.substr(20);
|
|
do
|
|
{
|
|
srand(time(0));
|
|
int rNum = rand() % 9000 + 1000;
|
|
genNick = baseNick + itoa(rNum);
|
|
} while (Network->findNick(genNick));
|
|
stringstream s;
|
|
s << getCharYY()
|
|
<< " SN "
|
|
<< theClient->getCharYYXXX()
|
|
<< " "
|
|
<< genNick
|
|
<< ends
|
|
<< endl;
|
|
Write( s );
|
|
//Write("%s SN %s %s", getCharYY().c_str(), theClient->getCharYYXXX().c_str(), genNick.c_str());
|
|
return ;
|
|
}
|
|
|
|
void cservice::validateNickName(iClient* theClient)
|
|
{
|
|
if (theClient->getMode(iClient::MODE_SERVICES))
|
|
return;
|
|
bool isRegNick = false;
|
|
string ownerUser = NickIsRegisteredTo(theClient->getNickName());
|
|
//elog << "cservice::validateNickName " << theClient->getNickName() <<"'s owner user is '" << ownerUser << "'" << endl;
|
|
if (!ownerUser.empty())
|
|
isRegNick = true;
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (isRegNick)
|
|
{
|
|
if (!theUser)
|
|
{
|
|
generateNickName(theClient);
|
|
Notice(theClient, "You don't own that nick, your nick has been changed by force.");
|
|
return;
|
|
}
|
|
else if ((string_lower(theUser->getNickName()) != string_lower(theClient->getNickName())))
|
|
{
|
|
generateNickName(theClient);
|
|
Notice(theClient, "You don't own that nick, your nick has been changed by force.");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void cservice::setOutputTotal(const iClient* theClient, unsigned int count)
|
|
{
|
|
/*
|
|
* This function sets the current output total in bytes
|
|
* for this client.
|
|
*/
|
|
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) );
|
|
|
|
if (!tmpData)
|
|
{
|
|
return;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
tmpData->outputCount = count;
|
|
}
|
|
|
|
unsigned int cservice::getOutputTotal(const iClient* theClient)
|
|
{
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) );
|
|
|
|
if (!tmpData)
|
|
{
|
|
return 0;
|
|
}
|
|
//assert(tmpData != NULL);
|
|
|
|
return tmpData->outputCount;
|
|
}
|
|
|
|
bool cservice::hasOutputFlooded(iClient* theClient)
|
|
{
|
|
|
|
if( (getLastRecieved(theClient) + flood_duration) <= ::time(NULL) )
|
|
{
|
|
/*
|
|
* Reset a few things, they're out of the flood period now.
|
|
* Or, this is the first message from them.
|
|
*/
|
|
|
|
setOutputTotal(theClient, 0);
|
|
setFloodPoints(theClient, 0);
|
|
setLastRecieved(theClient, ::time(NULL));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Inside the flood period, check their output count.
|
|
*/
|
|
|
|
if(getOutputTotal(theClient) > output_flood)
|
|
{
|
|
/*
|
|
* Check admin access, if present then
|
|
* don't trigger.
|
|
*/
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser && getAdminAccessLevel(theUser))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
setOutputTotal(theClient, 0);
|
|
setLastRecieved(theClient, ::time(NULL));
|
|
Notice(theClient, "I think I've sent you a little "
|
|
"too much data, I'm going to ignore you "
|
|
"for a while.");
|
|
|
|
// Send a silence numeric target, and mask to ignore
|
|
// messages from this user.
|
|
string silenceMask = string( "*!*" )
|
|
+ theClient->getUserName()
|
|
+ "@"
|
|
+ theClient->getInsecureHost();
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " SILENCE "
|
|
<< theClient->getCharYYXXX()
|
|
<< " "
|
|
<< silenceMask
|
|
<< ends;
|
|
Write( s );
|
|
|
|
time_t expireTime = currentTime() + 3600;
|
|
|
|
silenceList.insert(silenceListType::value_type(silenceMask,
|
|
std::make_pair(expireTime, theClient->getCharYYXXX())));
|
|
|
|
setIgnored(theClient, true);
|
|
|
|
if (getConfigVar("FLOOD_MESSAGES")->asInt()==1)
|
|
{
|
|
logAdminMessage("OUTPUT-FLOOD from %s",
|
|
theClient->getNickUserHost().c_str());
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void cservice::OnPrivateMessage( iClient* theClient,
|
|
const string& Message,
|
|
bool secure )
|
|
{
|
|
/*
|
|
* Private message handler. Pass off the command to the relevant
|
|
* handler.
|
|
*/
|
|
|
|
if (isIgnored(theClient)) return ;
|
|
|
|
StringTokenizer st( Message ) ;
|
|
if( st.empty() )
|
|
{
|
|
Notice( theClient, "Incomplete command");
|
|
return ;
|
|
}
|
|
|
|
#ifdef USE_COMMAND_LOG
|
|
commandLog << (secure ? "[" : "<") << theClient->getNickUserHost() << (secure ? "] " : "> ") << Message << endl;
|
|
#endif
|
|
|
|
/*
|
|
* Do flood checking - admins at 750 or above are excempt.
|
|
* N.B: Only check that *after* someone has flooded ;)
|
|
*/
|
|
const string Command = string_upper( st[ 0 ] ) ;
|
|
|
|
/*
|
|
* Just quickly, abort if someone tries to LOGIN or NEWPASS
|
|
* unsecurely.
|
|
*/
|
|
|
|
if (!secure && ((Command == "LOGIN") || (Command == "NEWPASS") || (Command == "SUSPENDME")) )
|
|
{
|
|
Notice(theClient, "To use %s, you must /msg %s@%s",
|
|
Command.c_str(),
|
|
nickName.c_str(),
|
|
getUplinkName().c_str());
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* If the person issuing this command is an authenticated admin, we need to log
|
|
* it to the admin log for accountability purposes.
|
|
*
|
|
* Moved this logging further down the function (to SQL) so that only recognised
|
|
* commands are logged. This stops logging of passwords etc
|
|
*
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser && getAdminAccessLevel(theUser))
|
|
{
|
|
adminLog << ::time(NULL) << " " << theClient->getRealNickUserHost() << " " << st.assemble() << endl;
|
|
}
|
|
*/
|
|
|
|
/* Attempt to find a handler for this method. */
|
|
|
|
commandMapType::iterator commHandler = commandMap.find( Command ) ;
|
|
if( commHandler == commandMap.end() )
|
|
{
|
|
/* Don't reply to unknown commands, but add to their flood
|
|
* total :)
|
|
*/
|
|
if (hasFlooded(theClient, "PRIVMSG"))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
if (hasOutputFlooded(theClient))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
// Why use 3 here? Should be in config file
|
|
// (Violation of "rule of numbers")
|
|
setFloodPoints(theClient, getFloodPoints(theClient) + 3);
|
|
ipFloodMap[theClient->getIP()] += 3;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check users flood limit, if exceeded..
|
|
*/
|
|
|
|
if (hasFlooded(theClient, Message))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
if (hasOutputFlooded(theClient))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
setFloodPoints(theClient, getFloodPoints(theClient)
|
|
+ commHandler->second->getFloodPoints() );
|
|
ipFloodMap[theClient->getIP()] += commHandler->second->getFloodPoints();
|
|
|
|
totalCommands++;
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
|
|
/* Check IP restriction (if admin level) - this is in case you get added as admin AFTER logging in */
|
|
if (theUser && needIPRcheck(theUser) && (Command != "LOGIN"))
|
|
{
|
|
/* ok, they have a valid user and are listed as admin (and this is not a login request) */
|
|
if (!passedIPR(theClient))
|
|
{
|
|
/* not passed IPR yet, do it */
|
|
if (!checkIPR(theClient, theUser))
|
|
{
|
|
/* failed IPR! */
|
|
Notice(theClient, "Insufficient access for this command. (IPR)");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
/* Log command to SQL here, if Admin */
|
|
if (theUser && getAdminAccessLevel(theUser) && (Command != "LOGIN") && (Command != "NEWPASS") && (Command != "SUSPENDME"))
|
|
{
|
|
stringstream theLog;
|
|
theLog << "INSERT INTO adminlog (user_id,cmd,args,timestamp,issue_by)"
|
|
<< " VALUES("
|
|
<< theUser ->getID()
|
|
<< ","
|
|
<< "'" << escapeSQLChars(Command) << "'"
|
|
<< ","
|
|
<< "'" << escapeSQLChars(st.assemble(1)) << "'"
|
|
<< ","
|
|
<< currentTime()
|
|
<< ","
|
|
<< "'" << escapeSQLChars(theClient->getRealNickUserHost()) << "'"
|
|
<< ")"
|
|
<< ends;
|
|
|
|
if( !SQLDb->Exec(theLog ) )
|
|
// if( PGRES_COMMAND_OK != status )
|
|
{
|
|
elog << "cservice::adminlog> Something went wrong: "
|
|
<< theLog.str().c_str()
|
|
<< " "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
commHandler->second->Exec( theClient, Message ) ;
|
|
}
|
|
|
|
xClient::OnPrivateMessage( theClient, Message ) ;
|
|
}
|
|
|
|
void cservice::OnCTCP( iClient* theClient, const string& CTCP,
|
|
const string& Message, bool )
|
|
{
|
|
/**
|
|
* CTCP hander. Deal with PING, GENDER and VERSION.
|
|
* Hit users with a '5' flood score for CTCP's.
|
|
* This should be in the config file.
|
|
*/
|
|
|
|
incStat("CORE.CTCP");
|
|
|
|
if (isIgnored(theClient)) return ;
|
|
|
|
if (hasFlooded(theClient, "CTCP"))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
setFloodPoints(theClient, getFloodPoints(theClient) + 5 );
|
|
ipFloodMap[theClient->getIP()]+=5;
|
|
|
|
StringTokenizer st( CTCP ) ;
|
|
if( st.empty() )
|
|
{
|
|
return ;
|
|
}
|
|
|
|
const string Command = string_upper(st[0]);
|
|
|
|
if(Command == "PING" || Command=="ECHO")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP, Message);
|
|
}
|
|
else if(Command == "GENDER")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP,
|
|
"Tried to be a man again - there was a slip - now I'm an IT");
|
|
}
|
|
else if(Command == "VERSION")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP,
|
|
"UnderNet Channel Services III ["
|
|
__DATE__ " " __TIME__
|
|
"] Release 2.1.5");
|
|
}
|
|
else if(Command == "PROBLEM?")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "Blame Bleep!");
|
|
}
|
|
else if(Command == "PUSHER")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "Pak, Chooie, Unf.");
|
|
}
|
|
else if(Command == "SOUND")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "I'm deaf, remember?");
|
|
}
|
|
else if(Command == "DCC")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "REJECT");
|
|
}
|
|
else if(Command == "FLOOD")
|
|
/* Ignored */;
|
|
else if(Command == "PAGE")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "I'm always here, no need to page");
|
|
}
|
|
else if(Command == "USERINFO")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "I'm a user, not an abuser");
|
|
}
|
|
else if(Command == "TIME")
|
|
{
|
|
xClient::DoCTCP(theClient, CTCP.c_str(), "Time you got a watch?");
|
|
}
|
|
else
|
|
{
|
|
xClient::DoCTCP(theClient, "ERRMSG", CTCP.c_str());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if this user is 'forced' onto this channel.
|
|
*/
|
|
unsigned short cservice::isForced(sqlChannel* theChan, sqlUser* theUser)
|
|
{
|
|
if (!theChan->forceMap.empty())
|
|
{
|
|
sqlChannel::forceMapType::iterator ptr = theChan->forceMap.find(theUser->getID());
|
|
if(ptr != theChan->forceMap.end())
|
|
{
|
|
/* So they do, return their forced level. */
|
|
return ptr->second.first;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Confirms a user is logged in by returning a pointer to
|
|
* the sqlUser record.
|
|
* If 'alert' is true, send a notice to the user informing
|
|
* them that they must be logged in.
|
|
*/
|
|
sqlUser* cservice::isAuthed(iClient* theClient, bool alert)
|
|
{
|
|
networkData* tmpData =
|
|
static_cast< networkData* >( theClient->getCustomData(this) ) ;
|
|
|
|
if(!tmpData)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//assert( tmpData != 0 ) ;
|
|
|
|
sqlUser* theUser = tmpData->currentUser;
|
|
|
|
if( theUser )
|
|
{
|
|
return theUser;
|
|
}
|
|
|
|
if( alert )
|
|
{
|
|
Notice(theClient,
|
|
"Sorry, You must be logged in to use this command.");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Locates a cservice user record by 'id', the username of this user.
|
|
*/
|
|
sqlUser* cservice::getUserRecord(const string& id)
|
|
{
|
|
/*
|
|
* Check if this is a lookup by nick
|
|
*/
|
|
if (id[0]=='=')
|
|
{
|
|
const char* theNick = id.c_str();
|
|
// Skip the '='
|
|
++theNick;
|
|
|
|
iClient *client = Network->findNick(theNick);
|
|
if (client) return isAuthed(client,false);
|
|
|
|
return NULL;
|
|
}
|
|
/*
|
|
* Check if this record is already in the cache.
|
|
*/
|
|
|
|
sqlUserHashType::iterator ptr = sqlUserCache.find(id);
|
|
if(ptr != sqlUserCache.end())
|
|
{
|
|
// Found something!
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cmaster::getUserRecord> Cache hit for "
|
|
<< id
|
|
<< endl;
|
|
#endif
|
|
|
|
ptr->second->setLastUsed(currentTime());
|
|
userCacheHits++;
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* We didn't find anything in the cache, fetch the data from
|
|
* the backend and create a new sqlUser object.
|
|
*/
|
|
|
|
sqlUser* theUser = new (std::nothrow) sqlUser(SQLDb);
|
|
assert( theUser != 0 ) ;
|
|
|
|
if (theUser->loadData(id))
|
|
{
|
|
sqlUserCache.insert(sqlUserHashType::value_type(id, theUser));
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cmaster::getUserRecord> There are "
|
|
<< sqlUserCache.size()
|
|
<< " elements in the cache."
|
|
<< endl;
|
|
#endif
|
|
|
|
userHits++;
|
|
|
|
// Return the new user to the caller
|
|
theUser->setLastUsed(currentTime());
|
|
return theUser;
|
|
}
|
|
else
|
|
{
|
|
delete theUser ;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Locates a cservice user record by 'id', the userId number of this user. (Seven)
|
|
*/
|
|
sqlUser* cservice::getUserRecord(int Id)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT user_name FROM users WHERE id = "
|
|
<< Id
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{ logDebugMessage("Error on getUserRecordUserIdQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "getUserRecordUserIdQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return NULL;
|
|
} else if (SQLDb->Tuples() == 0)
|
|
{
|
|
logDebugMessage("getUserRecordUserIdQuery = 0 !");
|
|
return NULL;
|
|
}
|
|
string id = SQLDb->GetValue(0,0);
|
|
/*
|
|
* Check if this record is already in the cache.
|
|
*/
|
|
|
|
sqlUserHashType::iterator ptr = sqlUserCache.find(id);
|
|
if(ptr != sqlUserCache.end())
|
|
{
|
|
// Found something!
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cmaster::getUserRecord> Cache hit for "
|
|
<< id
|
|
<< endl;
|
|
#endif
|
|
|
|
ptr->second->setLastUsed(currentTime());
|
|
userCacheHits++;
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* We didn't find anything in the cache, fetch the data from
|
|
* the backend and create a new sqlUser object.
|
|
*/
|
|
|
|
sqlUser* theUser = new (std::nothrow) sqlUser(SQLDb);
|
|
assert( theUser != 0 ) ;
|
|
|
|
if (theUser->loadData(id))
|
|
{
|
|
sqlUserCache.insert(sqlUserHashType::value_type(id, theUser));
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cmaster::getUserRecord> There are "
|
|
<< sqlUserCache.size()
|
|
<< " elements in the cache."
|
|
<< endl;
|
|
#endif
|
|
|
|
userHits++;
|
|
|
|
// Return the new user to the caller
|
|
theUser->setLastUsed(currentTime());
|
|
return theUser;
|
|
}
|
|
else
|
|
{
|
|
delete theUser ;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void cservice::updateUserCacheUserName(sqlUser* updateUser, const string& newUserName)
|
|
{
|
|
sqlUserHashType::iterator ptr = sqlUserCache.find(updateUser->getUserName());
|
|
if(ptr != sqlUserCache.end())
|
|
{
|
|
// Found something!
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cmaster::getUserRecord> Cache hit for "
|
|
<< updateUser->getUserName()
|
|
<< endl;
|
|
#endif
|
|
|
|
ptr->second->setLastUsed(currentTime());
|
|
userCacheHits++;
|
|
//(*ptr)->first = string(newUserName);
|
|
//(*ptr).swap(sqlUserHashType::value_type(newUserName,updateUser));
|
|
//sqlUserCache[ptr->first].swap(sqlUserHashType::value_type(newUserName,updateUser));
|
|
updateUser->setUserName(newUserName);
|
|
sqlUserCache.erase(ptr);
|
|
sqlUserCache.insert(sqlUserHashType::value_type(newUserName, updateUser));
|
|
return;
|
|
}
|
|
|
|
sqlUser* theUser = new (std::nothrow) sqlUser(SQLDb);
|
|
assert( theUser != 0 ) ;
|
|
|
|
if (theUser->loadData(updateUser->getID()))
|
|
{
|
|
sqlUserCache.insert(sqlUserHashType::value_type(updateUser->getUserName(), theUser));
|
|
#ifdef LOG_SQL
|
|
elog << "cmaster::getUserRecord> There are "
|
|
<< sqlUserCache.size()
|
|
<< " elements in the cache."
|
|
<< endl;
|
|
#endif
|
|
userHits++;
|
|
|
|
// Return the new user to the caller
|
|
theUser->setLastUsed(currentTime());
|
|
theUser->setUserName(newUserName);
|
|
return;
|
|
}
|
|
logDebugMessage("Failed to update userCache. User %s (%i)", updateUser->getUserName().c_str(), updateUser->getID());
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Locates a channel record by 'id', the channel name.
|
|
*/
|
|
sqlChannel* cservice::getChannelRecord(const string& id)
|
|
{
|
|
|
|
/*
|
|
* Check if this record is already in the cache.
|
|
*/
|
|
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.find(id);
|
|
if(ptr != sqlChannelCache.end())
|
|
{
|
|
channelCacheHits++;
|
|
ptr->second->setLastUsed(currentTime());
|
|
|
|
// Return the channel to the caller
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* We didn't find anything in the cache.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Loads a channel from the cache by 'id'.
|
|
*/
|
|
sqlChannel* cservice::getChannelRecord(int id)
|
|
{
|
|
|
|
/*
|
|
* Check if this record is already in the cache.
|
|
*/
|
|
|
|
sqlChannelIDHashType::iterator ptr = sqlChannelIDCache.find(id);
|
|
if(ptr != sqlChannelIDCache.end())
|
|
{
|
|
channelCacheHits++;
|
|
ptr->second->setLastUsed(currentTime());
|
|
|
|
// Return the channel to the caller
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* We didn't find anything in the cache.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
sqlLevel* cservice::getLevelRecord( sqlUser* theUser, sqlChannel* theChan )
|
|
{
|
|
// Check if the record is already in the cache.
|
|
pair<int, int> thePair( theUser->getID(), theChan->getID() );
|
|
|
|
sqlLevelHashType::iterator ptr = sqlLevelCache.find(thePair);
|
|
if(ptr != sqlLevelCache.end())
|
|
{
|
|
// Found something!
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cmaster::getLevelRecord> Cache hit for "
|
|
<< "user-id:chan-id "
|
|
<< theUser->getID() << ":"
|
|
<< theChan->getID()
|
|
<< endl;
|
|
#endif
|
|
|
|
levelCacheHits++;
|
|
ptr->second->setLastUsed(currentTime());
|
|
return ptr->second ;
|
|
}
|
|
|
|
/*
|
|
* We didn't find anything in the cache.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Check if a user has already passed IPR checks
|
|
*/
|
|
bool cservice::passedIPR(iClient* theClient)
|
|
{
|
|
networkData* tmpData = static_cast< networkData* >(theClient->getCustomData(this));
|
|
|
|
/* if the user has no network data, the haven't passed IPR */
|
|
if (!tmpData)
|
|
return false;
|
|
|
|
if (tmpData->ipr_ts > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set the client's IPR timestamp (can also be used to clear it)
|
|
*/
|
|
|
|
void cservice::setIPRts(iClient* theClient, unsigned int _ipr_ts)
|
|
{
|
|
networkData* tmpData = static_cast< networkData* >(theClient->getCustomData(this));
|
|
|
|
if (!tmpData)
|
|
return;
|
|
|
|
/* set the timestamp */
|
|
tmpData->ipr_ts = _ipr_ts;
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Check a user against IP restrictions
|
|
*/
|
|
bool cservice::checkIPR( iClient* theClient, sqlUser* theUser )
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT allowmask,allowrange1,allowrange2,added FROM "
|
|
<< "ip_restrict WHERE user_id = "
|
|
<< theUser->getID()
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::checkIPR::sqlQuery> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
// if (PGRES_TUPLES_OK != status)
|
|
{
|
|
/* SQL error, fail them */
|
|
elog << "cservice::checkIPR> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return false;
|
|
}
|
|
if (SQLDb->Tuples() < 1)
|
|
{
|
|
#ifdef IPR_DEFAULT_REJECT
|
|
/* no entries, fail them */
|
|
return false;
|
|
#else
|
|
/* no entries, allow them */
|
|
return true;
|
|
#endif
|
|
}
|
|
/* cycle through results to find a match */
|
|
bool ipr_match = false;
|
|
unsigned int ipr_ts = 0;
|
|
unsigned int tmpIP = xIP(theClient->getIP()).GetLongIP();
|
|
for (unsigned int i=0; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/* get some variables out of the db row */
|
|
std::string ipr_allowmask = SQLDb->GetValue(i, 0);
|
|
unsigned int ipr_allowrange1 = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
unsigned int ipr_allowrange2 = atoi(SQLDb->GetValue(i, 2).c_str());
|
|
ipr_ts = atoi(SQLDb->GetValue(i, 3).c_str());
|
|
|
|
/* is this an IP range? */
|
|
if (ipr_allowrange2 > 0)
|
|
{
|
|
/* yes it is, is the client IP between range1 and range2? */
|
|
if ((tmpIP >= ipr_allowrange1) && (tmpIP <= ipr_allowrange2))
|
|
{
|
|
ipr_match = true;
|
|
break;
|
|
}
|
|
} else {
|
|
/* no, is it a single IP? */
|
|
if (ipr_allowrange1 > 0)
|
|
{
|
|
/* yes it is, does the IP match range1? */
|
|
if (tmpIP == ipr_allowrange1)
|
|
{
|
|
ipr_match = true;
|
|
break;
|
|
}
|
|
} else {
|
|
/* no, is it a hostmask? */
|
|
if (ipr_allowmask.size() > 0)
|
|
{
|
|
/* yes it is, does it match our hostname? */
|
|
if (!match(ipr_allowmask, theClient->getRealInsecureHost()) ||
|
|
!match(ipr_allowmask, xIP(theClient->getIP()).GetNumericIP()))
|
|
{
|
|
ipr_match = true;
|
|
break;
|
|
}
|
|
} else {
|
|
/* no, fail */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* check if we found a match yet */
|
|
if (!ipr_match)
|
|
{
|
|
/* no match, fail them */
|
|
return false;
|
|
} else {
|
|
/* IP restriction check passed - mark it against this user */
|
|
setIPRts(theClient, ipr_ts);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch the number of failed logins for a client
|
|
*/
|
|
unsigned int cservice::getFailedLogins(iClient* theClient)
|
|
{
|
|
networkData* tmpData = static_cast< networkData* >(theClient->getCustomData(this));
|
|
|
|
/* if the user has no network data, they have zero failed logins */
|
|
if (!tmpData)
|
|
return 0;
|
|
|
|
return tmpData->failed_logins;
|
|
}
|
|
|
|
/**
|
|
* Set the number of failed logins for a client
|
|
*/
|
|
void cservice::setFailedLogins(iClient* theClient, unsigned int _failed_logins)
|
|
{
|
|
networkData* tmpData = static_cast< networkData* >(theClient->getCustomData(this));
|
|
|
|
/* if the user has no network data, we cant set it */
|
|
if (!tmpData)
|
|
return;
|
|
|
|
/* set the failed login counter */
|
|
tmpData->failed_logins = _failed_logins;
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Returns true or false to whether IPR checks are required.
|
|
*/
|
|
bool cservice::needIPRcheck(sqlUser* theUser)
|
|
{
|
|
/* don't need to check in the case of alumni */
|
|
if (theUser->getFlag(sqlUser::F_ALUMNI))
|
|
return 0;
|
|
/* check if they have access to '*' */
|
|
sqlChannel* theChan = getChannelRecord("*");
|
|
if (!theChan)
|
|
{
|
|
elog << "cservice::needIPRcheck> Unable to "
|
|
<< "locate channel '*'!"
|
|
<< endl ;
|
|
::exit(0);
|
|
}
|
|
sqlLevel* theLevel = getLevelRecord(theUser, theChan);
|
|
if (theLevel)
|
|
{
|
|
if (theLevel->getAccess() > 0)
|
|
return true;
|
|
}
|
|
/* if we reach here, no IPR checks are needed */
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the admin access level a particular user has.
|
|
*/
|
|
short int cservice::getAdminAccessLevel( sqlUser* theUser, bool verify )
|
|
{
|
|
|
|
/*
|
|
* First thing, check if this ACCOUNT has been globally
|
|
* suspended.
|
|
*/
|
|
if (theUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (theUser->getFlag(sqlUser::F_NOADMIN))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Are they an alumni (check except for 'verify' command) ? */
|
|
if (!verify && (theUser->getFlag(sqlUser::F_ALUMNI)))
|
|
return 0;
|
|
|
|
sqlChannel* theChan = getChannelRecord("*");
|
|
if (!theChan)
|
|
{
|
|
elog << "cservice::getAdminAccessLevel> Unable to "
|
|
<< "locate channel '*'! Sorry, I can't continue.."
|
|
<< endl;
|
|
::exit(0);
|
|
}
|
|
|
|
sqlLevel* theLevel = getLevelRecord(theUser, theChan);
|
|
if(theLevel)
|
|
{
|
|
/* check if they have been suspended! */
|
|
if (theLevel->getSuspendExpire() > currentTime())
|
|
return 0;
|
|
return theLevel->getAccess();
|
|
}
|
|
|
|
// By default, users have level 0 admin access.
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the coder access level a particular user has.
|
|
*/
|
|
short int cservice::getCoderAccessLevel( sqlUser* theUser )
|
|
{
|
|
if (theUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
sqlChannel* theChan = getChannelRecord(coderChan);
|
|
if (!theChan)
|
|
{
|
|
elog << "cservice::getAdminAccessLevel> Unable to "
|
|
<< "locate channel '"
|
|
<< coderChan.c_str()
|
|
<< "'! Sorry, I can't continue.."
|
|
<< endl;
|
|
::exit(0);
|
|
}
|
|
|
|
sqlLevel* theLevel = getLevelRecord(theUser, theChan);
|
|
if(theLevel)
|
|
{
|
|
return theLevel->getAccess();
|
|
}
|
|
|
|
// By default, users have level 0 admin access.
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the access level a particular user has on a particular
|
|
* channel taking into account channel & user level suspensions.
|
|
* Also used to return the level of access granted to a forced access.
|
|
*
|
|
* Usage: When determining if we should grant a permission to a user
|
|
* to access a particular command/function.
|
|
* To determine the effect access level of a target user.
|
|
*/
|
|
short int cservice::getEffectiveAccessLevel( sqlUser* theUser,
|
|
sqlChannel* theChan, bool notify )
|
|
{
|
|
|
|
/*
|
|
* First thing, check if this ACCOUNT has been globally
|
|
* suspended.
|
|
*/
|
|
|
|
if (theUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
if (theUser->isAuthed() && notify)
|
|
{
|
|
noticeAllAuthedClients(theUser, "Your account has been suspended.");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Have a look to see if this user has forced some access.
|
|
*/
|
|
|
|
unsigned short forcedAccess = isForced(theChan, theUser);
|
|
if (forcedAccess)
|
|
{
|
|
return forcedAccess;
|
|
}
|
|
|
|
sqlLevel* theLevel = getLevelRecord(theUser, theChan);
|
|
if( !theLevel )
|
|
{
|
|
return 0 ;
|
|
}
|
|
|
|
/* Then, check to see if the channel has been suspended. */
|
|
|
|
if (theChan->getFlag(sqlChannel::F_SUSPEND))
|
|
{
|
|
/* Send them a notice to let them know they've been bad? */
|
|
if (theUser->isAuthed() && notify)
|
|
{
|
|
noticeAllAuthedClients(theUser, "The channel %s has been suspended by a cservice administrator.",
|
|
theChan->getName().c_str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check to see if this particular access record has been
|
|
* suspended too.
|
|
*/
|
|
|
|
if (theLevel->getSuspendExpire() > currentTime())
|
|
{
|
|
// Send them a notice.
|
|
if (theUser->isAuthed() && notify)
|
|
{
|
|
//TODO Do this with getresponse
|
|
noticeAllAuthedClients(theUser, getResponse(theUser,
|
|
language::acc_susp).c_str(),theChan->getName().c_str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* We need to use getAdminAccessLevel in case the check is made against *,
|
|
* otherwise userflags (like ALUMNI) are not taken into account.
|
|
*/
|
|
|
|
sqlChannel* adminChan = getChannelRecord("*");
|
|
if (theChan == adminChan)
|
|
{
|
|
return getAdminAccessLevel(theUser, false);
|
|
}
|
|
|
|
|
|
return theLevel->getAccess();
|
|
}
|
|
|
|
/**
|
|
* Returns the access level a particular user has on a particular
|
|
* channel. Plain and simple. If the user has 500 in the channel
|
|
* record, this function returns 500.
|
|
*/
|
|
short int cservice::getAccessLevel( sqlUser* theUser,
|
|
sqlChannel* theChan )
|
|
{
|
|
sqlLevel* theLevel = getLevelRecord(theUser, theChan);
|
|
if(theLevel)
|
|
{
|
|
return theLevel->getAccess();
|
|
}
|
|
|
|
/* By default, users have level 0 access on a channel. */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the help message for the specified topic in
|
|
* this user's prefered language
|
|
*/
|
|
const string cservice::getHelpMessage(sqlUser* theUser, string topic)
|
|
{
|
|
int lang_id = 1;
|
|
|
|
if (theUser)
|
|
lang_id = theUser->getLanguageId();
|
|
|
|
pair <int, string> thePair(lang_id, topic);
|
|
helpTableType::iterator ptr = helpTable.find(thePair);
|
|
if (ptr != helpTable.end())
|
|
return ptr->second;
|
|
|
|
if (lang_id != 1)
|
|
return getHelpMessage(NULL, topic);
|
|
|
|
return string("");
|
|
}
|
|
|
|
/**
|
|
* Execute an SQL query to retrieve all the help messages.
|
|
*/
|
|
void cservice::loadHelpTable()
|
|
{
|
|
if( SQLDb->Exec("SELECT language_id,topic,contents FROM help", true ) )
|
|
//if (PGRES_TUPLES_OK == status)
|
|
for (unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
helpTable.insert(helpTableType::value_type(std::make_pair(
|
|
atoi(SQLDb->GetValue(i, 0).c_str()),
|
|
SQLDb->GetValue(i, 1)),
|
|
SQLDb->GetValue(i, 2)));
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::loadHelpTable]: Loaded "
|
|
<< helpTable.size()
|
|
<< " help messages."
|
|
<< endl;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Returns response id "response_id" for this user's prefered
|
|
* language.
|
|
*/
|
|
const string cservice::getResponse( sqlUser* theUser, int response_id,
|
|
string msg )
|
|
{
|
|
|
|
// Language defaults to English
|
|
int lang_id = 1;
|
|
|
|
if (theUser)
|
|
{
|
|
lang_id = theUser->getLanguageId();
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 string( "Unable to retrieve response. Please contact a cservice "
|
|
"administrator." ) ;
|
|
}
|
|
|
|
/**
|
|
* Execute an SQL query to retrieve all the translation data.
|
|
*/
|
|
void cservice::loadTranslationTable()
|
|
{
|
|
if( SQLDb->Exec("SELECT id,code,name FROM languages", true ) )
|
|
//if (PGRES_TUPLES_OK == status)
|
|
for (unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
languageTable.insert(languageTableType::value_type(SQLDb->GetValue(i, 1),
|
|
std::make_pair(atoi(SQLDb->GetValue(i, 0).c_str()),
|
|
SQLDb->GetValue(i, 2))));
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::loadTranslationTable]: Loaded "
|
|
<< languageTable.size()
|
|
<< " languages."
|
|
<< endl;
|
|
#endif
|
|
|
|
if( SQLDb->Exec(
|
|
"SELECT language_id,response_id,text FROM translations", true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/*
|
|
* Add to our translations table.
|
|
*/
|
|
|
|
int lang_id = atoi(SQLDb->GetValue( i, 0 ).c_str());
|
|
int resp_id = atoi(SQLDb->GetValue( i, 1 ).c_str());
|
|
|
|
pair<int, int> thePair( lang_id, resp_id ) ;
|
|
|
|
translationTable.insert(
|
|
translationTableType::value_type(
|
|
thePair, SQLDb->GetValue( i, 2 )) );
|
|
}
|
|
}
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::loadTranslationTable]: Loaded "
|
|
<< translationTable.size()
|
|
<< " translations."
|
|
<< endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
int cservice::rehashMOTD() {
|
|
/*
|
|
* Trying to rehash the MOTD.
|
|
* If it succeeds, we return when it was last adjusted.
|
|
*/
|
|
|
|
int lang_id = 1;
|
|
pair<int, int> thePair( lang_id, language::motd );
|
|
translationTableType::iterator ptr = translationTable.find(thePair);
|
|
if(ptr != translationTable.end())
|
|
{
|
|
translationTable.erase(thePair);
|
|
stringstream selectQuery;
|
|
selectQuery << "SELECT text,last_updated FROM "
|
|
<< "translations WHERE language_id = "
|
|
<< lang_id
|
|
<< "AND response_id = "
|
|
<< language::motd
|
|
<< ends;
|
|
if( SQLDb->Exec( selectQuery, true ) )
|
|
{
|
|
translationTable.insert(translationTableType::value_type(thePair, SQLDb->GetValue(0,0)) );
|
|
int last_updated = atoi(SQLDb->GetValue(0,1));
|
|
return last_updated;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool cservice::isOnChannel( const string& /* chanName */ ) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* This member function executes an SQL query to return all
|
|
* suspended access level records, and unsuspend them.
|
|
* TODO
|
|
*/
|
|
void cservice::expireSuspends()
|
|
{
|
|
#ifdef LOG_DEBUG
|
|
elog << "cservice::expireSuspends> Checking for expired Suspensions.."
|
|
<< endl;
|
|
#endif
|
|
time_t expiredTime = currentTime();
|
|
stringstream expireQuery;
|
|
expireQuery << "SELECT user_id,channel_id FROM levels "
|
|
<< "WHERE suspend_expires <= "
|
|
<< expiredTime
|
|
<< " AND suspend_expires <> 0"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "expireSuspends::sqlQuery> "
|
|
<< expireQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(expireQuery, true ) )
|
|
//if( PGRES_TUPLES_OK != status )
|
|
{
|
|
elog << "cservice::expireSuspends> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* Loops over the results set, and attempt to locate
|
|
* this level record in the cache.
|
|
*/
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::expireSuspends> Found "
|
|
<< SQLDb->Tuples()
|
|
<< " expired suspensions."
|
|
<< endl;
|
|
#endif
|
|
|
|
/*
|
|
* Place our query results into temporary storage, because
|
|
* we might have to execute other queries inside the
|
|
* loop which will invalidate our results set.
|
|
*/
|
|
|
|
typedef vector < pair < string, string > > expireVectorType;
|
|
expireVectorType expireVector;
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
expireVector.push_back(expireVectorType::value_type(
|
|
SQLDb->GetValue(i, 0),
|
|
SQLDb->GetValue(i, 1) )
|
|
);
|
|
}
|
|
|
|
for (expireVectorType::const_iterator resultPtr = expireVector.begin();
|
|
resultPtr != expireVector.end(); ++resultPtr)
|
|
{
|
|
/*
|
|
* Attempt to find this level record in the cache.
|
|
*/
|
|
pair<int, int> thePair( atoi(resultPtr->first.c_str()),
|
|
atoi(resultPtr->second.c_str()) );
|
|
|
|
sqlLevelHashType::iterator Lptr = sqlLevelCache.find(thePair);
|
|
if(Lptr != sqlLevelCache.end())
|
|
{
|
|
/* Found it in the cache, remove suspend. */
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cservice::expireSuspends> "
|
|
<< "Found level record in cache: "
|
|
<< resultPtr->first
|
|
<< ":"
|
|
<< resultPtr->second
|
|
<< endl;
|
|
#endif
|
|
(Lptr->second)->setSuspendExpire(0);
|
|
(Lptr->second)->setSuspendLevel(0);
|
|
(Lptr->second)->setSuspendBy(string());
|
|
(Lptr->second)->setSuspendReason(string());
|
|
}
|
|
|
|
/*
|
|
* Execute a query to update the status in the db.
|
|
*/
|
|
|
|
} // for()
|
|
|
|
stringstream updateQuery;
|
|
updateQuery << "UPDATE levels SET suspend_expires = 0, suspend_level = 0, suspend_by = '', suspend_reason = ''"
|
|
<< " WHERE suspend_expires <= "
|
|
<< expiredTime
|
|
<< " AND suspend_expires <> 0";
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "expireSuspends::sqlQuery> "
|
|
<< updateQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(updateQuery ) )
|
|
//if( status != PGRES_COMMAND_OK)
|
|
{
|
|
elog << "cservice::expireSuspends> Unable to "
|
|
<< "update record while unsuspending."
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function removes any ignores that have expired.
|
|
*/
|
|
void cservice::expireSilence()
|
|
{
|
|
|
|
silenceListType::iterator ptr = silenceList.begin();
|
|
while (ptr != silenceList.end())
|
|
{
|
|
if ( ptr->second.first < currentTime() )
|
|
{
|
|
string theMask = ptr->first;
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " SILENCE "
|
|
<< "*"
|
|
<< " -"
|
|
<< theMask
|
|
<< ends;
|
|
Write( s );
|
|
|
|
/*
|
|
* Locate this user by numeric.
|
|
* If the numeric is still in use, clear the ignored flag.
|
|
* If someone else has inherited this numeric, no prob,
|
|
* its cleared anyway.
|
|
*/
|
|
|
|
iClient* theClient = Network->findClient(ptr->second.second);
|
|
if (theClient)
|
|
{
|
|
setIgnored(theClient, false);
|
|
}
|
|
|
|
++ptr;
|
|
silenceList.erase(theMask);
|
|
} else {
|
|
++ptr;
|
|
}
|
|
|
|
} // while()
|
|
|
|
}
|
|
|
|
/**
|
|
* This member function executes an SQL query to return all
|
|
* bans that have expired. It removes them from the internal
|
|
* cache as well as the database.
|
|
*/
|
|
void cservice::expireBans()
|
|
{
|
|
#ifdef LOG_DEBUG
|
|
elog << "cservice::expireBans> Checking for expired bans.."
|
|
<< endl;
|
|
#endif
|
|
time_t expiredTime = currentTime();
|
|
stringstream expireQuery;
|
|
expireQuery << "SELECT channel_id,id FROM bans "
|
|
<< "WHERE expires <= "
|
|
<< expiredTime
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlQuery> "
|
|
<< expireQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(expireQuery, true ) )
|
|
//if( PGRES_TUPLES_OK != status )
|
|
{
|
|
elog << "cservice::expireBans> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* Loops over the results set, and attempt to locate
|
|
* this ban in the cache.
|
|
*/
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::expireBans> Found "
|
|
<< SQLDb->Tuples()
|
|
<< " expired bans."
|
|
<< endl;
|
|
#endif
|
|
|
|
/*
|
|
* Place our query results into temporary storage, because
|
|
* we might have to execute other queries inside the
|
|
* loop which will invalidate our results set.
|
|
*/
|
|
typedef vector < pair < unsigned int, unsigned int > > expireVectorType;
|
|
expireVectorType expireVector;
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
expireVector.push_back(expireVectorType::value_type(
|
|
atoi(SQLDb->GetValue(i, 0).c_str()),
|
|
atoi(SQLDb->GetValue(i, 1).c_str()) )
|
|
);
|
|
}
|
|
|
|
for (expireVectorType::const_iterator resultPtr = expireVector.begin();
|
|
resultPtr != expireVector.end(); ++resultPtr)
|
|
{
|
|
sqlChannel* theChan = getChannelRecord( resultPtr->first );
|
|
if (!theChan)
|
|
{
|
|
// TODO: Debuglog.
|
|
continue;
|
|
}
|
|
|
|
#ifdef LOG_DEBUG
|
|
elog << "Checking bans for "
|
|
<< theChan->getName()
|
|
<< endl;
|
|
#endif
|
|
|
|
/* Attempt to find the ban according to its id */
|
|
map< int,sqlBan* >::iterator ptr =
|
|
theChan->banList.find(resultPtr->second);
|
|
|
|
/* Was a ban found ? */
|
|
if (ptr != theChan->banList.end())
|
|
{
|
|
sqlBan* theBan = ptr->second;
|
|
theChan->banList.erase(ptr);
|
|
|
|
Channel* tmpChan = Network->findChannel(
|
|
theChan->getName());
|
|
if (tmpChan)
|
|
{
|
|
UnBan(tmpChan, theBan->getBanMask());
|
|
}
|
|
|
|
#ifdef LOG_DEBUG
|
|
elog << "Cleared Ban "
|
|
<< theBan->getBanMask()
|
|
<< " from cache"
|
|
<< endl;
|
|
#endif
|
|
|
|
delete(theBan);
|
|
}
|
|
else
|
|
{
|
|
// BUG: Correct me if Im wrong, but this will crash,
|
|
// right?
|
|
sqlBan* theBan = ptr->second;
|
|
elog << "Unable to find ban "
|
|
<< theBan->getBanMask()
|
|
<< " with id "
|
|
<< theBan->getID()
|
|
<< endl;
|
|
}
|
|
} /* Forall results in set */
|
|
|
|
stringstream deleteQuery;
|
|
deleteQuery << "DELETE FROM bans "
|
|
<< "WHERE expires <= "
|
|
<< expiredTime
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlQuery> "
|
|
<< deleteQuery.str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(deleteQuery ) )
|
|
//if( PGRES_COMMAND_OK != status )
|
|
{
|
|
elog << "cservice::expireBans> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return ;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This function iterates over the usercache, and removes
|
|
* accounts not accessed in a certain timeframe.
|
|
* N.B: do *NOT* expire those with a networkClient set.
|
|
* (Ie: those currently logged in).
|
|
*/
|
|
void cservice::cacheExpireUsers()
|
|
{
|
|
logDebugMessage("Beginning User cache cleanup:");
|
|
sqlUserHashType::iterator ptr = sqlUserCache.begin();
|
|
sqlUser* tmpUser;
|
|
clock_t startTime = ::clock();
|
|
clock_t endTime = 0;
|
|
int purgeCount = 0;
|
|
int updateCount = 0;
|
|
string removeKey;
|
|
|
|
while (ptr != sqlUserCache.end())
|
|
{
|
|
tmpUser = ptr->second;
|
|
/*
|
|
* Have a quick look if this person has been logged in more than 24 hrs.
|
|
* If so, update the last-seen so their account doesn't expire after xx days. ;)
|
|
*/
|
|
if ( tmpUser->isAuthed() && ((tmpUser->getInstantiatedTS() + 86400) < ::time(NULL)) )
|
|
{
|
|
/* check to see if we have a last seen time (bug workaround) - if not, make one */
|
|
|
|
stringstream queryString;
|
|
queryString << "SELECT last_seen FROM users_lastseen WHERE user_id="
|
|
<< tmpUser->getID()
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::cacheExpireUsers::sqlQuery> "
|
|
<< queryString.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( SQLDb->Exec(queryString, true ) )
|
|
// if (PGRES_TUPLES_OK == status)
|
|
{
|
|
if (SQLDb->Tuples() < 1)
|
|
{
|
|
/* no rows returned - create a dummy record that will be updated
|
|
* by setLastSeen after this loop
|
|
*/
|
|
stringstream updateQuery;
|
|
updateQuery << "INSERT INTO users_lastseen (user_id,"
|
|
<< "last_seen,last_updated) VALUES("
|
|
<< tmpUser->getID()
|
|
<< ",date_part('epoch', CURRENT_TIMESTAMP)::int,date_part('epoch', CURRENT_TIMESTAMP)::int)"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::cacheExpireUsers::sqlQuery> "
|
|
<< updateQuery.str()
|
|
<< endl;
|
|
#endif
|
|
SQLDb->Exec(updateQuery.str());
|
|
}
|
|
}
|
|
/* update their details */
|
|
tmpUser->setLastSeen(currentTime());
|
|
tmpUser->setInstantiatedTS(::time(NULL));
|
|
updateCount++;
|
|
}
|
|
|
|
/*
|
|
* If this user has been idle one hour, and currently
|
|
* isn't logged in, boot him out the window.
|
|
*/
|
|
if ( ((tmpUser->getLastUsed() + 3600) < currentTime()) &&
|
|
!tmpUser->isAuthed() )
|
|
{
|
|
#ifdef LOG_DEBUG
|
|
elog << "cservice::cacheExpireUsers> "
|
|
<< tmpUser->getUserName()
|
|
<< "; last used: "
|
|
<< tmpUser->getLastUsed()
|
|
<< endl;
|
|
#endif
|
|
purgeCount++;
|
|
removeKey = ptr->first;
|
|
delete(ptr->second);
|
|
/* Advance the iterator past the soon to be
|
|
* removed element. */
|
|
++ptr;
|
|
sqlUserCache.erase(removeKey);
|
|
}
|
|
else
|
|
{
|
|
++ptr;
|
|
}
|
|
}
|
|
endTime = ::clock();
|
|
logDebugMessage("User cache cleanup complete; Removed %i user records in %i ms.",
|
|
purgeCount, (endTime - startTime) / CLOCKS_PER_SEC);
|
|
logDebugMessage("I also updated %i last_seen records for people logged in for >24 hours.",
|
|
updateCount);
|
|
}
|
|
|
|
void cservice::fixUsersLastSeen()
|
|
{
|
|
typedef vector <std::pair < unsigned int, string > > uidVectorType;
|
|
uidVectorType uidVector;
|
|
vector <unsigned int> toFixVector;
|
|
|
|
stringstream queryString;
|
|
queryString << "SELECT id,user_name FROM users"
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(queryString, true))
|
|
{
|
|
logDebugMessage("Error on cservice::fixUsersLastSeenIDQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "cservice::fixUsersLastSeenIDQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
}
|
|
for (unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
uidVector.push_back(uidVectorType::value_type(atoi(SQLDb->GetValue(i,0).c_str()),SQLDb->GetValue(i,1).c_str()));
|
|
|
|
if (uidVector.empty()) return;
|
|
|
|
logDebugMessage("Total usercount to check fixUsersLastSeen: %i",(int)uidVector.size());
|
|
|
|
for (size_t i=0; i<uidVector.size(); i++)
|
|
{
|
|
queryString.str("");
|
|
queryString << "SELECT last_seen FROM users_lastseen WHERE user_id="
|
|
<< uidVector.at(i).first
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(queryString, true))
|
|
{
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "cservice::fixUsersLastSeenLastSeenQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
logDebugMessage("Error on cservice::fixUsersLastSeenLastSeenQuery");
|
|
#endif
|
|
return;
|
|
}
|
|
if (SQLDb->Tuples() == 0)
|
|
//logDebugMessage("Found %i result(s) for userid (%i)",SQLDb->Tuples(),uidVector.at(i));
|
|
{
|
|
sqlUser* wipeUser;
|
|
sqlUserHashType::iterator usrItr = sqlUserCache.find(uidVector.at(i).second);
|
|
if (usrItr != sqlUserCache.end())
|
|
wipeUser = usrItr->second;
|
|
else
|
|
wipeUser = getUserRecord((int)uidVector.at(i).first);
|
|
if (wipeUser)
|
|
{
|
|
// Somehow the user has no creation time, so set it now, and leave alone
|
|
if (wipeUser->getCreatedTS() == 0)
|
|
{
|
|
wipeUser->setCreatedTS(currentTime());
|
|
wipeUser->commit(this->getInstance());
|
|
}
|
|
else //only at least 1 day old users can expire
|
|
if ((wipeUser->getCreatedTS() + 86400 < currentTime()) && (wipeUser->getLastSeen() == 0))
|
|
{
|
|
toFixVector.push_back(uidVector.at(i).first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
logDebugMessage("Beginning to wipe %i users:",(int)toFixVector.size());
|
|
for (size_t i=0; i<toFixVector.size(); i++)
|
|
wipeUser(toFixVector.at(i),false);
|
|
logDebugMessage("Completed.");
|
|
}
|
|
|
|
void cservice::cacheExpireLevels()
|
|
{
|
|
logDebugMessage("Beginning Channel Level-cache cleanup:");
|
|
|
|
/*
|
|
* While we are at this, we'll clear out any FORCE'd access's
|
|
* in channels.
|
|
*/
|
|
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.begin();
|
|
while (ptr != sqlChannelCache.end())
|
|
{
|
|
sqlChannel* theChan = (ptr)->second;
|
|
if(theChan->forceMap.size() > 0)
|
|
{
|
|
logDebugMessage("Clearing out %i FORCE(s) from channel %s",
|
|
theChan->forceMap.size(), theChan->getName().c_str());
|
|
theChan->forceMap.clear();
|
|
}
|
|
/*
|
|
* While we're here, lets see if this channel has been idle for a while.
|
|
* If so, we might want to part and turn off autojoin.. etc.
|
|
*/
|
|
|
|
if ( ((currentTime() - theChan->getLastUsed()) >= partIdleChan)
|
|
&& theChan->getInChan()
|
|
&& !theChan->getFlag(sqlChannel::F_SPECIAL) )
|
|
{
|
|
/*
|
|
* So long! and thanks for all the fish.
|
|
*/
|
|
|
|
theChan->setInChan(false);
|
|
theChan->removeFlag(sqlChannel::F_AUTOJOIN);
|
|
theChan->commit();
|
|
joinCount--;
|
|
writeChannelLog(theChan, me, sqlChannel::EV_IDLE, "");
|
|
logDebugMessage("I've just left %s because its too quiet.",
|
|
theChan->getName().c_str());
|
|
Part(theChan->getName(), "So long! (And thanks for all the fish)");
|
|
}
|
|
|
|
++ptr;
|
|
}
|
|
logDebugMessage("Channel Level cache-cleanup complete.");
|
|
}
|
|
|
|
bool cservice::deleteUserFromTable(unsigned int userId, const string& table)
|
|
{
|
|
/* We can safely do this, because the user is so long seen
|
|
* that is *very probably* is not in the users cache
|
|
*/
|
|
string userIdStr = "user_id";
|
|
if (table == "pending") userIdStr = "manager_id";
|
|
if (table == "users") userIdStr = "id";
|
|
if (table == "pending_mgrchange") userIdStr = "new_manager_id";
|
|
stringstream queryString;
|
|
queryString << "DELETE FROM " << table << " WHERE " << userIdStr << " = "
|
|
<< userId
|
|
<< endl;
|
|
if (!SQLDb->Exec(queryString,true))
|
|
{
|
|
logDebugMessage("wipeUser FAILED to delete user %i from %s",userId,table.c_str());
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::wipeUser> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
logDebugMessage(SQLDb->ErrorMessage().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::wipeUser(unsigned int userId, bool expired)
|
|
{
|
|
sqlUser* tmpUser = getUserRecord(userId);
|
|
assert(tmpUser != 0);
|
|
string removeKey;
|
|
//Don't expire any newly created, never logged user sooner than 10 minutes
|
|
if (((int)(currentTime() - tmpUser->getCreatedTS()) < 600) && (expired)) goto cacheclean;
|
|
if (tmpUser->getFlag(sqlUser::F_NOPURGE))
|
|
{
|
|
//logDebugMessage("User %s (%i) had NOPURGE flag, but Purging It anyway!",tmpUser->getUserName().c_str(),userId);
|
|
if (!tmpUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
logDebugMessage("User %s (%i) has NOPURGE flag, so Not Purging It!",tmpUser->getUserName().c_str(),userId);
|
|
goto cacheclean;
|
|
}
|
|
else
|
|
logDebugMessage("User %s (%i) is NOPURGE but SUSPENDED, so Purging It!",tmpUser->getUserName().c_str(),userId);
|
|
//return true;
|
|
}
|
|
deleteUserFromTable(userId,"acl");
|
|
deleteUserFromTable(userId,"levels");
|
|
deleteUserFromTable(userId,"notices");
|
|
deleteUserFromTable(userId,"notes");
|
|
deleteUserFromTable(userId,"pending");
|
|
deleteUserFromTable(userId,"pending_emailchanges");
|
|
deleteUserFromTable(userId,"pending_pwreset");
|
|
deleteUserFromTable(userId,"pending_mgrchange");
|
|
deleteUserFromTable(userId,"supporters");
|
|
deleteUserFromTable(userId,"objections");
|
|
deleteUserFromTable(userId,"userlog");
|
|
deleteUserFromTable(userId,"fraud_list_data");
|
|
deleteUserFromTable(userId,"users_lastseen");
|
|
deleteUserFromTable(userId,"users");
|
|
|
|
if (expired) logAdminMessage("User %s (%s) has expired",tmpUser->getUserName().c_str(), tmpUser->getEmail().c_str());
|
|
else logDebugMessage("Deleted(wipeUser) %s (%i) from the database.", tmpUser->getUserName().c_str(),userId);
|
|
|
|
//sqlUser* tmpUser = getUserRecord(userId) inserted in the sqlUserCache, so we clean from chache all the time
|
|
//if (!expired) //so we wipe by force even from UserCache
|
|
//{
|
|
cacheclean:
|
|
sqlUserHashType::iterator usrItr = sqlUserCache.find(tmpUser->getUserName());
|
|
if (usrItr != sqlUserCache.end())
|
|
{
|
|
removeKey = usrItr->first;
|
|
delete(usrItr->second);
|
|
sqlUserCache.erase(removeKey);
|
|
}
|
|
//}
|
|
|
|
return true;
|
|
}
|
|
|
|
void cservice::ExpireUsers()
|
|
{
|
|
if (UsersExpireDBDays == 0) return;
|
|
logDebugMessage("Performing Database Users Expire");
|
|
int usersCount=0;
|
|
stringstream queryString;
|
|
queryString << "SELECT user_id FROM users_lastseen WHERE last_seen<="
|
|
<< currentTime()-UsersExpireDBDays
|
|
<< ends;
|
|
if( !SQLDb->Exec(queryString, true ))
|
|
{
|
|
logDebugMessage("An Error occured while retrieve database information on USERS-EXPIRE query");
|
|
return;
|
|
}
|
|
if (SQLDb->Tuples() < 1)
|
|
{
|
|
logDebugMessage("Removed 0 users from the database");
|
|
return;
|
|
}
|
|
vector <unsigned int> UserIDs;
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
UserIDs.push_back(atoi(SQLDb->GetValue(i,0).c_str()));
|
|
for (unsigned int i = 0 ; i < UserIDs.size(); i++)
|
|
if (wipeUser(UserIDs.at(i),true)) ++usersCount;
|
|
UserIDs.clear();
|
|
logDebugMessage("Removed %i users from the database",usersCount);
|
|
return;
|
|
}
|
|
|
|
void cservice::DeleteChannel(sqlChannel* theChan)
|
|
{
|
|
stringstream theQuery;
|
|
/* iterate over the channel userlist */
|
|
vector< iClient* > opList;
|
|
Channel* tmpChan = Network->findChannel(theChan->getName());
|
|
/* only parse the following if the channel exists on the network
|
|
* if there is nobody in the channel, there is nobody to op.
|
|
*/
|
|
if (tmpChan)
|
|
{
|
|
for (Channel::userIterator chanUsers = tmpChan->userList_begin();
|
|
chanUsers != tmpChan->userList_end(); ++chanUsers)
|
|
{
|
|
ChannelUser* tmpUser = chanUsers->second;
|
|
iClient* tmpClient = tmpUser->getClient();
|
|
sqlUser* tUser = isAuthed(tmpClient, false);
|
|
if (!tUser)
|
|
continue;
|
|
/* are they globally suspended? */
|
|
if (tUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
continue;
|
|
sqlLevel* theLevel = getLevelRecord(tUser, theChan);
|
|
/* check if they have access (and not suspended) */
|
|
if (theLevel)
|
|
{
|
|
if (theLevel->getSuspendExpire() > currentTime())
|
|
continue;
|
|
if (theLevel->getAccess() >= 100)
|
|
{
|
|
/* they're 100+, op them */
|
|
opList.push_back(tmpClient);
|
|
}
|
|
}
|
|
}
|
|
/* actually do the ops */
|
|
if (!opList.empty())
|
|
{
|
|
/* check we are in the channel, and opped */
|
|
ChannelUser* tmpBotUser = tmpChan->findUser(getInstance());
|
|
if (tmpBotUser)
|
|
{
|
|
if (!tmpBotUser->getMode(ChannelUser::MODE_O))
|
|
{
|
|
/* op ourselves so that we can do the reops */
|
|
stringstream s;
|
|
s << getCharYY()
|
|
<< " M "
|
|
<< theChan->getName()
|
|
<< " +o "
|
|
<< getCharYYXXX()
|
|
<< ends;
|
|
Write( s );
|
|
/* update the channel state */
|
|
tmpBotUser->setMode(ChannelUser::MODE_O);
|
|
}
|
|
}
|
|
/* do the ops - if we were not in the channel before, this will
|
|
* auto-join and op the bot anyway.
|
|
*/
|
|
Op(tmpChan, opList);
|
|
}
|
|
}
|
|
|
|
/* Reset everything back to nice default values. */
|
|
|
|
theChan->clearFlags();
|
|
theChan->setMassDeopPro(3);
|
|
theChan->setFloodPro(0);
|
|
theChan->setNoTake(0);
|
|
theChan->setURL("");
|
|
theChan->setDescription("");
|
|
theChan->setComment("");
|
|
theChan->setKeywords("");
|
|
theChan->setRegisteredTS(0);
|
|
theChan->setChannelMode("+tn");
|
|
theChan->commit();
|
|
|
|
/*
|
|
* Permanently delete all associated Level records for this channel.
|
|
*/
|
|
|
|
theQuery.str("");
|
|
theQuery << "DELETE FROM levels WHERE channel_id = "
|
|
<< theChan->getID()
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlQuery> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery ) )
|
|
//if( status != PGRES_COMMAND_OK )
|
|
{
|
|
elog << "PURGE> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bin 'em all.
|
|
*/
|
|
|
|
cservice::sqlLevelHashType::const_iterator ptr = sqlLevelCache.begin();
|
|
cservice::sqlLevelHashType::key_type thePair;
|
|
|
|
while(ptr != sqlLevelCache.end())
|
|
{
|
|
sqlLevel* tmpLevel = ptr->second;
|
|
unsigned int channel_id = ptr->first.second;
|
|
|
|
if (channel_id == theChan->getID())
|
|
{
|
|
thePair = ptr->first;
|
|
|
|
#ifdef LOG_DEBUG
|
|
elog << "Purging Level Record for: " << thePair.second << " (UID: " << thePair.first << ")" << endl;
|
|
#endif
|
|
|
|
++ptr;
|
|
sqlLevelCache.erase(thePair);
|
|
|
|
delete(tmpLevel);
|
|
} else
|
|
{
|
|
++ptr;
|
|
}
|
|
}
|
|
|
|
/* Remove from cache.. part channel. */
|
|
sqlChannelCache.erase(theChan->getName());
|
|
sqlChannelIDCache.erase(theChan->getID());
|
|
/* no longer interested in this channel */
|
|
getUplink()->UnRegisterChannelEvent( theChan->getName(), this ) ;
|
|
/* remove mode 'R' (no longer registered) */
|
|
//Channel* tmpChan = Network->findChannel(theChan->getName());
|
|
if (tmpChan) getUplink()->Mode(NULL, tmpChan, string("-R"), string() );
|
|
Part(theChan->getName());
|
|
joinCount--;
|
|
|
|
delete(theChan);
|
|
}
|
|
|
|
void cservice::ExpireChannels()
|
|
{
|
|
if ((startMIA == 0) || (endMIA == 0)) return;
|
|
logDebugMessage("Performing Channel Database Purge");
|
|
bool userIsAuthed = false;
|
|
unsigned int mngrID;
|
|
unsigned int mngrLastSeen;
|
|
unsigned int _startMIA = currentTime()-startMIA;
|
|
unsigned int _endMIA = currentTime()-endMIA;
|
|
unsigned int purgeCount=0;
|
|
string manager = "No Manager";
|
|
string managerEmail = "No Email Address";
|
|
stringstream managerQuery;
|
|
stringstream theQuery;
|
|
//sqlUser* theUser;
|
|
vector <sqlChannel*> delChanList;
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.begin();
|
|
while (ptr != sqlChannelCache.end())
|
|
{
|
|
sqlChannel* theChan = (ptr)->second;
|
|
if((theChan->getName() == "*") || (theChan->getFlag(sqlChannel::F_NOPURGE)))
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
managerQuery.str("");
|
|
managerQuery << "SELECT users.user_name,users.email,users.id "
|
|
<< "FROM users,levels "
|
|
<< "WHERE levels.user_id = users.id "
|
|
<< "AND levels.access = 500 "
|
|
<< "AND levels.channel_id = "
|
|
<< theChan->getID()
|
|
<< " LIMIT 1"
|
|
<< ends;
|
|
|
|
if( !SQLDb->Exec(managerQuery, true ) )
|
|
//if( status != PGRES_TUPLES_OK )
|
|
{
|
|
#ifdef LOG_SQL
|
|
elog << "PURGE> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return ;
|
|
}
|
|
if (SQLDb->Tuples() == 0)
|
|
{
|
|
//Channel already purged
|
|
logDebugMessage("Channel %s has NO Manager,purging it:",theChan->getName().c_str());
|
|
delChanList.push_back(theChan);
|
|
//logDebugMessage("Channel %s should be purged",theChan->getName().c_str());
|
|
logAdminMessage("Channel %s has expired",theChan->getName().c_str());
|
|
writeChannelLog(theChan, me, sqlChannel::EV_PURGE, "has purged " + theChan->getName() + " (automatic channel expire), " +
|
|
"Manager was " + manager + " (" + managerEmail + ")" );
|
|
purgeCount++;
|
|
++ptr;
|
|
continue;
|
|
} else {
|
|
manager = SQLDb->GetValue(0,0);
|
|
managerEmail = SQLDb->GetValue(0,1);
|
|
mngrID = atoi(SQLDb->GetValue(0,2).c_str());
|
|
sqlUserHashType::iterator ptr2 = sqlUserCache.find(manager);
|
|
if(ptr2 != sqlUserCache.end())
|
|
{ // Found something!
|
|
#ifdef LOG_CACHE_HITS
|
|
elog << "cmaster::getUserRecord> Cache hit for "
|
|
<< manager
|
|
<< endl;
|
|
#endif
|
|
ptr2->second->setLastUsed(currentTime());
|
|
userCacheHits++;
|
|
sqlUser* theUser = ptr2->second;
|
|
if (theUser->isAuthed()) userIsAuthed=true; else userIsAuthed=false;
|
|
//if (ptr2->second->isAuthed()) userIsAuthed=true;
|
|
}
|
|
theQuery.str("");
|
|
theQuery << "SELECT last_seen FROM users_lastseen WHERE user_id = "
|
|
<< mngrID
|
|
<< endl;
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
{
|
|
elog << "PURGE> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return;
|
|
}
|
|
if (SQLDb->Tuples() != 0) {
|
|
mngrLastSeen = atoi(SQLDb->GetValue(0,0).c_str());
|
|
} else { logDebugMessage("Warning: missing lastseen data for managerUser %s",manager.c_str()); }
|
|
if (((mngrLastSeen<=_endMIA) && (theChan->getFlag(sqlChannel::F_MIA))) || ((mngrLastSeen<=_startMIA) && (theChan->getFlag(sqlChannel::F_CAUTION)) && (!userIsAuthed)))
|
|
{
|
|
delChanList.push_back(theChan);
|
|
//logDebugMessage("Channel %s should be purged",theChan->getName().c_str());
|
|
logAdminMessage("Channel %s has expired",theChan->getName().c_str());
|
|
writeChannelLog(theChan, me, sqlChannel::EV_PURGE, "has purged " + theChan->getName() + " (automatic channel expire), " +
|
|
"Manager was " + manager + " (" + managerEmail + ")" );
|
|
manager = "No Manager";
|
|
managerEmail = "No Email Address";
|
|
purgeCount++;
|
|
}
|
|
else
|
|
{
|
|
if ((mngrLastSeen<=_startMIA) && (!theChan->getFlag(sqlChannel::F_MIA)) && (!userIsAuthed))
|
|
{ //MIA procedure
|
|
theChan->setFlag(sqlChannel::F_MIA);
|
|
theChan->setFlag(sqlChannel::F_LOCKED);
|
|
theChan->setFlag(sqlChannel::F_AUTOTOPIC);
|
|
theChan->setDescription(MIAStartDesc);
|
|
theChan->setURL(MIAURL);
|
|
theChan->commit();
|
|
if (theChan->getInChan()) doAutoTopic(theChan);
|
|
logDebugMessage("Applied MIA procedure for channel: %s",theChan->getName().c_str());
|
|
}
|
|
if ((mngrLastSeen>_startMIA) && (theChan->getFlag(sqlChannel::F_MIA)) && (userIsAuthed))
|
|
{
|
|
theChan->removeFlag(sqlChannel::F_MIA);
|
|
theChan->removeFlag(sqlChannel::F_LOCKED);
|
|
theChan->setFlag(sqlChannel::F_CAUTION);
|
|
theChan->setFlag(sqlChannel::F_AUTOTOPIC);
|
|
theChan->setDescription(MIAEndDesc);
|
|
theChan->setURL("");
|
|
theChan->commit();
|
|
if (theChan->getInChan()) doAutoTopic(theChan);
|
|
}
|
|
}
|
|
}
|
|
++ptr;
|
|
} //while
|
|
for (unsigned int i=0; i<delChanList.size(); i++)
|
|
{
|
|
DeleteChannel(delChanList[i]);
|
|
}
|
|
delChanList.clear();
|
|
logDebugMessage("Removed %i channels from the database",purgeCount);
|
|
}
|
|
|
|
|
|
/**
|
|
* Check the database to see if anything has changed.
|
|
* TODO: Lots.
|
|
*/
|
|
void cservice::processDBUpdates()
|
|
{
|
|
logDebugMessage("[DB-UPDATE]: Looking for changes:");
|
|
updateChannels();
|
|
updateUsers();
|
|
updateLevels();
|
|
updateBans();
|
|
logDebugMessage("[DB-UPDATE]: Complete.");
|
|
}
|
|
|
|
void cservice::updateChannels()
|
|
{
|
|
stringstream theQuery ;
|
|
|
|
theQuery << "SELECT "
|
|
<< sql::channel_fields
|
|
<< ",date_part('epoch', CURRENT_TIMESTAMP)::int as db_unixtime FROM "
|
|
<< "channels WHERE last_updated >= "
|
|
<< lastChannelRefresh
|
|
<< " AND registered_ts <> 0"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::processDBUpdates]:sqlQuery: "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
//if (status != PGRES_TUPLES_OK)
|
|
{
|
|
elog << "*** [CMaster::updateChannels]: SQL error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (SQLDb->Tuples() <= 0)
|
|
{
|
|
/* Nothing to see here.. */
|
|
return;
|
|
}
|
|
|
|
/* Update our time offset incase things drift.. */
|
|
dbTimeOffset = atoi(SQLDb->GetValue(0,"db_unixtime").c_str()) - ::time(NULL);
|
|
unsigned int updates = 0;
|
|
unsigned int newchans = 0;
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
sqlChannelHashType::iterator ptr =
|
|
sqlChannelCache.find(SQLDb->GetValue(i, 1));
|
|
|
|
if(ptr != sqlChannelCache.end())
|
|
{
|
|
/* Found something! Update it. */
|
|
(ptr->second)->setAllMembers(i);
|
|
updates++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Not in the cache.. must be a new channel.
|
|
* Create new channel record, insert in cache.
|
|
*/
|
|
|
|
sqlChannel* newChan = new (std::nothrow) sqlChannel(SQLDb);
|
|
assert( newChan != 0 ) ;
|
|
|
|
newChan->setAllMembers(i);
|
|
|
|
time_t channel_ts = 0;
|
|
Channel* tmpChan = Network->findChannel(newChan->getName());
|
|
channel_ts = tmpChan ? tmpChan->getCreationTime() : ::time(NULL);
|
|
|
|
newChan->setChannelTS(channel_ts);
|
|
newChan->setFlag(sqlChannel::F_AUTOJOIN);
|
|
newChan->setLastUsed(currentTime());
|
|
newChan->commit();
|
|
|
|
stringstream tmpTS;
|
|
tmpTS << channel_ts;
|
|
string channelTS = tmpTS.str();
|
|
|
|
if (tmpChan)
|
|
getUplink()->Mode(NULL, tmpChan, string("+R"), channelTS);
|
|
|
|
sqlChannelCache.insert(sqlChannelHashType::value_type(newChan->getName(), newChan));
|
|
sqlChannelIDCache.insert(sqlChannelIDHashType::value_type(newChan->getID(), newChan));
|
|
MyUplink->RegisterChannelEvent(newChan->getName(), this);
|
|
|
|
Join(newChan->getName(), string("+tnR"), newChan->getChannelTS(), true);
|
|
newChan->setInChan(true);
|
|
joinCount++;
|
|
|
|
//Send a welcome notice to the channel
|
|
if (!welcomeNewChanMessage.empty())
|
|
Notice(newChan->getName(), TokenStringsParams(welcomeNewChanMessage.c_str(), newChan->getName().c_str()).c_str());
|
|
|
|
//Set a welcome topic of the new channel, only if the actual topic is empty
|
|
#ifdef TOPIC_TRACK
|
|
if (!welcomeNewChanTopic.empty())
|
|
if (tmpChan && tmpChan->getTopic().empty())
|
|
Topic(tmpChan, welcomeNewChanTopic);
|
|
#endif
|
|
|
|
logDebugMessage("[DB-UPDATE]: Found new channel: %s", newChan->getName().c_str());
|
|
newchans++;
|
|
}
|
|
|
|
}
|
|
|
|
logDebugMessage("[DB-UPDATE]: Refreshed %i channel records, loaded %i new channel(s).",
|
|
updates, newchans);
|
|
|
|
/* Set the "Last refreshed from channels table" timestamp. */
|
|
lastChannelRefresh = atoi(SQLDb->GetValue(0,"db_unixtime").c_str());
|
|
}
|
|
|
|
//Upde One specified user's level record (used for global unsuspension)
|
|
void cservice::updateUserLevels(sqlUser* theUser)
|
|
{
|
|
stringstream theQuery ;
|
|
|
|
theQuery << "SELECT "
|
|
<< sql::level_fields
|
|
<< " FROM levels WHERE user_id = "
|
|
<< theUser->getID()
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "updateUserLevelsQuery: "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
//if (status != PGRES_TUPLES_OK)
|
|
{
|
|
elog << "updateUserLevelsQuery error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (SQLDb->Tuples() <= 0)
|
|
{
|
|
/* Nothing to see here.. */
|
|
return;
|
|
}
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
unsigned int channel_id = atoi(SQLDb->GetValue(i, 0).c_str());
|
|
unsigned int user_id = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
sqlChannel* theChan = getChannelRecord(channel_id);
|
|
|
|
/*
|
|
* If we don't have the channel cached, its not registered so
|
|
* we aren't interested in this level record.
|
|
*/
|
|
|
|
if (!theChan) continue;
|
|
|
|
pair<int, int> thePair( user_id, channel_id );
|
|
|
|
sqlLevelHashType::iterator ptr = sqlLevelCache.find(thePair);
|
|
|
|
if(ptr != sqlLevelCache.end())
|
|
{
|
|
/* Found something! Update it. */
|
|
(ptr->second)->setAllMembers(i);
|
|
} else
|
|
{
|
|
/*
|
|
* Must be a new level record, add it.
|
|
*/
|
|
|
|
sqlLevel* newLevel = new (std::nothrow) sqlLevel(SQLDb);
|
|
newLevel->setAllMembers(i);
|
|
sqlLevelCache.insert(sqlLevelHashType::value_type(thePair, newLevel));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check the levels table for recent updates.
|
|
*/
|
|
void cservice::updateLevels()
|
|
{
|
|
stringstream theQuery ;
|
|
|
|
theQuery << "SELECT "
|
|
<< sql::level_fields
|
|
<< ",date_part('epoch', CURRENT_TIMESTAMP)::int as db_unixtime FROM "
|
|
<< "levels WHERE last_updated >= "
|
|
<< lastLevelRefresh
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::updateLevels]: sqlQuery: "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
//if (status != PGRES_TUPLES_OK)
|
|
{
|
|
elog << "*** [CMaster::updateLevels]: SQL error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (SQLDb->Tuples() <= 0)
|
|
{
|
|
/* Nothing to see here.. */
|
|
return;
|
|
}
|
|
|
|
/* Update our time offset incase things drift.. */
|
|
dbTimeOffset = atoi(SQLDb->GetValue(0,"db_unixtime").c_str()) - ::time(NULL);
|
|
unsigned int updates = 0;
|
|
unsigned int newlevs = 0;
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
unsigned int channel_id = atoi(SQLDb->GetValue(i, 0).c_str());
|
|
unsigned int user_id = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
sqlChannel* theChan = getChannelRecord(channel_id);
|
|
|
|
/*
|
|
* If we don't have the channel cached, its not registered so
|
|
* we aren't interested in this level record.
|
|
*/
|
|
|
|
if (!theChan) continue;
|
|
|
|
pair<int, int> thePair( user_id, channel_id );
|
|
|
|
sqlLevelHashType::iterator ptr = sqlLevelCache.find(thePair);
|
|
|
|
if(ptr != sqlLevelCache.end())
|
|
{
|
|
/* Found something! Update it. */
|
|
(ptr->second)->setAllMembers(i);
|
|
updates++;
|
|
} else
|
|
{
|
|
/*
|
|
* Must be a new level record, add it.
|
|
*/
|
|
|
|
sqlLevel* newLevel = new (std::nothrow) sqlLevel(SQLDb);
|
|
newLevel->setAllMembers(i);
|
|
sqlLevelCache.insert(sqlLevelHashType::value_type(thePair, newLevel));
|
|
newlevs++;
|
|
}
|
|
}
|
|
|
|
logDebugMessage("[DB-UPDATE]: Refreshed %i level record(s), loaded %i new level record(s).",
|
|
updates, newlevs);
|
|
|
|
/* Set the "Last refreshed from levels table" timestamp. */
|
|
lastLevelRefresh = atoi(SQLDb->GetValue(0,"db_unixtime").c_str());
|
|
}
|
|
|
|
/*
|
|
* Check the users table to see if there have been any updates since we last looked.
|
|
*/
|
|
void cservice::updateUsers()
|
|
{
|
|
stringstream theQuery ;
|
|
|
|
theQuery << "SELECT "
|
|
<< sql::user_fields
|
|
<< ",date_part('epoch', CURRENT_TIMESTAMP)::int as db_unixtime FROM "
|
|
<< "users WHERE last_updated >= "
|
|
<< lastUserRefresh
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::updateUsers]: sqlQuery: "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
// if (status != PGRES_TUPLES_OK)
|
|
{
|
|
elog << "*** [CMaster::updateUsers]: SQL error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return;
|
|
}
|
|
|
|
if (SQLDb->Tuples() <= 0)
|
|
{
|
|
/* Nothing to see here.. */
|
|
return;
|
|
}
|
|
|
|
/* Update our time offset incase things drift.. */
|
|
dbTimeOffset = atoi(SQLDb->GetValue(0,"db_unixtime").c_str()) - ::time(NULL);
|
|
unsigned int updates = 0;
|
|
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
sqlUserHashType::iterator ptr =
|
|
sqlUserCache.find(SQLDb->GetValue(i, 1));
|
|
|
|
if(ptr != sqlUserCache.end())
|
|
{
|
|
/* Found something! Update it */
|
|
(ptr->second)->setAllMembers(i);
|
|
updates++;
|
|
}
|
|
}
|
|
|
|
logDebugMessage("[DB-UPDATE]: Refreshed %i user record(s).",
|
|
updates);
|
|
|
|
/* Set the "Last refreshed from Users table" timestamp. */
|
|
lastUserRefresh = atoi(SQLDb->GetValue(0,"db_unixtime").c_str());
|
|
}
|
|
|
|
void cservice::updateBans()
|
|
{
|
|
/* Todo */
|
|
}
|
|
|
|
/**
|
|
* Timer handler.
|
|
* This member handles a number of timers, dispatching
|
|
* control to the relevant member for the timer
|
|
* triggered.
|
|
*/
|
|
void cservice::OnTimer(const xServer::timerID& timer_id, void*)
|
|
{
|
|
if (timer_id == limit_timerID)
|
|
{
|
|
updateLimits();
|
|
|
|
/* Refresh Timers */
|
|
time_t theTime = time(NULL) + limitCheckPeriod;
|
|
limit_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == dBconnection_timerID)
|
|
{
|
|
checkDbConnectionStatus();
|
|
|
|
/* Refresh Timers */
|
|
time_t theTime = time(NULL) + connectCheckFreq;
|
|
dBconnection_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == update_timerID)
|
|
{
|
|
processDBUpdates();
|
|
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + updateInterval;
|
|
update_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == expire_timerID)
|
|
{
|
|
expireBans();
|
|
expireSuspends();
|
|
expireSilence();
|
|
expireGlines();
|
|
expireWhitelist();
|
|
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + expireInterval;
|
|
expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == users_expire_timerID)
|
|
{
|
|
ExpireUsers();
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + expireDBUserPeriod;
|
|
users_expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == channels_expire_timerID)
|
|
{
|
|
ExpireChannels();
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + expireDBChannelPeriod;
|
|
channels_expire_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == cache_timerID)
|
|
{
|
|
cacheExpireUsers();
|
|
cacheExpireLevels();
|
|
|
|
fixUsersLastSeen();
|
|
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + cacheInterval;
|
|
cache_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == webrelay_timerID)
|
|
{
|
|
/* Check for new webrelay messages */
|
|
string webrelayQuery;
|
|
|
|
webrelayQuery = "SELECT created_ts,contents FROM webnotices WHERE ";
|
|
webrelayQuery += "created_ts <= date_part('epoch', CURRENT_TIMESTAMP)::int ";
|
|
webrelayQuery += "ORDER BY created_ts";
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::OnTimer::sqlQuery> "
|
|
<< webrelayQuery.c_str()
|
|
<< endl;
|
|
#endif
|
|
int webrelay_messagecount = 0;
|
|
|
|
if( SQLDb->Exec(webrelayQuery, true ) )
|
|
// if (PGRES_TUPLES_OK == status)
|
|
{
|
|
/* process messages */
|
|
webrelay_messagecount = SQLDb->Tuples();
|
|
string webrelay_msg;
|
|
unsigned long webrelay_ts = 0;
|
|
for (int i=0; i < webrelay_messagecount; i++)
|
|
{
|
|
webrelay_ts = atoi(SQLDb->GetValue(i,0).c_str());
|
|
webrelay_msg = SQLDb->GetValue(i,1);
|
|
|
|
logAdminMessage("%s", webrelay_msg.c_str());
|
|
}
|
|
/* delete old messages */
|
|
if (webrelay_messagecount > 0)
|
|
{
|
|
char web_ts[15];
|
|
sprintf(web_ts, "%li", webrelay_ts);
|
|
webrelayQuery = "DELETE FROM webnotices WHERE created_ts <= ";
|
|
webrelayQuery += web_ts;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::OnTimer::sqlQuery> "
|
|
<< webrelayQuery.c_str()
|
|
<< endl;
|
|
#endif
|
|
if (!SQLDb->Exec(webrelayQuery) )
|
|
// PGRES_COMMAND_OK)
|
|
{
|
|
/* log error */
|
|
elog << "cservice::webrelay> SQL Query Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Refresh Timer */
|
|
time_t theTime = time(NULL) + webrelayPeriod;
|
|
webrelay_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
|
|
if (timer_id == pending_timerID)
|
|
{
|
|
/*
|
|
* Load the list of pending channels and calculate/save some stats.
|
|
*/
|
|
checkValidUsersAndChannelsState();
|
|
checkIncomings();
|
|
loadPendingChannelList();
|
|
checkTrafficPass();
|
|
checkObjections();
|
|
checkAccepts();
|
|
checkRewievs();
|
|
cleanUpPendings();
|
|
|
|
/*
|
|
* Load a list of channels in NOTIFICATION stage and send them
|
|
* a notice.
|
|
*/
|
|
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channels.name,channels.id,pending.created_ts"
|
|
<< " FROM pending,channels"
|
|
<< " WHERE channels.id = pending.channel_id"
|
|
<< " AND pending.status = 2;"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cmaster::loadPendingChannelList> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
unsigned int noticeCount = 0;
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
// if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
noticeCount++;
|
|
string channelName = SQLDb->GetValue(i,0);
|
|
unsigned int channel_id =
|
|
atoi(SQLDb->GetValue(i, 1).c_str());
|
|
unsigned int created_ts =
|
|
atoi(SQLDb->GetValue(i, 2).c_str());
|
|
Channel* tmpChan = Network->findChannel(channelName);
|
|
|
|
if (tmpChan)
|
|
{
|
|
serverNotice(tmpChan,
|
|
"This channel is currently being processed for registration. "
|
|
"If you wish to view the details of the application or to object, please visit: "
|
|
"%s?id=%i-%i OR use INFO and/or OBJECT commands.", pendingPageURL.c_str(), created_ts, channel_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
logDebugMessage("Loaded Pending Channels notification list, I have just notified %i channels that they are under registration.",
|
|
noticeCount);
|
|
|
|
/* Refresh Timer */
|
|
|
|
time_t theTime = time(NULL) +pendingChanPeriod;
|
|
pending_timerID = MyUplink->RegisterTimer(theTime, this, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a notice to a channel from the server.
|
|
* TODO: Move this method to xServer.
|
|
*/
|
|
bool cservice::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 ) ;
|
|
|
|
stringstream s;
|
|
s << MyUplink->getCharYY()
|
|
<< " O "
|
|
<< theChannel->getName()
|
|
<< " :"
|
|
<< buf
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Send a notice to a channel from the server.
|
|
* TODO: Move this method to xServer.
|
|
*/
|
|
bool cservice::serverNotice( Channel* theChannel, const string& Message)
|
|
{
|
|
stringstream s;
|
|
s << MyUplink->getCharYY()
|
|
<< " O "
|
|
<< theChannel->getName()
|
|
<< " :"
|
|
<< Message
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Log a message to the admin channel and the logfile.
|
|
*/
|
|
bool cservice::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.
|
|
//Channel* tmpChan = Network->findChannel(getConfigVar("CMASTER.RELAY_CHAN")->asString());
|
|
Channel* tmpChan = Network->findChannel(relayChan);
|
|
if (!tmpChan)
|
|
{
|
|
elog << "cservice::logAdminMessage> Unable to locate relay "
|
|
<< "channel on network!"
|
|
<< endl;
|
|
return false;
|
|
}
|
|
|
|
string message = string( "[" ) + nickName + "] " + buf ;
|
|
serverNotice(tmpChan, message);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Log a privileged admin channel message
|
|
*/
|
|
bool cservice::logPrivAdminMessage(const char* format, ... )
|
|
{
|
|
char buf[1024] = { 0 };
|
|
va_list _list ;
|
|
|
|
va_start(_list, format);
|
|
vsnprintf(buf, 1024, format, _list);
|
|
va_end(_list);
|
|
|
|
/* try to locate the privileged relay channel */
|
|
Channel* tmpChan = Network->findChannel(privrelayChan);
|
|
if (!tmpChan)
|
|
{
|
|
elog << "cservice::logPrivAdminMessage> Unable to locate "
|
|
<< "prileved relay channel on network!"
|
|
<< endl;
|
|
return false;
|
|
}
|
|
|
|
string message = string("[") + nickName + "] " + buf;
|
|
serverNotice(tmpChan, message);
|
|
return true;
|
|
}
|
|
|
|
bool cservice::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 debug channel.
|
|
//Channel* tmpChan = Network->findChannel(getConfigVar("CMASTER.DEBUG_CHAN")->asString());
|
|
Channel* tmpChan = Network->findChannel(debugChan);
|
|
if (!tmpChan)
|
|
{
|
|
elog << "cservice::logAdminMessage> Unable to locate debug "
|
|
<< "channel on network!"
|
|
<< endl;
|
|
return false;
|
|
}
|
|
|
|
string message = string( "[" ) + nickName + "] " + buf ;
|
|
serverNotice(tmpChan, message);
|
|
return true;
|
|
}
|
|
|
|
bool cservice::sqlLogCommand(iClient* theClient, const char* format, ... )
|
|
{
|
|
unsigned int userId = 0;
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser) userId = theUser->getID(); //return false; //I have no better idea for the moment
|
|
|
|
string message = TokenStringsParams(format);
|
|
StringTokenizer st( message ) ;
|
|
if( st.empty() )
|
|
{
|
|
elog << "cservice::sqlLogCommand> Incomplete command" << endl;
|
|
return false;
|
|
}
|
|
|
|
const string Command = string_upper( st[ 0 ] ) ;
|
|
|
|
/* Log command to SQL */
|
|
if ((Command != "LOGIN") && (Command != "NEWPASS") && (Command != "SUSPENDME"))
|
|
{
|
|
stringstream theLog;
|
|
theLog << "INSERT INTO adminlog (user_id,cmd,args,timestamp,issue_by)"
|
|
<< " VALUES("
|
|
<< userId //theUser->getID()
|
|
<< ","
|
|
<< "'" << escapeSQLChars(Command) << "'"
|
|
<< ","
|
|
<< "'" << escapeSQLChars(st.assemble(1)) << "'"
|
|
<< ","
|
|
<< currentTime()
|
|
<< ","
|
|
<< "'" << escapeSQLChars(theClient->getRealNickUserHost()) << "'"
|
|
<< ")"
|
|
<< ends;
|
|
|
|
elog << theLog.str().c_str() << endl;
|
|
|
|
if( !SQLDb->Exec(theLog ) )
|
|
// if( PGRES_COMMAND_OK != status )
|
|
{
|
|
elog << "cservice::adminlog> Something went wrong: "
|
|
<< theLog.str().c_str()
|
|
<< " "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* *** The Judge *** */
|
|
|
|
void cservice::AddToValidResponseString(const string& resp)
|
|
{
|
|
if (validResponseString == "") validResponseString = resp;
|
|
else validResponseString += "," + resp;
|
|
return;
|
|
}
|
|
|
|
unsigned int cservice::getPendingChanId(const string& chanName)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT id FROM channels WHERE name = '"
|
|
<< escapeSQLChars(chanName)
|
|
<< "' AND registered_ts = 0"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on getPendingChanId");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "getPendingChanId> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
logDebugMessage("getPendingChanId> SQL Error: %s", SQLDb->ErrorMessage().c_str());
|
|
return 0;
|
|
}
|
|
else if (SQLDb->Tuples() > 0) return atoi(SQLDb->GetValue(0,0)) ;
|
|
return 0;
|
|
}
|
|
|
|
const string cservice::getPendingChanName(unsigned int chanId)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT name FROM channels WHERE id = "
|
|
<< chanId
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on getPendingChanName");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "getPendingChanName> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
logDebugMessage("getPendingChanName> SQL Error: %s", SQLDb->ErrorMessage().c_str());
|
|
return("");
|
|
}
|
|
else if (SQLDb->Tuples() > 0) return SQLDb->GetValue(0,0) ;
|
|
return("");
|
|
}
|
|
|
|
bool cservice::isValidUser(const string& userName)
|
|
{
|
|
validResponseString = "";
|
|
sqlUser* tmpUser = getUserRecord(userName);
|
|
if (!tmpUser)
|
|
{
|
|
validResponseString = "INEXISTENT";
|
|
return false;
|
|
}
|
|
|
|
/* Check if user is NOREG/LOCKED/locked werification answer
|
|
* if user noreg type is 0, then it is NOREG, if type=5 then it is LOCKED, if type = 6 then user_name is referring to the locked verification answer.
|
|
*/
|
|
stringstream theQuery;
|
|
theQuery << "SELECT user_name,email,type FROM noreg WHERE user_name IS NOT NULL OR email IS NOT NULL"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
#ifdef LOG_SQL
|
|
elog << "JUDGE.EmailQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
string matchString;
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
if (atoi(SQLDb->GetValue(i,2)) == 6)
|
|
{
|
|
matchString = SQLDb->GetValue(i,0);
|
|
if (matchString[0] == '!')
|
|
{
|
|
matchString.erase(0,1);
|
|
if (!match(matchString,tmpUser->getVerifData()))
|
|
{
|
|
AddToValidResponseString("INVALID VERIF");
|
|
}
|
|
}
|
|
else
|
|
{ /* This should be the matchcase metod - TODO: need to find a solution */
|
|
if (!casematch(matchString,tmpUser->getVerifData()))
|
|
//if (0 != strcmp(matchString,tmpUser->getVerifData()))
|
|
{
|
|
AddToValidResponseString("INVALID VERIF");
|
|
}
|
|
}
|
|
}
|
|
matchString = SQLDb->GetValue(i,0);
|
|
if (!match(matchString,tmpUser->getUserName()) && (matchString != "*"))
|
|
{
|
|
if (atoi(SQLDb->GetValue(i,2)) < 4)
|
|
{
|
|
AddToValidResponseString("NOREG");
|
|
}
|
|
//This case is taken account by the webinterface
|
|
//We skip this because we only look after *existing* usernames
|
|
//if (atoi(SQLDb->GetValue(i,2)) == 4)
|
|
// AddToValidResponseString("FRAUD");
|
|
if (atoi(SQLDb->GetValue(i,2)) == 5)
|
|
AddToValidResponseString("LOCKED");
|
|
}
|
|
matchString = SQLDb->GetValue(i,1);
|
|
if (!match(matchString,tmpUser->getEmail()) && (matchString != "*"))
|
|
{
|
|
if (atoi(SQLDb->GetValue(i,2)) < 4)
|
|
{
|
|
AddToValidResponseString("INV.E-MAIL");
|
|
}
|
|
//This case is taken account by the webinterface
|
|
//TODO: probably we should handle this case too
|
|
//if (atoi(SQLDb->GetValue(i,2)) == 4)
|
|
// AddToValidResponseString("FRAUD");
|
|
if (atoi(SQLDb->GetValue(i,2)) == 5)
|
|
AddToValidResponseString("LOCKED E-MAIL");
|
|
}
|
|
}
|
|
}
|
|
bool isValid = true;
|
|
if (tmpUser->getFlag(sqlUser::F_FRAUD))
|
|
{
|
|
AddToValidResponseString("FRAUD");
|
|
isValid = false;
|
|
}
|
|
if (tmpUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
AddToValidResponseString("SUSPEND");
|
|
isValid = false;
|
|
}
|
|
if (!isValid) return false;
|
|
return true;
|
|
}
|
|
|
|
bool cservice::isValidUser(const string& userName, iClient* theClient)
|
|
{
|
|
if (!isValidUser(userName))
|
|
{
|
|
Notice(theClient,"Invalid username (%s)",validResponseString.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::isValidSupporter(const string& suppUser)
|
|
{
|
|
sqlUser* theUser = getUserRecord(suppUser);
|
|
if (!theUser) return false;
|
|
//{
|
|
// validResponseString = "INEXISTENT";
|
|
// return false;
|
|
//}
|
|
//Check ever logged on IRC
|
|
if (theUser->getLastIP().size() == 0)
|
|
{
|
|
validResponseString = "NEVER_LOGGED";
|
|
return false;
|
|
}
|
|
//Check MIN_DAYS_BEFORE_SUPPORT
|
|
if ((currentTime()-theUser->getCreatedTS()) < (MinDaysBeforeSupport * JudgeDaySeconds))
|
|
{
|
|
validResponseString = "TOO_NEW";
|
|
return false;
|
|
}
|
|
//Check last_seen <> 21 days
|
|
if ((currentTime()-theUser->getLastSeen()) > (21 * JudgeDaySeconds))
|
|
{
|
|
validResponseString = "SEEN_LONG_AGO";
|
|
return false;
|
|
}
|
|
//Check MAX_CONCURRENT_SUPPORTS
|
|
stringstream theQuery;
|
|
theQuery << "SELECT COUNT(*) FROM users,channels,supporters,pending WHERE "
|
|
<< "lower(users.user_name) = '"
|
|
<< escapeSQLChars(string_lower(suppUser))
|
|
<< "' AND pending.channel_id = supporters.channel_id AND users.id = supporters.user_id "
|
|
<< " AND channels.registered_ts = 0"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{ logDebugMessage("Error on Judge.SupportingCountQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Jude.SupportingCountQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
if ((SQLDb->Tuples() != 0) && (atoi(SQLDb->GetValue(0,0)) > (int)MaxConcurrentSupports))
|
|
{
|
|
validResponseString = "TOO_MANY_SUPPORTS";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::isValidApplicant(sqlUser* theUser)
|
|
{
|
|
//sqlUser* theUser = getUserRecord(userName);
|
|
if (!theUser) return false;
|
|
|
|
/* Check if the user has ever logged in on IRC. */
|
|
if (theUser->getLastIP().size() == 0)
|
|
{
|
|
validResponseString = "Target user must login to X on IRC to own a channel.";
|
|
return false;
|
|
}
|
|
|
|
// Check theUser has passed suffucient days after it's user registration
|
|
if ((currentTime()-theUser->getCreatedTS()) < (MinDaysBeforeReg * JudgeDaySeconds))
|
|
{
|
|
validResponseString = "TOO_NEW";
|
|
return false;
|
|
}
|
|
|
|
/* Check if already has a channel */
|
|
stringstream theQuery;
|
|
theQuery << "SELECT * FROM levels,channels WHERE channels.id=levels.channel_id AND channels.registered_ts>0 AND levels.user_id="
|
|
<< theUser->getID()
|
|
<< " AND levels.access = '500'"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{ logDebugMessage("Error on Judge.REGISTER.500Query");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Jude.REGISTER.500Query> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
if (SQLDb->Tuples() != 0)
|
|
{
|
|
validResponseString = "ALREADY_HAVE_CHAN";
|
|
return false;
|
|
}
|
|
|
|
/* Check if has a pending application */
|
|
theQuery.str("");
|
|
theQuery << "SELECT * FROM pending WHERE status<3 AND manager_id="
|
|
<< theUser->getID()
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{ logDebugMessage("Error on Judge.REGISTER.PendingQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.REGISTER.PendingQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
if (SQLDb->Tuples() != 0)
|
|
{
|
|
validResponseString = "ALREADY_HAVE_PENDINGCHAN";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::isValidChannel(const string& chName)
|
|
{
|
|
/* Check if channelname is locked */
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channel_name,type FROM noreg WHERE channel_name IS NOT NULL"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.isValidChannelchannel_nameQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.isValidChannelchannel_nameQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
if (!match(string_lower(SQLDb->GetValue(i,0)),string_lower(chName)))
|
|
{
|
|
if (atoi(SQLDb->GetValue(i,1)) < 4)
|
|
validResponseString = "NOREG";
|
|
if (atoi(SQLDb->GetValue(i,1)) == 5)
|
|
validResponseString = "LOCKED";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::isValidChannel(const string& chName, iClient* theClient)
|
|
{
|
|
validResponseString = "";
|
|
sqlUser* theUser = isAuthed(theClient,true);
|
|
if (!theUser) return false;
|
|
/*
|
|
* First, check the channel isn't already registered.
|
|
*/
|
|
sqlChannel* theChan;
|
|
theChan = getChannelRecord(chName);
|
|
if (theChan)
|
|
{
|
|
validResponseString = TokenStringsParams(getResponse(theUser,
|
|
language::chan_already_reg,
|
|
string("%s is already registered with me.")).c_str(),
|
|
chName.c_str());
|
|
Notice(theClient,validResponseString);
|
|
return false;
|
|
}
|
|
|
|
/* Check if channelname is locked */
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channel_name,type FROM noreg WHERE channel_name IS NOT NULL"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.channel_nameQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.channel_nameQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
if (!match(string_lower(SQLDb->GetValue(i,0)),string_lower(chName)))
|
|
{
|
|
if (atoi(SQLDb->GetValue(i,1)) < 4)
|
|
validResponseString = "Cannot register, channel is NOREG.";
|
|
if (atoi(SQLDb->GetValue(i,1)) == 5)
|
|
validResponseString = "Cannot register, channel is LOCKED.";
|
|
Notice(theClient,validResponseString);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::RejectChannel(unsigned int chanId, const string& reason)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "UPDATE pending SET status = '9',"
|
|
<< " last_updated = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< " decision_ts = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< " decision = 'by The Judge: "
|
|
<< reason
|
|
<< "', reviewed = 'Y', reviewed_by_id = "
|
|
<< ReviewerId
|
|
<< " WHERE channel_id = " << chanId
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.RejectChannelQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.RejectChannelQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0) return true; else return false;
|
|
}
|
|
|
|
bool cservice::RewievChannel(unsigned int chanId)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "UPDATE pending SET status = '8',"
|
|
<< "last_updated = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< "check_start_ts = date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< "WHERE channel_id = " << chanId
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.RewievtChannelQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.RewievChannelQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0) return true; else return false;
|
|
}
|
|
|
|
bool cservice::AcceptChannel(unsigned int chanId, const string& reason)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "UPDATE pending SET status = '3',"
|
|
<< " last_updated = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< " decision_ts = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< " decision = 'by The Judge: "
|
|
<< reason
|
|
<< "', reviewed = 'Y', reviewed_by_id = "
|
|
<< ReviewerId
|
|
<< " WHERE channel_id = " << chanId
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.AcceptChannelQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.AcceptChannelQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
} else if (SQLDb->Tuples() != 0) return true; else return false;
|
|
}
|
|
|
|
bool cservice::sqlRegisterChannel(iClient* theClient, sqlUser* mngrUsr, const string& chanName)
|
|
{
|
|
|
|
/*
|
|
* Create the new channel and insert it into the cache.
|
|
* If the channel exists on IRC, grab the creation timestamp
|
|
* and use this as the channel_ts in the Db.
|
|
*/
|
|
|
|
unsigned int channel_ts = 0;
|
|
Channel* tmpChan = Network->findChannel(chanName);
|
|
channel_ts = tmpChan ? tmpChan->getCreationTime() : ::time(NULL);
|
|
|
|
sqlChannel* newChan = new (std::nothrow) sqlChannel(SQLDb);
|
|
newChan->setName(chanName);
|
|
newChan->setChannelTS(channel_ts);
|
|
newChan->setRegisteredTS(currentTime());
|
|
newChan->setChannelMode("+tnR");
|
|
newChan->setFlag(sqlChannel::F_AUTOJOIN);
|
|
newChan->setLastUsed(currentTime());
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
|
|
/*
|
|
* If this channel exists in the database (without a registered_ts set),
|
|
* then it is currently unclaimed. This register command will
|
|
* update the timestamp, and proceed to adduser.
|
|
*/
|
|
|
|
stringstream checkQuery;
|
|
checkQuery << "SELECT id FROM channels WHERE "
|
|
<< "registered_ts = 0 AND lower(name) = '"
|
|
<< escapeSQLChars(string_lower(chanName))
|
|
<< "'"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlRegisterChannelQuery> " << checkQuery.str().c_str() << endl;
|
|
#endif
|
|
|
|
bool isUnclaimed = false;
|
|
if (SQLDb->Exec(checkQuery, true))
|
|
// if ((status = bot->SQLDb->Exec(checkQuery.str().c_str())) == PGRES_TUPLES_OK)
|
|
{
|
|
if (SQLDb->Tuples() > 0) isUnclaimed = true;
|
|
}
|
|
|
|
if (isUnclaimed)
|
|
{
|
|
/*
|
|
* Quick query to set registered_ts back for this chan.
|
|
*/
|
|
|
|
stringstream reclaimQuery;
|
|
|
|
reclaimQuery << "UPDATE channels SET registered_ts = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< " last_updated = date_part('epoch', CURRENT_TIMESTAMP)::int, "
|
|
<< " flags = 0, description = '', url = '', comment = '', keywords = '', channel_mode = '+tn' "
|
|
<< " WHERE lower(name) = '"
|
|
<< escapeSQLChars(string_lower(chanName))
|
|
<< "'"
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "sqlRegChannReclaimQuery> " << reclaimQuery.str().c_str() << endl;
|
|
#endif
|
|
|
|
if (!SQLDb->Exec(reclaimQuery)) return false;
|
|
}
|
|
else /* We perform a normal registration. */
|
|
{
|
|
newChan->insertRecord();
|
|
}
|
|
|
|
/*
|
|
* Now add the target chap at 500 in the new channel. To do this, we need to know
|
|
* the db assigned channel id of the newly created channel :/
|
|
*/
|
|
stringstream idQuery;
|
|
idQuery << "SELECT id FROM channels WHERE "
|
|
<< "lower(name) = '"
|
|
<< escapeSQLChars(string_lower(chanName))
|
|
<< "'"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlRegChannidQuery> " << idQuery.str().c_str() << endl;
|
|
#endif
|
|
|
|
unsigned int theId = 0;
|
|
|
|
if (SQLDb->Exec(idQuery, true))
|
|
// if ((status = bot->SQLDb->Exec(idQuery.str().c_str())) ==
|
|
//PGRES_TUPLES_OK)
|
|
{
|
|
if (SQLDb->Tuples() > 0)
|
|
{
|
|
theId = atoi(SQLDb->GetValue(0, 0));
|
|
newChan->setID(theId);
|
|
|
|
sqlChannelCache.insert(cservice::sqlChannelHashType::value_type(newChan->getName(), newChan));
|
|
sqlChannelIDCache.insert(cservice::sqlChannelIDHashType::value_type(newChan->getID(), newChan));
|
|
} else
|
|
{
|
|
/*
|
|
* If we can't find the channel in the db, something has gone
|
|
* horribly wrong.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
} else
|
|
{
|
|
return false;
|
|
}
|
|
newChan->commit();
|
|
/*
|
|
* Create the new manager.
|
|
*/
|
|
|
|
sqlLevel* newManager = new (std::nothrow) sqlLevel(SQLDb);
|
|
newManager->setChannelId(newChan->getID());
|
|
newManager->setUserId(mngrUsr->getID());
|
|
newManager->setAccess(500);
|
|
newManager->setAdded(currentTime());
|
|
if (theClient == getInstance())
|
|
{
|
|
newManager->setAddedBy("(" + getNickName() + ") " + getInstance()->getNickUserHost());
|
|
newManager->setLastModifBy("*** The Judge ***");
|
|
}
|
|
else
|
|
{
|
|
newManager->setAddedBy("(" + theUser->getUserName() + ") " + theClient->getNickUserHost());
|
|
newManager->setLastModifBy("(" + theUser->getUserName() + ") " + theClient->getNickUserHost());
|
|
}
|
|
newManager->setLastModif(currentTime());
|
|
if (!newManager->insertRecord())
|
|
{
|
|
validResponseString = "Couldn't automatically add the level 500 Manager, check if doesn't already exist.";
|
|
logDebugMessage(validResponseString.c_str());
|
|
delete(newManager);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Insert this new 500 into the level cache.
|
|
*/
|
|
|
|
pair<int, int> thePair( newManager->getUserId(), newManager->getChannelId());
|
|
sqlLevelCache.insert(cservice::sqlLevelHashType::value_type(thePair, newManager));
|
|
|
|
/* set channel mode R - tmpChan is created further above */
|
|
stringstream tmpTS;
|
|
tmpTS << channel_ts;
|
|
string channelTS = tmpTS.str();
|
|
|
|
if (tmpChan)
|
|
getUplink()->Mode(NULL, tmpChan, string("+R"), channelTS );
|
|
getUplink()->RegisterChannelEvent(chanName.c_str(),this);
|
|
writeChannelLog(newChan, theClient, sqlChannel::EV_REGISTER, "to " + mngrUsr->getUserName());
|
|
|
|
Join(newChan->getName(), string("+tnR"), newChan->getChannelTS(), true);
|
|
newChan->setInChan(true);
|
|
joinCount++;
|
|
|
|
//Send a welcome notice to the channel
|
|
if (!welcomeNewChanMessage.empty())
|
|
Notice(newChan->getName(), TokenStringsParams(welcomeNewChanMessage.c_str(), newChan->getName().c_str()).c_str());
|
|
|
|
//Set a welcome topic of the new channel, only if the actual topic is empty
|
|
#ifdef TOPIC_TRACK
|
|
if (!welcomeNewChanTopic.empty())
|
|
if (tmpChan && tmpChan->getTopic().empty())
|
|
Topic(tmpChan, welcomeNewChanTopic);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cservice::wipeChannel(unsigned int id)
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "DELETE FROM pending WHERE channel_id = " << id << ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.WipePendingQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.WipePendingQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
theQuery.str("");
|
|
theQuery << "DELETE FROM objections WHERE channel_id = " << id << ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.WipeObjectionsQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.WipeObjectionsQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
theQuery.str("");
|
|
theQuery << "DELETE FROM supporters WHERE channel_id = " << id << ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.WipeSupportersQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.WipeSupportersQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
/*
|
|
// This has to be deleted !!!
|
|
theQuery.str("");
|
|
theQuery << "DELETE FROM levels WHERE channel_id = " << id << ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.WipeLevelsQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.WipeLevelsQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
theQuery.str("");
|
|
theQuery << "DELETE FROM bans WHERE channel_id = " << id << ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.WipeBansQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.WipeBansQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return false;
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
void cservice::checkValidUsersAndChannelsState()
|
|
{
|
|
//First we check out channel<--> manager validity
|
|
typedef std::vector <std::pair<unsigned int,string> > pendingChanListType;
|
|
pendingChanListType chanList, managerList;
|
|
unsigned int currentChanId = 0;
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channel_id,channels.name,manager_id,users.user_name FROM channels,pending,users "
|
|
<< "WHERE channels.id = pending.channel_id AND users.id = pending.manager_id "
|
|
<< "AND (pending.status <> 3 AND pending.status <> 9 AND pending.status <> 4)"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.validChanAndMngrQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.validChanAndMngrQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
unsigned int chanId = atoi(SQLDb->GetValue(i,0));
|
|
string chanName = SQLDb->GetValue(i,1);
|
|
unsigned int mngrId = atoi(SQLDb->GetValue(i,2));
|
|
string mngrUser = SQLDb->GetValue(i,3);
|
|
//chanList.push_back(std::make_pair(chanId,chanName));
|
|
//managerList.push_back(std::make_pair(mngrId,mngrUser));
|
|
chanList.push_back(pendingChanListType::value_type(chanId,chanName));
|
|
managerList.push_back(pendingChanListType::value_type(mngrId,mngrUser));
|
|
}
|
|
}
|
|
if (!chanList.empty())
|
|
{
|
|
logDebugMessage("Checking all pending channels validity ...");
|
|
for (size_t i = 0; i < chanList.size(); ++i)
|
|
{
|
|
//logDebugMessage("Checking channel %s's validity ...",chanList[i].second.c_str());
|
|
if (!isValidChannel(chanList.at(i).second))
|
|
{
|
|
string rejectReason = "Invalid channel (" + validResponseString +")";
|
|
validResponseString.clear();
|
|
RejectChannel(chanList[i].first,rejectReason);
|
|
sqlUser* mgrUsr = getUserRecord(managerList[i].first);
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s has been rejected with reason: %s", chanList[i].second.c_str(),rejectReason.c_str());
|
|
logDebugMessage(rejectReason.c_str());
|
|
} //else logDebugMessage("VALID");
|
|
//logDebugMessage("Checking channel %s's Manager validity ...",chanList[i].second.c_str());
|
|
if (!isValidUser(managerList[i].second))
|
|
{
|
|
string rejectReason = "Invalid applicant (" + validResponseString +")";
|
|
validResponseString.clear();
|
|
RejectChannel(chanList[i].first,rejectReason);
|
|
sqlUser* mgrUsr = getUserRecord(managerList[i].first);
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s has been rejected with reason: %s", chanList[i].second.c_str(),rejectReason.c_str());
|
|
logDebugMessage(rejectReason.c_str());
|
|
logDebugMessage("Rejected channel %s",chanList[i].second.c_str());
|
|
}// else logDebugMessage("VALID");
|
|
} //end of if (chanList.size() > 0)
|
|
}
|
|
//Now we iterate through all the supporters, and looking for invalidity
|
|
//And we don't forget that one supporter might be supporting for multiple channels
|
|
theQuery.str("");
|
|
theQuery << "SELECT user_id, user_name,channel_id,channels.name FROM supporters,users,channels "
|
|
<< "WHERE channels.id = supporters.channel_id AND users.id = supporters.user_id"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.validSuppsQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.validSuppsQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("Checking all supporters validity ...");
|
|
//logDebugMessage("Found %i supporters,",SQLDb->Tuples());
|
|
unsigned int suppUserId, chanId;
|
|
string suppUserName,chanName;
|
|
pendingChanListType suppChanList, suppList;
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
suppUserId = atoi(SQLDb->GetValue(i,0));
|
|
suppUserName = SQLDb->GetValue(i,1);
|
|
chanId = atoi(SQLDb->GetValue(i,2));
|
|
chanName = SQLDb->GetValue(i,3);
|
|
suppChanList.push_back(pendingChanListType::value_type(chanId,chanName));
|
|
suppList.push_back(pendingChanListType::value_type(suppUserId,suppUserName));
|
|
//logDebugMessage("Processed %i",i);
|
|
}
|
|
for (size_t i=0; i<suppList.size(); i++)
|
|
{
|
|
suppUserName = suppList[i].second;
|
|
chanName = suppChanList[i].second;
|
|
chanId = suppChanList[i].first;
|
|
string mngrUsr(""); // = managerList.find(chanId);
|
|
//logDebugMessage("Checking validity of suporter %s supporting channel %s",suppUserName.c_str(),chanName.c_str());
|
|
if (!isValidUser(suppUserName))
|
|
{
|
|
string rejectReason = "Invalid supporter " + suppUserName + " (" + validResponseString +")";
|
|
validResponseString.clear();
|
|
logDebugMessage(rejectReason.c_str());
|
|
for (unsigned int j=0; j < chanList.size(); j++ )
|
|
{
|
|
if (chanList[j].first == chanId)
|
|
{
|
|
mngrUsr = managerList[j].second;
|
|
//logDebugMessage("FOUND MANAGER %s",mngrUsr.c_str());
|
|
break;
|
|
}
|
|
}
|
|
if (mngrUsr.empty())
|
|
{
|
|
//logDebugMessage("Not Found the Manager");
|
|
//logDebugMessage("Because the channel is or Rejected Or Accepted !");
|
|
return;
|
|
}
|
|
sqlUser* mgrUsr = getUserRecord(mngrUsr);
|
|
NoteAllAuthedClients(mgrUsr,rejectReason.c_str());
|
|
if (currentChanId != chanId)
|
|
{
|
|
RejectChannel(chanId,rejectReason);
|
|
logDebugMessage("Rejected channel %s",chanName.c_str());
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s has been rejected with reason: %s", chanName.c_str(),rejectReason.c_str());
|
|
}
|
|
currentChanId = chanId;
|
|
} //isValidUser
|
|
} //for
|
|
suppChanList.clear();
|
|
suppList.clear();
|
|
} //Tuples != 0
|
|
else if (!chanList.empty()) logDebugMessage("WARNING: Not found any supporter!");
|
|
chanList.clear();
|
|
managerList.clear();
|
|
return;
|
|
}
|
|
|
|
void cservice::checkIncomings()
|
|
{
|
|
string chanName;
|
|
unsigned int chanId;
|
|
//int mngrId;
|
|
string mngrUser;
|
|
string reason = "Failed supporters confirmation.";
|
|
std::vector<std::pair<std::pair<int,string>, string> > rejectList;
|
|
unsigned int pendingTime = SupportDays * JudgeDaySeconds; //currentTime()
|
|
stringstream theQuery;
|
|
// theQuery << "SELECT channels.name,channels.id,manager_id,users.user_name FROM channels,pending,users WHERE channels.id = pending.channel_id "
|
|
theQuery << "SELECT channels.name,channels.id,users.user_name FROM channels,pending,users WHERE channels.id = pending.channel_id "
|
|
<< "AND pending.status = 0 AND (pending.created_ts + "
|
|
<< pendingTime
|
|
<< ") < date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< " AND users.id = manager_id"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.IncomingQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.IncomingQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("List of expiring Incoming applications:");
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
chanName = SQLDb->GetValue(i,0);
|
|
chanId = atoi(SQLDb->GetValue(i,1));
|
|
mngrUser = SQLDb->GetValue(i,2);
|
|
rejectList.push_back(std::make_pair(std::make_pair(chanId,chanName),mngrUser));
|
|
logDebugMessage("Rejected channel application %s with reason: %s",chanName.c_str(),reason.c_str());
|
|
}
|
|
if (!rejectList.empty())
|
|
{
|
|
for (unsigned int i=0; i<rejectList.size(); i++ )
|
|
{
|
|
sqlUser* mgrUsr = getUserRecord(rejectList[i].second);
|
|
incompleteChanRegsType::iterator theApp = incompleteChanRegs.find(mgrUsr->getID());
|
|
reason = "Failed supporters confirmation.";
|
|
if (theApp != incompleteChanRegs.end())
|
|
{
|
|
delete(theApp->second);
|
|
incompleteChanRegs.erase(theApp);
|
|
reason = "Failed to enumerate all required paramaters for channel application";
|
|
}
|
|
RejectChannel(rejectList[i].first.first,reason);
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s has been rejected with reason: %s", rejectList[i].first.second.c_str(),reason.c_str());
|
|
}
|
|
rejectList.clear();
|
|
}
|
|
}// else logDebugMessage("No Expiring Incoming Applications Found!");
|
|
return;
|
|
}
|
|
|
|
void cservice::checkTrafficPass()
|
|
{
|
|
if (pendingChannelList.size() == 0)
|
|
{
|
|
//logDebugMessage("No Expiring pending channel application found");
|
|
return;
|
|
}
|
|
|
|
pendingChannelListType::iterator ptr = pendingChannelList.begin();
|
|
while (ptr != pendingChannelList.end())
|
|
{
|
|
sqlPendingChannel* pendingChan = ptr->second;
|
|
|
|
//Let's begin with optimism
|
|
bool JoinsPass = true;
|
|
bool uniqueJoinsPass = true;
|
|
bool minSupportersPass = true;
|
|
bool minSupportersJoinPass = true;
|
|
string rejectReason = "";
|
|
|
|
unsigned int trafficTime = MaxDays * JudgeDaySeconds;
|
|
time_t elapsedDays = pendingChan->checkStart + time_t(trafficTime);
|
|
if (elapsedDays < currentTime())
|
|
{
|
|
//Satisfied or not surely we can stop listening channel events
|
|
MyUplink->UnRegisterChannelEvent(ptr->first, this);
|
|
//Check if the channel was visited at least at ONCE by the required MinSupporters count supporters
|
|
unsigned int actualMinSupporters = pendingChan->uniqueSupporterList.size();
|
|
if (actualMinSupporters < MinSupporters)
|
|
{
|
|
//rejectReason = "Insuffucient number of supporters that visited the channel";
|
|
minSupportersPass = false;
|
|
logDebugMessage("Insufficient number of supporters that visited the pending channel %s (%i/%i)",ptr->first.c_str(),actualMinSupporters,MinSupporters);
|
|
}
|
|
else // <- if yes, we check if one of the supporters has an insufficient MinSupportersJoin joincount
|
|
{
|
|
sqlPendingChannel::trafficListType::iterator ptr2 = pendingChan->uniqueSupporterList.begin();
|
|
while (ptr2 != pendingChan->uniqueSupporterList.end())
|
|
{
|
|
if (ptr2->second->join_count < MinSupportersJoin)
|
|
{
|
|
unsigned int supporterUserId = ptr2->second->ip_number;
|
|
unsigned int actualMinSupportersJoin = ptr2->second->join_count;
|
|
minSupportersJoinPass = false;
|
|
logDebugMessage("Insufficient supporter joincount of supporter userid %i (%i/%i) on pending channel %s",supporterUserId,actualMinSupportersJoin,MinSupportersJoin,ptr->first.c_str());
|
|
}
|
|
++ptr2;
|
|
} //end while
|
|
} //end else
|
|
|
|
//Next, we check after general channel activity:
|
|
//(total) join_count, and the unique_join_count
|
|
if (pendingChan->unique_join_count < UniqueJoins)
|
|
{
|
|
uniqueJoinsPass = false;
|
|
logDebugMessage("Insufficient number of IP's (%i/%i) that visited the pending channel %s",pendingChan->unique_join_count,UniqueJoins,ptr->first.c_str());
|
|
}
|
|
if (pendingChan->join_count < Joins)
|
|
{
|
|
JoinsPass = false;
|
|
logDebugMessage("Insufficient number of joincounts (%i/%i) that visited the pending channel %s",pendingChan->join_count,Joins,ptr->first.c_str());
|
|
}
|
|
//We all passed, so we move the channel to the next Notification phase
|
|
if ((JoinsPass) && (uniqueJoinsPass) && (minSupportersPass) && (minSupportersJoinPass))
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "UPDATE pending SET status = '2',"
|
|
<< "check_start_ts = date_part('epoch', CURRENT_TIMESTAMP)::int,"
|
|
<< "last_updated = date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< "WHERE channel_id = " << pendingChan->channel_id
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::checkTrafficPass.moveToState2Notification> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
logDebugMessage("Error on update pending trafficCheck -> notification");
|
|
else
|
|
logDebugMessage("Channel %s has passed traffic checking, successfully moved to Notification stage",ptr->first.c_str());
|
|
}
|
|
} //(elapsedDays < currentTime())
|
|
if ((!uniqueJoinsPass) || (!JoinsPass))
|
|
rejectReason = "Insufficient channel activity";
|
|
if ((!minSupportersPass) || (!minSupportersJoinPass))
|
|
rejectReason = "Insufficient suporter activity";
|
|
if (!rejectReason.empty())
|
|
{
|
|
RejectChannel(pendingChan->channel_id,rejectReason);
|
|
logDebugMessage("Rejecting channel %s with reason: %s",ptr->first.c_str(),rejectReason.c_str());
|
|
ptr->second = NULL;
|
|
delete(pendingChan);
|
|
pendingChannelList.erase(ptr);
|
|
}
|
|
++ptr;
|
|
} /* while() */
|
|
}
|
|
|
|
void cservice::checkObjections()
|
|
{
|
|
if (!RewievOnObject) return;
|
|
std::vector<std::pair<std::pair<int,string>, string> > objectList;
|
|
unsigned int notifTime = NotifyDays * JudgeDaySeconds;
|
|
int actualChan = 0;
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channels.name,channels.id,users.user_name FROM channels,pending,users,objections "
|
|
<< "WHERE channels.id = pending.channel_id "
|
|
<< "AND pending.status = 2 AND (pending.check_start_ts + "
|
|
<< notifTime
|
|
<< ") < date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< " AND users.id = manager_id"
|
|
<< " AND objections.channel_id = pending.channel_id"
|
|
//<< " LIMIT 1"
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.ObjectionkQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.ObjectionkQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("List of applications moved to Ready to rewiev:");
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
string chanName = SQLDb->GetValue(i,0);
|
|
int chanId = atoi(SQLDb->GetValue(i,1));
|
|
string mngrUser = SQLDb->GetValue(i,2);
|
|
if (chanId != actualChan) //more than one objection per channel lead to multiple db hit, so we need to limit to one result
|
|
{
|
|
actualChan = chanId;
|
|
objectList.push_back(std::make_pair(std::make_pair(chanId,chanName),mngrUser));
|
|
logDebugMessage(chanName.c_str());
|
|
}
|
|
}
|
|
}
|
|
if (!objectList.empty())
|
|
for (unsigned int i=0; i<objectList.size(); i++ )
|
|
{
|
|
RewievChannel(objectList[i].first.first);
|
|
logDebugMessage("Channel %s has been moved to Ready to review",objectList[i].first.second.c_str());
|
|
sqlUser* mgrUsr = getUserRecord(objectList[i].second.c_str());
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s is now at state Ready to rewiev", objectList[i].first.second.c_str());
|
|
}
|
|
objectList.clear();
|
|
return;
|
|
}
|
|
|
|
void cservice::checkAccepts()
|
|
{
|
|
std::vector<std::pair<std::pair<int,string>, string> > acceptList;
|
|
unsigned int notifTime = NotifyDays * JudgeDaySeconds;
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channels.name,channels.id,users.user_name FROM channels,pending,users "
|
|
<< "WHERE channels.id = pending.channel_id "
|
|
<< "AND pending.status = 2 AND (pending.check_start_ts + "
|
|
<< notifTime
|
|
<< ") < date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< " AND users.id = manager_id"
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.AcceptQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.AcceptQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("List of applications moved to ACCEPT:");
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
string chanName = SQLDb->GetValue(i,0);
|
|
int chanId = atoi(SQLDb->GetValue(i,1));
|
|
string mngrUser = SQLDb->GetValue(i,2);
|
|
acceptList.push_back(std::make_pair(std::make_pair(chanId,chanName),mngrUser));
|
|
logDebugMessage(chanName.c_str());
|
|
}
|
|
}
|
|
if (!acceptList.empty())
|
|
for (unsigned int i=0; i<acceptList.size(); i++ )
|
|
{
|
|
AcceptChannel(acceptList[i].first.first,"ACCEPTED");
|
|
sqlUser* mgrUsr = getUserRecord(acceptList[i].second.c_str());
|
|
if (sqlRegisterChannel(getInstance(), mgrUsr, acceptList[i].first.second.c_str()))
|
|
{
|
|
logAdminMessage("%s (The Judge) has registered %s to %s", getInstance()->getNickName().c_str(),
|
|
acceptList[i].first.second.c_str(), mgrUsr->getUserName().c_str());
|
|
NoteAllAuthedClients(mgrUsr,"Your channel application of %s is Accepted", acceptList[i].first.second.c_str());
|
|
} else
|
|
logDebugMessage("(The Judge) FAILED to sqlRegisterChannel");
|
|
}
|
|
acceptList.clear();
|
|
}
|
|
|
|
/**
|
|
* After a time we clenup any "never" reviewed channel
|
|
*/
|
|
void cservice::checkRewievs()
|
|
{
|
|
if (!RewievsExpireTime) return;
|
|
std::vector<std::pair<std::pair<int,string>, string> > rewievList;
|
|
unsigned int rewievTime = RewievsExpireTime * JudgeDaySeconds;
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channels.name,channels.id,users.user_name FROM channels,pending,users "
|
|
<< "WHERE channels.id = pending.channel_id "
|
|
<< "AND pending.status = 8 AND (pending.check_start_ts + "
|
|
<< rewievTime
|
|
<< ") < date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< " AND users.id = manager_id"
|
|
<< ends;
|
|
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.rewievQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.rewievQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("List of Wiped applications:");
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
string chanName = SQLDb->GetValue(i,0);
|
|
int chanId = atoi(SQLDb->GetValue(i,1));
|
|
string mngrUser = SQLDb->GetValue(i,3);
|
|
rewievList.push_back(std::make_pair(std::make_pair(chanId,chanName),mngrUser));
|
|
logDebugMessage(chanName.c_str());
|
|
}
|
|
}
|
|
if (!rewievList.empty())
|
|
for (unsigned int i=0; i<rewievList.size(); i++ )
|
|
{
|
|
if (wipeChannel(rewievList[i].first.first))
|
|
logDebugMessage("Expired and wiped Ready to Review channel application %s",rewievList[i].first.second.c_str());
|
|
else
|
|
logDebugMessage("(The Judge) Failed to wipeChannel(ready to Review) %s",rewievList[i].first.second.c_str());
|
|
}
|
|
rewievList.clear();
|
|
}
|
|
|
|
// After a time, we cleanup de databes from old application data's: pending channels, supporters, etc
|
|
//But this is valuable *only* for channels Accepted OR Rejected !!!
|
|
void cservice::cleanUpPendings()
|
|
{
|
|
//If PendingsExpireTime == 0 than feature is disabled
|
|
if (!PendingsExpireTime) return;
|
|
unsigned int expireTime = PendingsExpireTime * JudgeDaySeconds;
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channel_id FROM pending "
|
|
<< "WHERE (pending.status = 3 OR pending.status = 9 OR pending.status = 4) "
|
|
<< "AND (pending.last_updated + "
|
|
<< expireTime
|
|
<< ") < date_part('epoch', CURRENT_TIMESTAMP)::int "
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on Judge.checkPendingCleanupsQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "Judge.checkPendingCleanupsQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return;
|
|
} else if (SQLDb->Tuples() != 0)
|
|
{
|
|
logDebugMessage("Found %i channel(s) to pendingCleanup",SQLDb->Tuples());
|
|
vector <unsigned int> wipeChanList;
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
wipeChanList.push_back(atoi(SQLDb->GetValue(i,0)));
|
|
for (unsigned int i = 0; i < wipeChanList.size(); i++)
|
|
{
|
|
wipeChannel(wipeChanList[i]);
|
|
logDebugMessage("Wiped channel %i",wipeChanList[i]);
|
|
}
|
|
wipeChanList.clear();
|
|
} //else logDebugMessage("No pendingCleanup found!");
|
|
return;
|
|
}
|
|
|
|
void cservice::loadIncompleteChanRegs()
|
|
{
|
|
// This is a 2 phase process ...
|
|
// 1. We're iterate over all channels in pending table, checking after 0 status,
|
|
// description, real name.
|
|
// 2. Iterating all over the supporters table finding which chanId's has supporters
|
|
// If the found supporters count == RequiredSupporters we skip
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channel_id,channels.name,manager_id,managername,pending.description FROM pending,channels "
|
|
<< "WHERE channels.id=pending.channel_id AND status = 0"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "loadIncomplChanRegs> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery ) )
|
|
//if( PGRES_COMMAND_OK != status )
|
|
{
|
|
elog << "loadIncomplChanRegs> Something went wrong: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
logDebugMessage("loadIncomplChanRegs ERROR: %s", SQLDb->ErrorMessage().c_str());
|
|
return;
|
|
}
|
|
if (SQLDb->Tuples() == 0) return;
|
|
|
|
for (unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
{
|
|
sqlIncompleteChannel* newApp = new (std::nothrow) sqlIncompleteChannel(SQLDb);
|
|
assert(newApp != 0);
|
|
newApp->chanId = atoi(SQLDb->GetValue(i,0));
|
|
//elog << "chanId=" << newApp->chanId << endl;
|
|
newApp->chanName = SQLDb->GetValue(i,1);
|
|
//elog << "chanName=" << newApp->chanName << endl;
|
|
unsigned int mngrId = atoi(SQLDb->GetValue(i,2));
|
|
//elog << "mngrId=" << mngrId << endl;
|
|
newApp->RealName = SQLDb->GetValue(i,3);
|
|
//elog << "RealName=" << newApp->RealName << endl;
|
|
newApp->Description = SQLDb->GetValue(i,4);
|
|
//elog << "Description=" << newApp->Description << endl;
|
|
|
|
incompleteChanRegs.insert(cservice::incompleteChanRegsType::value_type(mngrId,newApp));
|
|
}
|
|
|
|
// 2nd part
|
|
/*
|
|
incompleteChanRegsType::iterator ptr = incompleteChanRegs.begin();
|
|
while (ptr != incompleteChanRegs.end())
|
|
{
|
|
sqlIncompleteChannel* theApp = (ptr)->second;
|
|
|
|
if (theApp->Description.empty())
|
|
{ //Do not do anything if not completed until description
|
|
++ptr;
|
|
continue;
|
|
}
|
|
theQuery.str("");
|
|
theQuery << "SELECT user_id FROM supporters WHERE channel_id = "
|
|
<< theApp->chanId
|
|
<< " AND support = '?'"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "loadIncomplChanRegs.loadSupporters> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery ) )
|
|
//if( PGRES_COMMAND_OK != status )
|
|
{
|
|
elog << "loadIncomplChanRegs.loadSupporters> Something went wrong: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
logDebugMessage("loadIncomplChanRegs.loadSupporters ERROR: %s", SQLDb->ErrorMessage().c_str());
|
|
return;
|
|
}
|
|
unsigned int rCount = SQLDb->Tuples();
|
|
if ((rCount > 0) && (rCount < RequiredSupporters))
|
|
{
|
|
for (unsigned int i=0; i < rCount; ++i)
|
|
{
|
|
unsigned int suppId = atoi(SQLDb->GetValue(i,0));
|
|
theApp->supps.insert(sqlIncompleteChannel::supporterListType::value_type(suppId,NULL));
|
|
}
|
|
for (sqlIncompleteChannel::supporterListType::iterator itr = theApp->supps.begin();
|
|
itr != theApp->supps.end(); ++itr)
|
|
itr->second = getUserRecord(itr->first);
|
|
}
|
|
++ptr;
|
|
}
|
|
*/
|
|
return;
|
|
}
|
|
|
|
string cservice::userStatusFlags( const string& theUser )
|
|
{
|
|
|
|
string flagString = "";
|
|
|
|
sqlUserHashType::iterator ptr = sqlUserCache.find(theUser);
|
|
|
|
if(ptr != sqlUserCache.end())
|
|
{
|
|
flagString = 'L';
|
|
/* (depreciated)
|
|
* if(tmpUser->getFlag(sqlUser::F_LOGGEDIN)) flagString += 'U';
|
|
*/
|
|
}
|
|
|
|
return flagString;
|
|
}
|
|
|
|
bool cservice::validUserMask(const string& userMask) const
|
|
{
|
|
|
|
// Check that a '!' exists, and that the nickname
|
|
// is no more than 15 characters //50 ;-)
|
|
StringTokenizer st1( userMask, '!' ) ;
|
|
if( (st1.size() != 2) || (st1[ 0 ].size() > 50) )
|
|
{
|
|
return false ;
|
|
}
|
|
|
|
// Check that a '@' exists and that the username is
|
|
// no more than 12 characters //25 ;-)
|
|
StringTokenizer st2( st1[ 1 ], '@' ) ;
|
|
|
|
if( (st2.size() != 2) || (st2[ 0 ].size() > 25) )
|
|
{
|
|
return false ;
|
|
}
|
|
|
|
// Be sure that the hostname is no more than 128 characters
|
|
if( st2[ 1 ].size() > 128 )
|
|
{
|
|
return false ;
|
|
}
|
|
|
|
// Tests have passed
|
|
return true ;
|
|
}
|
|
|
|
void cservice::OnChannelMode( Channel* theChan, ChannelUser* sourceUser,
|
|
const xServer::modeVectorType& modeVector )
|
|
{
|
|
#ifdef CATCH_OPMODES
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if (!reggedChan) return;
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
string modeString, antimode;
|
|
if (sourceUser == NULL) return; //mode made by a server!
|
|
if (sourceUser->getClient()->getMode(iClient::MODE_SERVICES)) return;
|
|
string chanName = string_lower(theChan->getName());
|
|
bool currentPolarity = true;
|
|
|
|
if (currentPolarity) modeString = "+"; else modeString = "-";
|
|
|
|
for (xServer::modeVectorType::const_iterator mItr = modeVector.begin() ;
|
|
mItr != modeVector.end() ; ++mItr)
|
|
{
|
|
bool polarity = (*mItr).first;
|
|
if (polarity != currentPolarity)
|
|
{
|
|
currentPolarity = polarity;
|
|
if (modeString.size() < 2) modeString = "";
|
|
if (polarity) modeString += "+"; else modeString += "-";
|
|
}
|
|
if ((*mItr).second == Channel::MODE_M) modeString += "m";
|
|
if ((*mItr).second == Channel::MODE_I) modeString += "i";
|
|
if ((*mItr).second == Channel::MODE_R) modeString += "r";
|
|
//if ((*mItr).second == Channel::MODE_K) modeString += "k"; //nope, it's with parameters, handled by OnChannelModeK
|
|
//if ((*mItr).second == Channel::MODE_L) modeString += "l"; //nope, it's with parameters, handled by OnChannelModeL
|
|
if ((*mItr).second == Channel::MODE_S) modeString += "s";
|
|
if ((*mItr).second == Channel::MODE_P) modeString += "p";
|
|
if ((*mItr).second == Channel::MODE_T) modeString += "t";
|
|
if ((*mItr).second == Channel::MODE_N) modeString += "n";
|
|
if ((*mItr).second == Channel::MODE_D) modeString += "D";
|
|
} // for()
|
|
if (sourceUser->isMadeOpMode())
|
|
{
|
|
string logModeString = theChan->getName() + " " + modeString;
|
|
logAdminMessage("*** OPMODE by %s MODE %s",sourceUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(sourceUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cservice::OnChannelModeV( Channel* theChan, ChannelUser* theChanUser,
|
|
const xServer::voiceVectorType& theTargets)
|
|
{
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if(!reggedChan)
|
|
{
|
|
// elog << "cservice::OnChannelModeV> WARNING, unable to "
|
|
// << "locate channel record"
|
|
// << " for registered channel event: "
|
|
// << theChan->getName()
|
|
// << endl;
|
|
return;
|
|
}
|
|
|
|
if(!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
// List of clients to devoice.
|
|
vector< iClient* > deVoiceList;
|
|
|
|
bool currentPolarity = true;
|
|
string logModeString;
|
|
string logModeClientList;
|
|
|
|
if (currentPolarity) logModeString = "+"; else logModeString = "-";
|
|
|
|
for( xServer::voiceVectorType::const_iterator ptr = theTargets.begin() ;
|
|
ptr != theTargets.end() ; ++ptr )
|
|
{
|
|
ChannelUser* tmpUser = ptr->second;
|
|
bool polarity = ptr->first;
|
|
|
|
if (polarity != currentPolarity)
|
|
{
|
|
currentPolarity = polarity;
|
|
if (logModeString.size() < 2) logModeString = "";
|
|
if (polarity) logModeString += "+"; else logModeString += "-";
|
|
}
|
|
logModeString += "v";
|
|
logModeClientList += " " + tmpUser->getClient()->getNickName();
|
|
|
|
if (polarity)
|
|
{
|
|
// If somebody is being voiced.
|
|
// If the channel is NOVOICE, devoice everyone who tries to get voiced!
|
|
if (reggedChan->getFlag(sqlChannel::F_NOVOICE))
|
|
{
|
|
if ( !tmpUser->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
deVoiceList.push_back(tmpUser->getClient());
|
|
}
|
|
}
|
|
} // for()
|
|
|
|
/*
|
|
* Send notices and perform the devoice's. (But don't deop anything thats +k).
|
|
*/
|
|
|
|
if( !deVoiceList.empty() )
|
|
{
|
|
if ((theChanUser) && (reggedChan->getFlag(sqlChannel::F_NOVOICE)) )
|
|
{
|
|
Notice( theChanUser->getClient(),
|
|
"The NOVOICE flag is set on %s",
|
|
reggedChan->getName().c_str());
|
|
}
|
|
if (theChanUser->getMode(ChannelUser::MODE_O))
|
|
if (!theChanUser->getClient()->getMode(iClient::MODE_SERVICES))
|
|
DeOp(theChan,theChanUser->getClient());
|
|
DeVoice(theChan, deVoiceList);
|
|
}
|
|
#ifdef CATCH_OPMODES
|
|
if (theChanUser && theChanUser->isMadeOpMode())
|
|
{
|
|
logModeString = theChan->getName() + " " + logModeString + logModeClientList;
|
|
logAdminMessage("*** OPMODE by %s MODE %s",theChanUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
//For now if the client isn't authed will NOT log ...
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(theChanUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cservice::OnChannelModeO( Channel* theChan, ChannelUser* theChanUser,
|
|
const xServer::opVectorType& theTargets)
|
|
{
|
|
/*
|
|
* There are a number of things to do when we recieve a mode O for
|
|
* a channel/targets.
|
|
* Firstly, we check the status of certain channel flags is it set
|
|
* NOOP? is it set STRICTOP?
|
|
* We will only ever recieve events for channels that are registered.
|
|
*/
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if(!reggedChan)
|
|
{
|
|
// elog << "cservice::OnChannelModeO> WARNING, unable to "
|
|
// << "locate channel record"
|
|
// << " for registered channel event: "
|
|
// << theChan->getName()
|
|
// << endl;
|
|
return;
|
|
}
|
|
|
|
if(!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
|
|
// List of clients to deop.
|
|
vector< iClient* > deopList;
|
|
|
|
// If we find a situation where we need to deop the person who has
|
|
// performed the mode, do so.
|
|
bool sourceHasBeenBad = false;
|
|
|
|
int deopCounter = 0;
|
|
bool currentPolarity = true;
|
|
string logModeString;
|
|
string logModeClientList;
|
|
|
|
if (currentPolarity) logModeString = "+"; else logModeString = "-";
|
|
|
|
for( xServer::opVectorType::const_iterator ptr = theTargets.begin() ;
|
|
ptr != theTargets.end() ; ++ptr )
|
|
{
|
|
ChannelUser* tmpUser = ptr->second;
|
|
bool polarity = ptr->first;
|
|
|
|
if (polarity != currentPolarity)
|
|
{
|
|
currentPolarity = polarity;
|
|
if (logModeString.size() < 2) logModeString = "";
|
|
if (polarity) logModeString += "+"; else logModeString += "-";
|
|
}
|
|
logModeString += "o";
|
|
logModeClientList += " " + tmpUser->getClient()->getNickName();
|
|
|
|
if (polarity)
|
|
{
|
|
// If somebody is being opped.
|
|
|
|
// If the channel is NOOP, deop everyone who tries to
|
|
// get opped!
|
|
if (reggedChan->getFlag(sqlChannel::F_NOOP))
|
|
{
|
|
if ( !tmpUser->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
deopList.push_back(tmpUser->getClient());
|
|
}
|
|
|
|
sqlUser* authUser = isAuthed(tmpUser->getClient(), false);
|
|
|
|
// Has the target user's account been suspended?
|
|
if (authUser && authUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
if (theChanUser) Notice(theChanUser->getClient(), "The user %s (%s) has been suspended by a CService Administrator.",
|
|
authUser->getUserName().c_str(), tmpUser->getClient()->getNickName().c_str());
|
|
deopList.push_back(tmpUser->getClient());
|
|
sourceHasBeenBad = true;
|
|
}
|
|
|
|
// If the channel is STRICTOP, deop everyone who isn't
|
|
// authenticated or and doesn't have access on the
|
|
// channel.
|
|
|
|
if (reggedChan->getFlag(sqlChannel::F_STRICTOP))
|
|
{
|
|
if (!authUser)
|
|
{
|
|
// Not authed, deop.
|
|
if ( !tmpUser->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
deopList.push_back(tmpUser->getClient());
|
|
sourceHasBeenBad = true;
|
|
// Authed but doesn't have access... deop.
|
|
}
|
|
else if (!(getEffectiveAccessLevel(authUser,reggedChan, false) >= level::op))
|
|
{
|
|
if ( !tmpUser->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
deopList.push_back(tmpUser->getClient());
|
|
sourceHasBeenBad = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The 'Fun' Part. Scan through channel bans to see if this hostmask
|
|
* is 'banned' at 75 or below.
|
|
*/
|
|
|
|
sqlBan* theBan = isBannedOnChan(reggedChan, tmpUser->getClient());
|
|
if( theBan && (theBan->getLevel() <= 75) )
|
|
{
|
|
if ( !tmpUser->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
{
|
|
deopList.push_back(tmpUser->getClient());
|
|
sourceHasBeenBad = true;
|
|
|
|
/* Tell the person bein op'd that they can't */
|
|
Notice(tmpUser->getClient(),
|
|
"You are not allowed to be opped on %s",
|
|
reggedChan->getName().c_str());
|
|
|
|
/* Tell the person doing the op'ing this is bad */
|
|
if (theChanUser)
|
|
{
|
|
Notice(theChanUser->getClient(),
|
|
"%s isn't allowed to be opped on %s",
|
|
tmpUser->getClient()->getNickName().c_str(),
|
|
reggedChan->getName().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // if()
|
|
else
|
|
{
|
|
/* Somebody is being deopped? */
|
|
deopCounter++;
|
|
|
|
/* What if someone deop'd us?! */
|
|
if (tmpUser->getClient() == me)
|
|
{
|
|
logAdminMessage("I've been DEOPped on %s!",
|
|
reggedChan->getName().c_str());
|
|
xClient::Op(theChan, me);
|
|
}
|
|
}
|
|
} // for()
|
|
|
|
/*
|
|
* Send notices and perform the deop's. (But don't deop anything thats +k).
|
|
*/
|
|
|
|
if (theChanUser && sourceHasBeenBad && !theChanUser->getClient()->getMode(iClient::MODE_SERVICES))
|
|
deopList.push_back(theChanUser->getClient());
|
|
|
|
if( !deopList.empty() )
|
|
{
|
|
if ((theChanUser) && (reggedChan->getFlag(sqlChannel::F_NOOP)) )
|
|
{
|
|
Notice( theChanUser->getClient(),
|
|
"The NOOP flag is set on %s",
|
|
reggedChan->getName().c_str());
|
|
}
|
|
|
|
if ((theChanUser) && (reggedChan->getFlag(sqlChannel::F_STRICTOP)) )
|
|
{
|
|
Notice( theChanUser->getClient(),
|
|
"The STRICTOP flag is set on %s",
|
|
reggedChan->getName().c_str());
|
|
}
|
|
|
|
DeOp(theChan, deopList);
|
|
}
|
|
|
|
/*
|
|
* Have more than 'maxdeoppro' been deopped?
|
|
* If so, suspend and kick 'em. (Unless they're +k of course ;)
|
|
*/
|
|
|
|
if ((theChanUser) && (deopCounter >= reggedChan->getMassDeopPro())
|
|
&& (reggedChan->getMassDeopPro() > 0) && !theChanUser->getClient()->getMode(iClient::MODE_SERVICES))
|
|
{
|
|
doInternalBanAndKick(reggedChan, theChanUser->getClient(),
|
|
"### MASSDEOPPRO TRIGGERED! ###");
|
|
}
|
|
#ifdef CATCH_OPMODES
|
|
if (theChanUser && theChanUser->isMadeOpMode())
|
|
{
|
|
logModeString = theChan->getName() + " " + logModeString + logModeClientList;
|
|
logAdminMessage("*** OPMODE by %s MODE %s",theChanUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(theChanUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void cservice::OnChannelModeL( Channel* theChan, bool polarity,
|
|
ChannelUser* theChanUser, const unsigned int& lim)
|
|
{
|
|
#ifdef CATCH_OPMODES
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if (!reggedChan) return;
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
string modeString;
|
|
if (theChanUser == NULL) return; //mode made by a server!
|
|
if (theChanUser->getClient()->getMode(iClient::MODE_SERVICES)) return;
|
|
if (theChanUser->isMadeOpMode())
|
|
{
|
|
string logModeString;
|
|
if (polarity) logModeString = "+l"; else logModeString = "-l";
|
|
logModeString = theChan->getName() + " " + logModeString;
|
|
if (lim > 0) logModeString = TokenStringsParams("%s %i",logModeString.c_str(),lim);
|
|
logAdminMessage("*** OPMODE by %s MODE %s",theChanUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(theChanUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cservice::OnChannelModeK( Channel* theChan, bool polarity,
|
|
ChannelUser* theChanUser, const std::string& keyStr)
|
|
{
|
|
#ifdef CATCH_OPMODES
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if (!reggedChan) return;
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
string modeString;
|
|
if (theChanUser == NULL) return; //mode made by a server!
|
|
if (theChanUser->getClient()->getMode(iClient::MODE_SERVICES)) return;
|
|
if (theChanUser->isMadeOpMode())
|
|
{
|
|
string logModeString;
|
|
if (polarity) logModeString = "+k "; else logModeString = "-k ";
|
|
logModeString = theChan->getName() + " " + logModeString + keyStr;
|
|
logAdminMessage("*** OPMODE by %s MODE %s",theChanUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(theChanUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cservice::OnChannelModeB( Channel* theChan, ChannelUser* theChanUser,
|
|
const xServer::banVectorType& addresses)
|
|
{
|
|
#ifdef CATCH_OPMODES
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if(!reggedChan)
|
|
{
|
|
// elog << "cservice::OnChannelModeV> WARNING, unable to "
|
|
// << "locate channel record"
|
|
// << " for registered channel event: "
|
|
// << theChan->getName()
|
|
// << endl;
|
|
return;
|
|
}
|
|
|
|
if(!reggedChan->getInChan())
|
|
{
|
|
//Do not monitor channels i am not in
|
|
return;
|
|
}
|
|
|
|
bool currentPolarity = true;
|
|
string logModeString;
|
|
string logModeBaddresses;
|
|
|
|
if (currentPolarity) logModeString = "+"; else logModeString = "-";
|
|
|
|
for( xServer::banVectorType::const_iterator ptr = addresses.begin() ;
|
|
ptr != addresses.end() ; ++ptr )
|
|
{
|
|
|
|
bool polarity = ptr->first;
|
|
|
|
if (polarity != currentPolarity)
|
|
{
|
|
currentPolarity = polarity;
|
|
if (logModeString.size() < 2) logModeString = "";
|
|
if (polarity) logModeString += "+"; else logModeString += "-";
|
|
}
|
|
logModeString += "b";
|
|
logModeBaddresses += " " + ptr->second;
|
|
}
|
|
if (theChanUser && theChanUser->isMadeOpMode())
|
|
{
|
|
logModeString = theChan->getName() + " " + logModeString + logModeBaddresses;
|
|
logAdminMessage("*** OPMODE by %s MODE %s",theChanUser->getClient()->getNickName().c_str(),logModeString.c_str());
|
|
logModeString = "OPMODE " + logModeString;
|
|
sqlLogCommand(theChanUser->getClient(),logModeString.c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cservice::OnEvent( const eventType& theEvent,
|
|
void* data1, void* data2, void* data3, void* data4 )
|
|
{
|
|
switch( theEvent )
|
|
{
|
|
case EVT_XQUERY:
|
|
{
|
|
iServer* theServer = static_cast< iServer* >( data1 );
|
|
const char* Routing = reinterpret_cast< char* >( data2 );
|
|
const char* Message = reinterpret_cast< char* >( data3 );
|
|
elog << "CSERVICE.CC: " << theServer->getName() << " " << Routing << " " << Message << endl;
|
|
//As it is possible to run multiple GNUWorld clients on one server, first parameter should be a nickname.
|
|
//If it ain't us, ignore the message, the message is probably meant for another client here.
|
|
StringTokenizer st( Message ) ;
|
|
if( st.size() < 2 )
|
|
{
|
|
//No command or no nick supplied
|
|
break;
|
|
}
|
|
if (st[0] == getNickName())
|
|
{
|
|
string Command = string_upper(st[1]);
|
|
if (Command == "LOGIN")
|
|
{
|
|
doXQLogin(theServer, Routing, Message);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EVT_ACCOUNT:
|
|
{
|
|
iClient* tmpUser = static_cast< iClient* >( data1 ) ;
|
|
networkData* tmpData = static_cast< networkData* >(tmpUser->getCustomData(this) ) ;
|
|
/* Lookup this user account, if its not there.. trouble */
|
|
sqlUser* theUser = getUserRecord(tmpUser->getAccount());
|
|
if (theUser)
|
|
{
|
|
tmpData->currentUser = theUser;
|
|
theUser->addAuthedClient(tmpUser);
|
|
}
|
|
break;
|
|
}
|
|
case EVT_BURST_ACK:
|
|
{
|
|
#ifdef USING_NEFARIOUS
|
|
iServer* theServer = static_cast< iServer* >( data1 );
|
|
if ( theServer == MyUplink->getUplink() )
|
|
{
|
|
//If WE were in burst, it means need to iterate through all the servers, and validate all the nick
|
|
// in short ALL the Nicknames, regardless of the server
|
|
xNetwork::const_clientIterator cItr = Network->clients_begin();
|
|
for ( ; cItr != Network->clients_end(); ++cItr)
|
|
{
|
|
iClient* tmpClient = cItr->second;
|
|
validateNickName(tmpClient);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//only the theServer's clients lists need to be validated
|
|
xNetwork::const_clientIterator cItr = Network->clients_begin();
|
|
for ( ; cItr != Network->clients_end(); ++cItr)
|
|
{
|
|
iClient* tmpClient = cItr->second;
|
|
if (tmpClient->getIntYY() == theServer->getIntYY())
|
|
validateNickName(tmpClient);
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
case EVT_QUIT:
|
|
case EVT_KILL:
|
|
{
|
|
/*
|
|
* We need to deauth this user if they're authed.
|
|
* Also, clean up their custom data memory.
|
|
*/
|
|
|
|
iClient* tmpUser = (theEvent == EVT_QUIT) ?
|
|
static_cast< iClient* >( data1 ) :
|
|
static_cast< iClient* >( data2 ) ;
|
|
|
|
sqlUser* tmpSqlUser = isAuthed(tmpUser, false);
|
|
if (tmpSqlUser)
|
|
{
|
|
tmpSqlUser->removeAuthedClient(tmpUser);
|
|
tmpSqlUser->removeFlag(sqlUser::F_LOGGEDIN);
|
|
#ifdef LOG_DEBUG
|
|
elog << "cservice::OnEvent> Deauthenticated "
|
|
<< "client: " << tmpUser << " from "
|
|
<< "user: "
|
|
<< tmpSqlUser->getUserName()
|
|
<< endl;
|
|
#endif
|
|
}
|
|
|
|
// Clear up the custom data structure we appended to
|
|
// this iClient.
|
|
networkData* tmpData = static_cast< networkData* >(
|
|
tmpUser->getCustomData(this) ) ;
|
|
tmpUser->removeCustomData(this);
|
|
|
|
delete(tmpData);
|
|
customDataAlloc--;
|
|
|
|
break ;
|
|
} // case EVT_KILL/case EVT_QUIT
|
|
|
|
case EVT_NICK:
|
|
{
|
|
/*
|
|
* Give this new user a custom data structure!
|
|
*/
|
|
|
|
iClient* tmpUser =
|
|
static_cast< iClient* >( data1 );
|
|
networkData* newData = new (std::nothrow) networkData();
|
|
assert( newData != 0 ) ;
|
|
|
|
customDataAlloc++;
|
|
|
|
// Not authed.. (yet!)
|
|
newData->currentUser = NULL;
|
|
tmpUser->setCustomData(this,
|
|
static_cast< void* >( newData ) );
|
|
|
|
/*
|
|
* Well.. they might be already auth'd.
|
|
* In which case, we'll receieve mode r and accountname for
|
|
* this person.
|
|
*/
|
|
if (tmpUser->isModeR())
|
|
{
|
|
/* Lookup this user account, if its not there.. trouble */
|
|
sqlUser* theUser = getUserRecord(tmpUser->getAccount());
|
|
if (theUser)
|
|
{
|
|
newData->currentUser = theUser;
|
|
theUser->addAuthedClient(tmpUser);
|
|
}
|
|
}
|
|
|
|
#ifdef USING_NEFARIOUS
|
|
iServer* tmpServer = Network->findServer(tmpUser->getIntYY());
|
|
if ((!this->getUplink()->isBursting()) && (!tmpServer->isBursting()))
|
|
validateNickName(tmpUser);
|
|
#endif
|
|
break;
|
|
} // case EVT_NICK
|
|
case EVT_CHNICK:
|
|
{
|
|
#ifdef USING_NEFARIOUS
|
|
validateNickName(static_cast< iClient* >( data1 ));
|
|
#endif
|
|
break;
|
|
} // case EVT_CHNICK
|
|
case EVT_GLINE:
|
|
{
|
|
if(!data1) //TODO: find out how we get this (Do we even ever get this?)
|
|
{
|
|
return ;
|
|
}
|
|
Gline* newG = static_cast< Gline* >(data1);
|
|
|
|
csGline* newGline = findGline(newG->getUserHost());
|
|
if(!newGline)
|
|
{
|
|
newGline = new (std::nothrow) csGline(SQLDb);
|
|
assert (newGline != NULL);
|
|
iServer* serverAdded = Network->findServer(newG->getSetBy());
|
|
if(serverAdded)
|
|
newGline->setAddedBy(serverAdded->getName());
|
|
else
|
|
newGline->setAddedBy("Unknown");
|
|
}
|
|
else
|
|
{
|
|
if(newGline->getLastUpdated() >= newG->getLastmod())
|
|
{
|
|
return ;
|
|
}
|
|
|
|
}
|
|
newGline->setAddedOn(::time(0));
|
|
newGline->setHost(newG->getUserHost());
|
|
newGline->setReason(newG->getReason());
|
|
newGline->setExpires(newG->getExpiration());
|
|
|
|
newGline->Insert();
|
|
newGline->loadData(newGline->getHost());
|
|
|
|
addGline(newGline);
|
|
|
|
break;
|
|
} // case EVT_GLINE
|
|
case EVT_REMGLINE:
|
|
{
|
|
if(!data1)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
Gline* newG = static_cast< Gline* >(data1);
|
|
csGline* newGline = findGline(newG->getUserHost());
|
|
if(newGline)
|
|
{
|
|
remGline(newGline);
|
|
newGline->Delete();
|
|
delete newGline;
|
|
}
|
|
break;
|
|
} // case EVT_REMGLINE
|
|
} // switch()
|
|
xClient::OnEvent( theEvent,
|
|
data1, data2, data3, data4 ) ;
|
|
}
|
|
|
|
/**
|
|
* Support function to deVoice all voiced users on a channel.
|
|
*/
|
|
void cservice::deVoiceAllOnChan(Channel* theChan)
|
|
{
|
|
if( !theChan )
|
|
{
|
|
/* Don't try this on a null channel. */
|
|
return;
|
|
}
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
|
|
if (!reggedChan)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Check we're actually opped first.. */
|
|
|
|
ChannelUser* tmpBotUser = theChan->findUser(getInstance());
|
|
if( !tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
vector< iClient* > deVoiceList;
|
|
|
|
for( Channel::const_userIterator ptr = theChan->userList_begin();
|
|
ptr != theChan->userList_end() ; ++ptr )
|
|
{
|
|
if( ptr->second->getMode(ChannelUser::MODE_V))
|
|
{
|
|
deVoiceList.push_back( ptr->second->getClient());
|
|
}
|
|
}
|
|
|
|
if( !deVoiceList.empty() )
|
|
{
|
|
DeVoice(theChan, deVoiceList);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Support function to deop all opped users on a channel.
|
|
*/
|
|
void cservice::deopAllOnChan(Channel* theChan)
|
|
{
|
|
if( !theChan )
|
|
{
|
|
/* Don't try this on a null channel. */
|
|
return;
|
|
}
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
|
|
if (!reggedChan)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Check we're actually opped first.. */
|
|
|
|
ChannelUser* tmpBotUser = theChan->findUser(getInstance());
|
|
if( !tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
vector< iClient* > deopList;
|
|
|
|
for( Channel::const_userIterator ptr = theChan->userList_begin();
|
|
ptr != theChan->userList_end() ; ++ptr )
|
|
{
|
|
if( ptr->second->getMode(ChannelUser::MODE_O))
|
|
{
|
|
|
|
/* Don't deop +k things */
|
|
if ( !ptr->second->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
deopList.push_back( ptr->second->getClient() );
|
|
|
|
} // If opped.
|
|
}
|
|
|
|
if( !deopList.empty() )
|
|
{
|
|
DeOp(theChan, deopList);
|
|
}
|
|
|
|
}
|
|
|
|
size_t cservice::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->getMode(ChannelUser::MODE_O))
|
|
{
|
|
chanOps++;
|
|
} // If opped.
|
|
}
|
|
|
|
return chanOps;
|
|
}
|
|
|
|
/**
|
|
* Support function to deop all non authed opped users on a channel.
|
|
*/
|
|
void cservice::deopAllUnAuthedOnChan(Channel* theChan)
|
|
{
|
|
// TODO: assert( theChan != 0 ) ;
|
|
|
|
if( !theChan )
|
|
{
|
|
/* Don't try this on a null channel. */
|
|
return;
|
|
}
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
|
|
if( !reggedChan || !reggedChan->getInChan() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Check we're actually opped first.. */
|
|
|
|
ChannelUser* tmpBotUser = theChan->findUser(getInstance());
|
|
if(! tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O))
|
|
{
|
|
return;
|
|
}
|
|
|
|
vector< iClient* > deopList;
|
|
|
|
for( Channel::const_userIterator ptr = theChan->userList_begin();
|
|
ptr != theChan->userList_end() ; ++ptr )
|
|
{
|
|
if( ptr->second->getMode(ChannelUser::MODE_O))
|
|
{
|
|
/* Are they authed? */
|
|
sqlUser* authUser = isAuthed(ptr->second->getClient(), false);
|
|
|
|
if (!authUser)
|
|
{
|
|
/* Not authed, deop this guy + Don't deop +k things */
|
|
if ( !ptr->second->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
{
|
|
deopList.push_back( ptr->second->getClient() );
|
|
}
|
|
|
|
/* Authed but no access? Tough. :) */
|
|
}
|
|
else if ((reggedChan) && !(getEffectiveAccessLevel(authUser, reggedChan, false) >= level::op))
|
|
{
|
|
/* Don't deop +k things */
|
|
if ( !ptr->second->getClient()->getMode(iClient::MODE_SERVICES) )
|
|
{
|
|
deopList.push_back( ptr->second->getClient() );
|
|
}
|
|
}
|
|
|
|
} // if opped.
|
|
} // forall users in channel.
|
|
|
|
if( !deopList.empty() )
|
|
{
|
|
DeOp(theChan, deopList);
|
|
}
|
|
|
|
}
|
|
|
|
void cservice::deopSuspendedOnChan(Channel* theChan, sqlUser* theUser)
|
|
{
|
|
// TODO: assert( theChan != 0 ) ;
|
|
|
|
if( !theChan )
|
|
{
|
|
/* Don't try this on a null channel. */
|
|
return;
|
|
}
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
|
|
if( !reggedChan || !reggedChan->getInChan() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Check we're actually opped first.. */
|
|
|
|
ChannelUser* tmpBotUser = theChan->findUser(getInstance());
|
|
if(! tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O))
|
|
{
|
|
return;
|
|
}
|
|
|
|
vector< iClient* > deopList;
|
|
if (!theUser->networkClientList.empty())
|
|
for( sqlUser::networkClientListType::iterator cliPtr = theUser->networkClientList.begin() ;
|
|
cliPtr != theUser->networkClientList.end() ; ++cliPtr )
|
|
{
|
|
iClient* tmpClient = (*cliPtr);
|
|
ChannelUser* tmpUser = theChan->findUser(tmpClient);
|
|
if ((tmpUser) && (tmpUser->getMode(ChannelUser::MODE_O) && (!tmpClient->getMode(iClient::MODE_SERVICES))))
|
|
deopList.push_back(tmpClient);
|
|
}
|
|
|
|
if( !deopList.empty() )
|
|
DeOp(theChan, deopList);
|
|
return;
|
|
}
|
|
|
|
void cservice::DoTheRightThing(Channel* tmpChan)
|
|
{
|
|
if (!tmpChan) return;
|
|
|
|
ChannelUser* tmpBotUser = tmpChan->findUser(getInstance());
|
|
if (!tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O))
|
|
return;
|
|
|
|
sqlChannel* theChan = getChannelRecord(tmpChan->getName());
|
|
if(theChan->getFlag(sqlChannel::F_NOOP))
|
|
{
|
|
deopAllOnChan(tmpChan);
|
|
}
|
|
if(theChan->getFlag(sqlChannel::F_STRICTOP))
|
|
{
|
|
deopAllUnAuthedOnChan(tmpChan);
|
|
}
|
|
if(theChan->getFlag(sqlChannel::F_NOVOICE))
|
|
{
|
|
deVoiceAllOnChan(tmpChan);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void cservice::DoTheRightThing()
|
|
{
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.begin();
|
|
while (ptr != sqlChannelCache.end())
|
|
{
|
|
sqlChannel* theChan = (ptr)->second;
|
|
if (!theChan->getInChan())
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
//* Check we're actually opped first..
|
|
Channel* tmpChan = Network->findChannel(theChan->getName());
|
|
//*** Alternatively ***
|
|
//Channel* tmpChan = Network->findChannel(ptr->first);
|
|
//sqlChannel* theChan = getChannelRecord(tmpChan->getName());
|
|
if (!tmpChan)
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
ChannelUser* tmpBotUser = tmpChan->findUser(getInstance());
|
|
if( !tmpBotUser || !tmpBotUser->getMode(ChannelUser::MODE_O) )
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
if(theChan->getFlag(sqlChannel::F_NOOP))
|
|
{
|
|
deopAllOnChan(tmpChan);
|
|
}
|
|
if(theChan->getFlag(sqlChannel::F_STRICTOP))
|
|
{
|
|
deopAllUnAuthedOnChan(tmpChan);
|
|
}
|
|
if(theChan->getFlag(sqlChannel::F_NOVOICE))
|
|
{
|
|
deVoiceAllOnChan(tmpChan);
|
|
}
|
|
++ptr;
|
|
} //While
|
|
return;
|
|
}
|
|
/**
|
|
* Handler for registered channel events.
|
|
* Performs a number of functions, autoop, autovoice, bankicks, etc.
|
|
*/
|
|
void cservice::OnChannelEvent( const channelEventType& whichEvent,
|
|
Channel* theChan,
|
|
void* data1, void* data2, void* data3, void* data4 )
|
|
{
|
|
iClient* theClient = 0 ;
|
|
|
|
switch( whichEvent )
|
|
{
|
|
case EVT_CREATE:
|
|
case EVT_JOIN:
|
|
{
|
|
/*
|
|
* We should only ever recieve events for registered channels, or those
|
|
* that are 'pending'. If we do get past the pending check, there must be
|
|
* some kind of database inconsistancy.
|
|
*/
|
|
|
|
theClient = static_cast< iClient* >( data1 ) ;
|
|
|
|
pendingChannelListType::iterator ptr = pendingChannelList.find(theChan->getName());
|
|
|
|
if(ptr != pendingChannelList.end())
|
|
{
|
|
/*
|
|
* Firstly, is this join a result of a server bursting onto the network?
|
|
* If this is the case, its not a manual /join.
|
|
*/
|
|
|
|
iServer* theServer = Network->findServer( theClient->getIntYY() ) ;
|
|
if (!theServer->isBursting())
|
|
{
|
|
/*
|
|
* Yes, this channel is pending registration, update join count
|
|
* and check out this user joining.
|
|
*/
|
|
|
|
ptr->second->join_count++;
|
|
|
|
/*
|
|
* Now, has this users IP joined this channel before?
|
|
* If not - we keep a record of it.
|
|
*/
|
|
|
|
sqlPendingChannel::trafficListType::iterator Tptr =
|
|
ptr->second->trafficList.find(theClient->getIP());
|
|
|
|
sqlPendingTraffic* trafRecord;
|
|
|
|
/*
|
|
* If we have more than 50 unique IP's join, we don't bother
|
|
* recording anymore.
|
|
*/
|
|
|
|
if (ptr->second->unique_join_count < 50)
|
|
{
|
|
if(Tptr == ptr->second->trafficList.end())
|
|
{
|
|
/* New IP, create and write the record. */
|
|
|
|
trafRecord = new sqlPendingTraffic(SQLDb);
|
|
trafRecord->ip_number = theClient->getIP();
|
|
trafRecord->join_count = 1;
|
|
trafRecord->channel_id = ptr->second->channel_id;
|
|
trafRecord->insertRecord();
|
|
|
|
ptr->second->trafficList.insert(sqlPendingChannel::trafficListType::value_type(
|
|
theClient->getIP(), trafRecord));
|
|
#ifdef LOG_DEBUG
|
|
logDebugMessage("Created a new IP traffic record for IP#%u (%s) on %s",
|
|
theClient->getIP(), theClient->getNickUserHost().c_str(),
|
|
theChan->getName().c_str());
|
|
#endif
|
|
} else
|
|
{
|
|
/* Already cached, update and save. */
|
|
trafRecord = Tptr->second;
|
|
trafRecord->join_count++;
|
|
//trafRecord->commit();
|
|
}
|
|
|
|
ptr->second->unique_join_count = ptr->second->trafficList.size();
|
|
|
|
//logDebugMessage("New total for IP#%u on %s is %i",
|
|
// theClient->getIP(), theChan->getName().c_str(),
|
|
// trafRecord->join_count);
|
|
}
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (!theUser)
|
|
{
|
|
/*
|
|
* If this user isn't authed, he can't possibly be flagged
|
|
* as one of the valid supporters, so we drop out.
|
|
*/
|
|
|
|
xClient::OnChannelEvent( whichEvent, theChan,
|
|
data1, data2, data3, data4 );
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* Now, if this guy is a supporter, we bump his join count up.
|
|
*/
|
|
|
|
sqlPendingChannel::supporterListType::iterator Supptr = ptr->second->supporterList.find(theUser->getID());
|
|
if (Supptr != ptr->second->supporterList.end())
|
|
{
|
|
Supptr->second++;
|
|
ptr->second->commitSupporter(Supptr->first, Supptr->second);
|
|
#ifdef LOG_DEBUG
|
|
logDebugMessage("New total for Supporter #%i (%s) on %s is %i.", theUser->getID(),
|
|
theUser->getUserName().c_str(), theChan->getName().c_str(), Supptr->second);
|
|
#endif
|
|
}
|
|
|
|
xClient::OnChannelEvent( whichEvent, theChan,
|
|
data1, data2, data3, data4 );
|
|
return ;
|
|
|
|
} /* Is server bursting? */
|
|
} /* Is channel on pending list */
|
|
|
|
sqlChannel* reggedChan = getChannelRecord(theChan->getName());
|
|
if(!reggedChan)
|
|
{
|
|
// elog << "cservice::OnChannelEvent> WARNING, "
|
|
// << "unable to locate channel record"
|
|
// << " for registered channel event: "
|
|
// << theChan->getName()
|
|
// << endl;
|
|
return ;
|
|
}
|
|
|
|
/* This is a registered channel, check it is set +R.
|
|
* If not, set it to +R (channel creation)
|
|
*/
|
|
if (!theChan->getMode(Channel::MODE_REG)) {
|
|
stringstream tmpTS;
|
|
tmpTS << reggedChan->getChannelTS();
|
|
string channelTS = tmpTS.str();
|
|
MyUplink->Mode(NULL, theChan, string("+R"), channelTS );
|
|
}
|
|
|
|
/* If this is a registered channel, but we're not in it -
|
|
* then we're not interested in the following commands!
|
|
*/
|
|
if (!reggedChan->getInChan())
|
|
{
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* First thing we do - check if this person is banned.
|
|
* If so, they're booted out.
|
|
*/
|
|
|
|
if (checkBansOnJoin(theChan, reggedChan, theClient))
|
|
{
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_WELCOME
|
|
if (strlen(reggedChan->getWelcome().c_str()) > 0)
|
|
{
|
|
Notice(theClient, "(%s) %s",
|
|
theChan->getName().c_str(),
|
|
reggedChan->getWelcome().c_str());
|
|
}
|
|
#endif
|
|
|
|
/* Is it time to set an autotopic? */
|
|
if (reggedChan->getFlag(sqlChannel::F_AUTOTOPIC) &&
|
|
(reggedChan->getLastTopic()
|
|
+ topic_duration <= currentTime()))
|
|
{
|
|
doAutoTopic(reggedChan);
|
|
}
|
|
|
|
/* Deal with auto-op first - check this users access level. */
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (!theUser)
|
|
{
|
|
/* If not authed, bye. */
|
|
break;
|
|
}
|
|
|
|
/* Check access in this channel. */
|
|
int accessLevel = getEffectiveAccessLevel(theUser, reggedChan, false);
|
|
if (!accessLevel)
|
|
{
|
|
/* No access.. */
|
|
break;
|
|
}
|
|
|
|
sqlLevel* theLevel = getLevelRecord(theUser, reggedChan);
|
|
if(!theLevel)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Auto voice? */
|
|
if (theLevel->getFlag(sqlLevel::F_AUTOVOICE))
|
|
{
|
|
if (!(accessLevel >= level::voice)) {
|
|
theLevel->removeFlag(sqlLevel::F_AUTOVOICE);
|
|
break;
|
|
}
|
|
if (!reggedChan->getFlag(sqlChannel::F_NOVOICE))
|
|
Voice(theChan, theClient);
|
|
break;
|
|
}
|
|
|
|
/* Check noop isn't set */
|
|
if (reggedChan->getFlag(sqlChannel::F_NOOP))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Check strictop isn't on, and this user is < 100 */
|
|
if (reggedChan->getFlag(sqlChannel::F_STRICTOP))
|
|
{
|
|
if (!(accessLevel >= level::op))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Next, see if they have auto op set. */
|
|
if (theLevel->getFlag(sqlLevel::F_AUTOOP))
|
|
{
|
|
if (!(accessLevel >= level::op)) {
|
|
theLevel->removeFlag(sqlLevel::F_AUTOOP);
|
|
break;
|
|
}
|
|
Op(theChan, theClient);
|
|
break;
|
|
}
|
|
|
|
if (theLevel->getFlag(sqlLevel::F_AUTOHALFOP))
|
|
{
|
|
if (!(accessLevel >= level::halfop)) {
|
|
theLevel->removeFlag(sqlLevel::F_AUTOHALFOP);
|
|
break;
|
|
}
|
|
HalfOp(theChan, theClient);
|
|
break;
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
case EVT_SERVERMODE:
|
|
{
|
|
//if(!data1) return;
|
|
elog << "mod.cservice: GOT SERVER MODE EVENT!" << std::endl;
|
|
logDebugMessage("GOT SERVER MODE EVENT!");
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
} // switch()
|
|
|
|
xClient::OnChannelEvent( whichEvent, theChan,
|
|
data1, data2, data3, data4 );
|
|
}
|
|
|
|
/**
|
|
* This function matches a client against the bans stored in the
|
|
* database for this channel.
|
|
* Returns an sqlBan if it matches, false otherwise.
|
|
* 'theChan' and 'theClient' must _not_ be null.
|
|
*/
|
|
sqlBan* cservice::isBannedOnChan(sqlChannel* theChan, iClient* theClient)
|
|
{
|
|
map < int,sqlBan* >::const_iterator ptr = theChan->banList.begin();
|
|
|
|
for( ; ptr != theChan->banList.end() ; ++ptr )
|
|
{
|
|
// NOTE: This is a band-aid, it does not correct the actual
|
|
// problem..
|
|
sqlBan* theBan = ptr->second;
|
|
if( 0 == theBan )
|
|
{
|
|
elog << "cservice::isBannedOnChan> Invalid ban!"
|
|
<< endl ;
|
|
continue ;
|
|
}
|
|
if(theBan->getMatcher()->matches(theClient))
|
|
{
|
|
return theBan;
|
|
}
|
|
} /* for() */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* This function compares a client with any active bans set in the DB.
|
|
* If matched, the ban is applied and the user is kicked.
|
|
* Returns true if matched, false if not.
|
|
* N.B: Called from OnChannelEvent, theClient is guarantee'd to be in the
|
|
* channel and netChan will exist.
|
|
*--------------------------------------------------------------------------*/
|
|
bool cservice::checkBansOnJoin( Channel* netChan, sqlChannel* theChan,
|
|
iClient* theClient )
|
|
{
|
|
|
|
sqlBan* theBan = isBannedOnChan(theChan, theClient);
|
|
|
|
// TODO: Don't reapply this ban to the network if it already is set in the channel object.
|
|
// (ircu banlist could be full).
|
|
// TODO: Ban through the server.
|
|
// TODO: Violation of rule of numbers
|
|
/* If we found a matching ban */
|
|
if( theBan && (theBan->getLevel() >= 75) )
|
|
{
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " M "
|
|
<< theChan->getName()
|
|
<< " +b "
|
|
<< theBan->getBanMask()
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
/* remove the ban (even if it doesnt exist, it will return false anyway) */
|
|
netChan->removeBan( theBan->getBanMask() ) ;
|
|
/* set the ban */
|
|
netChan->setBan( theBan->getBanMask() ) ;
|
|
|
|
string kickReason = "(" + theBan->getSetBy() + ") " + theBan->getReason();
|
|
if ((theBan->getSetBy().empty()) || (theBan->getSetBy() == nickName)) kickReason = theBan->getReason();
|
|
/* Don't kick banned +k bots */
|
|
if ( !theClient->getMode(iClient::MODE_SERVICES) )
|
|
{
|
|
Kick(netChan, theClient, kickReason);
|
|
// string( "("
|
|
// + theBan->getSetBy()
|
|
// + ") "
|
|
// + theBan->getReason()) );
|
|
|
|
}
|
|
return true;
|
|
} /* Matching Ban > 75 */
|
|
|
|
/*
|
|
* If they're banned < 75, return true, but don't
|
|
* do anything.
|
|
*/
|
|
if( theBan && (theBan->getLevel() < 75) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void cservice::OnWhois( iClient* sourceClient,
|
|
iClient* targetClient )
|
|
{
|
|
/*
|
|
* Return info about 'targetClient' to 'sourceClient'
|
|
*/
|
|
// TODO: Only use one stringstream here
|
|
|
|
stringstream s;
|
|
s << getCharYY()
|
|
<< " 311 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " " << targetClient->getUserName()
|
|
<< " " << targetClient->getInsecureHost()
|
|
<< " * :"
|
|
<< ends;
|
|
Write( s );
|
|
|
|
stringstream s4;
|
|
s4 << getCharYY()
|
|
<< " 317 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " 0 " << targetClient->getConnectTime()
|
|
<< " :seconds idle, signon time"
|
|
<< ends;
|
|
Write( s4 );
|
|
|
|
if (targetClient->isOper())
|
|
{
|
|
stringstream s5;
|
|
s5 << getCharYY()
|
|
<< " 313 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " :is an IRC Operator"
|
|
<< ends;
|
|
Write( s5 );
|
|
}
|
|
|
|
sqlUser* theUser = isAuthed(targetClient, false);
|
|
|
|
if (theUser)
|
|
{
|
|
stringstream s6;
|
|
s6 << getCharYY()
|
|
<< " 316 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " :is Logged in as "
|
|
<< theUser->getUserName()
|
|
<< ends;
|
|
Write( s6 );
|
|
}
|
|
|
|
if (isIgnored(targetClient))
|
|
{
|
|
stringstream s7;
|
|
s7 << getCharYY()
|
|
<< " 316 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " :is currently being ignored. "
|
|
<< ends;
|
|
Write( s7 );
|
|
}
|
|
|
|
stringstream s3;
|
|
s3 << getCharYY()
|
|
<< " 318 "
|
|
<< sourceClient->getCharYYXXX()
|
|
<< " " << targetClient->getNickName()
|
|
<< " :End of /WHOIS list."
|
|
<< ends;
|
|
Write( s3 );
|
|
}
|
|
|
|
void cservice::updateLimits()
|
|
{
|
|
/*
|
|
* Forall cached channel records, perform an update of the
|
|
* channel limit.
|
|
*/
|
|
sqlChannelHashType::iterator ptr = sqlChannelCache.begin();
|
|
while (ptr != sqlChannelCache.end())
|
|
{
|
|
sqlChannel* theChan = (ptr)->second;
|
|
|
|
/*
|
|
* Don't have the Floating Limit flag set?
|
|
*/
|
|
if (!theChan->getFlag(sqlChannel::F_FLOATLIM))
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
/*
|
|
* X isn't even in the channel?
|
|
*/
|
|
if (!theChan->getInChan())
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
|
|
Channel* tmpChan = Network->findChannel(theChan->getName());
|
|
|
|
/*
|
|
* For some magical reason the channel doesn't even exist?
|
|
*/
|
|
if (!tmpChan)
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If its not time to update the limit for this channel yet.
|
|
*/
|
|
if (theChan->getLastLimitCheck() + theChan->getLimitPeriod() > currentTime())
|
|
{
|
|
++ptr;
|
|
continue;
|
|
}
|
|
|
|
doFloatingLimit(theChan, tmpChan);
|
|
|
|
++ptr;
|
|
}
|
|
}
|
|
|
|
void cservice::doFloatingLimit(sqlChannel* reggedChan, Channel* theChan)
|
|
{
|
|
/*
|
|
* This event is triggered when its "time" to do autolimits.
|
|
*/
|
|
unsigned int newLimit = theChan->size() + reggedChan->getLimitOffset();
|
|
|
|
/* Don't bother if the new limit is the same as the old one. */
|
|
if (newLimit == theChan->getLimit()) return;
|
|
|
|
/* Also don't bother if the difference between the old limit and
|
|
* the new limit is < 'grace' */
|
|
int currentDif = abs((int)theChan->getLimit() - (int)newLimit);
|
|
if (currentDif <= (int)reggedChan->getLimitGrace()) return;
|
|
|
|
/*
|
|
* If the new limit is above our max limit, don't bother
|
|
* either.
|
|
*/
|
|
|
|
if (reggedChan->getLimitMax() && (newLimit >= reggedChan->getLimitMax())) return;
|
|
|
|
/*
|
|
* Check we're actually opped.
|
|
*/
|
|
|
|
ChannelUser* tmpBotUser = theChan->findUser(getInstance());
|
|
if (!tmpBotUser) return;
|
|
if (!tmpBotUser->getMode(ChannelUser::MODE_O)) return;
|
|
|
|
theChan->setMode(Channel::MODE_L);
|
|
theChan->setLimit(newLimit);
|
|
reggedChan->setLastLimitCheck(currentTime());
|
|
|
|
incStat("CORE.FLOATLIM.ALTER");
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " M "
|
|
<< theChan->getName()
|
|
<< " +l "
|
|
<< newLimit
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
incStat("CORE.FLOATLIM.ALTER.BYTES", strlen(s.str().c_str()));
|
|
}
|
|
|
|
/*--doAutoTopic---------------------------------------------------------------
|
|
*
|
|
* This support function sets the autotopic in a particular channel.
|
|
*/
|
|
void cservice::doAutoTopic(sqlChannel* theChan)
|
|
{
|
|
|
|
/* Quickly drop out if nothing is set.. */
|
|
if ( theChan->getDescription().empty() && theChan->getURL().empty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
string extra ;
|
|
if( !theChan->getURL().empty() )
|
|
{
|
|
extra = " ( " + theChan->getURL() + " )" ;
|
|
}
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " T "
|
|
<< theChan->getName()
|
|
<< " :"
|
|
<< theChan->getDescription()
|
|
<< extra
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
theChan->setLastTopic(currentTime());
|
|
}
|
|
|
|
/**
|
|
* Bans a user via IRC and the database with 'theReason',
|
|
* and then kicks. theChan cannot be null.
|
|
*/
|
|
bool cservice::doInternalBanAndKick(sqlChannel* theChan,
|
|
iClient* theClient, const string& theReason)
|
|
{
|
|
/*
|
|
* Check to see if this banmask already exists in the
|
|
* channel. (Ugh, and overlapping too.. hmm).
|
|
*/
|
|
|
|
/* Create a new Ban record */
|
|
sqlBan* newBan = new (std::nothrow) sqlBan(SQLDb);
|
|
assert( newBan != 0 ) ;
|
|
|
|
string banTarget = Channel::createBan(theClient);
|
|
|
|
// TODO: Build a suitable constructor in sqlBan
|
|
newBan->setChannelID(theChan->getID());
|
|
newBan->setBanMask(banTarget);
|
|
newBan->setSetBy(nickName);
|
|
newBan->setSetTS(currentTime());
|
|
newBan->setLevel(25);
|
|
|
|
/* Move 360 to config */
|
|
newBan->setExpires( 300 + currentTime());
|
|
newBan->setReason(theReason);
|
|
|
|
/*
|
|
* Check for duplicates, if none found -
|
|
* add to internal list and commit to the db.
|
|
*/
|
|
|
|
map< int,sqlBan* >::const_iterator ptr = theChan->banList.begin();
|
|
while (ptr != theChan->banList.end())
|
|
{
|
|
const sqlBan* theBan = ptr->second;
|
|
|
|
if(string_lower(banTarget) == string_lower(theBan->getBanMask()))
|
|
{
|
|
/*
|
|
* If this mask is already banned, we're just getting
|
|
* lagged info.
|
|
*/
|
|
return true;
|
|
}
|
|
++ptr;
|
|
}
|
|
|
|
//theChan->banList[newBan->getID()] = newBan;
|
|
|
|
/* Insert this new record into the database. */
|
|
newBan->insertRecord();
|
|
|
|
/* Insert to our internal List. */
|
|
theChan->banList.insert(std::map<int,sqlBan*>::value_type(newBan->getID(),newBan));
|
|
|
|
/*
|
|
* Finally, if this guy is auth'd.. suspend his account.
|
|
*/
|
|
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser)
|
|
{
|
|
sqlLevel* accessRec = getLevelRecord(theUser, theChan);
|
|
if (accessRec && (accessRec->getSuspendExpire() < (currentTime() + 300)))
|
|
{
|
|
int susLev = accessRec->getAccess() + 1;
|
|
if (accessRec->getSuspendLevel() < susLev)
|
|
accessRec->setSuspendLevel(susLev);
|
|
accessRec->setSuspendExpire(currentTime() + 300);
|
|
accessRec->setSuspendBy(nickName);
|
|
accessRec->commit();
|
|
}
|
|
}
|
|
|
|
Channel* netChan = Network->findChannel(theChan->getName());
|
|
|
|
// Oh dear?
|
|
if (!netChan)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Kick( netChan, theClient, theReason ) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool cservice::doInternalBanAndKick(sqlChannel* theChan,
|
|
iClient* theClient, unsigned short banLevel, unsigned int banExpire, const string& theReason)
|
|
{
|
|
/*
|
|
* Check to see if this banmask already exists in the
|
|
* channel. (Ugh, and overlapping too.. hmm).
|
|
*/
|
|
|
|
/* Create a new Ban record */
|
|
sqlBan* newBan = new (std::nothrow) sqlBan(SQLDb);
|
|
assert( newBan != 0 ) ;
|
|
|
|
string banTarget = Channel::createUniformBan(theClient);
|
|
|
|
// TODO: Build a suitable constructor in sqlBan
|
|
newBan->setChannelID(theChan->getID());
|
|
newBan->setBanMask(banTarget);
|
|
newBan->setSetBy(getNickName());
|
|
newBan->setSetTS(currentTime());
|
|
newBan->setLevel(banLevel);
|
|
|
|
newBan->setExpires(banExpire + currentTime());
|
|
newBan->setReason(theReason);
|
|
|
|
/*
|
|
* Check for duplicates, if none found -
|
|
* add to internal list and commit to the db.
|
|
*/
|
|
|
|
map< int,sqlBan* >::const_iterator ptr = theChan->banList.begin();
|
|
while (ptr != theChan->banList.end())
|
|
{
|
|
const sqlBan* theBan = ptr->second;
|
|
|
|
if(string_lower(banTarget) == string_lower(theBan->getBanMask()))
|
|
{
|
|
/*
|
|
* If this mask is already banned, we're just getting
|
|
* lagged info.
|
|
*/
|
|
return true;
|
|
}
|
|
++ptr;
|
|
}
|
|
|
|
//theChan->banList[newBan->getID()] = newBan;
|
|
|
|
/* Insert this new record into the database. */
|
|
newBan->insertRecord();
|
|
|
|
/* Insert to our internal List. */
|
|
theChan->banList.insert(std::map<int,sqlBan*>::value_type(newBan->getID(),newBan));
|
|
Channel* netChan = Network->findChannel(theChan->getName());
|
|
|
|
// Oh dear?
|
|
if (!netChan)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " M "
|
|
<< netChan->getName()
|
|
<< " +b "
|
|
<< newBan->getBanMask()
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
/* remove the ban (even if it doesnt exist, it will return false anyway) */
|
|
netChan->removeBan( newBan->getBanMask() ) ;
|
|
/* set the ban */
|
|
netChan->setBan( newBan->getBanMask() ) ;
|
|
|
|
vector <iClient*> toBoot;
|
|
|
|
for (Channel::userIterator chanUsers = netChan->userList_begin(); chanUsers != netChan->userList_end(); ++chanUsers)
|
|
{
|
|
ChannelUser* tmpUser = chanUsers->second;
|
|
if (tmpUser->getClient()->getIP() == theClient->getIP())
|
|
{
|
|
/* Don't kick +k things */
|
|
if (!tmpUser->getClient()->getMode(iClient::MODE_SERVICES))
|
|
{
|
|
toBoot.push_back(tmpUser->getClient());
|
|
}
|
|
}
|
|
}
|
|
Kick( netChan, toBoot, theReason ) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool cservice::doInternalBanAndKick(sqlChannel* theChan,
|
|
unsigned int IP, unsigned short banLevel, unsigned int banExpire, const string& theReason)
|
|
{
|
|
/*
|
|
* Check to see if this banmask already exists in the
|
|
* channel. (Ugh, and overlapping too.. hmm).
|
|
*/
|
|
|
|
/* Create a new Ban record */
|
|
sqlBan* newBan = new (std::nothrow) sqlBan(SQLDb);
|
|
assert( newBan != 0 ) ;
|
|
|
|
string banTarget = "*!*@" + xIP(IP).GetNumericIP();
|
|
|
|
// TODO: Build a suitable constructor in sqlBan
|
|
newBan->setChannelID(theChan->getID());
|
|
newBan->setBanMask(banTarget);
|
|
newBan->setSetBy(getNickName());
|
|
newBan->setSetTS(currentTime());
|
|
newBan->setLevel(banLevel);
|
|
|
|
newBan->setExpires(banExpire + currentTime());
|
|
newBan->setReason(theReason);
|
|
|
|
/*
|
|
* Check for duplicates, if none found -
|
|
* add to internal list and commit to the db.
|
|
*/
|
|
|
|
map< int,sqlBan* >::const_iterator ptr = theChan->banList.begin();
|
|
while (ptr != theChan->banList.end())
|
|
{
|
|
const sqlBan* theBan = ptr->second;
|
|
|
|
if(string_lower(banTarget) == string_lower(theBan->getBanMask()))
|
|
{
|
|
/*
|
|
* If this mask is already banned, we're just getting
|
|
* lagged info.
|
|
*/
|
|
return true;
|
|
}
|
|
++ptr;
|
|
}
|
|
|
|
//theChan->banList[newBan->getID()] = newBan;
|
|
|
|
/* Insert this new record into the database. */
|
|
newBan->insertRecord();
|
|
|
|
/* Insert to our internal List. */
|
|
theChan->banList.insert(std::map<int,sqlBan*>::value_type(newBan->getID(),newBan));
|
|
Channel* netChan = Network->findChannel(theChan->getName());
|
|
|
|
// Oh dear?
|
|
if (!netChan)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
stringstream s;
|
|
s << getCharYYXXX()
|
|
<< " M "
|
|
<< netChan->getName()
|
|
<< " +b "
|
|
<< newBan->getBanMask()
|
|
<< ends;
|
|
|
|
Write( s );
|
|
|
|
/* remove the ban (even if it doesnt exist, it will return false anyway) */
|
|
netChan->removeBan( newBan->getBanMask() ) ;
|
|
/* set the ban */
|
|
netChan->setBan( newBan->getBanMask() ) ;
|
|
|
|
vector <iClient*> toBoot;
|
|
|
|
for (Channel::userIterator chanUsers = netChan->userList_begin(); chanUsers != netChan->userList_end(); ++chanUsers)
|
|
{
|
|
ChannelUser* tmpUser = chanUsers->second;
|
|
if (tmpUser->getClient()->getIP() == IP)
|
|
{
|
|
/* Don't kick +k things */
|
|
if (!tmpUser->getClient()->getMode(iClient::MODE_SERVICES))
|
|
{
|
|
toBoot.push_back(tmpUser->getClient());
|
|
}
|
|
}
|
|
}
|
|
Kick( netChan, toBoot, theReason ) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool cservice::doInternalSuspend(sqlChannel* theChan,
|
|
iClient* theClient, unsigned short suspLevel, unsigned int suspExpire, const string& theReason)
|
|
{
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
if (theUser)
|
|
{
|
|
sqlLevel* accessRec = getLevelRecord(theUser, theChan);
|
|
if (accessRec && (accessRec->getSuspendExpire() < (currentTime() + suspExpire)))
|
|
{
|
|
if (accessRec->getSuspendLevel() < suspLevel)
|
|
accessRec->setSuspendLevel(suspLevel);
|
|
accessRec->setSuspendExpire(currentTime() + suspExpire);
|
|
accessRec->setSuspendBy(getNickName());
|
|
accessRec->setLastModif(currentTime());
|
|
accessRec->setLastModifBy(getInstance()->getNickUserHost());
|
|
accessRec->setSuspendReason(theReason);
|
|
accessRec->commit();
|
|
}
|
|
} else return false;
|
|
return true;
|
|
}
|
|
|
|
bool cservice::doInternalGline(iClient* theClient, const time_t& thePeriod, const string& theReason)
|
|
{
|
|
string UserHost = /*theClient->getUserName() + "@"*/ "*@" + theClient->getRealInsecureHost();
|
|
csGline *theGline = findGline(UserHost);
|
|
bool Up = false;
|
|
if(theGline)
|
|
Up = true;
|
|
else { theGline = new (std::nothrow) csGline(SQLDb);
|
|
assert(theGline != NULL); }
|
|
theGline->setHost(UserHost);
|
|
theGline->setExpires(unsigned(::time(0) + thePeriod));
|
|
theGline->setAddedBy(getUplinkName());
|
|
theGline->setReason(theReason);
|
|
theGline->setAddedOn(::time(0));
|
|
theGline->setLastUpdated(::time(0));
|
|
addGlineToUplink(theGline);
|
|
if (Up)
|
|
theGline->Update();
|
|
else
|
|
{
|
|
theGline->Insert();
|
|
//We need to update the Id
|
|
theGline->loadData(theGline->getHost());
|
|
addGline(theGline);
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
bool cservice::doInternalGline(unsigned int IP, const time_t& thePeriod, const string& theReason)
|
|
{
|
|
string UserHost = "*@" + xIP(IP).GetNumericIP();
|
|
csGline *theGline = findGline(UserHost);
|
|
bool Up = false;
|
|
if(theGline)
|
|
Up = true;
|
|
else { theGline = new (std::nothrow) csGline(SQLDb);
|
|
assert(theGline != NULL); }
|
|
theGline->setHost(UserHost);
|
|
theGline->setExpires(unsigned(::time(0) + thePeriod));
|
|
theGline->setAddedBy(getUplinkName());
|
|
theGline->setReason(theReason);
|
|
theGline->setAddedOn(::time(0));
|
|
theGline->setLastUpdated(::time(0));
|
|
addGlineToUplink(theGline);
|
|
if (Up)
|
|
theGline->Update();
|
|
else
|
|
{
|
|
theGline->Insert();
|
|
//We need to update the Id
|
|
theGline->loadData(theGline->getHost());
|
|
addGline(theGline);
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
/**
|
|
* This method writes a 'channellog' record, recording an event that has
|
|
* occured in this channel.
|
|
*/
|
|
|
|
void cservice::writeChannelLog(sqlChannel* theChannel, iClient* theClient,
|
|
unsigned short eventType, const string& theMessage)
|
|
{
|
|
sqlUser* theUser = isAuthed(theClient, false);
|
|
string userExtra = theUser ? theUser->getUserName() : "Not Logged In";
|
|
|
|
stringstream theLog;
|
|
theLog << "INSERT INTO channellog (ts, channelID, event, message, "
|
|
<< "last_updated) VALUES "
|
|
<< "("
|
|
<< currentTime()
|
|
<< ", "
|
|
<< theChannel->getID()
|
|
<< ", "
|
|
<< eventType
|
|
<< ", "
|
|
<< "'["
|
|
<< nickName
|
|
<< "]: "
|
|
<< theClient->getNickUserHost()
|
|
<< " (" << userExtra << ") "
|
|
<< escapeSQLChars(theMessage)
|
|
<< "', "
|
|
<< currentTime()
|
|
<< ")"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::writeChannelLog> "
|
|
<< theLog.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
// TODO: Is this right?
|
|
SQLDb->Exec(theLog);
|
|
//SQLDb->ExecCommandOk(theLog.str().c_str());
|
|
}
|
|
|
|
/**
|
|
* This function returns the last channel event of type 'eventType'
|
|
* (up to 'eventTime') for the channel given.
|
|
* It returns a blank string if none found.
|
|
*/
|
|
const string cservice::getLastChannelEvent(sqlChannel* theChannel,
|
|
unsigned short eventType, unsigned int& eventTime)
|
|
{
|
|
unsigned int ts;
|
|
stringstream queryString;
|
|
|
|
if (eventTime == 0)
|
|
ts = currentTime();
|
|
else
|
|
ts = eventTime;
|
|
|
|
queryString << "SELECT message FROM channellog WHERE "
|
|
<< "channelid = "
|
|
<< theChannel->getID()
|
|
<< " AND event = "
|
|
<< eventType
|
|
<< " AND ts <= "
|
|
<< ts
|
|
<< " ORDER BY ts DESC LIMIT 1"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::getLastChannelEvent> "
|
|
<< queryString.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if (SQLDb->Exec(queryString, true))
|
|
{
|
|
if (SQLDb->Tuples() < 1)
|
|
return "";
|
|
string reason = SQLDb->GetValue(0, 0);
|
|
return reason;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Global method to replace ' with \' in strings for safe placement in
|
|
* SQL statements.
|
|
*/
|
|
const string escapeSQLChars(const string& theString)
|
|
{
|
|
string retMe ;
|
|
|
|
for( string::const_iterator ptr = theString.begin() ;
|
|
ptr != theString.end() ; ++ptr )
|
|
{
|
|
if( *ptr == '\'' )
|
|
{
|
|
retMe += "\\\047" ;
|
|
}
|
|
else if ( *ptr == '\\' )
|
|
{
|
|
retMe += "\\\134" ;
|
|
}
|
|
else
|
|
{
|
|
retMe += *ptr ;
|
|
}
|
|
}
|
|
return retMe ;
|
|
}
|
|
|
|
const string searchSQL(const string& theString)
|
|
{
|
|
string retMe ;
|
|
|
|
for( string::const_iterator ptr = theString.begin() ;
|
|
ptr != theString.end() ; ++ptr )
|
|
{
|
|
if( *ptr == '*' )
|
|
{
|
|
retMe += "%" ;
|
|
}
|
|
else if ( *ptr == '?' )
|
|
{
|
|
retMe += "_" ;
|
|
}
|
|
else
|
|
{
|
|
retMe += *ptr ;
|
|
}
|
|
}
|
|
return retMe ;
|
|
}
|
|
|
|
time_t cservice::currentTime() const
|
|
{
|
|
/* Returns the current time according to the postgres server. */
|
|
return dbTimeOffset + ::time(NULL);
|
|
}
|
|
|
|
bool cservice::Notice( const iClient* Target, const string& Message )
|
|
{
|
|
bool returnMe = false ;
|
|
if( Connected && MyUplink )
|
|
{
|
|
setOutputTotal( Target, getOutputTotal(Target) + Message.size() );
|
|
char buffer[512] = { 0 };
|
|
char *b = buffer ;
|
|
const char *m = 0 ;
|
|
|
|
// TODO: This should be fixed.
|
|
// A walking timebomb.
|
|
for (m=Message.c_str();*m!=0;m++)
|
|
{
|
|
if (*m == '\n' || *m == '\r')
|
|
{
|
|
*b='\0';
|
|
MyUplink->Write( "%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
Target->getCharYYXXX().c_str(),
|
|
buffer ) ;
|
|
b=buffer;
|
|
}
|
|
else
|
|
{
|
|
if (b<buffer+509)
|
|
*(b++)=*m;
|
|
}
|
|
|
|
}
|
|
*b='\0';
|
|
returnMe = MyUplink->Write( "%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
Target->getCharYYXXX().c_str(),
|
|
buffer ) ;
|
|
}
|
|
return returnMe ;
|
|
}
|
|
|
|
bool cservice::Notice(const string& Channel, const char* Message, ...)
|
|
{
|
|
return xClient::Notice(Channel, Message);
|
|
}
|
|
|
|
string cservice::HostIsRegisteredTo(const string& theHost)
|
|
{
|
|
string theUser = string();
|
|
stringstream theQuery;
|
|
theQuery << "SELECT user_name FROM users WHERE "
|
|
<< "lower(users.hostname) = '"
|
|
<< escapeSQLChars(string_lower(theHost))
|
|
<< "'"
|
|
<< ends;
|
|
if (!SQLDb->Exec(theQuery, true))
|
|
{
|
|
logDebugMessage("Error on cservice::HostIsRegisteredToQuery");
|
|
#ifdef LOG_SQL
|
|
//elog << "sqlQuery> " << theQuery.str().c_str() << endl;
|
|
elog << "cservice::HostIsRegisteredToQuery> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
#endif
|
|
return theUser;
|
|
}
|
|
if (SQLDb->Tuples() > 0)
|
|
theUser = SQLDb->GetValue(0,0);
|
|
return theUser;
|
|
}
|
|
|
|
bool cservice::HostIsReserved(const string& theHost)
|
|
{
|
|
for (reservedHostsListType::iterator itr = reservedHostsList.begin(); itr != reservedHostsList.end(); itr++)
|
|
{
|
|
if (!match((*itr), theHost))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cservice::Notice( const iClient* Target, const char* Message,
|
|
... )
|
|
{
|
|
if( Connected && MyUplink && Message && Message[ 0 ] != 0 )
|
|
{
|
|
char buffer[ 512 ] = { 0 } ;
|
|
va_list list;
|
|
|
|
va_start(list, Message);
|
|
vsnprintf(buffer, 512, Message, list);
|
|
va_end(list);
|
|
|
|
setOutputTotal( Target, getOutputTotal(Target) + strlen(buffer) );
|
|
return MyUplink->Write("%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
Target->getCharYYXXX().c_str(),
|
|
buffer ) ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
void cservice::dbErrorMessage(iClient* theClient)
|
|
{
|
|
Notice(theClient,
|
|
"An error occured while performing this action, "
|
|
"the database may be unavailable. Please try again later.");
|
|
dbErrors++;
|
|
}
|
|
|
|
void cservice::loadPendingChannelList()
|
|
{
|
|
/*
|
|
* First thing first, if the list has something in it, we want to dump it
|
|
* out to the database.
|
|
* Then, clear the list free'ing up memory and finally emptying the list.
|
|
*/
|
|
|
|
if (pendingChannelList.size() > 0)
|
|
{
|
|
pendingChannelListType::iterator ptr = pendingChannelList.begin();
|
|
|
|
if( !SQLDb->Exec("BEGIN;" ) )
|
|
// if( PGRES_COMMAND_OK != begnew sqlPendingChannel(SQLDb);
|
|
{
|
|
elog << "Error starting transaction." << endl;
|
|
}
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "BEGIN" << endl;
|
|
#endif
|
|
|
|
while (ptr != pendingChannelList.end())
|
|
{
|
|
sqlPendingChannel* pendingChan = ptr->second;
|
|
|
|
/* Commit the record */
|
|
pendingChan->commit();
|
|
|
|
/* Stop listening on this channels events for now. */
|
|
MyUplink->UnRegisterChannelEvent(ptr->first, this);
|
|
|
|
ptr->second = NULL;
|
|
delete(pendingChan);
|
|
++ptr;
|
|
} /* while() */
|
|
|
|
if( !SQLDb->Exec("END;") )
|
|
// if( PGRES_COMMAND_OK != endStatus )
|
|
{
|
|
elog << "Error Ending transaction." << endl;
|
|
}
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "END" << endl;
|
|
#endif
|
|
|
|
pendingChannelList.clear();
|
|
}
|
|
|
|
/*
|
|
* For simplicity, we assume that if a pending channel is in state "1", then it has 10 valid
|
|
* supporters who have said "Yes" and we're looking at them.
|
|
*/
|
|
|
|
stringstream theQuery;
|
|
theQuery << "SELECT channels.name, pending.channel_id, user_id, pending.join_count, supporters.join_count, pending.unique_join_count, pending.check_start_ts"
|
|
<< " FROM pending,supporters,channels"
|
|
<< " WHERE pending.channel_id = supporters.channel_id"
|
|
<< " AND channels.id = pending.channel_id"
|
|
<< " AND pending.status = 1;"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "*** [CMaster::loadPendingChannelList]: Loading pending channel details."
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
string chanName = SQLDb->GetValue(i,0);
|
|
sqlPendingChannel* newPending;
|
|
|
|
/*
|
|
* Is this channel already in our pending list? If so, simply
|
|
* lookup and append this supporter to its list.
|
|
* If not, create a new 'pending' channel entry.
|
|
*/
|
|
|
|
pendingChannelListType::iterator ptr = pendingChannelList.find(chanName);
|
|
|
|
if(ptr != pendingChannelList.end())
|
|
{
|
|
// It already exists.
|
|
newPending = ptr->second;
|
|
}
|
|
else
|
|
{
|
|
newPending = new sqlPendingChannel(SQLDb);
|
|
newPending->channel_id =
|
|
atoi(SQLDb->GetValue(i,1).c_str());
|
|
newPending->join_count =
|
|
atoi(SQLDb->GetValue(i,3).c_str());
|
|
newPending->unique_join_count =
|
|
atoi(SQLDb->GetValue(i,5).c_str());
|
|
newPending->checkStart = atoi(SQLDb->GetValue(i,6).c_str());
|
|
//time_t checkStart = atoi(SQLDb->GetValue(i,5).c_str());
|
|
//unsigned int traffickTime = MaxDays * JudgeDaySeconds; //currentTime()
|
|
//if ((checkStart + traffickTime) > currentTime())
|
|
pendingChannelList.insert( pendingChannelListType::value_type(chanName, newPending) );
|
|
//else logDebugMessage("")
|
|
|
|
/*
|
|
* Lets register our interest in listening on JOIN events for this channel.
|
|
*/
|
|
MyUplink->RegisterChannelEvent(chanName, this);
|
|
}
|
|
|
|
/*
|
|
* Next, update the internal supporters list.
|
|
*/
|
|
newPending->supporterList.insert( sqlPendingChannel::supporterListType::value_type(
|
|
atoi(SQLDb->GetValue(i, 2).c_str()),
|
|
atoi(SQLDb->GetValue(i, 4).c_str()) ) );
|
|
}
|
|
}
|
|
|
|
logDebugMessage("Loaded Pending Channels, there are currently %i channels being traffic monitored.",
|
|
pendingChannelList.size());
|
|
|
|
#ifdef LOG_DEBUG
|
|
elog << "Loaded pending channels, there are currently "
|
|
<< pendingChannelList.size()
|
|
<< " channels being notified and recorded."
|
|
<< endl;
|
|
#endif
|
|
|
|
/*
|
|
* For each pending channel, load up its IP traffic
|
|
* cache.
|
|
*/
|
|
|
|
pendingChannelListType::iterator ptr = pendingChannelList.begin();
|
|
while (ptr != pendingChannelList.end())
|
|
{
|
|
sqlPendingChannel* pendingChan = ptr->second;
|
|
pendingChan->loadTrafficCache();
|
|
pendingChan->loadSupportersTraffic();
|
|
++ptr;
|
|
};
|
|
|
|
}
|
|
|
|
void cservice::checkDbConnectionStatus()
|
|
{
|
|
if( SQLDb->ConnectionBad() )
|
|
// if(SQLDb->Status() == CONNECTION_BAD)
|
|
{
|
|
logAdminMessage("\002WARNING:\002 Backend database connection has been lost, attempting to reconnect.");
|
|
elog << "cmaster::cmaster> Attempting to reconnect to database." << endl;
|
|
|
|
/* Remove the old database connection object. */
|
|
delete(SQLDb);
|
|
|
|
string Query = "host=" + confSqlHost + " dbname=" + confSqlDb + " port=" + confSqlPort + " user=" + confSqlUser
|
|
+ " password=" + confSqlPass;
|
|
|
|
SQLDb = new (std::nothrow) dbHandle( confSqlHost,
|
|
atoi( confSqlPort.c_str() ),
|
|
confSqlDb,
|
|
confSqlUser,
|
|
confSqlPass ) ;
|
|
// SQLDb = new (std::nothrow) cmDatabase( Query.c_str() ) ;
|
|
assert( SQLDb != 0 ) ;
|
|
|
|
if (SQLDb->ConnectionBad())
|
|
{
|
|
elog << "cmaster::cmaster> Unable to connect to SQL server."
|
|
<< endl
|
|
<< "cmaster::cmaster> PostgreSQL error message: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
|
|
connectRetries++;
|
|
if (connectRetries >= 6)
|
|
{
|
|
logAdminMessage("Unable to contact database after 6 attempts, shutting down.");
|
|
//MyUplink->flushBuffer();
|
|
::exit(0);
|
|
} else
|
|
{
|
|
logAdminMessage("Connection failed, retrying:");
|
|
}
|
|
|
|
|
|
} else
|
|
{
|
|
// TODO: Is this ok?
|
|
SQLDb->Exec("LISTEN channels_u; LISTEN users_u; LISTEN levels_u;");
|
|
// SQLDb->ExecCommandOk("LISTEN channels_u; LISTEN users_u; LISTEN levels_u;");
|
|
logAdminMessage("Successfully reconnected to database server. Panic over ;)");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void cservice::preloadChannelCache()
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT " << sql::channel_fields
|
|
<< " FROM channels WHERE "
|
|
<< "registered_ts <> 0"
|
|
<< ends;
|
|
|
|
elog << "*** [CMaster::preloadChannelCache]: Loading all registered channel records: "
|
|
<< endl;
|
|
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/* Add this information to the channel cache. */
|
|
|
|
sqlChannel* newChan = new (std::nothrow) sqlChannel(SQLDb);
|
|
assert( newChan != 0 ) ;
|
|
|
|
newChan->setAllMembers(i);
|
|
newChan->setLastUsed(currentTime());
|
|
|
|
sqlChannelCache.insert(sqlChannelHashType::value_type(newChan->getName(), newChan));
|
|
sqlChannelIDCache.insert(sqlChannelIDHashType::value_type(newChan->getID(), newChan));
|
|
|
|
} // for()
|
|
} // if()
|
|
|
|
elog << "*** [CMaster::preloadChannelCache]: Done. Loaded "
|
|
<< SQLDb->Tuples()
|
|
<< " registered channel records."
|
|
<< endl;
|
|
}
|
|
|
|
void cservice::preloadBanCache()
|
|
{
|
|
/*
|
|
* Execute a query to return all bans.
|
|
*/
|
|
|
|
stringstream theQuery;
|
|
theQuery << "SELECT " << sql::ban_fields
|
|
<< " FROM bans;"
|
|
<< ends;
|
|
|
|
elog << "*** [CMaster::preloadBanCache]: Precaching Bans table: "
|
|
<< endl;
|
|
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/*
|
|
* First, lookup this channel in the channel cache.
|
|
*/
|
|
|
|
unsigned int channel_id = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
sqlChannel* theChan = getChannelRecord(channel_id);
|
|
|
|
/*
|
|
* If we don't have the channel cached, its not registered so
|
|
* we aren't interested in this ban.
|
|
*/
|
|
|
|
if (!theChan) continue;
|
|
|
|
sqlBan* newBan = new (std::nothrow) sqlBan(SQLDb);
|
|
newBan->setAllMembers(i);
|
|
theChan->banList.insert(
|
|
std::make_pair( newBan->getID(), newBan ) ) ;
|
|
// theChan->banList[newBan->getID()] = newBan;
|
|
|
|
} // for()
|
|
} // if()
|
|
|
|
elog << "*** [CMaster::preloadBanCache]: Done. Loaded "
|
|
<< SQLDb->Tuples()
|
|
<< " bans."
|
|
<< endl;
|
|
}
|
|
|
|
void cservice::preloadLevelsCache()
|
|
{
|
|
/*
|
|
* Execute a query to return all level records.
|
|
*/
|
|
|
|
stringstream theQuery;
|
|
theQuery << "SELECT " << sql::level_fields
|
|
<< " FROM levels"
|
|
<< ends;
|
|
|
|
elog << "*** [CMaster::preloadLevelCache]: Precaching Level table: "
|
|
<< endl;
|
|
|
|
unsigned int goodCount = 0;
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/*
|
|
* First, lookup this channel in the channel cache.
|
|
*/
|
|
|
|
unsigned int channel_id = atoi(SQLDb->GetValue(i, 0).c_str());
|
|
unsigned int user_id = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
sqlChannel* theChan = getChannelRecord(channel_id);
|
|
|
|
/*
|
|
* If we don't have the channel cached, its not registered so
|
|
* we aren't interested in this level record.
|
|
*/
|
|
|
|
if (!theChan) continue;
|
|
|
|
pair<int, int> thePair( user_id, channel_id );
|
|
sqlLevel* newLevel = new (std::nothrow) sqlLevel(SQLDb);
|
|
newLevel->setAllMembers(i);
|
|
|
|
sqlLevelCache.insert(sqlLevelHashType::value_type(thePair, newLevel));
|
|
goodCount++;
|
|
|
|
} // for()
|
|
} // if()
|
|
|
|
elog << "*** [CMaster::preloadLevelCache]: Done. Loaded "
|
|
<< goodCount << " level records out of " << SQLDb->Tuples()
|
|
<< "."
|
|
<< endl;
|
|
}
|
|
|
|
/*
|
|
* Preload all the users within the last 'x' days, to
|
|
* save doing loads of lookups when we recieve 25,000 +r'd users
|
|
* during net.merge.
|
|
*/
|
|
|
|
void cservice::preloadUserCache()
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT " << sql::user_fields
|
|
<< " FROM users,users_lastseen WHERE "
|
|
<< " users_lastseen.user_id = users.id AND "
|
|
<< " users_lastseen.last_seen >= "
|
|
<< currentTime() - (preloadUserDays * 86400)
|
|
<< ends;
|
|
|
|
elog << "*** [CMaster::preloadUserCache]: Loading users accounts logged in within "
|
|
<< preloadUserDays
|
|
<< " days : "
|
|
<< endl;
|
|
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
// if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
sqlUser* newUser = new (std::nothrow) sqlUser(SQLDb);
|
|
assert( newUser != 0 ) ;
|
|
|
|
newUser->setAllMembers(i);
|
|
newUser->setLastUsed(currentTime());
|
|
sqlUserCache.insert(sqlUserHashType::value_type(newUser->getUserName(), newUser));
|
|
}
|
|
}
|
|
|
|
elog << "*** [CMaster::preloadUserCache]: Done. Loaded "
|
|
<< SQLDb->Tuples()
|
|
<< " user accounts."
|
|
<< endl;
|
|
}
|
|
|
|
void cservice::incStat(const string& name)
|
|
{
|
|
statsMapType::iterator ptr = statsMap.find( name );
|
|
|
|
if( ptr == statsMap.end() )
|
|
{
|
|
statsMap.insert(statsMapType::value_type(name, 1));
|
|
}
|
|
else
|
|
{
|
|
ptr->second++;
|
|
}
|
|
}
|
|
|
|
void cservice::incStat(const string& name, unsigned int amount)
|
|
{
|
|
statsMapType::iterator ptr = statsMap.find( name );
|
|
|
|
if( ptr == statsMap.end() )
|
|
{
|
|
statsMap.insert(statsMapType::value_type(name, 1));
|
|
}
|
|
else
|
|
{
|
|
ptr->second += amount;
|
|
}
|
|
}
|
|
|
|
void cservice::noticeAllAuthedClients(sqlUser* theUser, const char* Message, ... )
|
|
{
|
|
/*
|
|
* Loop over everyone who is authed as this user and give them a message.
|
|
*/
|
|
|
|
if( Connected && MyUplink && Message && Message[ 0 ] != 0 )
|
|
{
|
|
char buffer[ 512 ] = { 0 } ;
|
|
va_list list;
|
|
|
|
va_start(list, Message);
|
|
vsnprintf(buffer, 512, Message, list);
|
|
va_end(list);
|
|
|
|
/*
|
|
* Loop over all people auth'd as this user, and send them a
|
|
* message.
|
|
*/
|
|
|
|
for( sqlUser::networkClientListType::iterator ptr = theUser->networkClientList.begin() ;
|
|
ptr != theUser->networkClientList.end() ; ++ptr )
|
|
{
|
|
iClient* Target = (*ptr);
|
|
setOutputTotal( Target, getOutputTotal(Target) + strlen(buffer) );
|
|
MyUplink->Write("%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
Target->getCharYYXXX().c_str(),
|
|
buffer ) ;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void cservice::NoteAllAuthedClients(sqlUser* theUser, const char* Message, ... )
|
|
{
|
|
#ifdef USE_NOTICES
|
|
if( Connected && MyUplink && Message && Message[ 0 ] != 0 )
|
|
{
|
|
char buffer[ 512 ] = { 0 } ;
|
|
va_list list;
|
|
|
|
va_start(list, Message);
|
|
vsnprintf(buffer, 512, Message, list);
|
|
va_end(list);
|
|
|
|
/*
|
|
* Loop over all people auth'd as this user, and send them a
|
|
* message.
|
|
*/
|
|
if (theUser->networkClientList.size() != 0)
|
|
{
|
|
for( sqlUser::networkClientListType::iterator ptr = theUser->networkClientList.begin() ;
|
|
ptr != theUser->networkClientList.end() ; ++ptr )
|
|
{
|
|
iClient* Target = (*ptr);
|
|
setOutputTotal( Target, getOutputTotal(Target) + strlen(buffer) );
|
|
MyUplink->Write("%s O %s :%s\r\n",
|
|
getCharYYXXX().c_str(),
|
|
Target->getCharYYXXX().c_str(),
|
|
buffer ) ;
|
|
}
|
|
return;
|
|
}
|
|
|
|
string noteMessage = string(buffer);
|
|
|
|
stringstream queryString;
|
|
queryString << "DELETE FROM notices WHERE last_updated IN "
|
|
<< "(SELECT MIN(last_updated) FROM notices "
|
|
<< "WHERE user_id = "
|
|
<< theUser->getID()
|
|
<< " HAVING count(last_updated) >= "
|
|
<< MAXnotes
|
|
<< "3)"
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::NoteAllAuthedClients::DeleteNotice> "
|
|
<< queryString.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(queryString, true ) )
|
|
// if( PGRES_COMMAND_OK != status )
|
|
{
|
|
#ifdef LOG_SQL
|
|
elog << "NoteAllAuthedClients::commit> Something went wrong: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
#endif
|
|
logDebugMessage("NoteAllAuthedClients::DELETEFROM: ", SQLDb->ErrorMessage().c_str());
|
|
return;
|
|
}
|
|
static const char* queryHeader = "INSERT INTO notices (user_id,message,last_updated) VALUES (";
|
|
queryString.str("");
|
|
queryString << queryHeader
|
|
<< theUser->getID() << ", '"
|
|
<< escapeSQLChars(noteMessage) << "', "
|
|
<< "date_part('epoch', CURRENT_TIMESTAMP)::int);"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::NoteAllAuthedClients::Insert Note> "
|
|
<< queryString.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(queryString, true ) )
|
|
// if( PGRES_COMMAND_OK != status )
|
|
{
|
|
#ifdef LOG_SQL
|
|
elog << "NoteAllAuthedClients::commit> Something went wrong: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
#endif
|
|
logDebugMessage("An unknown error occured delivering the note.");
|
|
return;
|
|
}
|
|
} //if MyUpLink
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Checks if the password supplied correctly matches the password for
|
|
* this user.
|
|
*/
|
|
|
|
bool cservice::isPasswordRight(sqlUser* theUser, const string& password)
|
|
{
|
|
/*
|
|
* Compare password with MD5 hash stored in user record.
|
|
*/
|
|
|
|
/* MD5 hash algorithm object. */
|
|
md5 hash;
|
|
|
|
/* MD5Digest algorithm object.*/
|
|
md5Digest digest;
|
|
|
|
/* check that we have a valid md5 hash to prevent coredumps */
|
|
|
|
if (theUser->getPassword().size() < 9)
|
|
{
|
|
/* salt is 8 characters and there must be a password
|
|
* of some kind, so 9 is a 'safe' value.
|
|
*
|
|
* return a standard 'invalid password' to user
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
string salt = theUser->getPassword().substr(0, 8);
|
|
string md5Part = theUser->getPassword().substr(8);
|
|
string guess = salt + password;
|
|
|
|
// Build a MD5 hash based on our salt + the guessed password.
|
|
hash.update( (const unsigned char *)guess.c_str(), guess.size() );
|
|
hash.report( digest );
|
|
|
|
// Convert the digest into an array of int's to output as hex for
|
|
// comparison with the passwords generated by PHP.
|
|
int data[ MD5_DIGEST_LENGTH ] = { 0 } ;
|
|
|
|
for( size_t ii = 0; ii < MD5_DIGEST_LENGTH; ii++ )
|
|
{
|
|
data[ii] = digest[ii];
|
|
}
|
|
|
|
stringstream output;
|
|
output << std::hex;
|
|
output.fill('0');
|
|
|
|
for( size_t ii = 0; ii < MD5_DIGEST_LENGTH; ii++ )
|
|
{
|
|
output << std::setw(2) << data[ii];
|
|
}
|
|
output << ends;
|
|
|
|
if(md5Part != output.str().c_str() ) // If the MD5 hash's don't match..
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Return some slightly interesting stats as a result of a status *.
|
|
*/
|
|
void cservice::doCoderStats(iClient* theClient)
|
|
{
|
|
float userTotal = userCacheHits + userHits;
|
|
float userEf = userCacheHits ? ((float)userCacheHits / userTotal * 100) : 0;
|
|
|
|
Notice(theClient, "CMaster Channel Services internal status:");
|
|
|
|
Notice(theClient,"[User Record Stats] \002Cached Entries:\002 %i \002DB Requests:\002 %i \002Cache Hits:\002 %i \002Efficiency:\002 %.2f%%",
|
|
sqlUserCache.size(),
|
|
userHits,
|
|
userCacheHits,
|
|
userEf);
|
|
|
|
/*
|
|
* Count how many users are actually logged in right now.
|
|
*/
|
|
unsigned int authCount = 0;
|
|
sqlUserHashType::iterator ptr = sqlUserCache.begin();
|
|
sqlUser* tmpUser;
|
|
|
|
while (ptr != sqlUserCache.end())
|
|
{
|
|
tmpUser = ptr->second;
|
|
if (tmpUser->isAuthed()) authCount++;
|
|
++ptr;
|
|
}
|
|
|
|
/*
|
|
* Iterate over all the clients on the network and
|
|
* see how many are +x.
|
|
*/
|
|
|
|
unsigned int plusXCount = 0;
|
|
unsigned int plusWCount = 0;
|
|
unsigned int plusDCount = 0;
|
|
|
|
xNetwork::const_clientIterator ptr2 = Network->clients_begin();
|
|
while(ptr2 != Network->clients_end())
|
|
{
|
|
iClient* tmpClient = ptr2->second;
|
|
if (tmpClient->isModeX()) plusXCount++;
|
|
if (tmpClient->isModeW()) plusWCount++;
|
|
if (tmpClient->isModeD()) plusDCount++;
|
|
++ptr2;
|
|
}
|
|
|
|
Notice(theClient, "--- Total clients : %i", Network->clientList_size());
|
|
|
|
float authTotal = ((float)authCount / (float)Network->clientList_size()) * 100;
|
|
Notice(theClient, "--- Total Auth'd : %i (%.2f%% of total)",
|
|
authCount, authTotal);
|
|
|
|
float plusXTotal = ((float)plusXCount / (float)Network->clientList_size()) * 100;
|
|
Notice(theClient, "--- Total umode +x: %i (%.2f%% of total)",
|
|
plusXCount, plusXTotal);
|
|
|
|
float plusWTotal = ((float)plusWCount / (float)Network->clientList_size()) * 100;
|
|
Notice(theClient, "--- Total umode +w: %i (%.2f%% of total)",
|
|
plusWCount, plusWTotal);
|
|
|
|
float plusDTotal = ((float)plusDCount / (float)Network->clientList_size()) * 100;
|
|
Notice(theClient, "--- Total umode +d: %i (%.2f%% of total)",
|
|
plusDCount, plusDTotal);
|
|
|
|
float joinTotal = ((float)joinCount / (float)Network->channelList_size()) * 100;
|
|
Notice(theClient, "I am in %i channels out of %i on the network. (%.2f%%)",
|
|
joinCount, Network->channelList_size(), joinTotal);
|
|
|
|
unsigned int secs = (currentTime() - getUplink()->getStartTime());
|
|
|
|
float cPerSec = (float)totalCommands / (float)secs;
|
|
|
|
Notice(theClient, "I've received %i commands since I started (%.2f commands per second).",
|
|
totalCommands, cPerSec);
|
|
|
|
Notice(theClient,"\002Uptime:\002 %s",
|
|
prettyDuration(getUplink()->getStartTime() + dbTimeOffset).c_str());
|
|
|
|
/*
|
|
* Now, dump all the config settings.
|
|
*/
|
|
|
|
Notice(theClient, "-------------------------");
|
|
Notice(theClient, "Configuration Variables: ");
|
|
Notice(theClient, "-------------------------");
|
|
|
|
for( cservice::configHashType::iterator ptr = configTable.begin() ;
|
|
ptr != configTable.end() ; ++ptr )
|
|
{
|
|
Notice(theClient, "%s: %s", ptr->first.c_str(), ptr->second->asString().c_str());
|
|
}
|
|
}
|
|
|
|
void cservice::showAdmins(iClient* theClient)
|
|
{
|
|
Notice(theClient,"Status of currently logged '*' officials:");
|
|
|
|
stringstream authQuery;
|
|
authQuery << "SELECT users.user_name,levels.access FROM "
|
|
<< "users,levels WHERE users.id = levels.user_id "
|
|
<< "AND levels.channel_id = 1"
|
|
<< " ORDER BY levels.access DESC"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlQuery> "
|
|
<< authQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec( authQuery, true ) )
|
|
//if( PGRES_TUPLES_OK != status )
|
|
{
|
|
elog << "STATUS> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return;
|
|
}
|
|
string authList;
|
|
string nextPerson;
|
|
|
|
for (unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/*
|
|
* Look up this username in the cache.
|
|
*/
|
|
|
|
sqlUserHashType::iterator ptr = sqlUserCache.find(SQLDb->GetValue(i, 0));
|
|
|
|
if (ptr != sqlUserCache.end())
|
|
{
|
|
sqlUser* currentUser = ptr->second;
|
|
if (!currentUser->isAuthed())
|
|
{
|
|
continue ;
|
|
}
|
|
|
|
nextPerson += SQLDb->GetValue(i, 0);
|
|
nextPerson += "/\002";
|
|
|
|
/*
|
|
* Only show the online nickname if that person is in the target
|
|
* channel.
|
|
*
|
|
* Loop around all people auth'd as this nick and append their nicks
|
|
*/
|
|
for (sqlUser::networkClientListType::iterator ptr = currentUser->networkClientList.begin();
|
|
ptr != currentUser->networkClientList.end(); ++ptr)
|
|
{
|
|
iClient* tmpClient = (*ptr);
|
|
|
|
nextPerson += "";
|
|
nextPerson += tmpClient->getNickName();
|
|
nextPerson += " ";
|
|
}
|
|
|
|
nextPerson += "\002(";
|
|
nextPerson += SQLDb->GetValue(i, 1);
|
|
nextPerson += ") ";
|
|
|
|
/*
|
|
* Will this string overflow our Notice buffer?
|
|
* If so, dump it now..
|
|
*/
|
|
if (nextPerson.size() + authList.size() > 400)
|
|
{
|
|
Notice(theClient, "Auth: %s", authList.c_str());
|
|
authList.erase(authList.begin(), authList.end());
|
|
}
|
|
|
|
/*
|
|
* Add it on to our list.
|
|
*/
|
|
|
|
authList += nextPerson;
|
|
nextPerson.erase( nextPerson.begin(), nextPerson.end() );
|
|
}
|
|
} // for()
|
|
|
|
Notice(theClient, "Auth: %s", authList.c_str());
|
|
}
|
|
|
|
/*
|
|
* Function to retrieve the specified config variable from the in-memory cache.
|
|
*/
|
|
ConfigData* cservice::getConfigVar(const string& variable)
|
|
{
|
|
configHashType::iterator ptr = configTable.find(variable);
|
|
|
|
if(ptr != configTable.end())
|
|
{
|
|
return ptr->second;
|
|
} else {
|
|
return &empty_config;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pre-load the configuration information from the db.
|
|
*/
|
|
void cservice::loadConfigData()
|
|
{
|
|
stringstream theQuery;
|
|
theQuery << "SELECT var_name,contents FROM variables;"
|
|
<< ends;
|
|
|
|
if( SQLDb->Exec(theQuery, true ) )
|
|
// if( PGRES_TUPLES_OK == status )
|
|
{
|
|
for (unsigned int i = 0 ; i < SQLDb->Tuples(); i++)
|
|
{
|
|
ConfigData* newConfig = new (std::nothrow) ConfigData();
|
|
assert( newConfig != 0 ) ;
|
|
|
|
newConfig->string_value = SQLDb->GetValue(i, 1);
|
|
newConfig->int_value = atoi(newConfig->string_value.c_str());
|
|
|
|
configTable.insert(configHashType::value_type(SQLDb->GetValue(i, 0), newConfig));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
bool cservice::doXQLogin(iServer* theServer, const string& Routing, const string& Message)
|
|
{
|
|
//What's going to be in Message?
|
|
//<ournick> LOGIN <ip> <username> <password>
|
|
StringTokenizer st( Message );
|
|
if( st.size() < 5 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Are we allowing logins yet?
|
|
**/
|
|
unsigned int useLoginDelay = getConfigVar("USE_LOGIN_DELAY")->asInt();
|
|
unsigned int loginTime = getUplink()->getStartTime() + loginDelay;
|
|
if ( (useLoginDelay == 1) && (loginTime >= (unsigned int)currentTime()) )
|
|
{
|
|
//TODO Send error back
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
sqlUser* theUser = getUserRecord(st[3]);
|
|
if( !theUser )
|
|
{
|
|
elog << "doXQLogin: "
|
|
<< "Couldn't find user data for accountname: "
|
|
<< st[3]
|
|
<< endl;
|
|
//TODO Send error back
|
|
return false;
|
|
}
|
|
|
|
// IPR checks are performed against st[2], the IP passed to us by iauth
|
|
|
|
stringstream theQuery;
|
|
theQuery << "SELECT allowmask,allowrange1,allowrange2,added FROM "
|
|
<< "ip_restrict WHERE user_id = "
|
|
<< theUser->getID()
|
|
<< ends;
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::checkIPR::sqlQuery> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(theQuery, true ) )
|
|
{
|
|
/* SQL error, fail them */
|
|
elog << "cservice::checkIPR> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return false;
|
|
}
|
|
|
|
bool userHasIPR = true;
|
|
if (SQLDb->Tuples() < 1)
|
|
{
|
|
userHasIPR = false;
|
|
#ifdef IPR_DEFAULT_REJECT
|
|
/* no entries, fail them */
|
|
return false;
|
|
#else
|
|
/* no entries, allow them to pass through*/
|
|
#endif
|
|
}
|
|
|
|
if (userHasIPR)
|
|
{
|
|
/* cycle through results to find a match */
|
|
bool ipr_match = false;
|
|
unsigned int ipr_ts = 0;
|
|
unsigned int tmpIP = xIP(st[2], false).GetLongIP();
|
|
for (unsigned int i=0; i < SQLDb->Tuples(); i++)
|
|
{
|
|
/* get some variables out of the db row */
|
|
std::string ipr_allowmask = SQLDb->GetValue(i, 0);
|
|
unsigned int ipr_allowrange1 = atoi(SQLDb->GetValue(i, 1).c_str());
|
|
unsigned int ipr_allowrange2 = atoi(SQLDb->GetValue(i, 2).c_str());
|
|
ipr_ts = atoi(SQLDb->GetValue(i, 3).c_str());
|
|
|
|
/* is this an IP range? */
|
|
if (ipr_allowrange2 > 0)
|
|
{
|
|
/* yes it is, is the client IP between range1 and range2? */
|
|
if ((tmpIP >= ipr_allowrange1) && (tmpIP <= ipr_allowrange2))
|
|
{
|
|
ipr_match = true;
|
|
break;
|
|
}
|
|
} else {
|
|
/* no, is it a single IP? */
|
|
if (ipr_allowrange1 > 0)
|
|
{
|
|
/* yes it is, does the IP match range1? */
|
|
if (tmpIP == ipr_allowrange1)
|
|
{
|
|
ipr_match = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* check if we found a match yet */
|
|
if (!ipr_match)
|
|
{
|
|
/* no match, fail them */
|
|
//TODO Send error back
|
|
return false;
|
|
} else {
|
|
/* IP restriction check passed */
|
|
}
|
|
}
|
|
|
|
if (theUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
|
|
{
|
|
elog << "doXQLogin: "
|
|
<< "Globally suspended account tried to auth: "
|
|
<< st[3]
|
|
<< endl;
|
|
//TODO Send error back
|
|
return false;
|
|
}
|
|
|
|
unsigned int max_failed_logins = getConfigVar("FAILED_LOGINS")->asInt();
|
|
unsigned int failed_login_rate = getConfigVar("FAILED_LOGINS_RATE")->asInt();
|
|
|
|
/* if it's not configured, default to every 15 minutes */
|
|
if (failed_login_rate==0)
|
|
failed_login_rate = 900;
|
|
|
|
if (!isPasswordRight(theUser, st.assemble(4)))
|
|
{
|
|
theUser->incFailedLogins();
|
|
|
|
if ((max_failed_logins > 0) && (theUser->getFailedLogins() > max_failed_logins) &&
|
|
(theUser->getLastFailedLoginTS() < (time(NULL) - failed_login_rate)))
|
|
{
|
|
/* we have exceeded our maximum - alert relay channel
|
|
* work out a checksum for the password. Yes, I could have
|
|
* just used a checksum of the original password, but this
|
|
* means it's harder to 'fool' the check digit with a real
|
|
* password - create MD5 from original salt stored */
|
|
unsigned char checksum;
|
|
md5 hash;
|
|
md5Digest digest;
|
|
|
|
if (theUser->getPassword().size() < 9)
|
|
{
|
|
checksum = 0;
|
|
} else {
|
|
string salt = theUser->getPassword().substr(0, 8);
|
|
string guess = salt + st.assemble(2);
|
|
|
|
hash.update( (const unsigned char *)guess.c_str(), guess.size() );
|
|
hash.report( digest );
|
|
|
|
checksum = 0;
|
|
for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++)
|
|
{
|
|
/* add ascii value to check digit */
|
|
checksum += digest[i];
|
|
}
|
|
}
|
|
|
|
theUser->setLastFailedLoginTS(time(NULL));
|
|
logPrivAdminMessage("%d failed logins for %s (last attempt from iauth with IP %s), checksum %d).",
|
|
theUser->getFailedLogins(),
|
|
theUser->getUserName().c_str(),
|
|
st[2].c_str(),
|
|
checksum);
|
|
|
|
}
|
|
elog << "doXQLogin: "
|
|
<< "Wrong password supplied by: "
|
|
<< st[3] << "PASS: "
|
|
<< st.assemble(4)
|
|
<< endl;
|
|
//TODO Send error back
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Don't exceed MAXLOGINS.
|
|
*/
|
|
//TODO
|
|
|
|
/*
|
|
* If this user account is already authed against, send a notice to the other
|
|
* users warning them that someone else has logged in too.
|
|
*/
|
|
//TODO
|
|
|
|
|
|
|
|
elog << "doXQLogin: "
|
|
<< "Succesful auth for "
|
|
<< st[3]
|
|
<< endl;
|
|
//TODO Return confirmation
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Display a summary of channels "theUser" has access on to "theClient".
|
|
* Only show those above or equal to "minlevel".
|
|
*/
|
|
void cservice::outputChannelAccesses(iClient* theClient, sqlUser* theUser, sqlUser* tmpUser, unsigned int minlevel)
|
|
{
|
|
stringstream channelsQuery;
|
|
string channelList ;
|
|
|
|
channelsQuery << "SELECT channels.name,levels.access,levels.flags FROM levels,channels "
|
|
<< "WHERE levels.channel_id = channels.id AND channels.registered_ts <> 0 AND levels.user_id = "
|
|
<< theUser->getID()
|
|
<< " AND levels.access >= "
|
|
<< minlevel
|
|
<< " ORDER BY levels.access DESC"
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "CHANINFO::sqlQuery> "
|
|
<< channelsQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
string chanName ;
|
|
string chanAccess ;
|
|
unsigned int flags;
|
|
|
|
if( !SQLDb->Exec(channelsQuery, true ) )
|
|
// if( PGRES_TUPLES_OK != status )
|
|
{
|
|
Notice( theClient,
|
|
"Internal error: SQL failed" ) ;
|
|
|
|
elog << "CHANINFO> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return ;
|
|
}
|
|
|
|
for(unsigned int i = 0; i < SQLDb->Tuples(); i++)
|
|
{
|
|
flags = atoi(SQLDb->GetValue(i, 2));
|
|
if(flags & sqlLevel::F_AUTOINVITE)
|
|
{
|
|
chanName = "\002" + SQLDb->GetValue(i,0) + "\002";
|
|
} else {
|
|
chanName = SQLDb->GetValue(i,0);
|
|
}
|
|
chanAccess = SQLDb->GetValue(i,1);
|
|
|
|
// 4 for 2 spaces, 2 brackets + comma.
|
|
if ((channelList.size() + chanName.size() + chanAccess.size() +5) >= 450)
|
|
{
|
|
Notice(theClient,
|
|
getResponse(tmpUser,
|
|
language::channels,
|
|
string("Channels: %s")).c_str(),
|
|
channelList.c_str());
|
|
channelList.erase( channelList.begin(),
|
|
channelList.end() ) ;
|
|
}
|
|
|
|
if (channelList.size() != 0)
|
|
{
|
|
channelList += ", ";
|
|
}
|
|
channelList += chanName;
|
|
channelList += " (";
|
|
channelList += chanAccess;
|
|
channelList += ")";
|
|
} // for()
|
|
|
|
Notice(theClient,
|
|
getResponse(tmpUser,
|
|
language::channels,
|
|
string("Channels: %s")).c_str(),
|
|
channelList.c_str());
|
|
}
|
|
|
|
void Command::Usage( iClient* theClient )
|
|
{
|
|
bot->Notice( theClient, string( "SYNTAX: " ) + getInfo() ) ;
|
|
}
|
|
|
|
string cservice::CryptPass( const string& pass )
|
|
{
|
|
StringTokenizer st( pass ) ;
|
|
|
|
const char validChars[] =
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.$*_";
|
|
|
|
string salt ;
|
|
for( unsigned short int i = 0 ; i < 8 ; ++i )
|
|
{
|
|
int randNo = 1 + (int) (64.0 * rand() / (RAND_MAX + 1.0) );
|
|
salt += validChars[ randNo ] ;
|
|
}
|
|
|
|
/* Work out a MD5 hash of our salt + password */
|
|
md5 hash; // MD5 hash algorithm object.
|
|
md5Digest digest; // MD5Digest algorithm object.
|
|
|
|
stringstream output;
|
|
string newPass;
|
|
newPass = salt + st.assemble(0);
|
|
|
|
hash.update( (const unsigned char *)newPass.c_str(),
|
|
newPass.size() );
|
|
hash.report( digest );
|
|
|
|
/* Convert to Hex */
|
|
int data[ MD5_DIGEST_LENGTH ] = { 0 } ;
|
|
for( size_t ii = 0; ii < MD5_DIGEST_LENGTH; ii++ )
|
|
{
|
|
data[ii] = digest[ii];
|
|
}
|
|
|
|
output << std::hex;
|
|
output.fill('0');
|
|
for( size_t ii = 0; ii < MD5_DIGEST_LENGTH; ii++ )
|
|
{
|
|
output << std::setw(2) << data[ii];
|
|
}
|
|
output << ends;
|
|
|
|
return string( salt + output.str() );
|
|
}
|
|
|
|
bool cservice::SendMail(const string& address, const string& subject, const stringstream& mailtext)
|
|
{
|
|
std::ofstream TMail;
|
|
TMail.open("mailbody.txt", std::ios::out);
|
|
if (!TMail)
|
|
{
|
|
logDebugMessage(" *** ERROR while sending email");
|
|
return false;
|
|
}
|
|
TMail << mailtext.str().c_str();
|
|
TMail.close();
|
|
stringstream mailcommand;
|
|
mailcommand << "mail -s " << "\"" << subject.c_str() << "\" -r \"" << this->sendmailFrom.c_str() << "\" " << address.c_str() << " < mailbody.txt" << endl << ends;
|
|
elog << "mailcommand = '" << mailcommand.str() << endl << ends;
|
|
//Devnote: alternate way of sending mail: echo "Mail body text here" | mail -s "Subject" -r "from@address" email@address.com
|
|
system(mailcommand.str().c_str());
|
|
return true;
|
|
}
|
|
|
|
bool cservice::addGline( csGline* TempGline)
|
|
{
|
|
|
|
glineIterator ptr;
|
|
if(TempGline->getHost().substr(0,1) == "$") //check if its a realname gline
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ptr = glineList.find(TempGline->getHost());
|
|
if(ptr != glineList.end())
|
|
{
|
|
if(ptr->second != TempGline)
|
|
{
|
|
delete ptr->second;
|
|
glineList.erase(ptr);
|
|
}
|
|
}
|
|
glineList[TempGline->getHost()] = TempGline;
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::remGline( csGline* TempGline)
|
|
{
|
|
if(TempGline->getHost().substr(0,1) == "$")
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
glineList.erase(TempGline->getHost()) ;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
csGline* cservice::findGline( const string& HostName )
|
|
{
|
|
glineIterator ptr = glineList.find(HostName);
|
|
if(ptr != glineList.end())
|
|
{
|
|
return ptr->second;
|
|
}
|
|
|
|
return NULL ;
|
|
}
|
|
|
|
bool cservice::loadGlines()
|
|
{
|
|
static const char *Main = "SELECT Id,Host,AddedBy,AddedOn,ExpiresAt,LastUpdated,Reason FROM glines";
|
|
|
|
stringstream eraseQuery;
|
|
eraseQuery << "DELETE FROM glines";
|
|
if( !SQLDb->Exec( eraseQuery, true ) )
|
|
{
|
|
elog << "cservice::loadGlines::Erase> SQL Failure: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl;
|
|
return false;
|
|
}
|
|
|
|
stringstream theQuery;
|
|
theQuery << Main
|
|
<< ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "cservice::loadGlines> "
|
|
<< theQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec( theQuery, true ) )
|
|
//if( PGRES_TUPLES_OK != status )
|
|
{
|
|
elog << "cservice::loadGlines> SQL Failure: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
|
|
return false;
|
|
}
|
|
|
|
csGline *tempGline = NULL;
|
|
|
|
for( unsigned int i = 0 ; i < SQLDb->Tuples() ; i++ )
|
|
{
|
|
tempGline = new (std::nothrow) csGline(SQLDb);
|
|
assert( tempGline != NULL ) ;
|
|
|
|
tempGline->setId(SQLDb->GetValue(i,0));
|
|
tempGline->setHost(SQLDb->GetValue(i,1));
|
|
tempGline->setAddedBy(SQLDb->GetValue(i,2)) ;
|
|
tempGline->setAddedOn(static_cast< time_t >(
|
|
atoi( SQLDb->GetValue(i,3).c_str() ) )) ;
|
|
tempGline->setExpires(static_cast< time_t >(
|
|
atoi( SQLDb->GetValue(i,4).c_str() ) )) ;
|
|
tempGline->setLastUpdated(static_cast< time_t >(
|
|
atoi( SQLDb->GetValue(i,5).c_str() ) )) ;
|
|
tempGline->setReason(SQLDb->GetValue(i,6));
|
|
addGline(tempGline);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cservice::expireGlines()
|
|
{
|
|
|
|
int totalFound = 0;
|
|
csGline * tempGline;
|
|
list<string> remList;
|
|
list<string>::iterator remIterator;
|
|
for(glineIterator ptr = glineList.begin();ptr != glineList.end();++ptr)
|
|
{
|
|
tempGline = ptr->second;
|
|
if((tempGline->getExpires() <= ::time(0))
|
|
&& ((tempGline->getHost().substr(0,1) != "#") ||
|
|
(tempGline->getExpires() != 0)))
|
|
|
|
{
|
|
//remove the gline from the database
|
|
tempGline->Delete();
|
|
remList.push_back(ptr->first);
|
|
// ptr = glineList.erase(ptr);
|
|
delete tempGline;
|
|
++totalFound;
|
|
}
|
|
}
|
|
|
|
for(remIterator = remList.begin();remIterator != remList.end();)
|
|
{
|
|
glineList.erase(*remIterator);
|
|
remIterator = remList.erase(remIterator);
|
|
}
|
|
|
|
if(totalFound > 0)
|
|
elog << "[Refresh Glines]: "
|
|
<< totalFound << " expired"
|
|
<< endl;
|
|
return true;
|
|
|
|
}
|
|
|
|
bool cservice::expireWhitelist()
|
|
{
|
|
stringstream whitelistQuery;
|
|
whitelistQuery << "DELETE FROM whitelist WHERE "
|
|
<< "expiresat <= date_part('epoch', CURRENT_TIMESTAMP)::int AND "
|
|
<< "expiresat != 0" << ends;
|
|
|
|
#ifdef LOG_SQL
|
|
elog << "sqlQuery> "
|
|
<< whitelistQuery.str().c_str()
|
|
<< endl;
|
|
#endif
|
|
|
|
if( !SQLDb->Exec(whitelistQuery, true ) )
|
|
{
|
|
elog << "cservice::expireBans> SQL Error: "
|
|
<< SQLDb->ErrorMessage()
|
|
<< endl ;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cservice::addGlineToUplink(csGline* theGline)
|
|
{
|
|
int Expires;
|
|
if((theGline->getHost().substr(0,1) == "#")
|
|
&& (theGline->getExpires() == 0))
|
|
{
|
|
Expires = 730*3600*24; //gline::PERM_TIME;
|
|
}
|
|
else
|
|
{
|
|
Expires = theGline->getExpires() - time(0);
|
|
}
|
|
MyUplink->setGline(theGline->getAddedBy()
|
|
,theGline->getHost(),theGline->getReason()
|
|
,Expires,theGline->getLastUpdated(),this);
|
|
}
|
|
|
|
} // namespace gnuworld
|