From 39f593fedfd99560e0d2f2e58267d2f72c810eda Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 21 Mar 2015 14:17:53 +0000 Subject: [PATCH 1/7] ignore temporary object files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6d64f15f..eddc2070 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ __pycache__ # Compiled Object files *.o +*.o-* # Compiled Dynamic libraries *.so From 28bb50fa1ae985855e2a91ec967fa2e2a41ec3f9 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 21 Mar 2015 15:22:14 +0000 Subject: [PATCH 2/7] refactor config parsing --- include/znc/znc.h | 9 +- src/znc.cpp | 246 ++++++++++++++++++---------------------------- 2 files changed, 104 insertions(+), 151 deletions(-) diff --git a/include/znc/znc.h b/include/znc/znc.h index e7e2872f..347ad472 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -192,8 +192,13 @@ public: private: CFile* InitPidFile(); - bool DoRehash(CString& sError); - // Returns true if something was done + + bool ReadConfig(CConfig& config, CString& sError); + bool LoadGlobal(CConfig& config, CString& sError); + bool LoadUsers(CConfig& config, CString& sError); + bool LoadListeners(CConfig& config, CString& sError); + void UnloadRemovedModules(const MCString& msModules); + bool HandleUserDeletion(); CString MakeConfigHeader(); bool AddListener(const CString& sLine, CString& sError); diff --git a/src/znc.cpp b/src/znc.cpp index 018f3241..e776c649 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -893,42 +893,23 @@ void CZNC::BackupConfigOnce(const CString& sSuffix) { CUtils::PrintStatus(false, strerror(errno)); } -bool CZNC::ParseConfig(const CString& sConfig, CString& sError) -{ +bool CZNC::ParseConfig(const CString& sConfig, CString& sError) { m_sConfigFile = ExpandConfigPath(sConfig, false); - return DoRehash(sError); + CConfig config; + if (!ReadConfig(config, sError)) + return false; + + if (!LoadGlobal(config, sError)) + return false; + + if (!LoadUsers(config, sError)) + return false; + + return true; } -bool CZNC::RehashConfig(CString& sError) -{ - ALLMODULECALL(OnPreRehash(), NOTHING); - - // This clears m_msDelUsers - HandleUserDeletion(); - - // Mark all users as going-to-be deleted - m_msDelUsers = m_msUsers; - m_msUsers.clear(); - - if (DoRehash(sError)) { - ALLMODULECALL(OnPostRehash(), NOTHING); - - return true; - } - - // Rehashing failed, try to recover - CString s; - while (!m_msDelUsers.empty()) { - AddUser(m_msDelUsers.begin()->second, s); - m_msDelUsers.erase(m_msDelUsers.begin()); - } - - return false; -} - -bool CZNC::DoRehash(CString& sError) -{ +bool CZNC::ReadConfig(CConfig& config, CString& sError) { sError.clear(); CUtils::PrintAction("Opening config [" + m_sConfigFile + "]"); @@ -969,37 +950,47 @@ bool CZNC::DoRehash(CString& sError) m_pLockFile = pFile; CFile &File = *pFile; - CConfig config; if (!config.Parse(File, sError)) { CUtils::PrintStatus(false, sError); return false; } CUtils::PrintStatus(true); + // check if config is from old ZNC version and + // create a backup file if neccessary CString sSavedVersion; config.FindStringEntry("version", sSavedVersion); tuple tSavedVersion = make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(), sSavedVersion.Token(1, false, ".").ToUInt()); tuple tCurrentVersion = make_tuple(VERSION_MAJOR, VERSION_MINOR); if (tSavedVersion < tCurrentVersion) { - if (sSavedVersion.empty()) { - sSavedVersion = "< 0.203"; - } CUtils::PrintMessage("Found old config from ZNC " + sSavedVersion + ". Saving a backup of it."); BackupConfigOnce("pre-" + CString(VERSION_STR)); } else if (tSavedVersion > tCurrentVersion) { CUtils::PrintError("Config was saved from ZNC " + sSavedVersion + ". It may or may not work with current ZNC " + GetVersion()); } - m_vsBindHosts.clear(); - m_vsTrustedProxies.clear(); - m_vsMotd.clear(); + return true; +} - // Delete all listeners - while (!m_vpListeners.empty()) { - delete m_vpListeners[0]; - m_vpListeners.erase(m_vpListeners.begin()); - } +bool CZNC::RehashConfig(CString& sError) { + ALLMODULECALL(OnPreRehash(), NOTHING); + + CConfig config; + if (!ReadConfig(config, sError)) + return false; + + if (!LoadGlobal(config, sError)) + return false; + + // do not reload users - it's dangerous! + + ALLMODULECALL(OnPostRehash(), NOTHING); + return true; +} + +bool CZNC::LoadGlobal(CConfig& config, CString& sError) { + sError.clear(); MCString msModules; // Modules are queued for later loading @@ -1009,15 +1000,8 @@ bool CZNC::DoRehash(CString& sError) CString sModName = sModLine.Token(0); CString sArgs = sModLine.Token(1, true); - if (sModName == "saslauth" && tSavedVersion < make_tuple(0, 207)) { - // XXX compatibility crap, added in 0.207 - CUtils::PrintMessage("saslauth module was renamed to cyrusauth. Loading cyrusauth instead."); - sModName = "cyrusauth"; - } - if (msModules.find(sModName) != msModules.end()) { - sError = "Module [" + sModName + - "] already loaded"; + sError = "Module [" + sModName + "] already loaded"; CUtils::PrintError(sError); return false; } @@ -1051,41 +1035,19 @@ bool CZNC::DoRehash(CString& sError) msModules[sModName] = sArgs; } - CString sISpoofFormat, sISpoofFile; - config.FindStringEntry("ispoofformat", sISpoofFormat); - config.FindStringEntry("ispooffile", sISpoofFile); - if (!sISpoofFormat.empty() || !sISpoofFile.empty()) { - CModule *pIdentFileMod = GetModules().FindModule("identfile"); - if (!pIdentFileMod) { - CUtils::PrintAction("Loading global Module [identfile]"); - - CString sModRet; - bool bModRet = GetModules().LoadModule("identfile", "", CModInfo::GlobalModule, nullptr, nullptr, sModRet); - - CUtils::PrintStatus(bModRet, sModRet); - if (!bModRet) { - sError = sModRet; - return false; - } - - pIdentFileMod = GetModules().FindModule("identfile"); - msModules["identfile"] = ""; - } - - pIdentFileMod->SetNV("File", sISpoofFile); - pIdentFileMod->SetNV("Format", sISpoofFormat); - } - + m_vsMotd.clear(); config.FindStringVector("motd", vsList); for (const CString& sMotd : vsList) { AddMotd(sMotd); } + m_vsBindHosts.clear(); config.FindStringVector("bindhost", vsList); for (const CString& sHost : vsList) { AddBindHost(sHost); } + m_vsTrustedProxies.clear(); config.FindStringVector("trustedproxy", vsList); for (const CString& sProxy : vsList) { AddTrustedProxy(sProxy); @@ -1116,7 +1078,7 @@ bool CZNC::DoRehash(CString& sError) if (config.FindStringEntry("maxbuffersize", sVal)) m_uiMaxBufferSize = sVal.ToUInt(); if (config.FindStringEntry("protectwebsessions", sVal)) - m_bProtectWebSessions = sVal.ToBool(); + m_bProtectWebSessions = sVal.ToBool(); if (config.FindStringEntry("hideversion", sVal)) m_bHideVersion = sVal.ToBool(); @@ -1125,7 +1087,6 @@ bool CZNC::DoRehash(CString& sError) m_sSSLProtocols.Split(" ", vsProtocols, false, "", "", true, true); for (CString& sProtocol : vsProtocols) { - unsigned int uFlag = 0; bool bEnable = sProtocol.TrimPrefix("+"); bool bDisable = sProtocol.TrimPrefix("-"); @@ -1159,53 +1120,28 @@ bool CZNC::DoRehash(CString& sError) } } - // This has to be after SSLCertFile is handled since it uses that value - const char *szListenerEntries[] = { - "listen", "listen6", "listen4", - "listener", "listener6", "listener4" - }; + UnloadRemovedModules(msModules); - for (const char* szEntry : szListenerEntries) { - config.FindStringVector(szEntry, vsList); - for (const CString& sListener : vsList) { - if (!AddListener(szEntry + CString(" ") + sListener, sError)) - return false; - } - } + if (!LoadListeners(config, sError)) + return false; + + return true; +} + +bool CZNC::LoadUsers(CConfig& config, CString& sError) { + sError.clear(); + + m_msUsers.clear(); CConfig::SubConfig subConf; - CConfig::SubConfig::const_iterator subIt; - - config.FindSubConfig("listener", subConf); - for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { - CConfig* pSubConf = subIt->second.m_pSubConfig; - if (!AddListener(pSubConf, sError)) - return false; - if (!pSubConf->empty()) { - sError = "Unhandled lines in Listener config!"; - CUtils::PrintError(sError); - - CZNC::DumpConfig(pSubConf); - return false; - } - } - config.FindSubConfig("user", subConf); - for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { - const CString& sUserName = subIt->first; - CConfig* pSubConf = subIt->second.m_pSubConfig; - CUser* pRealUser = nullptr; + + for (const auto& subIt : subConf) { + const CString& sUserName = subIt.first; + CConfig* pSubConf = subIt.second.m_pSubConfig; CUtils::PrintMessage("Loading user [" + sUserName + "]"); - // Either create a CUser* or use an existing one - map::iterator it = m_msDelUsers.find(sUserName); - - if (it != m_msDelUsers.end()) { - pRealUser = it->second; - m_msDelUsers.erase(it); - } - CUser* pUser = new CUser(sUserName); if (!m_sStatusPrefix.empty()) { @@ -1232,16 +1168,7 @@ bool CZNC::DoRehash(CString& sError) } CString sErr; - if (pRealUser) { - if (!pRealUser->Clone(*pUser, sErr) - || !AddUser(pRealUser, sErr)) { - sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr; - DEBUG("CUser::Clone() failed in rehash"); - } - pUser->SetBeingDeleted(true); - delete pUser; - pUser = nullptr; - } else if (!AddUser(pUser, sErr)) { + if (!AddUser(pUser, sErr)) { sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr; } @@ -1256,19 +1183,54 @@ bool CZNC::DoRehash(CString& sError) } pUser = nullptr; - pRealUser = nullptr; } - if (!config.empty()) { - sError = "Unhandled lines in config!"; + if (m_msUsers.empty()) { + sError = "You must define at least one user in your config."; CUtils::PrintError(sError); - - DumpConfig(&config); return false; } + return true; +} + +bool CZNC::LoadListeners(CConfig& config, CString& sError) { + sError.clear(); + + // Delete all listeners + while (!m_vpListeners.empty()) { + delete m_vpListeners[0]; + m_vpListeners.erase(m_vpListeners.begin()); + } + + CConfig::SubConfig subConf; + config.FindSubConfig("listener", subConf); + + for (const auto& subIt : subConf) { + CConfig* pSubConf = subIt.second.m_pSubConfig; + if (!AddListener(pSubConf, sError)) + return false; + if (!pSubConf->empty()) { + sError = "Unhandled lines in Listener config!"; + CUtils::PrintError(sError); + + CZNC::DumpConfig(pSubConf); + return false; + } + } + + if (m_vpListeners.empty()) { + sError = "You must supply at least one Listener in your config."; + CUtils::PrintError(sError); + return false; + } + + return true; +} + +void CZNC::UnloadRemovedModules(const MCString& msModules) { + // unload modules which are no longer in the config - // Unload modules which are no longer in the config set ssUnload; for (CModule *pCurMod : GetModules()) { if (msModules.find(pCurMod->GetModName()) == msModules.end()) @@ -1281,20 +1243,6 @@ bool CZNC::DoRehash(CString& sError) else CUtils::PrintMessage("Could not unload [" + sMod + "]"); } - - if (m_msUsers.empty()) { - sError = "You must define at least one user in your config."; - CUtils::PrintError(sError); - return false; - } - - if (m_vpListeners.empty()) { - sError = "You must supply at least one Listen port in your config."; - CUtils::PrintError(sError); - return false; - } - - return true; } void CZNC::DumpConfig(const CConfig* pConfig) { From a56d4cae6a7346dcd1ee2b3daee8e0948b6131a7 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 21 Mar 2015 15:22:43 +0000 Subject: [PATCH 3/7] do not call OnAddUser hook during ZNC startup --- include/znc/znc.h | 2 +- src/znc.cpp | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/znc/znc.h b/include/znc/znc.h index 347ad472..d6acba8d 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -154,7 +154,7 @@ public: bool UpdateModule(const CString &sModule); bool DeleteUser(const CString& sUsername); - bool AddUser(CUser* pUser, CString& sErrorRet); + bool AddUser(CUser* pUser, CString& sErrorRet, bool bStartup = false); const std::map & GetUserMap() const { return(m_msUsers); } // Listener yummy diff --git a/src/znc.cpp b/src/znc.cpp index e776c649..5124b40c 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1168,7 +1168,7 @@ bool CZNC::LoadUsers(CConfig& config, CString& sError) { } CString sErr; - if (!AddUser(pUser, sErr)) { + if (!AddUser(pUser, sErr, true)) { sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr; } @@ -1466,7 +1466,7 @@ bool CZNC::DeleteUser(const CString& sUsername) { return true; } -bool CZNC::AddUser(CUser* pUser, CString& sErrorRet) { +bool CZNC::AddUser(CUser* pUser, CString& sErrorRet, bool bStartup) { if (FindUser(pUser->GetUserName()) != nullptr) { sErrorRet = "User already exists"; DEBUG("User [" << pUser->GetUserName() << "] - already exists"); @@ -1478,7 +1478,12 @@ bool CZNC::AddUser(CUser* pUser, CString& sErrorRet) { return false; } bool bFailed = false; - GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), &bFailed); + + // do not call OnAddUser hook during ZNC startup + if (!bStartup) { + GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), &bFailed); + } + if (bFailed) { DEBUG("AddUser [" << pUser->GetUserName() << "] aborted by a module [" << sErrorRet << "]"); From 479f3c3774b4e1b84bd894c34094785302cf0152 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sat, 21 Mar 2015 15:22:50 +0000 Subject: [PATCH 4/7] add changelog entries --- ChangeLog.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index b098dff1..617ed18a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,15 @@ +# (unreleased) + +## Changes + +* workarounds during config parsing for old ZNC versions removed + * incompatible configs give an error during startup indicating the problem + * versions including 0.206 and newer are still supported +* rehash only reloads global settings, including global modules and listeners + * users are not reloaded any more - which makes rehash less dangerous +* OnAddUser hook is only called if actually a new user added + * it is not called during ZNC startup any more + # ZNC 1.6.0 (2015-02-12) ## New From d542599475252e7fd06b221f96fc5ec025567a86 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sun, 22 Mar 2015 11:13:16 +0000 Subject: [PATCH 5/7] add compatibility for pre-1.0 configs --- src/znc.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/znc.cpp b/src/znc.cpp index 5124b40c..2cca9719 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1000,6 +1000,17 @@ bool CZNC::LoadGlobal(CConfig& config, CString& sError) { CString sModName = sModLine.Token(0); CString sArgs = sModLine.Token(1, true); + // compatibility for pre-1.0 configs + CString sSavedVersion; + config.FindStringEntry("version", sSavedVersion); + tuple tSavedVersion = make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(), + sSavedVersion.Token(1, false, ".").ToUInt()); + if (sModName == "saslauth" && tSavedVersion < make_tuple(0, 207)) { + CUtils::PrintMessage("saslauth module was renamed to cyrusauth. Loading cyrusauth instead."); + sModName = "cyrusauth"; + } + // end-compatibility for pre-1.0 configs + if (msModules.find(sModName) != msModules.end()) { sError = "Module [" + sModName + "] already loaded"; CUtils::PrintError(sError); @@ -1203,6 +1214,25 @@ bool CZNC::LoadListeners(CConfig& config, CString& sError) { m_vpListeners.erase(m_vpListeners.begin()); } + // compatibility for pre-1.0 configs + const char *szListenerEntries[] = { + "listen", "listen6", "listen4", + "listener", "listener6", "listener4" + }; + + VCString vsList; + config.FindStringVector("loadmodule", vsList); + + // This has to be after SSLCertFile is handled since it uses that value + for (const char* szEntry : szListenerEntries) { + config.FindStringVector(szEntry, vsList); + for (const CString& sListener : vsList) { + if (!AddListener(szEntry + CString(" ") + sListener, sError)) + return false; + } + } + // end-compatibility for pre-1.0 configs + CConfig::SubConfig subConf; config.FindSubConfig("listener", subConf); From 6fcf4d9ca9e9b0141b5863a50bb421d24b1eccc9 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Sun, 22 Mar 2015 11:26:24 +0000 Subject: [PATCH 6/7] update rehash command description --- src/ClientCommand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClientCommand.cpp b/src/ClientCommand.cpp index 6200b560..4e5a742b 100644 --- a/src/ClientCommand.cpp +++ b/src/ClientCommand.cpp @@ -1720,7 +1720,7 @@ void CClient::HelpUser(const CString& sFilter) { AddCommandHelp(Table, "ListPorts", "", "Show all active listeners", sFilter); AddCommandHelp(Table, "AddPort", "<[+]port> [bindhost [uriprefix]]", "Add another port for ZNC to listen on", sFilter); AddCommandHelp(Table, "DelPort", " [bindhost]", "Remove a port from ZNC", sFilter); - AddCommandHelp(Table, "Rehash", "", "Reload znc.conf from disk", sFilter); + AddCommandHelp(Table, "Rehash", "", "Reload global settings, modules, and listeners from znc.conf", sFilter); AddCommandHelp(Table, "SaveConfig", "", "Save the current settings to disk", sFilter); AddCommandHelp(Table, "ListUsers", "", "List all ZNC users and their connection status", sFilter); AddCommandHelp(Table, "ListAllUserNetworks", "", "List all ZNC users and their networks", sFilter); From 8d2ca76838e172220f423e78f41e9b6936a8e95a Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Tue, 31 Mar 2015 12:28:09 +0100 Subject: [PATCH 7/7] fail if no config version is not found --- src/znc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/znc.cpp b/src/znc.cpp index 2cca9719..56f18a28 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -960,6 +960,11 @@ bool CZNC::ReadConfig(CConfig& config, CString& sError) { // create a backup file if neccessary CString sSavedVersion; config.FindStringEntry("version", sSavedVersion); + if (sSavedVersion.empty()) { + CUtils::PrintError("Config does not contain a version identifier. It may be be too old or corrupt."); + return false; + } + tuple tSavedVersion = make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(), sSavedVersion.Token(1, false, ".").ToUInt()); tuple tCurrentVersion = make_tuple(VERSION_MAJOR, VERSION_MINOR);