diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 5872be90..0f989ef6 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -155,6 +155,7 @@ class CIRCNetwork { void CheckIRCConnect(); bool PutIRC(const CString& sLine); + bool PutIRC(const CMessage& Message); // Buffers void AddRawBuffer(const CMessage& Format, const CString& sText = "") { diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index 5c748af2..ed36e563 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -56,8 +56,46 @@ class CIRCSock : public CIRCSocket { void SockError(int iErrno, const CString& sDescription) override; void Timeout() override; void ReachedMaxBuffer() override; - + /** Sends a raw data line to the server. + * @param sLine The line to be sent. + * + * The line is first passed \e unmodified to the \ref + * CModule::OnSendToIRC() module hook. If no module halts the process, + * the line is then sent to the server. + * + * Prefer \l PutIRC() instead. + */ + void PutIRCRaw(const CString& sLine); + /** Sends a message to the server. + * See \l PutIRC(const CMessage&) for details. + */ void PutIRC(const CString& sLine); + /** Sends a message to the server. + * @param Message The message to be sent. + * @note Only known and compatible messages and tags are sent. + * + * This method can delay the delivery of the message to honor protection + * from flood. + * + * This method ensures that only tags, that were negotiated with CAP REQ + * and CAP ACK, are sent. Not all IRC server are capable of handling all + * messages and tags. Thus, in order to stay compatible with a variety of + * IRC servers, ZNC has to filter out messages and tags that the server + * has not explicitly acknowleged. + * + * Additional tags can be added via \l CIRCSock::SetTagSupport(). + * + * @warning Bypassing the filter may cause troubles to some older IRC + * servers. + * + * It is possible to bypass the filter by converting a message to a string + * using \l CMessage::ToString(), and passing the resulting raw line to the + * \l CIRCSock::PutIRCRaw(const CString& sLine): + * \code + * pServer->PutIRCRaw(Message.ToString()); + * \endcode + */ + void PutIRC(const CMessage& Message); void PutIRCQuick(const CString& sLine); //!< Should be used for PONG only void ResetChans(); void Quit(const CString& sQuitMsg = ""); @@ -71,6 +109,16 @@ class CIRCSock : public CIRCSocket { * should be resumed again. */ void ResumeCap(); + + bool IsTagEnabled(const CString& sTag) const { + return 1 == m_ssSupportedTags.count(sTag); + } + /** Registers a tag as being supported or unsupported by the server. + * This doesn't affect tags which the server sends. + * @param sTag The tag to register. + * @param bState Whether the client supports the tag. + */ + void SetTagSupport(const CString& sTag, bool bState); // Setters void SetPass(const CString& s) { m_sPass = s; } @@ -175,11 +223,12 @@ class CIRCSock : public CIRCSocket { static const time_t m_uCTCPFloodTime; static const unsigned int m_uCTCPFloodCount; MCString m_mISupport; - std::deque m_vsSendQueue; + std::deque m_vSendQueue; short int m_iSendsAllowed; unsigned short int m_uFloodBurst; double m_fFloodRate; bool m_bFloodProtection; + SCString m_ssSupportedTags; friend class CIRCFloodTimer; }; diff --git a/include/znc/Modules.h b/include/znc/Modules.h index 852e5157..ce0a39ff 100644 --- a/include/znc/Modules.h +++ b/include/znc/Modules.h @@ -1052,12 +1052,18 @@ class CModule { ModHandle GetDLL() { return m_pDLL; } - /** This function sends a given raw IRC line to the IRC server, if we + /** This function sends a given IRC line to the IRC server, if we * are connected to one. Else this line is discarded. * @param sLine The line which should be sent. * @return true if the line was queued for sending. */ virtual bool PutIRC(const CString& sLine); + /** This function sends a given IRC message to the IRC server, if we + * are connected to one. Else this message is discarded. + * @param Message The message which should be sent. + * @return true if the message was queued for sending. + */ + virtual bool PutIRC(const CMessage& Message); /** This function sends a given raw IRC line to a client. * If we are in a module hook which is called for a specific client, * only that client will get the line, else all connected clients will diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index afa8bf89..6ad65e6a 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -1376,6 +1376,17 @@ bool CIRCNetwork::PutIRC(const CString& sLine) { return true; } +bool CIRCNetwork::PutIRC(const CMessage& Message) { + CIRCSock* pIRCSock = GetIRCSock(); + + if (!pIRCSock) { + return false; + } + + pIRCSock->PutIRC(Message); + return true; +} + void CIRCNetwork::ClearQueryBuffer() { std::for_each(m_vQueries.begin(), m_vQueries.end(), std::default_delete()); diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index a8de9343..53734706 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -84,7 +84,7 @@ CIRCSock::CIRCSock(CIRCNetwork* pNetwork) m_lastCTCP(0), m_uNumCTCP(0), m_mISupport(), - m_vsSendQueue(), + m_vSendQueue(), m_iSendsAllowed(pNetwork->GetFloodBurst()), m_uFloodBurst(pNetwork->GetFloodBurst()), m_fFloodRate(pNetwork->GetFloodRate()), @@ -1131,14 +1131,18 @@ bool CIRCSock::OnWallopsMessage(CMessage& Message) { } void CIRCSock::PutIRC(const CString& sLine) { + PutIRC(CMessage(sLine)); +} + +void CIRCSock::PutIRC(const CMessage& Message) { // Only print if the line won't get sent immediately (same condition as in // TrySend()!) if (m_bFloodProtection && m_iSendsAllowed <= 0) { DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") ZNC -> IRC [" - << CDebug::Filter(sLine) << "] (queued)"); + << CDebug::Filter(Message.ToString()) << "] (queued)"); } - m_vsSendQueue.push_back(sLine); + m_vSendQueue.push_back(Message); TrySend(); } @@ -1150,33 +1154,45 @@ void CIRCSock::PutIRCQuick(const CString& sLine) { << m_pNetwork->GetName() << ") ZNC -> IRC [" << CDebug::Filter(sLine) << "] (queued to front)"); } - m_vsSendQueue.push_front(sLine); + m_vSendQueue.emplace_front(sLine); TrySend(); } void CIRCSock::TrySend() { // This condition must be the same as in PutIRC() and PutIRCQuick()! - while (!m_vsSendQueue.empty() && + while (!m_vSendQueue.empty() && (!m_bFloodProtection || m_iSendsAllowed > 0)) { m_iSendsAllowed--; - bool bSkip = false; - CString& sLine = m_vsSendQueue.front(); + CMessage& Message = m_vSendQueue.front(); - CMessage Message(sLine); + MCString mssTags; + for (const auto& it : Message.GetTags()) { + if (IsTagEnabled(it.first)) { + mssTags[it.first] = it.second; + } + } + Message.SetTags(mssTags); Message.SetNetwork(m_pNetwork); + + bool bSkip = false; IRCSOCKMODULECALL(OnSendToIRCMessage(Message), &bSkip); if (!bSkip) { - CString sCopy = Message.ToString(); - IRCSOCKMODULECALL(OnSendToIRC(sCopy), &bSkip); - if (!bSkip) { - DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" - << m_pNetwork->GetName() << ") ZNC -> IRC [" - << CDebug::Filter(sCopy) << "]"); - Write(sCopy + "\r\n"); - } + PutIRCRaw(Message.ToString()); } - m_vsSendQueue.pop_front(); + m_vSendQueue.pop_front(); + } +} + +void CIRCSock::PutIRCRaw(const CString& sLine) { + CString sCopy = sLine; + bool bSkip = false; + IRCSOCKMODULECALL(OnSendToIRC(sCopy), &bSkip); + if (!bSkip) { + DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" + << m_pNetwork->GetName() << ") ZNC -> IRC [" + << CDebug::Filter(sCopy) << "]"); + Write(sCopy + "\r\n"); } } @@ -1473,3 +1489,12 @@ void CIRCSock::ResetChans() { it.second->Reset(); } } + +void CIRCSock::SetTagSupport(const CString& sTag, bool bState) { + if (bState) { + m_ssSupportedTags.insert(sTag); + } else { + m_ssSupportedTags.erase(sTag); + } +} + diff --git a/src/Modules.cpp b/src/Modules.cpp index f763386c..fbc0ac91 100644 --- a/src/Modules.cpp +++ b/src/Modules.cpp @@ -1001,13 +1001,16 @@ bool CModule::OnServerCapAvailable(const CString& sCap) { return false; } void CModule::OnServerCapResult(const CString& sCap, bool bSuccess) {} bool CModule::PutIRC(const CString& sLine) { - return (m_pNetwork) ? m_pNetwork->PutIRC(sLine) : false; + return m_pNetwork ? m_pNetwork->PutIRC(sLine) : false; +} +bool CModule::PutIRC(const CMessage& Message) { + return m_pNetwork ? m_pNetwork->PutIRC(Message) : false; } bool CModule::PutUser(const CString& sLine) { - return (m_pNetwork) ? m_pNetwork->PutUser(sLine, m_pClient) : false; + return m_pNetwork ? m_pNetwork->PutUser(sLine, m_pClient) : false; } bool CModule::PutStatus(const CString& sLine) { - return (m_pNetwork) ? m_pNetwork->PutStatus(sLine, m_pClient) : false; + return m_pNetwork ? m_pNetwork->PutStatus(sLine, m_pClient) : false; } unsigned int CModule::PutModule(const CTable& table) { if (!m_pUser) return 0;