/** * 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 #include #include #include #include #include #include #include #include #include #include #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", " ", 30)); RegisterCommand(new ACCESSCommand(this, "ACCESS", " [-min n] [-max n] [-op] [-voice] [-none] [-modif]", 5)); RegisterCommand(new CHANINFOCommand(this, "CHANINFO", "<#channel>", 3)); RegisterCommand(new CHANINFOCommand(this, "INFO", "", 3)); RegisterCommand(new ISREGCommand(this, "ISREG", "<#channel>", 4)); RegisterCommand(new VERIFYCommand(this, "VERIFY", "", 3)); //RegisterCommand(new SEARCHCommand(this, "SEARCH", "[-min ] ", 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 ", 15)); RegisterCommand(new NOTECommand(this, "NOTE", "send , read all, erase ", 10)); RegisterCommand(new NOTECommand(this, "NOTES", "send , read all, erase ", 10)); RegisterCommand(new OBJECTCommand(this, "OBJECT", "<#channel> ", 8)); RegisterCommand(new CANCELCommand(this, "CANCEL", "<#channel> ", 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> ", 8)); RegisterCommand(new REMUSERCommand(this, "REMUSER", "<#channel> ", 4)); RegisterCommand(new MODINFOCommand(this, "MODINFO", "<#channel> [ACCESS ] [AUTOMODE ] [INVITE ]", 6)); RegisterCommand(new SETCommand(this, "SET", "[#channel] or, SET or, SET LANG or, SET MAXLOGINS .", 6)); RegisterCommand(new INVITECommand(this, "INVITE", "<#channel> <#channel> <#channel> ... ", 2)); RegisterCommand(new TOPICCommand(this, "TOPIC", "<#channel> ", 4)); RegisterCommand(new BANLISTCommand(this, "BANLIST", "<#channel>", 3)); RegisterCommand(new KICKCommand(this, "KICK", "<#channel> [reason]", 4)); RegisterCommand(new STATUSCommand(this, "STATUS", "<#channel>", 4)); RegisterCommand(new SUSPENDCommand(this, "SUSPEND", "<#channel> [duration] [level] [reason]", 5)); RegisterCommand(new UNSUSPENDCommand(this, "UNSUSPEND", "<#channel> [reason]", 5)); RegisterCommand(new BANCommand(this, "BAN", "<#channel> [duration] [level] [reason]", 5)); RegisterCommand(new UNBANCommand(this, "UNBAN", "<#channel> <*!*user@*.host>", 5)); RegisterCommand(new LBANLISTCommand(this, "LBANLIST", "<#channel> ", 5)); RegisterCommand(new NEWPASSCommand(this, "NEWPASS", "", 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", "", 15)); RegisterCommand(new WHITELISTCommand(this, "WHITELIST", " [duration] [reason]", 10)); RegisterCommand(new SCANCommand(this, "SCAN", "NICK|NICKNAME ", 10)); RegisterCommand(new SCANHOSTCommand(this, "SCANHOST", " [-all]", 10)); RegisterCommand(new SCANUNAMECommand(this, "SCANUNAME", " [-all]", 10)); RegisterCommand(new SCANEMAILCommand(this, "SCANEMAIL", " [-all]", 10)); RegisterCommand(new REMIGNORECommand(this, "REMIGNORE", "", 5)); RegisterCommand(new REGISTERCommand(this, "REGISTER", "<#channel>", 8)); RegisterCommand(new REMOVEALLCommand(this, "REMOVEALL", "<#channel>", 15)); RegisterCommand(new PURGECommand(this, "PURGE", " [-noop] ", 8)); RegisterCommand(new ACCEPTCommand(this, "ACCEPT", "<#channel> ", 8)); RegisterCommand(new REJECTCommand(this, "REJECT", "<#channel> ", 8)); RegisterCommand(new RENAMECommand(this, "RENAME", " ", 5)); RegisterCommand(new FORCECommand(this, "FORCE", "<#channel>", 8)); RegisterCommand(new UNFORCECommand(this, "UNFORCE", "<#channel>", 8)); RegisterCommand(new SERVNOTICECommand(this, "SERVNOTICE", "<#channel> ", 5)); RegisterCommand(new SAYCommand(this, "SAY", "<#channel> ", 5)); RegisterCommand(new SAYCommand(this, "DO", "<#channel> ", 5)); RegisterCommand(new QUOTECommand(this, "QUOTE", "", 5)); RegisterCommand(new REHASHCommand(this, "REHASH", "[translations | help | config | motd]", 5)); RegisterCommand(new STATSCommand(this, "STATS", "", 8)); RegisterCommand(new ADDCOMMENTCommand(this, "ADDCOMMENT", " ", 10)); RegisterCommand(new SHUTDOWNCommand(this, "SHUTDOWN", "", 10)); #ifdef ALLOW_HELLO RegisterCommand( new HELLOCommand( this, "HELLO", " <1-3> ", 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 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 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 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 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 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 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 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 > uidVectorType; uidVectorType uidVector; vector 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 " << 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; isecond; 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 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 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= " << 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 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 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 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 > 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, 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; igetID()); 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, 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, 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; igetNickName().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, 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; iExec(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 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::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::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 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::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 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 (bWrite( "%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 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? // LOGIN 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 remList; list::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