void Cleanup() { Limits::iterator it; time_t now = time(NULL); for (it = m_chans.begin(); it != m_chans.end(); ++it) { // The timeout for this channel did not expire yet? if (it->second.first + (time_t)m_iThresholdSecs >= now) continue; CChan *pChan = m_pNetwork->FindChan(it->first); if (it->second.second >= m_iThresholdMsgs && pChan && pChan->IsDetached()) { // The channel is detached and it is over the // messages limit. Since we only track those // limits for non-detached channels or for // channels which we detached, this means that // we detached because of a flood. PutModule("Flood in [" + pChan->GetName() + "] is over, " "re-attaching..."); // No buffer playback, makes sense, doesn't it? pChan->ClearBuffer(); pChan->JoinUser(); } Limits::iterator it2 = it++; m_chans.erase(it2); // Without this Bad Things (tm) could happen if (it == m_chans.end()) break; } }
bool CIRCSock::OnJoinMessage(CJoinMessage& Message) { const CNick& Nick = Message.GetNick(); CString sChan = Message.GetParam(0); CChan* pChan = nullptr; if (Nick.NickEquals(GetNick())) { m_pNetwork->AddChan(sChan, false); pChan = m_pNetwork->FindChan(sChan); if (pChan) { pChan->Enable(); pChan->SetIsOn(true); PutIRC("MODE " + sChan); } } else { pChan = m_pNetwork->FindChan(sChan); } if (pChan) { pChan->AddNick(Nick.GetNickMask()); Message.SetChan(pChan); IRCSOCKMODULECALL(OnJoinMessage(Message), NOTHING); if (pChan->IsDetached()) { return true; } } return false; }
bool CIRCSock::OnPartMessage(CPartMessage& Message) { const CNick& Nick = Message.GetNick(); CString sChan = Message.GetTarget(); CChan* pChan = m_pNetwork->FindChan(sChan); bool bDetached = false; if (pChan) { pChan->RemNick(Nick.GetNick()); Message.SetChan(pChan); IRCSOCKMODULECALL(OnPartMessage(Message), NOTHING); if (pChan->IsDetached()) bDetached = true; } if (Nick.NickEquals(GetNick())) { m_pNetwork->DelChan(sChan); } /* * We use this boolean because * m_pNetwork->DelChan() will delete this channel * and thus we would dereference an * already-freed pointer! */ return bDetached; }
void CheckAttach(const CString& sMessage, CChan& Channel) { if (Channel.IsDetached()) { if (sMessage.AsLower().find(m_pNetwork->GetCurNick().AsLower()) != CString::npos) { Channel.JoinUser(); } } }
void TryAttach(CChan& Channel) { const CString& sChan = Channel.GetName(); if (Channel.IsDetached() && IsAutoAttach(sChan)) { Channel.JoinUser(); } }
bool CIRCSock::OnActionMessage(CActionMessage& Message) { bool bResult = false; CChan* pChan = nullptr; CString sTarget = Message.GetTarget(); if (sTarget.Equals(GetNick())) { IRCSOCKMODULECALL(OnPrivCTCPMessage(Message), &bResult); if (bResult) return true; IRCSOCKMODULECALL(OnPrivActionMessage(Message), &bResult); if (bResult) return true; if (!m_pNetwork->IsUserOnline() || !m_pNetwork->GetUser()->AutoClearQueryBuffer()) { const CNick& Nick = Message.GetNick(); CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick()); if (pQuery) { CActionMessage Format; Format.Clone(Message); Format.SetNick(_NAMEDFMT(Nick.GetNickMask())); Format.SetTarget("{target}"); Format.SetText("{text}"); pQuery->AddBuffer(Format, Message.GetText()); } } } else { pChan = m_pNetwork->FindChan(sTarget); if (pChan) { Message.SetChan(pChan); FixupChanNick(Message.GetNick(), pChan); IRCSOCKMODULECALL(OnChanCTCPMessage(Message), &bResult); if (bResult) return true; IRCSOCKMODULECALL(OnChanActionMessage(Message), &bResult); if (bResult) return true; if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) { CActionMessage Format; Format.Clone(Message); Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask())); Format.SetTarget(_NAMEDFMT(Message.GetTarget())); Format.SetText("{text}"); pChan->AddBuffer(Format, Message.GetText()); } } } return (pChan && pChan->IsDetached()); }
bool CClient::OnJoinMessage(CJoinMessage& Message) { CString sChans = Message.GetTarget(); CString sKeys = Message.GetKey(); VCString vsChans; sChans.Split(",", vsChans, false); sChans.clear(); VCString vsKeys; sKeys.Split(",", vsKeys, true); sKeys.clear(); for (unsigned int a = 0; a < vsChans.size(); a++) { Message.SetTarget(vsChans[a]); Message.SetKey((a < vsKeys.size()) ? vsKeys[a] : ""); if (m_pNetwork) { // May be nullptr. Message.SetChan(m_pNetwork->FindChan(vsChans[a])); } bool bContinue = false; NETWORKMODULECALL(OnUserJoinMessage(Message), m_pUser, m_pNetwork, this, &bContinue); if (bContinue) continue; CString sChannel = Message.GetTarget(); CString sKey = Message.GetKey(); if (m_pNetwork) { CChan* pChan = m_pNetwork->FindChan(sChannel); if (pChan) { if (pChan->IsDetached()) pChan->AttachUser(this); else pChan->JoinUser(sKey); continue; } else if (!sChannel.empty()) { pChan = new CChan(sChannel, m_pNetwork, false); if (m_pNetwork->AddChan(pChan)) { pChan->SetKey(sKey); } } } if (!sChannel.empty()) { sChans += (sChans.empty()) ? sChannel : CString("," + sChannel); if (!vsKeys.empty()) { sKeys += (sKeys.empty()) ? sKey : CString("," + sKey); } } } Message.SetTarget(sChans); Message.SetKey(sKeys); return sChans.empty(); }
bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) { CString sTarget = Message.GetTarget(); bool bResult = false; if (sTarget.Equals(GetNick())) { IRCSOCKMODULECALL(OnPrivNoticeMessage(Message), &bResult); if (bResult) return true; if (!m_pNetwork->IsUserOnline()) { // If the user is detached, add to the buffer CNoticeMessage Format; Format.Clone(Message); Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetNickMask()))); Format.SetTarget("{target}"); Format.SetText("{text}"); m_pNetwork->AddNoticeBuffer(Format, Message.GetText()); } return false; } else { CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan) { Message.SetChan(pChan); FixupChanNick(Message.GetNick(), pChan); IRCSOCKMODULECALL(OnChanNoticeMessage(Message), &bResult); if (bResult) return true; if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) { CNoticeMessage Format; Format.Clone(Message); Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask())); Format.SetTarget(_NAMEDFMT(Message.GetTarget())); Format.SetText("{text}"); pChan->AddBuffer(Format, Message.GetText()); } } return (pChan && pChan->IsDetached()); } }
void CChan::Clone(CChan& chan) { // We assume that m_sName and m_pNetwork are equal SetBufferCount(chan.GetBufferCount(), true); SetKeepBuffer(chan.KeepBuffer()); SetKey(chan.GetKey()); SetDefaultModes(chan.GetDefaultModes()); if (IsDetached() != chan.IsDetached()) { // Only send something if it makes sense // (= Only detach if client is on the channel // and only attach if we are on the channel) if (IsOn()) { if (IsDetached()) { JoinUser(false, ""); } else { DetachUser(); } } SetDetached(chan.IsDetached()); } }
void SetChan(const CString& sLine) { const CString var = sLine.Token(1).AsLower(); CString username = sLine.Token(2); CString chan = sLine.Token(3); CString value = sLine.Token(4, true); if (value.empty()) { PutModule("Usage: setchan <variable> <username> <chan> <value>"); return; } CUser* user = GetUser(username); if (!user) return; CChan* pChan = user->FindChan(chan); if (!pChan) { PutModule("Error: Channel not found: " + chan); return; } if (var == "defmodes") { pChan->SetDefaultModes(value); PutModule("DefModes = " + value); } else if (var == "buffer") { unsigned int i = value.ToUInt(); pChan->SetBufferCount(i); PutModule("Buffer = " + CString(i)); } else if (var == "inconfig") { bool b = value.ToBool(); pChan->SetInConfig(b); PutModule("InConfig = " + CString(b)); } else if (var == "keepbuffer") { bool b = value.ToBool(); pChan->SetKeepBuffer(b); PutModule("KeepBuffer = " + CString(b)); } else if (var == "detached") { bool b = value.ToBool(); if (pChan->IsDetached() != b) { if (b) pChan->DetachUser(); else pChan->AttachUser(); } PutModule("Detached = " + CString(b)); } else PutModule("Error: Unknown variable"); }
void Message(CChan& Channel) { Limits::iterator it; time_t now = time(NULL); // First: Clean up old entries and reattach where necessary Cleanup(); it = m_chans.find(Channel.GetName()); if (it == m_chans.end()) { // We don't track detached channels if (Channel.IsDetached()) return; // This is the first message for this channel, start a // new timeout. std::pair<time_t, unsigned int> tmp(now, 1); m_chans[Channel.GetName()] = tmp; return; } // No need to check it->second.first (expiry time), since // Cleanup() would have removed it if it was expired. if (it->second.second >= m_iThresholdMsgs) { // The channel already hit the limit and we detached the // user, but it is still being flooded, reset the timeout it->second.first = now; it->second.second++; return; } it->second.second++; if (it->second.second < m_iThresholdMsgs) return; // The channel hit the limit, reset the timeout so that we keep // it detached for longer. it->second.first = now; Channel.DetachUser(); if (!GetNV("silent").ToBool()) { PutModule("Channel [" + Channel.GetName() + "] was " "flooded, you've been detached"); } }
bool CIRCSock::OnTopicMessage(CTopicMessage& Message) { const CNick& Nick = Message.GetNick(); CChan* pChan = m_pNetwork->FindChan(Message.GetParam(0)); if (pChan) { Message.SetChan(pChan); bool bReturn = false; IRCSOCKMODULECALL(OnTopicMessage(Message), &bReturn); if (bReturn) return true; pChan->SetTopicOwner(Nick.GetNick()); pChan->SetTopicDate((unsigned long)time(nullptr)); pChan->SetTopic(Message.GetTopic()); } return (pChan && pChan->IsDetached()); }
void TryAttach(const CNick& Nick, CChan& Channel, CString& Message) { const CString& sChan = Channel.GetName(); const CString& sHost = Nick.GetHostMask(); const CString& sMessage = Message; VAttachIter it; if (!Channel.IsDetached()) return; // Any negated match? for (it = m_vMatches.begin(); it != m_vMatches.end(); ++it) { if (it->IsNegated() && it->IsMatch(sChan, sHost, sMessage)) return; } // Now check for a positive match for (it = m_vMatches.begin(); it != m_vMatches.end(); ++it) { if (!it->IsNegated() && it->IsMatch(sChan, sHost, sMessage)) { Channel.AttachUser(); return; } } }
void GetChan(const CString& sLine) { const CString var = sLine.Token(1).AsLower(); CString username = sLine.Token(2); CString chan = sLine.Token(3, true); if (var.empty()) { PutModule("Usage: getchan <variable> [username] <chan>"); return; } if (chan.empty()) { chan = username; username = ""; } if (username.empty()) { username = m_pUser->GetUserName(); } CUser* user = GetUser(username); if (!user) return; CChan* pChan = user->FindChan(chan); if (!pChan) { PutModule("Error: Channel not found: " + chan); return; } if (var == "defmodes") PutModule("DefModes = " + pChan->GetDefaultModes()); else if (var == "buffer") PutModule("Buffer = " + CString(pChan->GetBufferCount())); else if (var == "inconfig") PutModule("InConfig = " + pChan->InConfig()); else if (var == "keepbuffer") PutModule("KeepBuffer = " + pChan->KeepBuffer()); else if (var == "detached") PutModule("Detached = " + pChan->IsDetached()); else PutModule("Error: Unknown variable"); }
bool CIRCSock::OnKickMessage(CKickMessage& Message) { CString sChan = Message.GetParam(0); CString sKickedNick = Message.GetKickedNick(); CChan* pChan = m_pNetwork->FindChan(sChan); if (pChan) { Message.SetChan(pChan); IRCSOCKMODULECALL(OnKickMessage(Message), NOTHING); // do not remove the nick till after the OnKick call, so modules // can do Chan.FindNick or something to get more info. pChan->RemNick(sKickedNick); } if (GetNick().Equals(sKickedNick) && pChan) { pChan->SetIsOn(false); // Don't try to rejoin! pChan->Disable(); } return (pChan && pChan->IsDetached()); }
bool CIRCSock::OnModeMessage(CModeMessage& Message) { const CNick& Nick = Message.GetNick(); CString sTarget = Message.GetTarget(); CString sModes = Message.GetModes(); CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan) { pChan->ModeChange(sModes, &Nick); if (pChan->IsDetached()) { return true; } } else if (sTarget == m_Nick.GetNick()) { CString sModeArg = sModes.Token(0); bool bAdd = true; /* no module call defined (yet?) MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pNetwork->GetUser(), nullptr, ); */ for (unsigned int a = 0; a < sModeArg.size(); a++) { const unsigned char& uMode = sModeArg[a]; if (uMode == '+') { bAdd = true; } else if (uMode == '-') { bAdd = false; } else { if (bAdd) { m_scUserModes.insert(uMode); } else { m_scUserModes.erase(uMode); } } } } return false; }
void Attach(CChan& channel) { if (!channel.IsDetached()) return; channel.JoinUser(); }
bool CIRCSock::OnCTCPMessage(CCTCPMessage& Message) { bool bResult = false; CChan* pChan = nullptr; CString sTarget = Message.GetTarget(); if (sTarget.Equals(GetNick())) { if (Message.IsReply()) { IRCSOCKMODULECALL(OnCTCPReplyMessage(Message), &bResult); return bResult; } else { IRCSOCKMODULECALL(OnPrivCTCPMessage(Message), &bResult); if (bResult) return true; } } else { pChan = m_pNetwork->FindChan(sTarget); if (pChan) { Message.SetChan(pChan); FixupChanNick(Message.GetNick(), pChan); IRCSOCKMODULECALL(OnChanCTCPMessage(Message), &bResult); if (bResult) return true; } } const CNick& Nick = Message.GetNick(); const CString& sMessage = Message.GetText(); const MCString& mssCTCPReplies = m_pNetwork->GetUser()->GetCTCPReplies(); CString sQuery = sMessage.Token(0).AsUpper(); MCString::const_iterator it = mssCTCPReplies.find(sQuery); bool bHaveReply = false; CString sReply; if (it != mssCTCPReplies.end()) { sReply = m_pNetwork->ExpandString(it->second); bHaveReply = true; if (sReply.empty()) { return true; } } if (!bHaveReply && !m_pNetwork->IsUserAttached()) { if (sQuery == "VERSION") { sReply = CZNC::GetTag(false); } else if (sQuery == "PING") { sReply = sMessage.Token(1, true); } } if (!sReply.empty()) { time_t now = time(nullptr); // If the last CTCP is older than m_uCTCPFloodTime, reset the counter if (m_lastCTCP + m_uCTCPFloodTime < now) m_uNumCTCP = 0; m_lastCTCP = now; // If we are over the limit, don't reply to this CTCP if (m_uNumCTCP >= m_uCTCPFloodCount) { DEBUG("CTCP flood detected - not replying to query"); return true; } m_uNumCTCP++; PutIRC("NOTICE " + Nick.GetNick() + " :\001" + sQuery + " " + sReply + "\001"); return true; } return (pChan && pChan->IsDetached()); }
bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { const CString& sCmd = Message.GetCommand(); CString sServer = Message.GetNick().GetHostMask(); unsigned int uRaw = Message.GetCode(); CString sNick = Message.GetParam(0); bool bResult = false; IRCSOCKMODULECALL(OnNumericMessage(Message), &bResult); if (bResult) return true; switch (uRaw) { case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay if (m_bAuthed && sServer == "irc.znc.in") { // m_bAuthed == true => we already received another 001 => we // might be in a traffic loop m_pNetwork->PutStatus( "ZNC seems to be connected to itself, disconnecting..."); Quit(); return true; } m_pNetwork->SetIRCServer(sServer); // Now that we are connected, let nature take its course SetTimeout(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ); PutIRC("WHO " + sNick); m_bAuthed = true; m_pNetwork->PutStatus("Connected!"); const vector<CClient*>& vClients = m_pNetwork->GetClients(); for (CClient* pClient : vClients) { CString sClientNick = pClient->GetNick(false); if (!sClientNick.Equals(sNick)) { // If they connected with a nick that doesn't match the one // we got on irc, then we need to update them pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick); } } SetNick(sNick); IRCSOCKMODULECALL(OnIRCConnected(), NOTHING); m_pNetwork->ClearRawBuffer(); m_pNetwork->AddRawBuffer(BufferMessage(Message)); m_pNetwork->IRCConnected(); break; } case 5: ParseISupport(Message); m_pNetwork->UpdateExactRawBuffer(BufferMessage(Message)); break; case 10: { // :irc.server.com 010 nick <hostname> <port> :<info> CString sHost = Message.GetParam(1); CString sPort = Message.GetParam(2); CString sInfo = Message.GetParam(3); m_pNetwork->PutStatus( "Server [" + m_pNetwork->GetCurrentServer()->GetString(false) + "] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]"); m_pNetwork->PutStatus( "Perhaps you want to add it as a new server."); // Don't send server redirects to the client return true; } case 2: case 3: case 4: case 250: // highest connection count case 251: // user count case 252: // oper count case 254: // channel count case 255: // client count case 265: // local users case 266: // global users m_pNetwork->UpdateRawBuffer(sCmd, BufferMessage(Message)); break; case 305: m_pNetwork->SetIRCAway(false); break; case 306: m_pNetwork->SetIRCAway(true); break; case 324: { // MODE // :irc.server.com 324 nick #chan +nstk key CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { pChan->SetModes(Message.GetParams(2)); // We don't SetModeKnown(true) here, // because a 329 will follow if (!pChan->IsModeKnown()) { // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return true; } if (pChan->IsDetached()) { return true; } } } break; case 329: { // :irc.server.com 329 nick #chan 1234567890 CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { unsigned long ulDate = Message.GetParam(2).ToULong(); pChan->SetCreationDate(ulDate); if (!pChan->IsModeKnown()) { pChan->SetModeKnown(true); // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return true; } if (pChan->IsDetached()) { return true; } } } break; case 331: { // :irc.server.com 331 yournick #chan :No topic is set. CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { pChan->SetTopic(""); if (pChan->IsDetached()) { return true; } } break; } case 332: { // :irc.server.com 332 yournick #chan :This is a topic CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { CString sTopic = Message.GetParam(2); pChan->SetTopic(sTopic); if (pChan->IsDetached()) { return true; } } break; } case 333: { // :irc.server.com 333 yournick #chan setternick 1112320796 CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { sNick = Message.GetParam(2); unsigned long ulDate = Message.GetParam(3).ToULong(); pChan->SetTopicOwner(sNick); pChan->SetTopicDate(ulDate); if (pChan->IsDetached()) { return true; } } break; } case 352: { // WHO // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name sNick = Message.GetParam(5); CString sChan = Message.GetParam(1); CString sIdent = Message.GetParam(2); CString sHost = Message.GetParam(3); if (sNick.Equals(GetNick())) { m_Nick.SetIdent(sIdent); m_Nick.SetHost(sHost); } m_pNetwork->SetIRCNick(m_Nick); m_pNetwork->SetIRCServer(sServer); const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (CChan* pChan : vChans) { pChan->OnWho(sNick, sIdent, sHost); } CChan* pChan = m_pNetwork->FindChan(sChan); if (pChan && pChan->IsDetached()) { return true; } break; } case 353: { // NAMES // :irc.server.com 353 nick @ #chan :nick1 nick2 // Todo: allow for non @+= server msgs CChan* pChan = m_pNetwork->FindChan(Message.GetParam(2)); // If we don't know that channel, some client might have // requested a /names for it and we really should forward this. if (pChan) { CString sNicks = Message.GetParam(3); pChan->AddNicks(sNicks); if (pChan->IsDetached()) { return true; } } break; } case 366: { // end of names list // :irc.server.com 366 nick #chan :End of /NAMES list. CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (pChan) { if (pChan->IsOn()) { // If we are the only one in the chan, set our default modes if (pChan->GetNickCount() == 1) { CString sModes = pChan->GetDefaultModes(); if (sModes.empty()) { sModes = m_pNetwork->GetUser()->GetDefaultChanModes(); } if (!sModes.empty()) { PutIRC("MODE " + pChan->GetName() + " " + sModes); } } } if (pChan->IsDetached()) { // don't put it to clients return true; } } break; } case 375: // begin motd case 422: // MOTD File is missing if (m_pNetwork->GetIRCServer().Equals(sServer)) { m_pNetwork->ClearMotdBuffer(); } case 372: // motd case 376: // end motd if (m_pNetwork->GetIRCServer().Equals(sServer)) { m_pNetwork->AddMotdBuffer(BufferMessage(Message)); } break; case 437: // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel if (m_pNetwork->IsChan(Message.GetParam(1)) || sNick != "*") break; case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal chars case 433: { CString sBadNick = Message.GetParam(1); if (!m_bAuthed) { SendAltNick(sBadNick); return true; } break; } case 451: // :irc.server.com 451 CAP :You have not registered // Servers that don't support CAP will give us this error, don't send // it to the client if (sNick.Equals("CAP")) return true; case 470: { // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2 // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel // freenode style numeric CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1)); if (!pChan) { // unreal style numeric pChan = m_pNetwork->FindChan(Message.GetParam(2)); } if (pChan) { pChan->Disable(); m_pNetwork->PutStatus("Channel [" + pChan->GetName() + "] is linked to " "another channel and was thus disabled."); } break; } case 670: // :hydra.sector5d.org 670 kylef :STARTTLS successful, go ahead with TLS handshake // // 670 is a response to `STARTTLS` telling the client to switch to // TLS if (!GetSSL()) { StartTLS(); m_pNetwork->PutStatus("Switched to SSL (STARTTLS)"); } return true; } return false; }
void CIRCSock::ReadLine(const CString& sData) { CString sLine = sData; sLine.TrimRight("\n\r"); DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") IRC -> ZNC [" << sLine << "]"); NETWORKMODULECALL(OnRaw(sLine), m_pNetwork->GetUser(), m_pNetwork, NULL, return); if (sLine.Equals("PING ", false, 5)) { // Generate a reply and don't forward this to any user, // we don't want any PING forwarded PutIRC("PONG " + sLine.substr(5)); return; } else if (sLine.Token(1).Equals("PONG")) { // Block PONGs, we already responded to the pings return; } else if (sLine.Equals("ERROR ", false, 6)) { //ERROR :Closing Link: nick[24.24.24.24] (Excess Flood) CString sError(sLine.substr(6)); sError.TrimPrefix(); m_pNetwork->PutStatus("Error from Server [" + sError + "]"); return; } CString sCmd = sLine.Token(1); if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) { CString sServer = sLine.Token(0).LeftChomp_n(); unsigned int uRaw = sCmd.ToUInt(); CString sNick = sLine.Token(2); CString sRest = sLine.Token(3, true); switch (uRaw) { case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick if (m_bAuthed && sServer == "irc.znc.in") { // m_bAuthed == true => we already received another 001 => we might be in a traffic loop m_pNetwork->PutStatus("ZNC seems to be connected to itself, disconnecting..."); Quit(); return; } m_pNetwork->SetIRCServer(sServer); SetTimeout(540, TMO_READ); // Now that we are connected, let nature take its course PutIRC("WHO " + sNick); m_bAuthed = true; m_pNetwork->PutStatus("Connected!"); vector<CClient*>& vClients = m_pNetwork->GetClients(); for (unsigned int a = 0; a < vClients.size(); a++) { CClient* pClient = vClients[a]; CString sClientNick = pClient->GetNick(false); if (!sClientNick.Equals(sNick)) { // If they connected with a nick that doesn't match the one we got on irc, then we need to update them pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick); } } SetNick(sNick); NETWORKMODULECALL(OnIRCConnected(), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); m_pNetwork->ClearRawBuffer(); m_pNetwork->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; } case 5: ParseISupport(sRest); m_pNetwork->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 10: { // :irc.server.com 010 nick <hostname> <port> :<info> CString sHost = sRest.Token(0); CString sPort = sRest.Token(1); CString sInfo = sRest.Token(2, true).TrimPrefix_n(); m_pNetwork->PutStatus("Server [" + m_pNetwork->GetCurrentServer()->GetString(false) + "] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]"); m_pNetwork->PutStatus("Perhaps you want to add it as a new server."); // Don't send server redirects to the client return; } case 2: case 3: case 4: case 250: // highest connection count case 251: // user count case 252: // oper count case 254: // channel count case 255: // client count case 265: // local users case 266: // global users m_pNetwork->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 305: m_pNetwork->SetIRCAway(false); break; case 306: m_pNetwork->SetIRCAway(true); break; case 324: { // MODE sRest.Trim(); CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { pChan->SetModes(sRest.Token(1, true)); // We don't SetModeKnown(true) here, // because a 329 will follow if (!pChan->IsModeKnown()) { // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return; } } } break; case 329: { sRest.Trim(); CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { unsigned long ulDate = sLine.Token(4).ToULong(); pChan->SetCreationDate(ulDate); if (!pChan->IsModeKnown()) { pChan->SetModeKnown(true); // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return; } } } break; case 331: { // :irc.server.com 331 yournick #chan :No topic is set. CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { pChan->SetTopic(""); } break; } case 332: { // :irc.server.com 332 yournick #chan :This is a topic CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { CString sTopic = sLine.Token(4, true); sTopic.LeftChomp(); pChan->SetTopic(sTopic); } break; } case 333: { // :irc.server.com 333 yournick #chan setternick 1112320796 CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { sNick = sLine.Token(4); unsigned long ulDate = sLine.Token(5).ToULong(); pChan->SetTopicOwner(sNick); pChan->SetTopicDate(ulDate); } break; } case 352: { // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name sServer = sLine.Token(0); sNick = sLine.Token(7); CString sIdent = sLine.Token(4); CString sHost = sLine.Token(5); sServer.LeftChomp(); if (sNick.Equals(GetNick())) { m_Nick.SetIdent(sIdent); m_Nick.SetHost(sHost); } m_pNetwork->SetIRCNick(m_Nick); m_pNetwork->SetIRCServer(sServer); const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { vChans[a]->OnWho(sNick, sIdent, sHost); } break; } case 353: { // NAMES sRest.Trim(); // Todo: allow for non @+= server msgs CChan* pChan = m_pNetwork->FindChan(sRest.Token(1)); // If we don't know that channel, some client might have // requested a /names for it and we really should forward this. if (pChan) { CString sNicks = sRest.Token(2, true).TrimPrefix_n(); pChan->AddNicks(sNicks); } ForwardRaw353(sLine); // We forwarded it already, so return return; } case 366: { // end of names list m_pNetwork->PutUser(sLine); // First send them the raw // :irc.server.com 366 nick #chan :End of /NAMES list. CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { if (pChan->IsOn()) { // If we are the only one in the chan, set our default modes if (pChan->GetNickCount() == 1) { CString sModes = pChan->GetDefaultModes(); if (sModes.empty()) { sModes = m_pNetwork->GetUser()->GetDefaultChanModes(); } if (!sModes.empty()) { PutIRC("MODE " + pChan->GetName() + " " + sModes); } } } } return; // return so we don't send them the raw twice } case 375: // begin motd case 422: // MOTD File is missing m_pNetwork->ClearMotdBuffer(); case 372: // motd case 376: // end motd m_pNetwork->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 437: // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel if (m_pNetwork->IsChan(sRest.Token(0)) || sNick != "*") break; case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters case 433: { CString sBadNick = sRest.Token(0); if (!m_bAuthed) { SendAltNick(sBadNick); return; } break; } case 451: // :irc.server.com 451 CAP :You have not registered // Servers that dont support CAP will give us this error, dont send it to the client if (sNick.Equals("CAP")) return; case 470: { // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2 // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel // freenode style numeric CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (!pChan) { // unreal style numeric pChan = m_pNetwork->FindChan(sRest.Token(1)); } if (pChan) { pChan->Disable(); m_pNetwork->PutStatus("Channel [" + pChan->GetName() + "] is linked to " "another channel and was thus disabled."); } break; } } } else { CNick Nick(sLine.Token(0).TrimPrefix_n()); sCmd = sLine.Token(1); CString sRest = sLine.Token(2, true); if (sCmd.Equals("NICK")) { CString sNewNick = sRest.TrimPrefix_n(); bool bIsVisible = false; vector<CChan*> vFoundChans; const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { CChan* pChan = vChans[a]; if (pChan->ChangeNick(Nick.GetNick(), sNewNick)) { vFoundChans.push_back(pChan); if (!pChan->IsDetached()) { bIsVisible = true; } } } // Todo: use nick compare function here if (Nick.GetNick().Equals(GetNick())) { // We are changing our own nick, the clients always must see this! bIsVisible = true; SetNick(sNewNick); } NETWORKMODULECALL(OnNick(Nick, sNewNick, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (!bIsVisible) { return; } } else if (sCmd.Equals("QUIT")) { CString sMessage = sRest.TrimPrefix_n(); bool bIsVisible = false; // :[email protected] QUIT :message if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->PutStatus("You quit [" + sMessage + "]"); // We don't call module hooks and we don't // forward this quit to clients (Some clients // disconnect if they receive such a QUIT) return; } vector<CChan*> vFoundChans; const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { CChan* pChan = vChans[a]; if (pChan->RemNick(Nick.GetNick())) { vFoundChans.push_back(pChan); if (!pChan->IsDetached()) { bIsVisible = true; } } } NETWORKMODULECALL(OnQuit(Nick, sMessage, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (!bIsVisible) { return; } } else if (sCmd.Equals("JOIN")) { CString sChan = sRest.Token(0).TrimPrefix_n(); CChan* pChan; // Todo: use nick compare function if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->AddChan(sChan, false); pChan = m_pNetwork->FindChan(sChan); if (pChan) { pChan->ResetJoinTries(); pChan->Enable(); pChan->SetIsOn(true); PutIRC("MODE " + sChan); } } else { pChan = m_pNetwork->FindChan(sChan); } if (pChan) { pChan->AddNick(Nick.GetNickMask()); NETWORKMODULECALL(OnJoin(Nick.GetNickMask(), *pChan), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (pChan->IsDetached()) { return; } } } else if (sCmd.Equals("PART")) { CString sChan = sRest.Token(0).TrimPrefix_n(); CString sMsg = sRest.Token(1, true).TrimPrefix_n(); CChan* pChan = m_pNetwork->FindChan(sChan); bool bDetached = false; if (pChan) { pChan->RemNick(Nick.GetNick()); NETWORKMODULECALL(OnPart(Nick.GetNickMask(), *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (pChan->IsDetached()) bDetached = true; } // Todo: use nick compare function if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->DelChan(sChan); } /* * We use this boolean because * m_pNetwork->DelChan() will delete this channel * and thus we would dereference an * already-freed pointer! */ if (bDetached) { return; } } else if (sCmd.Equals("MODE")) { CString sTarget = sRest.Token(0); CString sModes = sRest.Token(1, true); if (sModes.Left(1) == ":") sModes = sModes.substr(1); CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan) { pChan->ModeChange(sModes, &Nick); if (pChan->IsDetached()) { return; } } else if (sTarget == m_Nick.GetNick()) { CString sModeArg = sModes.Token(0); bool bAdd = true; /* no module call defined (yet?) MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pNetwork->GetUser(), NULL, ); */ for (unsigned int a = 0; a < sModeArg.size(); a++) { const unsigned char& uMode = sModeArg[a]; if (uMode == '+') { bAdd = true; } else if (uMode == '-') { bAdd = false; } else { if (bAdd) { m_scUserModes.insert(uMode); } else { m_scUserModes.erase(uMode); } } } } } else if (sCmd.Equals("KICK")) { // :[email protected] KICK #chan nick :msg CString sChan = sRest.Token(0); CString sKickedNick = sRest.Token(1); CString sMsg = sRest.Token(2, true); sMsg.LeftChomp(); CChan* pChan = m_pNetwork->FindChan(sChan); if (pChan) { NETWORKMODULECALL(OnKick(Nick, sKickedNick, *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); // do not remove the nick till after the OnKick call, so modules // can do Chan.FindNick or something to get more info. pChan->RemNick(sKickedNick); } if (GetNick().Equals(sKickedNick) && pChan) { pChan->SetIsOn(false); // Don't try to rejoin! pChan->Disable(); } if ((pChan) && (pChan->IsDetached())) { return; } } else if (sCmd.Equals("NOTICE")) { // :[email protected] NOTICE #chan :Message CString sTarget = sRest.Token(0); CString sMsg = sRest.Token(1, true); sMsg.LeftChomp(); if (sMsg.WildCmp("\001*\001")) { sMsg.LeftChomp(); sMsg.RightChomp(); if (sTarget.Equals(GetNick())) { if (OnCTCPReply(Nick, sMsg)) { return; } } m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :\001" + sMsg + "\001"); return; } else { if (sTarget.Equals(GetNick())) { if (OnPrivNotice(Nick, sMsg)) { return; } } else { if (OnChanNotice(Nick, sTarget, sMsg)) { return; } } } if (Nick.GetNick().Equals(m_pNetwork->GetIRCServer())) { m_pNetwork->PutUser(":" + Nick.GetNick() + " NOTICE " + sTarget + " :" + sMsg); } else { m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :" + sMsg); } return; } else if (sCmd.Equals("TOPIC")) { // :[email protected] TOPIC #chan :This is a topic CChan* pChan = m_pNetwork->FindChan(sLine.Token(2)); if (pChan) { CString sTopic = sLine.Token(3, true); sTopic.LeftChomp(); NETWORKMODULECALL(OnTopic(Nick, *pChan, sTopic), m_pNetwork->GetUser(), m_pNetwork, NULL, return); pChan->SetTopicOwner(Nick.GetNick()); pChan->SetTopicDate((unsigned long) time(NULL)); pChan->SetTopic(sTopic); if (pChan->IsDetached()) { return; // Don't forward this } sLine = ":" + Nick.GetNickMask() + " TOPIC " + pChan->GetName() + " :" + sTopic; } } else if (sCmd.Equals("PRIVMSG")) {