From fec1a3fe917e0da055165172175dfc425f0485d9 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Wed, 24 Aug 2011 13:52:34 +0100 Subject: [PATCH] Add CIRCNetwork which now hold each IRC Connection. This allows a user to have multiple networks. A user can login as a network by supplying PASS [user[/network]:]pass or USER user[/network] on connect. A user can also switch between networks by using /msg *status JumpNetwork --- Config.h | 16 +- IRCNetwork.cpp | 796 +++++++++++++++++++++++++++++++++++++++++++++++++ IRCNetwork.h | 153 ++++++++++ Listener.cpp | 4 +- Makefile.in | 4 +- User.cpp | 740 +++++++++++---------------------------------- User.h | 85 ++---- znc.cpp | 65 ++-- znc.h | 3 +- 9 files changed, 1204 insertions(+), 662 deletions(-) create mode 100644 IRCNetwork.cpp create mode 100644 IRCNetwork.h diff --git a/Config.h b/Config.h index 460ed27d..202f7306 100644 --- a/Config.h +++ b/Config.h @@ -67,13 +67,17 @@ public: return true; } - bool FindStringVector(const CString& sName, VCString& vsList) { + bool FindStringVector(const CString& sName, VCString& vsList, bool bErase = true) { EntryMap::iterator it = m_ConfigEntries.find(sName); vsList.clear(); if (it == m_ConfigEntries.end()) return false; vsList = it->second; - m_ConfigEntries.erase(it); + + if (bErase) { + m_ConfigEntries.erase(it); + } + return true; } @@ -109,14 +113,18 @@ public: return false; } - bool FindSubConfig(const CString& sName, SubConfig& Config) { + bool FindSubConfig(const CString& sName, SubConfig& Config, bool bErase = true) { SubConfigMap::iterator it = m_SubConfigs.find(sName); if (it == m_SubConfigs.end()) { Config.clear(); return false; } Config = it->second; - m_SubConfigs.erase(it); + + if (bErase) { + m_SubConfigs.erase(it); + } + return true; } diff --git a/IRCNetwork.cpp b/IRCNetwork.cpp new file mode 100644 index 00000000..d7672c27 --- /dev/null +++ b/IRCNetwork.cpp @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2004-2011 See the AUTHORS file for details. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "IRCNetwork.h" +#include "Modules.h" +#include "User.h" +#include "FileUtils.h" +#include "Config.h" +#include "Client.h" +#include "IRCSock.h" +#include "Server.h" +#include "Chan.h" +#include "znc.h" + +bool CIRCNetwork::IsValidNetwork(const CString& sNetwork) { + // ^[-\w]+$ + + if (sNetwork.empty()) { + return false; + } + + const char *p = sNetwork.c_str(); + while (*p) { + if (*p != '_' && *p != '-' && !isalnum(*p)) { + return false; + } + + p++; + } + + return true; +} + +CIRCNetwork::CIRCNetwork(CUser *pUser, const CString& sName) { + m_pUser = NULL; + SetUser(pUser); + m_sName = sName; + + m_pModules = new CModules; + + m_pIRCSock = NULL; + m_uServerIdx = 0; + + m_sChanPrefixes = ""; + m_bIRCAway = false; + + m_RawBuffer.SetLineCount(100); // This should be more than enough raws, especially since we are buffering the MOTD separately + m_MotdBuffer.SetLineCount(200); // This should be more than enough motd lines + m_QueryBuffer.SetLineCount(250); +} + +CIRCNetwork::CIRCNetwork(CUser *pUser, const CIRCNetwork *pNetwork, bool bCloneChans) { + m_pUser = NULL; + SetUser(pUser); + m_sName = pNetwork->GetName(); + + m_pModules = new CModules; + + m_pIRCSock = NULL; + m_uServerIdx = 0; + + m_sChanPrefixes = ""; + m_bIRCAway = false; + + m_RawBuffer.SetLineCount(100); // This should be more than enough raws, especially since we are buffering the MOTD separately + m_MotdBuffer.SetLineCount(200); // This should be more than enough motd lines + m_QueryBuffer.SetLineCount(250); + + // Servers + const vector& vServers = pNetwork->GetServers(); + CString sServer; + CServer* pCurServ = GetCurrentServer(); + + if (pCurServ) { + sServer = pCurServ->GetName(); + } + + DelServers(); + + unsigned int a; + for (a = 0; a < vServers.size(); a++) { + CServer* pServer = vServers[a]; + AddServer(pServer->GetName(), pServer->GetPort(), pServer->GetPass(), pServer->IsSSL()); + } + + m_uServerIdx = 0; + for (a = 0; a < m_vServers.size(); a++) { + if (sServer.Equals(m_vServers[a]->GetName())) { + m_uServerIdx = a + 1; + break; + } + } + if (m_uServerIdx == 0) { + m_uServerIdx = m_vServers.size(); + CIRCSock* pSock = GetIRCSock(); + + if (pSock) { + PutStatus("Jumping servers because this server is no longer in the list"); + pSock->Quit(); + } + } + // !Servers + + // Chans + const vector& vChans = pNetwork->GetChans(); + for (a = 0; a < vChans.size(); a++) { + CChan* pNewChan = vChans[a]; + CChan* pChan = FindChan(pNewChan->GetName()); + + if (pChan) { + pChan->SetInConfig(pNewChan->InConfig()); + } else { + AddChan(pNewChan->GetName(), pNewChan->InConfig()); + } + } + + for (a = 0; a < m_vChans.size(); a++) { + CChan* pChan = m_vChans[a]; + CChan* pNewChan = pNetwork->FindChan(pChan->GetName()); + + if (!pNewChan) { + pChan->SetInConfig(false); + } else { + if (bCloneChans) + pChan->Clone(*pNewChan); + } + } + // !Chans +} + +CIRCNetwork::~CIRCNetwork() { + DelClients(); + DelServers(); + DelModules(); + + SetUser(NULL); + + for (unsigned int b = 0; b < m_vChans.size(); b++) { + delete m_vChans[b]; + } +} + +CString CIRCNetwork::GetNetworkPath() { + CString sNetworkPath = m_pUser->GetUserPath() + "/networks/" + m_sName; + + if (!CFile::Exists(sNetworkPath)) { + CDir::MakeDir(sNetworkPath); + } + + return sNetworkPath; +} + +void CIRCNetwork::DelClients() { + for (unsigned int c = 0; c < m_vClients.size(); c++) { + CClient* pClient = m_vClients[c]; + CZNC::Get().GetManager().DelSockByAddr(pClient); + } + + m_vClients.clear(); +} + +void CIRCNetwork::DelServers() { + for (unsigned int a = 0; a < m_vServers.size(); a++) { + delete m_vServers[a]; + } + + m_vServers.clear(); +} + +void CIRCNetwork::DelModules() { + delete m_pModules; + m_pModules = NULL; +} + +bool CIRCNetwork::ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade) { + VCString vsList; + VCString::const_iterator vit; + + if (!bUpgrade) { + pConfig->FindStringVector("loadmodule", vsList); + for (vit = vsList.begin(); vit != vsList.end(); ++vit) { + CString sValue = *vit; + CString sModName = sValue.Token(0); + + CUtils::PrintAction("Loading Module [" + sModName + "]"); + CString sModRet; + CString sArgs = sValue.Token(1, true); + + bool bModRet = GetModules().LoadModule(sModName, sArgs, CModInfo::NetworkModule, GetUser(), this, sModRet); + + CUtils::PrintStatus(bModRet, sModRet); + if (!bModRet) { + sError = sModRet; + return false; + } + } + } + + pConfig->FindStringVector("server", vsList); + for (vit = vsList.begin(); vit != vsList.end(); ++vit) { + CUtils::PrintAction("Adding Server [" + *vit + "]"); + CUtils::PrintStatus(AddServer(*vit)); + } + + pConfig->FindStringVector("chan", vsList); + for (vit = vsList.begin(); vit != vsList.end(); ++vit) { + AddChan(*vit, true); + } + + CConfig::SubConfig subConf; + CConfig::SubConfig::const_iterator subIt; + + pConfig->FindSubConfig("chan", subConf); + for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { + const CString& sChanName = subIt->first; + CConfig* pSubConf = subIt->second.m_pSubConfig; + CChan* pChan = new CChan(sChanName, this, true, pSubConf); + + if (!pSubConf->empty()) { + sError = "Unhandled lines in config for User [" + m_pUser->GetUserName() + "], Network [" + GetName() + "], Channel [" + sChanName + "]!"; + CUtils::PrintError(sError); + + CZNC::DumpConfig(pSubConf); + return false; + } + + // Save the channel name, because AddChan + // deletes the CChannel*, if adding fails + sError = pChan->GetName(); + if (!AddChan(pChan)) { + sError = "Channel [" + sError + "] defined more than once"; + CUtils::PrintError(sError); + return false; + } + sError.clear(); + } + + return true; +} + +CConfig CIRCNetwork::ToConfig() { + CConfig config; + + // Modules + CModules& Mods = GetModules(); + + if (!Mods.empty()) { + for (unsigned int a = 0; a < Mods.size(); a++) { + CString sArgs = Mods[a]->GetArgs(); + + if (!sArgs.empty()) { + sArgs = " " + sArgs; + } + + config.AddKeyValuePair("LoadModule", Mods[a]->GetModName() + sArgs); + } + } + + // Servers + for (unsigned int b = 0; b < m_vServers.size(); b++) { + config.AddKeyValuePair("Server", m_vServers[b]->GetString()); + } + + // Chans + for (unsigned int c = 0; c < m_vChans.size(); c++) { + CChan* pChan = m_vChans[c]; + if (pChan->InConfig()) { + config.AddSubConfig("Chan", pChan->GetName(), pChan->ToConfig()); + } + } + + return config; +} + +void CIRCNetwork::BounceAllClients() { + for (unsigned int a = 0; a < m_vClients.size(); a++) { + m_vClients[a]->BouncedOff(); + } + + m_vClients.clear(); +} + +void CIRCNetwork::ClientConnected(CClient *pClient) { + if (!m_pUser->MultiClients()) { + BounceAllClients(); + } + + m_vClients.push_back(pClient); + + if (m_RawBuffer.IsEmpty()) { + pClient->PutClient(":irc.znc.in 001 " + pClient->GetNick() + " :- Welcome to ZNC -"); + } else { + unsigned int uIdx = 0; + CString sLine; + + while (m_RawBuffer.GetLine(GetIRCNick().GetNick(), sLine, uIdx++)) { + pClient->PutClient(sLine); + } + + // The assumption is that the client got this nick from the 001 reply + pClient->SetNick(GetIRCNick().GetNick()); + } + + // Send the cached MOTD + unsigned int uIdx = 0; + CString sLine; + + while (m_MotdBuffer.GetLine(GetIRCNick().GetNick(), sLine, uIdx++)) { + pClient->PutClient(sLine); + } + + if (GetIRCSock() != NULL) { + CString sUserMode(""); + const set& scUserModes = GetIRCSock()->GetUserModes(); + for (set::const_iterator it = scUserModes.begin(); + it != scUserModes.end(); ++it) { + sUserMode += *it; + } + if (!sUserMode.empty()) { + pClient->PutClient(":" + GetIRCNick().GetNickMask() + " MODE " + GetIRCNick().GetNick() + " :+" + sUserMode); + } + } + + if (m_bIRCAway) { + // If they want to know their away reason they'll have to whois + // themselves. At least we can tell them their away status... + pClient->PutClient(":irc.znc.in 306 " + GetIRCNick().GetNick() + " :You have been marked as being away"); + } + + const vector& vChans = GetChans(); + for (unsigned int a = 0; a < vChans.size(); a++) { + if ((vChans[a]->IsOn()) && (!vChans[a]->IsDetached())) { + vChans[a]->JoinUser(true, "", pClient); + } + } + + CString sBufLine; + while (m_QueryBuffer.GetNextLine(GetIRCNick().GetNick(), sBufLine)) { + MODULECALL(OnPrivBufferPlayLine(*pClient, sBufLine), m_pUser, this, NULL, continue); + pClient->PutClient(sBufLine); + } + + // Tell them why they won't connect + if (!m_pUser->GetIRCConnectEnabled()) + pClient->PutStatus("You are currently disconnected from IRC. " + "Use 'connect' to reconnect."); +} + +void CIRCNetwork::ClientDisconnected(CClient *pClient) { + for (unsigned int a = 0; a < m_vClients.size(); a++) { + if (m_vClients[a] == pClient) { + m_vClients.erase(m_vClients.begin() + a); + break; + } + } +} + +CUser* CIRCNetwork::GetUser() { + return m_pUser; +} + +const CString& CIRCNetwork::GetName() const { + return m_sName; +} + +void CIRCNetwork::SetUser(CUser *pUser) { + for (unsigned int a = 0; a < m_vClients.size(); a++) { + m_vClients[a]->PutStatus("This network is being deleted or moved to another user."); + m_vClients[a]->SetNetwork(NULL); + } + + m_vClients.clear(); + + if (m_pUser) { + m_pUser->RemoveNetwork(this); + } + + m_pUser = pUser; + if (m_pUser) { + m_pUser->AddNetwork(this); + } +} + +bool CIRCNetwork::SetName(const CString& sName) { + if (IsValidNetwork(sName)) { + m_sName = sName; + return true; + } + + return false; +} + +bool CIRCNetwork::PutUser(const CString& sLine, CClient* pClient, CClient* pSkipClient) { + for (unsigned int a = 0; a < m_vClients.size(); a++) { + if ((!pClient || pClient == m_vClients[a]) && pSkipClient != m_vClients[a]) { + m_vClients[a]->PutClient(sLine); + + if (pClient) { + return true; + } + } + } + + return (pClient == NULL); +} + +bool CIRCNetwork::PutStatus(const CString& sLine, CClient* pClient, CClient* pSkipClient) { + for (unsigned int a = 0; a < m_vClients.size(); a++) { + if ((!pClient || pClient == m_vClients[a]) && pSkipClient != m_vClients[a]) { + m_vClients[a]->PutStatus(sLine); + + if (pClient) { + return true; + } + } + } + + return (pClient == NULL); +} + +// Channels + +const vector& CIRCNetwork::GetChans() const { return m_vChans; } + +CChan* CIRCNetwork::FindChan(const CString& sName) const { + for (unsigned int a = 0; a < m_vChans.size(); a++) { + CChan* pChan = m_vChans[a]; + if (sName.Equals(pChan->GetName())) { + return pChan; + } + } + + return NULL; +} + +bool CIRCNetwork::AddChan(CChan* pChan) { + if (!pChan) { + return false; + } + + for (unsigned int a = 0; a < m_vChans.size(); a++) { + if (m_vChans[a]->GetName().Equals(pChan->GetName())) { + delete pChan; + return false; + } + } + + m_vChans.push_back(pChan); + return true; +} + +bool CIRCNetwork::AddChan(const CString& sName, bool bInConfig) { + if (sName.empty() || FindChan(sName)) { + return false; + } + + CChan* pChan = new CChan(sName, this, bInConfig); + m_vChans.push_back(pChan); + return true; +} + +bool CIRCNetwork::DelChan(const CString& sName) { + for (vector::iterator a = m_vChans.begin(); a != m_vChans.end(); ++a) { + if (sName.Equals((*a)->GetName())) { + delete *a; + m_vChans.erase(a); + return true; + } + } + + return false; +} + +void CIRCNetwork::JoinChans() { + // Avoid divsion by zero, it's bad! + if (m_vChans.empty()) + return; + + // We start at a random offset into the channel list so that if your + // first 3 channels are invite-only and you got MaxJoins == 3, ZNC will + // still be able to join the rest of your channels. + unsigned int start = rand() % m_vChans.size(); + unsigned int uJoins = m_pUser->MaxJoins(); + set sChans; + for (unsigned int a = 0; a < m_vChans.size(); a++) { + unsigned int idx = (start + a) % m_vChans.size(); + CChan* pChan = m_vChans[idx]; + if (!pChan->IsOn() && !pChan->IsDisabled()) { + if (!JoinChan(pChan)) + continue; + + sChans.insert(pChan); + + // Limit the number of joins + if (uJoins != 0 && --uJoins == 0) + break; + } + } + + while (!sChans.empty()) + JoinChans(sChans); +} + +void CIRCNetwork::JoinChans(set& sChans) { + CString sKeys, sJoin; + bool bHaveKey = false; + size_t uiJoinLength = strlen("JOIN "); + + while (!sChans.empty()) { + set::iterator it = sChans.begin(); + const CString& sName = (*it)->GetName(); + const CString& sKey = (*it)->GetKey(); + size_t len = sName.length() + sKey.length(); + len += 2; // two comma + + if (!sKeys.empty() && uiJoinLength + len >= 512) + break; + + if (!sJoin.empty()) { + sJoin += ","; + sKeys += ","; + } + uiJoinLength += len; + sJoin += sName; + if (!sKey.empty()) { + sKeys += sKey; + bHaveKey = true; + } + sChans.erase(it); + } + + if (bHaveKey) + PutIRC("JOIN " + sJoin + " " + sKeys); + else + PutIRC("JOIN " + sJoin); +} + +bool CIRCNetwork::JoinChan(CChan* pChan) { + if (m_pUser->JoinTries() != 0 && pChan->GetJoinTries() >= m_pUser->JoinTries()) { + PutStatus("The channel " + pChan->GetName() + " could not be joined, disabling it."); + pChan->Disable(); + } else { + pChan->IncJoinTries(); + MODULECALL(OnTimerAutoJoin(*pChan), m_pUser, this, NULL, return false); + return true; + } + return false; +} + +bool CIRCNetwork::IsChan(const CString& sChan) const { + if (sChan.empty()) + return false; // There is no way this is a chan + if (GetChanPrefixes().empty()) + return true; // We can't know, so we allow everything + // Thanks to the above if (empty), we can do sChan[0] + return GetChanPrefixes().find(sChan[0]) != CString::npos; +} + +// Server list + +const vector& CIRCNetwork::GetServers() const { return m_vServers; } + +CServer* CIRCNetwork::FindServer(const CString& sName) const { + for (unsigned int a = 0; a < m_vServers.size(); a++) { + CServer* pServer = m_vServers[a]; + if (sName.Equals(pServer->GetName())) { + return pServer; + } + } + + return NULL; +} + +bool CIRCNetwork::DelServer(const CString& sName, unsigned short uPort, const CString& sPass) { + if (sName.empty()) { + return false; + } + + unsigned int a = 0; + bool bSawCurrentServer = false; + CServer* pCurServer = GetCurrentServer(); + + for (vector::iterator it = m_vServers.begin(); it != m_vServers.end(); it++, a++) { + CServer* pServer = *it; + + if (pServer == pCurServer) + bSawCurrentServer = true; + + if (!pServer->GetName().Equals(sName)) + continue; + + if (uPort != 0 && pServer->GetPort() != uPort) + continue; + + if (!sPass.empty() && pServer->GetPass() != sPass) + continue; + + m_vServers.erase(it); + + if (pServer == pCurServer) { + CIRCSock* pIRCSock = GetIRCSock(); + + // Make sure we don't skip the next server in the list! + if (m_uServerIdx) { + m_uServerIdx--; + } + + if (pIRCSock) { + pIRCSock->Quit(); + PutStatus("Your current server was removed, jumping..."); + } + } else if (!bSawCurrentServer) { + // Our current server comes after the server which we + // are removing. This means that it now got a different + // index in m_vServers! + m_uServerIdx--; + } + + delete pServer; + + return true; + } + + return false; +} + +bool CIRCNetwork::AddServer(const CString& sName) { + if (sName.empty()) { + return false; + } + + bool bSSL = false; + CString sLine = sName; + sLine.Trim(); + + CString sHost = sLine.Token(0); + CString sPort = sLine.Token(1); + + if (sPort.Left(1) == "+") { + bSSL = true; + sPort.LeftChomp(); + } + + unsigned short uPort = sPort.ToUShort(); + CString sPass = sLine.Token(2, true); + + return AddServer(sHost, uPort, sPass, bSSL); +} + +bool CIRCNetwork::AddServer(const CString& sName, unsigned short uPort, const CString& sPass, bool bSSL) { +#ifndef HAVE_LIBSSL + if (bSSL) { + return false; + } +#endif + + if (sName.empty()) { + return false; + } + + if (!uPort) { + uPort = 6667; + } + + // Check if server is already added + for (unsigned int a = 0; a < m_vServers.size(); a++) { + CServer* pServer = m_vServers[a]; + + if (!sName.Equals(pServer->GetName())) + continue; + + if (uPort != pServer->GetPort()) + continue; + + if (sPass != pServer->GetPass()) + continue; + + if (bSSL != pServer->IsSSL()) + continue; + + // Server is already added + return false; + } + + CServer* pServer = new CServer(sName, uPort, sPass, bSSL); + m_vServers.push_back(pServer); + + CheckIRCConnect(); + + return true; +} + +CServer* CIRCNetwork::GetNextServer() { + if (m_vServers.empty()) { + return NULL; + } + + if (m_uServerIdx >= m_vServers.size()) { + m_uServerIdx = 0; + } + + return m_vServers[m_uServerIdx++]; +} + +CServer* CIRCNetwork::GetCurrentServer() const { + unsigned int uIdx = (m_uServerIdx) ? m_uServerIdx -1 : 0; + + if (uIdx >= m_vServers.size()) { + return NULL; + } + + return m_vServers[uIdx]; +} + +void CIRCNetwork::SetIRCServer(const CString& s) { m_sIRCServer = s; } + +bool CIRCNetwork::SetNextServer(const CServer* pServer) { + for (unsigned int a = 0; a < m_vServers.size(); a++) { + if (m_vServers[a] == pServer) { + m_uServerIdx = a; + return true; + } + } + + return false; +} + +bool CIRCNetwork::IsLastServer() const { + return (m_uServerIdx >= m_vServers.size()); +} + +const CString& CIRCNetwork::GetIRCServer() const { return m_sIRCServer; } +const CNick& CIRCNetwork::GetIRCNick() const { return m_IRCNick; } + +void CIRCNetwork::SetIRCNick(const CNick& n) { + m_IRCNick = n; + + for (unsigned int a = 0; a < m_vClients.size(); a++) { + m_vClients[a]->SetNick(n.GetNick()); + } +} + +CString CIRCNetwork::GetCurNick() const { + const CIRCSock* pIRCSock = GetIRCSock(); + + if (pIRCSock) { + return pIRCSock->GetNick(); + } + + if (!m_vClients.empty()) { + return m_vClients[0]->GetNick(); + } + + return ""; +} + +bool CIRCNetwork::IsIRCConnected() const { + const CIRCSock* pSock = GetIRCSock(); + return (pSock && pSock->IsAuthed()); +} + +void CIRCNetwork::SetIRCSocket(CIRCSock* pIRCSock) { + m_pIRCSock = pIRCSock; +} + +void CIRCNetwork::IRCDisconnected() { + m_pIRCSock = NULL; + + SetIRCServer(""); + m_bIRCAway = false; + + // Get the reconnect going + CheckIRCConnect(); +} + +void CIRCNetwork::CheckIRCConnect() { + // Do we want to connect? + if (m_pUser->GetIRCConnectEnabled() && GetIRCSock() == NULL) + CZNC::Get().EnableConnectUser(); +} + +bool CIRCNetwork::PutIRC(const CString& sLine) { + CIRCSock* pIRCSock = GetIRCSock(); + + if (!pIRCSock) { + return false; + } + + pIRCSock->PutIRC(sLine); + return true; +} diff --git a/IRCNetwork.h b/IRCNetwork.h new file mode 100644 index 00000000..fa569bb7 --- /dev/null +++ b/IRCNetwork.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2004-2011 See the AUTHORS file for details. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _IRCNETWORK_H +#define _IRCNETWORK_H + +#include "zncconfig.h" +#include "ZNCString.h" +#include "Buffer.h" +#include "Nick.h" +#include "znc.h" + +class CModules; +class CUser; +class CFile; +class CConfig; +class CClient; +class CConfig; +class CChan; +class CServer; +class CIRCSock; + +class CIRCNetwork { +public: + static bool IsValidNetwork(const CString& sNetwork); + + CIRCNetwork(CUser *pUser, const CString& sName); + CIRCNetwork(CUser *pUser, const CIRCNetwork *pNetwork, bool bCloneChans = true); + ~CIRCNetwork(); + + CString GetNetworkPath(); + + void DelClients(); + void DelServers(); + + bool ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade = false); + CConfig ToConfig(); + + void BounceAllClients(); + + bool IsUserAttached() const { return !m_vClients.empty(); } + void ClientConnected(CClient *pClient); + void ClientDisconnected(CClient *pClient); + + CUser* GetUser(); + const CString& GetName() const; + bool IsNetworkAttached() const { return !m_vClients.empty(); }; + vector& GetClients() { return m_vClients; } + + void SetUser(CUser *pUser); + bool SetName(const CString& sName); + + // Modules + void DelModules(); + CModules& GetModules() { return *m_pModules; } + const CModules& GetModules() const { return *m_pModules; } + // !Modules + + bool PutUser(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); + bool PutStatus(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); + + const vector& GetChans() const; + CChan* FindChan(const CString& sName) const; + bool AddChan(CChan* pChan); + bool AddChan(const CString& sName, bool bInConfig); + bool DelChan(const CString& sName); + void JoinChans(); + + const CString& GetChanPrefixes() const { return m_sChanPrefixes; }; + void SetChanPrefixes(const CString& s) { m_sChanPrefixes = s; }; + bool IsChan(const CString& sChan) const; + + const vector& GetServers() const; + bool HasServers() const { return !m_vServers.empty(); } + CServer* FindServer(const CString& sName) const; + bool DelServer(const CString& sName, unsigned short uPort, const CString& sPass); + bool AddServer(const CString& sName); + bool AddServer(const CString& sName, unsigned short uPort, const CString& sPass = "", bool bSSL = false); + CServer* GetNextServer(); + CServer* GetCurrentServer() const; + void SetIRCServer(const CString& s); + bool SetNextServer(const CServer* pServer); + bool IsLastServer() const; + + CIRCSock* GetIRCSock() { return m_pIRCSock; } + const CIRCSock* GetIRCSock() const { return m_pIRCSock; } + const CString& GetIRCServer() const; + const CNick& GetIRCNick() const; + void SetIRCNick(const CNick& n); + CString GetCurNick() const; + bool IsIRCAway() const { return m_bIRCAway; } + void SetIRCAway(bool b) { m_bIRCAway = b; } + + /** This method will return whether the user is connected and authenticated to an IRC server. + */ + bool IsIRCConnected() const; + void SetIRCSocket(CIRCSock* pIRCSock); + void IRCDisconnected(); + void CheckIRCConnect(); + + bool PutIRC(const CString& sLine); + + // Buffers + void AddRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.AddLine(sPre, sPost, bIncNick); } + void UpdateRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.UpdateLine(sPre, sPost, bIncNick); } + void UpdateExactRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.UpdateExactLine(sPre, sPost, bIncNick); } + void ClearRawBuffer() { m_RawBuffer.Clear(); } + + void AddMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.AddLine(sPre, sPost, bIncNick); } + void UpdateMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.UpdateLine(sPre, sPost, bIncNick); } + void ClearMotdBuffer() { m_MotdBuffer.Clear(); } + + void AddQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.AddLine(sPre, sPost, bIncNick); } + void UpdateQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.UpdateLine(sPre, sPost, bIncNick); } + void ClearQueryBuffer() { m_QueryBuffer.Clear(); } + // !Buffers + +private: + bool JoinChan(CChan* pChan); + void JoinChans(set& sChans); + +protected: + CString m_sName; + CUser* m_pUser; + + CModules* m_pModules; + + vector m_vClients; + + CIRCSock* m_pIRCSock; + + vector m_vChans; + + CString m_sChanPrefixes; + + CString m_sIRCServer; + vector m_vServers; + unsigned int m_uServerIdx; ///< Index in m_vServers of our current server + 1 + + CNick m_IRCNick; + bool m_bIRCAway; + + CBuffer m_RawBuffer; + CBuffer m_MotdBuffer; + CBuffer m_QueryBuffer; +}; + +#endif // !_IRCNETWORK_H diff --git a/Listener.cpp b/Listener.cpp index 5c77c6d5..4e0dccef 100644 --- a/Listener.cpp +++ b/Listener.cpp @@ -55,11 +55,11 @@ bool CRealListener::ConnectionFrom(const CString& sHost, unsigned short uPort) { Csock* CRealListener::GetSockObj(const CString& sHost, unsigned short uPort) { CIncomingConnection *pClient = new CIncomingConnection(sHost, uPort, m_pParent->GetAcceptType()); if (CZNC::Get().AllowConnectionFrom(sHost)) { - GLOBALMODULECALL(OnClientConnect(pClient, sHost, uPort), NULL, NULL, NOTHING); + GLOBALMODULECALL(OnClientConnect(pClient, sHost, uPort), NULL, NULL, NULL, NOTHING); } else { pClient->Write(":irc.znc.in 464 unknown-nick :Too many anonymous connections from your IP\r\n"); pClient->Close(Csock::CLT_AFTERWRITE); - GLOBALMODULECALL(OnFailedLogin("", sHost), NULL, NULL, NOTHING); + GLOBALMODULECALL(OnFailedLogin("", sHost), NULL, NULL, NULL, NOTHING); } return pClient; } diff --git a/Makefile.in b/Makefile.in index 68ef4a7b..b4001efe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -28,8 +28,8 @@ INSTALL_PROGRAM := @INSTALL_PROGRAM@ INSTALL_SCRIPT := @INSTALL_SCRIPT@ INSTALL_DATA := @INSTALL_DATA@ -LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp User.cpp IRCSock.cpp Client.cpp \ - Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \ +LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp IRCNetwork.cpp User.cpp IRCSock.cpp \ + Client.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \ FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp \ WebModules.cpp Listener.cpp Config.cpp ZNCDebug.cpp BIN_SRCS := main.cpp diff --git a/User.cpp b/User.cpp index fbedc21b..cf5ce2e4 100644 --- a/User.cpp +++ b/User.cpp @@ -10,6 +10,7 @@ #include "Chan.h" #include "Config.h" #include "FileUtils.h" +#include "IRCNetwork.h" #include "IRCSock.h" #include "Server.h" #include "znc.h" @@ -26,23 +27,28 @@ public: private: protected: virtual void RunJob() { - vector& vClients = m_pUser->GetClients(); - CIRCSock* pIRCSock = m_pUser->GetIRCSock(); + vector vNetworks = m_pUser->GetNetworks(); - if (pIRCSock && pIRCSock->GetTimeSinceLastDataTransaction() >= 270) { - pIRCSock->PutIRC("PING :ZNC"); - } + for (size_t a = 0; a < vNetworks.size(); a++) { + CIRCNetwork* pNetwork = vNetworks[a]; + CIRCSock* pIRCSock = pNetwork->GetIRCSock(); - for (size_t a = 0; a < vClients.size(); a++) { - CClient* pClient = vClients[a]; - - if (pClient->GetTimeSinceLastDataTransaction() >= 270) { - pClient->PutClient("PING :ZNC"); + if (pIRCSock && pIRCSock->GetTimeSinceLastDataTransaction() >= 270) { + pIRCSock->PutIRC("PING :ZNC"); } - } - if (m_pUser->IsIRCConnected()) { - m_pUser->JoinChans(); + if (pNetwork->IsIRCConnected()) { + pNetwork->JoinChans(); + } + + vector& vClients = pNetwork->GetClients(); + for (size_t b = 0; b < vClients.size(); b++) { + CClient* pClient = vClients[b]; + + if (pClient->GetTimeSinceLastDataTransaction() >= 270) { + pClient->PutClient("PING :ZNC"); + } + } } } @@ -55,26 +61,19 @@ CUser::CUser(const CString& sUserName) // set path that depends on the user name: m_sUserPath = CZNC::Get().GetUserPath() + "/" + m_sUserName; - m_pIRCSock = NULL; m_fTimezoneOffset = 0; m_sNick = m_sCleanUserName; m_sIdent = m_sCleanUserName; m_sRealName = sUserName; - m_uServerIdx = 0; m_uBytesRead = 0; m_uBytesWritten = 0; m_pModules = new CModules; - m_RawBuffer.SetLineCount(100); // This should be more than enough raws, especially since we are buffering the MOTD separately - m_MotdBuffer.SetLineCount(200); // This should be more than enough motd lines - m_QueryBuffer.SetLineCount(250); m_bMultiClients = true; m_eHashType = HASH_NONE; m_bDenyLoadMod = false; m_bAdmin= false; - m_bIRCAway = false; m_bDenySetBindHost= false; m_sStatusPrefix = "*"; - m_sChanPrefixes = ""; m_uBufferCount = 50; m_uMaxJoinTries = 10; m_uMaxJoins = 5; @@ -89,16 +88,12 @@ CUser::CUser(const CString& sUserName) } CUser::~CUser() { + DelNetworks(); + DelClients(); DelModules(); - DelServers(); - - for (unsigned int b = 0; b < m_vChans.size(); b++) { - delete m_vChans[b]; - } - CZNC::Get().GetManager().DelCronByAddr(m_pUserTimer); } @@ -169,15 +164,6 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { const CString& sValue = *vit; AddCTCPReply(sValue.Token(0), sValue.Token(1, true)); } - pConfig->FindStringVector("server", vsList); - for (vit = vsList.begin(); vit != vsList.end(); ++vit) { - CUtils::PrintAction("Adding Server [" + *vit + "]"); - CUtils::PrintStatus(AddServer(*vit)); - } - pConfig->FindStringVector("chan", vsList); - for (vit = vsList.begin(); vit != vsList.end(); ++vit) { - AddChan(*vit, true); - } CString sValue; @@ -187,7 +173,7 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { if (sValue.ToBool()) { CUtils::PrintAction("Loading Module [bouncedcc]"); CString sModRet; - bool bModRet = GetModules().LoadModule("bouncedcc", "", CModInfo::UserModule, this, sModRet); + bool bModRet = GetModules().LoadModule("bouncedcc", "", CModInfo::UserModule, this, NULL, sModRet); CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { @@ -315,29 +301,34 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { return false; } - pConfig->FindSubConfig("chan", subConf); + pConfig->FindSubConfig("network", subConf); for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { - const CString& sChanName = subIt->first; - CConfig* pSubConf = subIt->second.m_pSubConfig; - CChan* pChan = new CChan(sChanName, this, true, pSubConf); + const CString& sNetworkName = subIt->first; - if (!pSubConf->empty()) { - sError = "Unhandled lines in config for User [" + GetUserName() + "], Channel [" + sChanName + "]!"; - CUtils::PrintError(sError); + CIRCNetwork *pNetwork = FindNetwork(sNetworkName); - CZNC::DumpConfig(pSubConf); - return false; + if (!pNetwork) { + pNetwork = new CIRCNetwork(this, sNetworkName); } - // Save the channel name, because AddChan - // deletes the CChannel*, if adding fails - sError = pChan->GetName(); - if (!AddChan(pChan)) { - sError = "Channel [" + sError + "] defined more than once"; - CUtils::PrintError(sError); + if (!pNetwork->ParseConfig(subIt->second.m_pSubConfig, sError)) { return false; } - sError.clear(); + } + + if (pConfig->FindStringVector("server", vsList, false) || pConfig->FindStringVector("chan", vsList, false) || pConfig->FindSubConfig("chan", subConf, false)) { + CIRCNetwork *pNetwork = FindNetwork("user"); + if (!pNetwork) { + pNetwork = AddNetwork("user"); + } + + if (pNetwork) { + CUtils::PrintMessage("NOTICE: Found deprecated config, upgrading to a network named [user]"); + + if (!pNetwork->ParseConfig(pConfig, sError, true)) { + return false; + } + } } pConfig->FindStringVector("loadmodule", vsList); @@ -360,8 +351,38 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { CUtils::PrintAction("Loading Module [" + sModName + "]"); CString sModRet; CString sArgs = sValue.Token(1, true); + bool bModRet; - bool bModRet = GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, this, sModRet); + CModInfo ModInfo; + if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sModName, sModRet)) { + sError = "Unable to find modinfo [" + sModName + "] [" + sModRet + "]"; + return false; + } + + if (!ModInfo.SupportsType(CModInfo::UserModule) && ModInfo.SupportsType(CModInfo::NetworkModule)) { + CUtils::PrintMessage("NOTICE: Module [" + sModName + "] is a network module, loading module for all networks in user."); + + // Do they have old NV? + CFile fNVFile = CFile(GetUserPath() + "/moddata/" + sModName + "/.registry"); + + for (vector::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { + if (fNVFile.Exists()) { + CString sNetworkModPath = (*it)->GetNetworkPath() + "/moddata/" + sModName; + if (!CFile::Exists(sNetworkModPath)) { + CDir::MakeDir(sNetworkModPath); + } + + fNVFile.Copy(sNetworkModPath + "/.registry"); + } + + bModRet = (*it)->GetModules().LoadModule(sModName, sArgs, CModInfo::NetworkModule, this, *it, sModRet); + if (!bModRet) { + break; + } + } + } else { + bModRet = GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, this, NULL, sModRet); + } CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { @@ -396,7 +417,7 @@ bool CUser::UpdateModule(const CString &sModule) { CString sErr; for (it2 = Affected.begin(); it2 != Affected.end(); ++it2) { - if (!it2->first->GetModules().LoadModule(sModule, it2->second, CModInfo::UserModule, it2->first, sErr)) { + if (!it2->first->GetModules().LoadModule(sModule, it2->second, CModInfo::UserModule, it2->first, NULL, sErr)) { error = true; DEBUG("Failed to reload [" << sModule << "] for [" << it2->first->GetUserName() << "]: " << sErr); @@ -406,6 +427,15 @@ bool CUser::UpdateModule(const CString &sModule) { return !error; } +void CUser::DelNetworks() { + for (unsigned int c = 0; c < m_vIRCNetworks.size(); c++) { + CIRCNetwork* pNetwork = m_vIRCNetworks[c]; + delete pNetwork; + } + + m_vIRCNetworks.clear(); +} + void CUser::DelClients() { for (unsigned int c = 0; c < m_vClients.size(); c++) { CClient* pClient = m_vClients[c]; @@ -415,33 +445,57 @@ void CUser::DelClients() { m_vClients.clear(); } -void CUser::DelServers() -{ - for (unsigned int a = 0; a < m_vServers.size(); a++) { - delete m_vServers[a]; +CIRCNetwork* CUser::AddNetwork(const CString &sNetwork) { + if (!CIRCNetwork::IsValidNetwork(sNetwork) || FindNetwork(sNetwork)) { + return NULL; } - m_vServers.clear(); + return new CIRCNetwork(this, sNetwork); } -void CUser::SetIRCSocket(CIRCSock* pIRCSock) { - m_pIRCSock = pIRCSock; +bool CUser::AddNetwork(CIRCNetwork *pNetwork) { + if (FindNetwork(pNetwork->GetName())) { + return false; + } + + m_vIRCNetworks.push_back(pNetwork); + + return true; } -bool CUser::IsIRCConnected() const -{ - const CIRCSock* pSock = GetIRCSock(); - return (pSock && pSock->IsAuthed()); +void CUser::RemoveNetwork(CIRCNetwork *pNetwork) { + for (vector::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { + if (pNetwork == *it) { + m_vIRCNetworks.erase(it); + return; + } + } } -void CUser::IRCDisconnected() { - m_pIRCSock = NULL; +bool CUser::DeleteNetwork(CString sNetwork) { + CIRCNetwork *pNetwork = FindNetwork(sNetwork); - SetIRCServer(""); - m_bIRCAway = false; + if (pNetwork) { + delete pNetwork; + return true; + } - // Get the reconnect going - CheckIRCConnect(); + return false; +} + +CIRCNetwork* CUser::FindNetwork(const CString& sNetwork) { + for (vector::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { + CIRCNetwork *pNetwork = *it; + if (pNetwork->GetName().Equals(sNetwork)) { + return pNetwork; + } + } + + return NULL; +} + +const vector& CUser::GetNetworks() const { + return m_vIRCNetworks; } CString CUser::ExpandString(const CString& sStr) const { @@ -464,7 +518,7 @@ CString& CUser::ExpandString(const CString& sStr, CString& sRet) const { sRet = sStr; sRet.Replace("%user%", GetUserName()); sRet.Replace("%defnick%", GetNick()); - sRet.Replace("%nick%", GetCurNick()); + sRet.Replace("%nick%", GetNick()); sRet.Replace("%altnick%", GetAltNick()); sRet.Replace("%ident%", GetIdent()); sRet.Replace("%realname%", GetRealName()); @@ -526,65 +580,9 @@ void CUser::UserConnected(CClient* pClient) { BounceAllClients(); } + pClient->PutClient(":irc.znc.in 001 " + pClient->GetNick() + " :- Welcome to ZNC -"); + m_vClients.push_back(pClient); - - if (m_RawBuffer.IsEmpty()) { - pClient->PutClient(":irc.znc.in 001 " + pClient->GetNick() + " :- Welcome to ZNC -"); - } else { - unsigned int uIdx = 0; - CString sLine; - - while (m_RawBuffer.GetLine(GetIRCNick().GetNick(), sLine, uIdx++)) { - pClient->PutClient(sLine); - } - - // The assumption is that the client got this nick from the 001 reply - pClient->SetNick(GetIRCNick().GetNick()); - } - - // Send the cached MOTD - unsigned int uIdx = 0; - CString sLine; - - while (m_MotdBuffer.GetLine(GetIRCNick().GetNick(), sLine, uIdx++)) { - pClient->PutClient(sLine); - } - - if (GetIRCSock() != NULL) { - CString sUserMode(""); - const set& scUserModes = GetIRCSock()->GetUserModes(); - for (set::const_iterator it = scUserModes.begin(); - it != scUserModes.end(); ++it) { - sUserMode += *it; - } - if (!sUserMode.empty()) { - pClient->PutClient(":" + GetIRCNick().GetNickMask() + " MODE " + GetIRCNick().GetNick() + " :+" + sUserMode); - } - } - - if (m_bIRCAway) { - // If they want to know their away reason they'll have to whois - // themselves. At least we can tell them their away status... - pClient->PutClient(":irc.znc.in 306 " + GetIRCNick().GetNick() + " :You have been marked as being away"); - } - - const vector& vChans = GetChans(); - for (unsigned int a = 0; a < vChans.size(); a++) { - if ((vChans[a]->IsOn()) && (!vChans[a]->IsDetached())) { - vChans[a]->JoinUser(true, "", pClient); - } - } - - CString sBufLine; - while (m_QueryBuffer.GetNextLine(GetIRCNick().GetNick(), sBufLine)) { - MODULECALL(OnPrivBufferPlayLine(*pClient, sBufLine), this, NULL, continue); - pClient->PutClient(sBufLine); - } - - // Tell them why they won't connect - if (!GetIRCConnectEnabled()) - pClient->PutStatus("You are currently disconnected from IRC. " - "Use 'connect' to reconnect."); } void CUser::UserDisconnected(CClient* pClient) { @@ -647,65 +645,12 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneChans) { // !Allowed Hosts - // Servers - const vector& vServers = User.GetServers(); - CString sServer; - CServer* pCurServ = GetCurrentServer(); - - if (pCurServ) { - sServer = pCurServ->GetName(); + // Networks + const vector& vNetworks = User.GetNetworks(); + for (a = 0; a < vNetworks.size(); a++) { + new CIRCNetwork(this, vNetworks[a], bCloneChans); } - - DelServers(); - - for (a = 0; a < vServers.size(); a++) { - CServer* pServer = vServers[a]; - AddServer(pServer->GetName(), pServer->GetPort(), pServer->GetPass(), pServer->IsSSL()); - } - - m_uServerIdx = 0; - for (a = 0; a < m_vServers.size(); a++) { - if (sServer.Equals(m_vServers[a]->GetName())) { - m_uServerIdx = a + 1; - break; - } - } - if (m_uServerIdx == 0) { - m_uServerIdx = m_vServers.size(); - CIRCSock* pSock = GetIRCSock(); - - if (pSock) { - PutStatus("Jumping servers because this server is no longer in the list"); - pSock->Quit(); - } - } - // !Servers - - // Chans - const vector& vChans = User.GetChans(); - for (a = 0; a < vChans.size(); a++) { - CChan* pNewChan = vChans[a]; - CChan* pChan = FindChan(pNewChan->GetName()); - - if (pChan) { - pChan->SetInConfig(pNewChan->InConfig()); - } else { - AddChan(pNewChan->GetName(), pNewChan->InConfig()); - } - } - - for (a = 0; a < m_vChans.size(); a++) { - CChan* pChan = m_vChans[a]; - CChan* pNewChan = User.FindChan(pChan->GetName()); - - if (!pNewChan) { - pChan->SetInConfig(false); - } else { - if (bCloneChans) - pChan->Clone(*pNewChan); - } - } - // !Chans + // !Networks // CTCP Replies m_mssCTCPReplies.clear(); @@ -739,9 +684,9 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneChans) { CModule* pCurMod = vCurMods.FindModule(pNewMod->GetModName()); if (!pCurMod) { - vCurMods.LoadModule(pNewMod->GetModName(), pNewMod->GetArgs(), CModInfo::UserModule, this, sModRet); + vCurMods.LoadModule(pNewMod->GetModName(), pNewMod->GetArgs(), CModInfo::UserModule, this, NULL, sModRet); } else if (pNewMod->GetArgs() != pCurMod->GetArgs()) { - vCurMods.ReloadModule(pNewMod->GetModName(), pNewMod->GetArgs(), this, sModRet); + vCurMods.ReloadModule(pNewMod->GetModName(), pNewMod->GetArgs(), this, NULL, sModRet); } } @@ -834,44 +779,6 @@ bool CUser::IsValid(CString& sErrMsg, bool bSkipPass) const { return true; } -bool CUser::AddChan(CChan* pChan) { - if (!pChan) { - return false; - } - - for (unsigned int a = 0; a < m_vChans.size(); a++) { - if (m_vChans[a]->GetName().Equals(pChan->GetName())) { - delete pChan; - return false; - } - } - - m_vChans.push_back(pChan); - return true; -} - -bool CUser::AddChan(const CString& sName, bool bInConfig) { - if (sName.empty() || FindChan(sName)) { - return false; - } - - CChan* pChan = new CChan(sName, this, bInConfig); - m_vChans.push_back(pChan); - return true; -} - -bool CUser::DelChan(const CString& sName) { - for (vector::iterator a = m_vChans.begin(); a != m_vChans.end(); ++a) { - if (sName.Equals((*a)->GetName())) { - delete *a; - m_vChans.erase(a); - return true; - } - } - - return false; -} - CConfig CUser::ToConfig() { CConfig config; CConfig passConfig; @@ -947,276 +854,15 @@ CConfig CUser::ToConfig() { } } - // Servers - for (unsigned int b = 0; b < m_vServers.size(); b++) { - config.AddKeyValuePair("Server", m_vServers[b]->GetString()); - } - - // Chans - for (unsigned int c = 0; c < m_vChans.size(); c++) { - CChan* pChan = m_vChans[c]; - if (pChan->InConfig()) { - config.AddSubConfig("Chan", pChan->GetName(), pChan->ToConfig()); - } + // Networks + for (unsigned int d = 0; d < m_vIRCNetworks.size(); d++) { + CIRCNetwork *pNetwork = m_vIRCNetworks[d]; + config.AddSubConfig("Network", pNetwork->GetName(), pNetwork->ToConfig()); } return config; } -CChan* CUser::FindChan(const CString& sName) const { - for (unsigned int a = 0; a < m_vChans.size(); a++) { - CChan* pChan = m_vChans[a]; - if (sName.Equals(pChan->GetName())) { - return pChan; - } - } - - return NULL; -} - -void CUser::JoinChans() { - // Avoid divsion by zero, it's bad! - if (m_vChans.empty()) - return; - - // We start at a random offset into the channel list so that if your - // first 3 channels are invite-only and you got MaxJoins == 3, ZNC will - // still be able to join the rest of your channels. - unsigned int start = rand() % m_vChans.size(); - unsigned int uJoins = m_uMaxJoins; - set sChans; - for (unsigned int a = 0; a < m_vChans.size(); a++) { - unsigned int idx = (start + a) % m_vChans.size(); - CChan* pChan = m_vChans[idx]; - if (!pChan->IsOn() && !pChan->IsDisabled()) { - if (!JoinChan(pChan)) - continue; - - sChans.insert(pChan); - - // Limit the number of joins - if (uJoins != 0 && --uJoins == 0) - break; - } - } - - while (!sChans.empty()) - JoinChans(sChans); -} - -void CUser::JoinChans(set& sChans) { - CString sKeys, sJoin; - bool bHaveKey = false; - size_t uiJoinLength = strlen("JOIN "); - - while (!sChans.empty()) { - set::iterator it = sChans.begin(); - const CString& sName = (*it)->GetName(); - const CString& sKey = (*it)->GetKey(); - size_t len = sName.length() + sKey.length(); - len += 2; // two comma - - if (!sKeys.empty() && uiJoinLength + len >= 512) - break; - - if (!sJoin.empty()) { - sJoin += ","; - sKeys += ","; - } - uiJoinLength += len; - sJoin += sName; - if (!sKey.empty()) { - sKeys += sKey; - bHaveKey = true; - } - sChans.erase(it); - } - - if (bHaveKey) - PutIRC("JOIN " + sJoin + " " + sKeys); - else - PutIRC("JOIN " + sJoin); -} - -bool CUser::JoinChan(CChan* pChan) { - if (JoinTries() != 0 && pChan->GetJoinTries() >= JoinTries()) { - PutStatus("The channel " + pChan->GetName() + " could not be joined, disabling it."); - pChan->Disable(); - } else { - pChan->IncJoinTries(); - MODULECALL(OnTimerAutoJoin(*pChan), this, NULL, return false); - return true; - } - return false; -} - -CServer* CUser::FindServer(const CString& sName) const { - for (unsigned int a = 0; a < m_vServers.size(); a++) { - CServer* pServer = m_vServers[a]; - if (sName.Equals(pServer->GetName())) { - return pServer; - } - } - - return NULL; -} - -bool CUser::DelServer(const CString& sName, unsigned short uPort, const CString& sPass) { - if (sName.empty()) { - return false; - } - - unsigned int a = 0; - bool bSawCurrentServer = false; - CServer* pCurServer = GetCurrentServer(); - - for (vector::iterator it = m_vServers.begin(); it != m_vServers.end(); it++, a++) { - CServer* pServer = *it; - - if (pServer == pCurServer) - bSawCurrentServer = true; - - if (!pServer->GetName().Equals(sName)) - continue; - - if (uPort != 0 && pServer->GetPort() != uPort) - continue; - - if (!sPass.empty() && pServer->GetPass() != sPass) - continue; - - m_vServers.erase(it); - - if (pServer == pCurServer) { - CIRCSock* pIRCSock = GetIRCSock(); - - // Make sure we don't skip the next server in the list! - if (m_uServerIdx) { - m_uServerIdx--; - } - - if (pIRCSock) { - pIRCSock->Quit(); - PutStatus("Your current server was removed, jumping..."); - } - } else if (!bSawCurrentServer) { - // Our current server comes after the server which we - // are removing. This means that it now got a different - // index in m_vServers! - m_uServerIdx--; - } - - delete pServer; - - return true; - } - - return false; -} - -bool CUser::AddServer(const CString& sName) { - if (sName.empty()) { - return false; - } - - bool bSSL = false; - CString sLine = sName; - sLine.Trim(); - - CString sHost = sLine.Token(0); - CString sPort = sLine.Token(1); - - if (sPort.Left(1) == "+") { - bSSL = true; - sPort.LeftChomp(); - } - - unsigned short uPort = sPort.ToUShort(); - CString sPass = sLine.Token(2, true); - - return AddServer(sHost, uPort, sPass, bSSL); -} - -bool CUser::AddServer(const CString& sName, unsigned short uPort, const CString& sPass, bool bSSL) { -#ifndef HAVE_LIBSSL - if (bSSL) { - return false; - } -#endif - - if (sName.empty()) { - return false; - } - - if (!uPort) { - uPort = 6667; - } - - // Check if server is already added - for (unsigned int a = 0; a < m_vServers.size(); a++) { - CServer* pServer = m_vServers[a]; - - if (!sName.Equals(pServer->GetName())) - continue; - - if (uPort != pServer->GetPort()) - continue; - - if (sPass != pServer->GetPass()) - continue; - - if (bSSL != pServer->IsSSL()) - continue; - - // Server is already added - return false; - } - - CServer* pServer = new CServer(sName, uPort, sPass, bSSL); - m_vServers.push_back(pServer); - - CheckIRCConnect(); - - return true; -} - -bool CUser::IsLastServer() const { - return (m_uServerIdx >= m_vServers.size()); -} - -CServer* CUser::GetNextServer() { - if (m_vServers.empty()) { - return NULL; - } - - if (m_uServerIdx >= m_vServers.size()) { - m_uServerIdx = 0; - } - - return m_vServers[m_uServerIdx++]; -} - -CServer* CUser::GetCurrentServer() const { - unsigned int uIdx = (m_uServerIdx) ? m_uServerIdx -1 : 0; - - if (uIdx >= m_vServers.size()) { - return NULL; - } - - return m_vServers[uIdx]; -} - -bool CUser::SetNextServer(const CServer* pServer) { - for (unsigned int a = 0; a < m_vServers.size(); a++) { - if (m_vServers[a] == pServer) { - m_uServerIdx = a; - return true; - } - } - - return false; -} - bool CUser::CheckPass(const CString& sPass) const { switch (m_eHashType) { @@ -1247,35 +893,23 @@ bool CUser::CheckPass(const CString& sPass) const { return (CClient*) CZNC::Get().GetManager().FindSockByName(sSockName); }*/ -CString CUser::GetLocalIP() { - CIRCSock* pIRCSock = GetIRCSock(); - - if (pIRCSock) { - return pIRCSock->GetLocalIP(); - } - - if (!m_vClients.empty()) { - return m_vClients[0]->GetLocalIP(); - } - - return ""; -} - CString CUser::GetLocalDCCIP() { if (!GetDCCBindHost().empty()) return GetDCCBindHost(); - return GetLocalIP(); -} -bool CUser::PutIRC(const CString& sLine) { - CIRCSock* pIRCSock = GetIRCSock(); - - if (!pIRCSock) { - return false; + for (vector::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { + CIRCNetwork *pNetwork = *it; + CIRCSock* pIRCSock = pNetwork->GetIRCSock(); + if (pIRCSock) { + return pIRCSock->GetLocalIP(); + } } - pIRCSock->PutIRC(sLine); - return true; + if (!GetAllClients().empty()) { + return GetAllClients()[0]->GetLocalIP(); + } + + return ""; } bool CUser::PutUser(const CString& sLine, CClient* pClient, CClient* pSkipClient) { @@ -1292,6 +926,19 @@ bool CUser::PutUser(const CString& sLine, CClient* pClient, CClient* pSkipClient return (pClient == NULL); } +bool CUser::PutAllUser(const CString& sLine, CClient* pClient, CClient* pSkipClient) { + PutUser(sLine, pClient, pSkipClient); + + for (vector::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { + CIRCNetwork* pNetwork = *it; + if (pNetwork->PutUser(sLine, pClient, pSkipClient)) { + return true; + } + } + + return (pClient == NULL); +} + bool CUser::PutStatus(const CString& sLine, CClient* pClient, CClient* pSkipClient) { for (unsigned int a = 0; a < m_vClients.size(); a++) { if ((!pClient || pClient == m_vClients[a]) && pSkipClient != m_vClients[a]) { @@ -1348,34 +995,11 @@ bool CUser::PutModNotice(const CString& sModule, const CString& sLine, CClient* return (pClient == NULL); } -CString CUser::GetCurNick() const { - const CIRCSock* pIRCSock = GetIRCSock(); - - if (pIRCSock) { - return pIRCSock->GetNick(); - } - - if (!m_vClients.empty()) { - return m_vClients[0]->GetNick(); - } - - return ""; -} - CString CUser::MakeCleanUserName(const CString& sUserName) { return sUserName.Token(0, false, "@").Replace_n(".", ""); } // Setters -bool CUser::IsChan(const CString& sChan) const { - if (sChan.empty()) - return false; // There is no way this is a chan - if (GetChanPrefixes().empty()) - return true; // We can't know, so we allow everything - // Thanks to the above if (empty), we can do sChan[0] - return GetChanPrefixes().find(sChan[0]) != CString::npos; -} - void CUser::SetNick(const CString& s) { m_sNick = s; } void CUser::SetAltNick(const CString& s) { m_sAltNick = s; } void CUser::SetIdent(const CString& s) { m_sIdent = s; } @@ -1392,7 +1016,6 @@ void CUser::SetDenyLoadMod(bool b) { m_bDenyLoadMod = b; } void CUser::SetAdmin(bool b) { m_bAdmin = b; } void CUser::SetDenySetBindHost(bool b) { m_bDenySetBindHost = b; } void CUser::SetDefaultChanModes(const CString& s) { m_sDefaultChanModes = s; } -void CUser::SetIRCServer(const CString& s) { m_sIRCServer = s; } void CUser::SetQuitMsg(const CString& s) { m_sQuitMsg = s; } void CUser::SetKeepBuffer(bool b) { m_bKeepBuffer = b; } @@ -1403,20 +1026,6 @@ bool CUser::SetBufferCount(unsigned int u, bool bForce) { return true; } -void CUser::CheckIRCConnect() { - // Do we want to connect? - if (m_bIRCConnectEnabled && GetIRCSock() == NULL) - CZNC::Get().EnableConnectUser(); -} - -void CUser::SetIRCNick(const CNick& n) { - m_IRCNick = n; - - for (unsigned int a = 0; a < m_vClients.size(); a++) { - m_vClients[a]->SetNick(n.GetNick()); - } -} - bool CUser::AddCTCPReply(const CString& sCTCP, const CString& sReply) { // Reject CTCP requests containing spaces if (sCTCP.find_first_of(' ') != CString::npos) { @@ -1445,6 +1054,22 @@ bool CUser::SetStatusPrefix(const CString& s) { // !Setters // Getters +vector CUser::GetAllClients() { + vector vClients; + + for (unsigned int a = 0; a < m_vIRCNetworks.size(); a++) { + for (unsigned int b = 0; b < m_vIRCNetworks[a]->GetClients().size(); b++) { + vClients.push_back(m_vIRCNetworks[a]->GetClients()[b]); + } + } + + for (unsigned int a = 0; a < m_vClients.size(); a++) { + vClients.push_back(m_vClients[a]); + } + + return vClients; +} + const CString& CUser::GetUserName() const { return m_sUserName; } const CString& CUser::GetCleanUserName() const { return m_sCleanUserName; } const CString& CUser::GetNick(bool bAllowDefault) const { return (bAllowDefault && m_sNick.empty()) ? GetCleanUserName() : m_sNick; } @@ -1456,17 +1081,14 @@ const CString& CUser::GetDCCBindHost() const { return m_sDCCBindHost; } const CString& CUser::GetPass() const { return m_sPass; } CUser::eHashType CUser::GetPassHashType() const { return m_eHashType; } const CString& CUser::GetPassSalt() const { return m_sPassSalt; } - bool CUser::DenyLoadMod() const { return m_bDenyLoadMod; } bool CUser::IsAdmin() const { return m_bAdmin; } bool CUser::DenySetBindHost() const { return m_bDenySetBindHost; } bool CUser::MultiClients() const { return m_bMultiClients; } const CString& CUser::GetStatusPrefix() const { return m_sStatusPrefix; } const CString& CUser::GetDefaultChanModes() const { return m_sDefaultChanModes; } -const vector& CUser::GetChans() const { return m_vChans; } -const vector& CUser::GetServers() const { return m_vServers; } -const CNick& CUser::GetIRCNick() const { return m_IRCNick; } -const CString& CUser::GetIRCServer() const { return m_sIRCServer; } + + CString CUser::GetQuitMsg() const { return (!m_sQuitMsg.Trim_n().empty()) ? m_sQuitMsg : CZNC::GetTag(false); } const MCString& CUser::GetCTCPReplies() const { return m_mssCTCPReplies; } unsigned int CUser::GetBufferCount() const { return m_uBufferCount; } diff --git a/User.h b/User.h index f0267814..e80e0b6f 100644 --- a/User.h +++ b/User.h @@ -23,6 +23,7 @@ class CChan; class CClient; class CConfig; class CFile; +class CIRCNetwork; class CIRCSock; class CUserTimer; class CServer; @@ -49,28 +50,15 @@ public: } CConfig ToConfig(); - CChan* FindChan(const CString& sName) const; - bool AddChan(CChan* pChan); - bool AddChan(const CString& sName, bool bInConfig); - bool DelChan(const CString& sName); - void JoinChans(); - CServer* FindServer(const CString& sName) const; - bool DelServer(const CString& sName, unsigned short uPort, const CString& sPass); - bool AddServer(const CString& sName); - bool AddServer(const CString& sName, unsigned short uPort, const CString& sPass = "", bool bSSL = false); - CServer* GetNextServer(); - CServer* GetCurrentServer() const; - bool SetNextServer(const CServer* pServer); bool CheckPass(const CString& sPass) const; bool AddAllowedHost(const CString& sHostMask); bool IsHostAllowed(const CString& sHostMask) const; bool IsValid(CString& sErrMsg, bool bSkipPass = false) const; static bool IsValidUserName(const CString& sUserName); static CString MakeCleanUserName(const CString& sUserName); - bool IsLastServer() const; + void DelNetworks(); void DelClients(); - void DelServers(); void DelModules(); // Unloads a module on all users who have it loaded and loads it again. @@ -81,23 +69,17 @@ public: const CModules& GetModules() const { return *m_pModules; } // !Modules - // Buffers - void AddRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.AddLine(sPre, sPost, bIncNick); } - void UpdateRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.UpdateLine(sPre, sPost, bIncNick); } - void UpdateExactRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.UpdateExactLine(sPre, sPost, bIncNick); } - void ClearRawBuffer() { m_RawBuffer.Clear(); } + // Networks + CIRCNetwork* AddNetwork(const CString &sNetwork); + bool DeleteNetwork(CString sNetwork); + bool AddNetwork(CIRCNetwork *pNetwork); + void RemoveNetwork(CIRCNetwork *pNetwork); + CIRCNetwork* FindNetwork(const CString& sNetwork); + const vector& GetNetworks() const; + // !Networks - void AddMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.AddLine(sPre, sPost, bIncNick); } - void UpdateMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.UpdateLine(sPre, sPost, bIncNick); } - void ClearMotdBuffer() { m_MotdBuffer.Clear(); } - - void AddQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.AddLine(sPre, sPost, bIncNick); } - void UpdateQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.UpdateLine(sPre, sPost, bIncNick); } - void ClearQueryBuffer() { m_QueryBuffer.Clear(); } - // !Buffers - - bool PutIRC(const CString& sLine); bool PutUser(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); + bool PutAllUser(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); bool PutStatus(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); bool PutStatusNotice(const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); bool PutModule(const CString& sModule, const CString& sLine, CClient* pClient = NULL, CClient* pSkipClient = NULL); @@ -107,23 +89,14 @@ public: void UserConnected(CClient* pClient); void UserDisconnected(CClient* pClient); - CString GetLocalIP(); CString GetLocalDCCIP(); - /** This method will return whether the user is connected and authenticated to an IRC server. - */ - bool IsIRCConnected() const; - void SetIRCSocket(CIRCSock* pIRCSock); - void IRCDisconnected(); - void CheckIRCConnect(); - CString ExpandString(const CString& sStr) const; CString& ExpandString(const CString& sStr, CString& sRet) const; CString AddTimestamp(const CString& sStr) const; CString& AddTimestamp(const CString& sStr, CString& sRet) const; - CString GetCurNick() const; bool Clone(const CUser& User, CString& sErrorRet, bool bCloneChans = true); void BounceAllClients(); @@ -144,14 +117,12 @@ public: void SetDenySetBindHost(bool b); bool SetStatusPrefix(const CString& s); void SetDefaultChanModes(const CString& s); - void SetIRCNick(const CNick& n); - void SetIRCServer(const CString& s); void SetQuitMsg(const CString& s); bool AddCTCPReply(const CString& sCTCP, const CString& sReply); bool DelCTCPReply(const CString& sCTCP); bool SetBufferCount(unsigned int u, bool bForce = false); void SetKeepBuffer(bool b); - void SetChanPrefixes(const CString& s) { m_sChanPrefixes = s; } + void SetBeingDeleted(bool b) { m_bBeingDeleted = b; } void SetTimestampFormat(const CString& s) { m_sTimestampFormat = s; } void SetTimestampAppend(bool b) { m_bAppendTimestamp = b; } @@ -161,13 +132,11 @@ public: void SetMaxJoins(unsigned int i) { m_uMaxJoins = i; } void SetSkinName(const CString& s) { m_sSkinName = s; } void SetIRCConnectEnabled(bool b) { m_bIRCConnectEnabled = b; } - void SetIRCAway(bool b) { m_bIRCAway = b; } // !Setters // Getters - vector& GetClients() { return m_vClients; } - CIRCSock* GetIRCSock() { return m_pIRCSock; } - const CIRCSock* GetIRCSock() const { return m_pIRCSock; } + vector& GetUserClients() { return m_vClients; } + vector GetAllClients(); const CString& GetUserName() const; const CString& GetCleanUserName() const; const CString& GetNick(bool bAllowDefault = true) const; @@ -185,9 +154,6 @@ public: bool GetTimestampPrepend() const; bool GetIRCConnectEnabled() const { return m_bIRCConnectEnabled; } - const CString& GetChanPrefixes() const { return m_sChanPrefixes; } - bool IsChan(const CString& sChan) const; - const CString& GetUserPath() const; bool DenyLoadMod() const; @@ -196,27 +162,19 @@ public: bool MultiClients() const; const CString& GetStatusPrefix() const; const CString& GetDefaultChanModes() const; - const vector& GetChans() const; - const vector& GetServers() const; - const CNick& GetIRCNick() const; - const CString& GetIRCServer() const; + CString GetQuitMsg() const; const MCString& GetCTCPReplies() const; unsigned int GetBufferCount() const; bool KeepBuffer() const; bool IsBeingDeleted() const { return m_bBeingDeleted; } - bool HasServers() const { return !m_vServers.empty(); } float GetTimezoneOffset() const { return m_fTimezoneOffset; } unsigned long long BytesRead() const { return m_uBytesRead; } unsigned long long BytesWritten() const { return m_uBytesWritten; } unsigned int JoinTries() const { return m_uMaxJoinTries; } unsigned int MaxJoins() const { return m_uMaxJoins; } - bool IsIRCAway() const { return m_bIRCAway; } CString GetSkinName() const; // !Getters -private: - bool JoinChan(CChan* pChan); - void JoinChans(set& sChans); protected: const CString m_sUserName; @@ -231,10 +189,7 @@ protected: CString m_sPassSalt; CString m_sStatusPrefix; CString m_sDefaultChanModes; - CString m_sChanPrefixes; - CNick m_IRCNick; - bool m_bIRCAway; - CString m_sIRCServer; + CString m_sQuitMsg; MCString m_mssCTCPReplies; CString m_sTimestampFormat; @@ -245,9 +200,6 @@ protected: CString m_sUserPath; // !Paths - CBuffer m_RawBuffer; - CBuffer m_MotdBuffer; - CBuffer m_QueryBuffer; bool m_bMultiClients; bool m_bDenyLoadMod; bool m_bAdmin; @@ -257,15 +209,12 @@ protected: bool m_bAppendTimestamp; bool m_bPrependTimestamp; bool m_bIRCConnectEnabled; - CIRCSock* m_pIRCSock; CUserTimer* m_pUserTimer; - vector m_vServers; - vector m_vChans; + vector m_vIRCNetworks; vector m_vClients; set m_ssAllowedHosts; - unsigned int m_uServerIdx; ///< Index in m_vServers of our current server + 1 unsigned int m_uBufferCount; unsigned long long m_uBytesRead; unsigned long long m_uBytesWritten; diff --git a/znc.cpp b/znc.cpp index 58142273..f1e0ec33 100644 --- a/znc.cpp +++ b/znc.cpp @@ -12,6 +12,7 @@ #include "IRCSock.h" #include "Server.h" #include "User.h" +#include "IRCNetwork.h" #include "Listener.h" #include "Config.h" #include @@ -102,17 +103,18 @@ bool CZNC::OnBoot() { return true; } -bool CZNC::ConnectUser(CUser *pUser) { - CString sSockName = "IRC::" + pUser->GetUserName(); - CIRCSock* pIRCSock = pUser->GetIRCSock(); +bool CZNC::ConnectNetwork(CIRCNetwork *pNetwork) { + CUser *pUser = pNetwork->GetUser(); + CString sSockName = "IRC::" + pUser->GetUserName() + "::" + pNetwork->GetName(); + CIRCSock* pIRCSock = pNetwork->GetIRCSock(); if (!pUser->GetIRCConnectEnabled()) return false; - if (pIRCSock || !pUser->HasServers()) + if (pIRCSock || !pNetwork->HasServers()) return false; - CServer* pServer = pUser->GetNextServer(); + CServer* pServer = pNetwork->GetNextServer(); if (!pServer) return false; @@ -122,10 +124,10 @@ bool CZNC::ConnectUser(CUser *pUser) { m_sConnectThrottle.AddItem(pServer->GetName()); - DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetString(false) << "] ..."); - pUser->PutStatus("Attempting to connect to [" + pServer->GetString(false) + "] ..."); + DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetString(false) << "] on network [" << pNetwork->GetName() << "]"); + pNetwork->PutStatus("Attempting to connect to [" + pServer->GetString(false) + "] ..."); - pIRCSock = new CIRCSock(pUser); + pIRCSock = new CIRCSock(pNetwork); pIRCSock->SetPass(pServer->GetPass()); bool bSSL = false; @@ -135,7 +137,7 @@ bool CZNC::ConnectUser(CUser *pUser) { } #endif - MODULECALL(OnIRCConnecting(pIRCSock), pUser, NULL, + MODULECALL(OnIRCConnecting(pIRCSock), pUser, pNetwork, NULL, DEBUG("Some module aborted the connection attempt"); pUser->PutStatus("Some module aborted the connection attempt"); delete pIRCSock; @@ -143,7 +145,7 @@ bool CZNC::ConnectUser(CUser *pUser) { ); if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, pUser->GetBindHost(), pIRCSock)) { - pUser->PutStatus("Unable to connect. (Bad host?)"); + pNetwork->PutStatus("Unable to connect. (Bad host?)"); } return true; @@ -168,12 +170,17 @@ bool CZNC::HandleUserDeletion() } m_msUsers.erase(pUser->GetUserName()); - CIRCSock* pIRCSock = pUser->GetIRCSock(); + vector vNetworks; + for (vector::iterator it2 = vNetworks.begin(); it2 != vNetworks.end(); ++it2) { + CIRCNetwork *pNetwork = *it2; + CIRCSock* pIRCSock = pNetwork->GetIRCSock(); - if (pIRCSock) { - m_Manager.DelSockByAddr(pIRCSock); + if (pIRCSock) { + m_Manager.DelSockByAddr(pIRCSock); + } } + pUser->DelNetworks(); pUser->DelClients(); pUser->DelModules(); CWebSock::FinishUserSessions(*pUser); @@ -1739,26 +1746,32 @@ protected: it++; m_uiPosNextUser = (m_uiPosNextUser + 1) % uiUserCount; - // Is this user disconnected? - if (pUser->GetIRCSock() != NULL) - continue; - // Does this user want to connect? if (!pUser->GetIRCConnectEnabled()) continue; - // Does this user have any servers? - if (!pUser->HasServers()) - continue; + vector vNetworks = pUser->GetNetworks(); + for (vector::iterator it2 = vNetworks.begin(); it2 != vNetworks.end(); ++it2) { + CIRCNetwork* pNetwork = *it2; - // The timer runs until it once didn't find any users to connect - bUsersLeft = true; + // Is this network disconnected? + if (pNetwork->GetIRCSock() != NULL) + continue; - DEBUG("Connecting user [" << pUser->GetUserName() << "]"); + // Does this user have any servers? + if (!pNetwork->HasServers()) + continue; - if (CZNC::Get().ConnectUser(pUser)) - // User connecting, wait until next time timer fires - return; + // The timer runs until it once didn't find any users to connect + bUsersLeft = true; + + DEBUG("Connecting user [" << pUser->GetUserName() << "/" << pNetwork->GetName() << "]"); + + if (CZNC::Get().ConnectNetwork(pNetwork)) { + // User connecting, wait until next time timer fires + return; + } + } } if (bUsersLeft == false) { diff --git a/znc.h b/znc.h index 9a6fd991..b18164e6 100644 --- a/znc.h +++ b/znc.h @@ -20,6 +20,7 @@ using std::map; class CListener; class CUser; +class CIRCNetwork; class CConnectUserTimer; class CConfig; class CFile; @@ -138,7 +139,7 @@ public: // !MOTD // Create a CIRCSocket. Return false if user cant connect - bool ConnectUser(CUser *pUser); + bool ConnectNetwork(CIRCNetwork *pNetwork); // This creates a CConnectUserTimer if we haven't got one yet void EnableConnectUser(); void DisableConnectUser();