From dcd259778af1098b9d1a2c07d2a3890175493bfa Mon Sep 17 00:00:00 2001 From: Dylan Lloyd Date: Mon, 23 Nov 2015 20:32:06 -0800 Subject: [PATCH 1/4] support separate SSLKeyFile & SSLDHParamFile configuration --- include/znc/znc.h | 4 ++++ src/Listener.cpp | 2 ++ src/znc.cpp | 18 +++++++++++++++++- third_party/Csocket | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/znc/znc.h b/include/znc/znc.h index 468bddb4..190b6194 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -128,6 +128,8 @@ public: CString GetUserPath() const; CString GetModPath() const; CString GetPemLocation() const; + CString GetKeyLocation() const; + CString GetDHParamLocation() const; const CString& GetConfigFile() const { return m_sConfigFile; } bool WritePemFile(); /** @deprecated Since 1.7.0. List of allowed bind hosts was a flawed design. */ @@ -233,6 +235,8 @@ protected: CString m_sStatusPrefix; CString m_sPidFile; CString m_sSSLCertFile; + CString m_sSSLKeyFile; + CString m_sSSLDHParamFile; CString m_sSSLCiphers; CString m_sSSLProtocols; VCString m_vsBindHosts; // TODO: remove (deprecated in 1.7.0) diff --git a/src/Listener.cpp b/src/Listener.cpp index 0cd2a374..b55916ec 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -35,6 +35,8 @@ bool CListener::Listen() { if (IsSSL()) { bSSL = true; m_pListener->SetPemLocation(CZNC::Get().GetPemLocation()); + m_pListener->SetKeyLocation(CZNC::Get().GetKeyLocation()); + m_pListener->SetDHParamLocation(CZNC::Get().GetDHParamLocation()); } #endif diff --git a/src/znc.cpp b/src/znc.cpp index 7c277323..146f8fbe 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -53,6 +53,8 @@ CZNC::CZNC() m_sStatusPrefix(""), m_sPidFile(""), m_sSSLCertFile(""), + m_sSSLKeyFile(""), + m_sSSLDHParamFile(""), m_sSSLCiphers(""), m_sSSLProtocols(""), m_vsBindHosts(), @@ -364,7 +366,7 @@ void CZNC::InitDirs(const CString& sArgvPath, const CString& sDataDir) { m_sZNCPath = sDataDir; } - m_sSSLCertFile = m_sZNCPath + "/znc.pem"; + m_sSSLCertFile = m_sSSLKeyFile = m_sSSLDHParamFile = m_sZNCPath + "/znc.pem"; } CString CZNC::GetConfPath(bool bAllowMkDir) const { @@ -413,6 +415,14 @@ CString CZNC::GetPemLocation() const { return CDir::ChangeDir("", m_sSSLCertFile); } +CString CZNC::GetKeyLocation() const { + return CDir::ChangeDir("", m_sSSLKeyFile); +} + +CString CZNC::GetDHParamLocation() const { + return CDir::ChangeDir("", m_sSSLDHParamFile); +} + CString CZNC::ExpandConfigPath(const CString& sConfigFile, bool bAllowMkDir) { CString sRetPath; @@ -462,6 +472,8 @@ bool CZNC::WriteConfig() { config.AddKeyValuePair("AnonIPLimit", CString(m_uiAnonIPLimit)); config.AddKeyValuePair("MaxBufferSize", CString(m_uiMaxBufferSize)); config.AddKeyValuePair("SSLCertFile", CString(m_sSSLCertFile)); + config.AddKeyValuePair("SSLKeyFile", CString(m_sSSLKeyFile)); + config.AddKeyValuePair("SSLDHParamFile", CString(m_sSSLDHParamFile)); config.AddKeyValuePair("ProtectWebSessions", CString(m_bProtectWebSessions)); config.AddKeyValuePair("HideVersion", CString(m_bHideVersion)); config.AddKeyValuePair("Version", CString(VERSION_STR)); @@ -1099,6 +1111,10 @@ bool CZNC::LoadGlobal(CConfig& config, CString& sError) { m_sStatusPrefix = sVal; if (config.FindStringEntry("sslcertfile", sVal)) m_sSSLCertFile = sVal; + if (config.FindStringEntry("sslkeyfile", sVal)) + m_sSSLKeyFile = sVal; + if (config.FindStringEntry("ssldhparamfile", sVal)) + m_sSSLDHParamFile = sVal; if (config.FindStringEntry("sslciphers", sVal)) m_sSSLCiphers = sVal; if (config.FindStringEntry("skin", sVal)) diff --git a/third_party/Csocket b/third_party/Csocket index 8d720c94..2852fc36 160000 --- a/third_party/Csocket +++ b/third_party/Csocket @@ -1 +1 @@ -Subproject commit 8d720c94977947ac73dce6901b92a13ee16aa2a9 +Subproject commit 2852fc364542c0635058f90d8a3dbad0455e9e7c From 391f89d6e21c5dd0a69af71140225de1b3e51795 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sat, 28 Nov 2015 19:19:37 +0000 Subject: [PATCH 2/4] Travis: notify #znc-dev through ZNC-Linker --- .travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 951ae297..b75ea51a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,10 +98,16 @@ after_success: notifications: irc: channels: - # travis encrypt "irc.freenode.net#znc-dev" -r znc/znc - # prevents forks from spamming #znc-dev by just enabling Travis - - secure: "ACdKvJT+gvuzDTfQkhnHI8aQNtXHI1tJOgFPss9XpF8025KYxrvhuff15VRhYuDCuNSuqhZYG9Oz+ZCQzu85ravkXVa3X+ze8SRoIIwAN2tpkr7KTfbJc4gD2S/RZUi432ICiQznHWnhV209XSLXY4qBhnR0OkCg5/pY8zievYw=" + # irc.freenode.net #znc-dev (via ZNC-Linker) + # encryption prevents forks from spamming #znc-dev by just enabling Travis + # Format is poorly documented at https://github.com/travis-ci/travis-ci/issues/2813 + - secure: "L7UcaWs3Y0y0QlOzUFnCJQ3clFXcik1ygswDdzE3q4lyUa7/4WeAoocJWLxp9PPL6hXZygsiXj4gIBWq4wqDEbsZzO+xZvleq2i3KGf7YdDgPTYJJN9ppIHR3Q+pmnODtYu1dHpLTunS2g7vVzJHNjIKQvORl9CTx9lDNHKjG5A=" on_success: always on_failure: always + skip_join: true + nick: + secure: "UGh4tbpviOD438ZOi+I3W6eh+R68tbpNIJc0uf5FK6xhHLLo3FRIozkL7CRVA3TxYHuahwerZpBR4TbAyc+gVUn5YRHjdsMIPHPCW/kh8ZkEJKNYXkHerbH0Np6PRLI58l6lmjMQbiWn+lqlgsHdVsyknqxDwoj7JDieE+5X53c=" + password: + secure: "B2F1coXddY1H0/v3OkiPnUlbnSN/wapRay1PUbN8tvm7IDYOjpivhsRk+31/4pauKOoF4AZhzP6F9mhGxkGYBQ23Td5Y6+uZeaDnTYnXjs8fbV0QY1Mx+2EkHDSkLQoYlYr06pK9q55X3uLK1zTz5Ye0Po3KzbeydxOyyG+xBGg=" sudo: required dist: trusty From 569f0575616e09b121410f642e2357707954ad9b Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sun, 29 Nov 2015 00:56:26 +0000 Subject: [PATCH 3/4] Call CTCP callback for actions too, as it was before switch to CMessage. Partyline had both of callbacks, one of which is redundant. Fix #1134 Fix #1190 --- modules/partyline.cpp | 4 ---- src/IRCSock.cpp | 4 ++++ test/Integration.cpp | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/partyline.cpp b/modules/partyline.cpp index 30960791..08ec8012 100644 --- a/modules/partyline.cpp +++ b/modules/partyline.cpp @@ -501,10 +501,6 @@ public: return HandleMessage("NOTICE", sTarget, sMessage); } - EModRet OnUserAction(CString& sTarget, CString& sMessage) override { - return HandleMessage("PRIVMSG", sTarget, "\001ACTION " + sMessage + "\001"); - } - EModRet OnUserCTCP(CString& sTarget, CString& sMessage) override { return HandleMessage("PRIVMSG", sTarget, "\001" + sMessage + "\001"); } diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 26ed560f..4b666d23 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -290,6 +290,8 @@ bool CIRCSock::OnActionMessage(CActionMessage& Message) { 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; @@ -310,6 +312,8 @@ bool CIRCSock::OnActionMessage(CActionMessage& Message) { 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; diff --git a/test/Integration.cpp b/test/Integration.cpp index 4fbfb11e..0effe6ac 100644 --- a/test/Integration.cpp +++ b/test/Integration.cpp @@ -1252,4 +1252,19 @@ TEST_F(ZNCTest, ShellModule) { client.ReadUntil("PRIVMSG nick :znc$"); } +TEST_F(ZNCTest, WatchModule) { + // TODO test other messages + // TODO test options + auto znc = Run();Z; + auto ircd = ConnectIRCd();Z; + auto client = LoginClient();Z; + client.Write("znc loadmod watch"); + client.Write("PRIVMSG *watch :add *"); + client.ReadUntil("Adding entry:"); + ircd.Write(":server 001 nick :Hello"); + ircd.Write(":nick JOIN :#znc"); + ircd.Write(":n!i@h PRIVMSG #znc :\001ACTION foo\001"); + client.ReadUntil(":$*!watch@znc.in PRIVMSG nick :* CTCP: n [ACTION foo] to [#znc]"); +} + } // namespace From bee266a08e2c38e780764b8da277d10bd5d0a61f Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sun, 29 Nov 2015 12:26:17 +0000 Subject: [PATCH 4/4] Fix test after fixing #1190 --- include/znc/Message.h | 14 +++++- test/IRCSockTest.cpp | 113 ++++++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 29 deletions(-) diff --git a/include/znc/Message.h b/include/znc/Message.h index 2b930aaa..b2958c4f 100644 --- a/include/znc/Message.h +++ b/include/znc/Message.h @@ -117,21 +117,23 @@ public: #ifndef SWIG template M& As() ZNC_LVREFQUAL { + static_assert(std::is_base_of{}, "Must be subclass of CMessage"); static_assert(sizeof(M) == sizeof(CMessage), "No data members allowed in CMessage subclasses."); return static_cast(*this); } template const M& As() const ZNC_LVREFQUAL { + static_assert(std::is_base_of{}, "Must be subclass of CMessage"); static_assert(sizeof(M) == sizeof(CMessage), "No data members allowed in CMessage subclasses."); return static_cast(*this); } - template + template {}>::type> operator M&() ZNC_LVREFQUAL { return As(); } - template + template {}>::type> operator const M&() const ZNC_LVREFQUAL { return As(); } @@ -158,6 +160,14 @@ private: bool m_bColon = false; }; +// For gtest +#ifdef GTEST_FAIL +template {}>::type> +inline ::std::ostream& operator<<(::std::ostream& os, const M& msg) { + return os << msg.ToString().Escape_n(CString::EDEBUG); +} +#endif + // The various CMessage subclasses are "mutable views" to the data held by CMessage. // They provide convenient access to message type speficic attributes, but are not // allowed to hold extra data of their own. diff --git a/test/IRCSockTest.cpp b/test/IRCSockTest.cpp index 1a79877c..2fceb899 100644 --- a/test/IRCSockTest.cpp +++ b/test/IRCSockTest.cpp @@ -19,9 +19,14 @@ #include "IRCTest.h" #include -using ::testing::IsEmpty; -using ::testing::ElementsAre; -using ::testing::ContainerEq; +using testing::ElementsAre; +using testing::ContainerEq; +using testing::InSequence; +using testing::Invoke; +using testing::IsEmpty; +using testing::Mock; +using testing::ResultOf; +using testing::_; class IRCSockTest : public IRCTest { protected: @@ -36,38 +41,92 @@ TEST_F(IRCSockTest, OnAccountMessage) { } TEST_F(IRCSockTest, OnActionMessage) { + // 2 callbacks are called in row: OnCTCP, OnAction. + // If OnCTCP returns HALT, OnAction isn't called. + struct ActionModule : TestModule { + ActionModule() { + Reset(); + } + void Reset() { + Mock::VerifyAndClear(this); + EXPECT_CALL(*this, OnPrivCTCPMessage(_)).Times(0); + EXPECT_CALL(*this, OnChanCTCPMessage(_)).Times(0); + EXPECT_CALL(*this, OnPrivActionMessage(_)).Times(0); + EXPECT_CALL(*this, OnChanActionMessage(_)).Times(0); + TestModule::Reset(); + } + MOCK_METHOD1(OnPrivCTCPMessage, EModRet(CCTCPMessage&)); + MOCK_METHOD1(OnChanCTCPMessage, EModRet(CCTCPMessage&)); + MOCK_METHOD1(OnPrivActionMessage, EModRet(CActionMessage&)); + MOCK_METHOD1(OnChanActionMessage, EModRet(CActionMessage&)); + }; + ActionModule testModule; + CZNC::Get().GetModules().push_back(&testModule); + CChan* pExpectedChan = m_pTestChan; + CMessage msg(":nick PRIVMSG #chan :\001ACTION hello\001"); - m_pTestModule->eAction = CModule::HALT; + auto CON = Invoke([&](CMessage& m) { + EXPECT_EQ(msg.ToString(), m.ToString()); + EXPECT_EQ(m_pTestNetwork, m.GetNetwork()); + EXPECT_EQ(pExpectedChan, m.GetChan()); + return CModule::CONTINUE; + }); + auto HAL = Invoke([&](CMessage& m) { + EXPECT_EQ(msg.ToString(), m.ToString()); + EXPECT_EQ(m_pTestNetwork, m.GetNetwork()); + EXPECT_EQ(pExpectedChan, m.GetChan()); + return CModule::HALT; + }); + auto Reset = [&]() { testModule.Reset(); m_pTestClient->Reset(); }; + + Reset(); + { + InSequence seq; + EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(CON); + EXPECT_CALL(testModule, OnChanActionMessage(_)).WillOnce(CON); + } m_pTestSock->ReadLine(msg.ToString()); - - EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnChanActionMessage")); - EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString())); - EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork)); - EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan)); - EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt - - m_pTestModule->eAction = CModule::CONTINUE; - m_pTestSock->ReadLine(msg.ToString()); - EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString())); - m_pTestClient->Reset(); - m_pTestModule->Reset(); + Reset(); + { + InSequence seq; + EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(CON); + EXPECT_CALL(testModule, OnChanActionMessage(_)).WillOnce(HAL); + } + m_pTestSock->ReadLine(msg.ToString()); + EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); + + Reset(); + EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(HAL); + m_pTestSock->ReadLine(msg.ToString()); + EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); msg.Parse(":nick PRIVMSG me :\001ACTION hello\001"); - m_pTestModule->eAction = CModule::HALT; + pExpectedChan = nullptr; + + Reset(); + { + InSequence seq; + EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(CON); + EXPECT_CALL(testModule, OnPrivActionMessage(_)).WillOnce(CON); + } m_pTestSock->ReadLine(msg.ToString()); - - EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnPrivActionMessage")); - EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString())); - EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork)); - EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr)); - EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt - - m_pTestModule->eAction = CModule::CONTINUE; - m_pTestSock->ReadLine(msg.ToString()); - EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString())); + + Reset(); + { + InSequence seq; + EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(CON); + EXPECT_CALL(testModule, OnPrivActionMessage(_)).WillOnce(HAL); + } + m_pTestSock->ReadLine(msg.ToString()); + EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); + + Reset(); + EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(HAL); + m_pTestSock->ReadLine(msg.ToString()); + EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); } TEST_F(IRCSockTest, OnAwayMessage) {