From da06aacd4dfc521c40e7dcd200bcaef9c78d9370 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sat, 5 Sep 2015 11:35:21 +0200 Subject: [PATCH] CIRCSock::OnNumericMessage() handler --- include/znc/IRCSock.h | 1 + src/IRCSock.cpp | 653 +++++++++++++++++++++--------------------- 2 files changed, 332 insertions(+), 322 deletions(-) diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index 67db3385..94045908 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -60,6 +60,7 @@ public: bool OnModeMessage(CModeMessage& Message); bool OnNickMessage(CNickMessage& Message); bool OnNoticeMessage(CNoticeMessage& Message); + bool OnNumericMessage(CNumericMessage& Message); bool OnPartMessage(CPartMessage& Message); bool OnQuitMessage(CQuitMessage& Message); bool OnTextMessage(CTextMessage& Message); diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 49c419d0..834752d1 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -182,328 +182,8 @@ void CIRCSock::ReadLine(const CString& sData) { if (Message.GetType() == CMessage::Type::Numeric) { CNumericMessage& NumericMsg = static_cast(Message); - CString sServer = Message.GetNick().GetHostMask(); - unsigned int uRaw = NumericMsg.GetCode(); - CString sNick = Message.GetParam(0); - CString sRest = Message.GetParams(1); - CString sTmp; - - 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(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ); // Now that we are connected, let nature take its course - PutIRC("WHO " + sNick); - - m_bAuthed = true; - m_pNetwork->PutStatus("Connected!"); - - const vector& 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(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); - - m_pNetwork->IRCConnected(); - - break; - } - case 5: - ParseISupport(sRest); - m_pNetwork->UpdateExactRawBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); - break; - case 10: { // :irc.server.com 010 nick : - 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; - } - 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 - sTmp = ":" + _NAMEDFMT(sServer) + " " + sCmd; - m_pNetwork->UpdateRawBuffer(sTmp, sTmp + " {target} " + _NAMEDFMT(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; - } - if (pChan->IsDetached()) { - return; - } - } - } - break; - case 329: { - sRest.Trim(); - CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); - - 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; - } - if (pChan->IsDetached()) { - return; - } - } - } - 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; - } - } - - 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; - } - } - - 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; - } - } - - 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& vChans = m_pNetwork->GetChans(); - - for (CChan* pChan : vChans) { - pChan->OnWho(sNick, sIdent, sHost); - } - - if (m_bNamesx && (sNick.size() > 1) && IsPermChar(sNick[1])) { - // sLine uses multi-prefix - - const vector& vClients = m_pNetwork->GetClients(); - for (CClient* pClient : vClients) { - if (pClient->HasNamesx()) { - m_pNetwork->PutUser(Message, pClient); - } else { - // The client doesn't support multi-prefix so we need to remove - // the other prefixes. - - CString sNewNick = sNick; - size_t pos = sNick.find_first_not_of(GetPerms()); - if (pos >= 2 && pos != CString::npos) { - sNewNick = sNick[0] + sNick.substr(pos); - } - CMessage Copy(Message); - Copy.SetParam(5, sNewNick); - m_pNetwork->PutUser(Copy, pClient); - } - } - - return; - } - - CChan* pChan = m_pNetwork->FindChan(sChan); - if (pChan && pChan->IsDetached()) { - return; - } - - 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); - if (pChan->IsDetached()) { - return; - } - } - - ForwardRaw353(Message); - - // We forwarded it already, so return - return; - } - case 366: { // end of names list - // :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); - } - } - } - if (pChan->IsDetached()) { - // don't put it to clients - return; - } - } - - 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(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(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; - } - 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; + if (OnNumericMessage(NumericMsg)) { + return; } } else { CNick Nick = Message.GetNick(); @@ -994,6 +674,335 @@ bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) { } } +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); + CString sRest = Message.GetParams(1); + CString sTmp; + + 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 true; + } + + m_pNetwork->SetIRCServer(sServer); + SetTimeout(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ); // Now that we are connected, let nature take its course + PutIRC("WHO " + sNick); + + m_bAuthed = true; + m_pNetwork->PutStatus("Connected!"); + + const vector& 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(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); + + m_pNetwork->IRCConnected(); + + break; + } + case 5: + ParseISupport(sRest); + m_pNetwork->UpdateExactRawBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); + break; + case 10: { // :irc.server.com 010 nick : + 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 + sTmp = ":" + _NAMEDFMT(sServer) + " " + sCmd; + m_pNetwork->UpdateRawBuffer(sTmp, sTmp + " {target} " + _NAMEDFMT(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 true; + } + if (pChan->IsDetached()) { + return true; + } + } + } + break; + case 329: { + sRest.Trim(); + CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); + + 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& vChans = m_pNetwork->GetChans(); + + for (CChan* pChan : vChans) { + pChan->OnWho(sNick, sIdent, sHost); + } + + if (m_bNamesx && (sNick.size() > 1) && IsPermChar(sNick[1])) { + // sLine uses multi-prefix + + const vector& vClients = m_pNetwork->GetClients(); + for (CClient* pClient : vClients) { + if (pClient->HasNamesx()) { + m_pNetwork->PutUser(Message, pClient); + } else { + // The client doesn't support multi-prefix so we need to remove + // the other prefixes. + + CString sNewNick = sNick; + size_t pos = sNick.find_first_not_of(GetPerms()); + if (pos >= 2 && pos != CString::npos) { + sNewNick = sNick[0] + sNick.substr(pos); + } + CMessage WhoMsg(Message); + WhoMsg.SetParam(5, sNewNick); + m_pNetwork->PutUser(WhoMsg, pClient); + } + } + + return true; + } + + CChan* pChan = m_pNetwork->FindChan(sChan); + if (pChan && pChan->IsDetached()) { + return true; + } + + 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); + if (pChan->IsDetached()) { + return true; + } + } + + ForwardRaw353(Message); + + // We forwarded it already, so return + return true; + } + case 366: { // end of names list + // :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); + } + } + } + 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(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(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 true; + } + 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 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(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; + } + 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; +} + bool CIRCSock::OnPartMessage(CPartMessage& Message) { const CNick& Nick = Message.GetNick(); CString sChan = Message.GetTarget();