void OnGetAvailableMods(set<CModInfo>& ssMods, CModInfo::EModuleType eType) override { CDir Dir; CModules::ModDirList dirs = CModules::GetModDirs(); while (!dirs.empty()) { set<CString> already; Dir.Fill(dirs.front().first); for (unsigned int a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); sPath.TrimSuffix(sName); if (!File.IsDir()) { if (sName.WildCmp("*.pyc")) { sName.RightChomp(4); } else if (sName.WildCmp("*.py") || sName.WildCmp("*.so")) { sName.RightChomp(3); } else { continue; } } TryAddModInfo(sPath, sName, ssMods, already, eType); } dirs.pop(); } }
void CModules::GetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) { ssMods.clear(); unsigned int a = 0; CDir Dir; ModDirList dirs = GetModDirs(); while (!dirs.empty()) { Dir.FillByWildcard(dirs.front().first, "*.so"); dirs.pop(); for (a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); CModInfo ModInfo; sName.RightChomp(3); CString sIgnoreRetMsg; if (GetModPathInfo(ModInfo, sName, sPath, sIgnoreRetMsg)) { if (ModInfo.IsGlobal() == bGlobal) { ssMods.insert(ModInfo); } } } } GLOBALMODULECALL(OnGetAvailableMods(ssMods, bGlobal), NULL, NULL, ); }
virtual void OnGetAvailableMods(set<CModInfo>& ssMods, CModInfo::EModuleType eType) { unsigned int a = 0; CDir Dir; CModules::ModDirList dirs = CModules::GetModDirs(); while (!dirs.empty()) { Dir.FillByWildcard(dirs.front().first, "*.pm"); dirs.pop(); for (a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); CModInfo ModInfo; sName.RightChomp(3); PSTART; PUSH_STR(sPath); PUSH_STR(sName); PUSH_PTR(CModInfo*, &ModInfo); PCALL("ZNC::Core::ModInfoByPath"); if (!SvTRUE(ERRSV) && ret == 2) { ssMods.insert(ModInfo); } PEND; } } }
virtual void OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) { if (bGlobal) { return; } unsigned int a = 0; CDir Dir; CModules::ModDirList dirs = CModules::GetModDirs(); while (!dirs.empty()) { Dir.FillByWildcard(dirs.front().first, "*.pm"); dirs.pop(); for (a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); CModInfo ModInfo; sName.RightChomp(3); PSTART; PUSH_STR(sPath); PUSH_STR(sName); PCALL("ZNC::Core::ModInfoByPath"); if (!SvTRUE(ERRSV) && ret == 1) { ModInfo.SetGlobal(false); ModInfo.SetDescription(PString(ST(0))); ModInfo.SetName(sName); ModInfo.SetPath(sPath); ssMods.insert(ModInfo); } PEND; } } }
virtual void OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) { if (bGlobal) { return; } CDir Dir; CModules::ModDirList dirs = CModules::GetModDirs(); while (!dirs.empty()) { set<CString> already; Dir.FillByWildcard(dirs.front().first, "*.py"); for (unsigned int a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); sPath.TrimSuffix(sName); sName.RightChomp(3); TryAddModInfo(sPath, sName, ssMods, already); } Dir.FillByWildcard(dirs.front().first, "*.pyc"); for (unsigned int a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); sPath.TrimSuffix(sName); sName.RightChomp(4); TryAddModInfo(sPath, sName, ssMods, already); } Dir.FillByWildcard(dirs.front().first, "*.so"); for (unsigned int a = 0; a < Dir.size(); a++) { CFile& File = *Dir[a]; CString sName = File.GetShortName(); CString sPath = File.GetLongName(); sPath.TrimSuffix(sName); sName.RightChomp(3); TryAddModInfo(sPath, sName, ssMods, already); } dirs.pop(); } }
CString CDir::ChangeDir(const CString& sPath, const CString& sAdd, const CString& sHome) { CString sHomeDir(sHome); if (sHomeDir.empty()) { sHomeDir = CFile::GetHomePath(); } if (sAdd == "~") { return sHomeDir; } CString sAddDir(sAdd); if (sAddDir.Left(2) == "~/") { sAddDir.LeftChomp(); sAddDir = sHomeDir + sAddDir; } CString sRet = ((sAddDir.size()) && (sAddDir[0] == '/')) ? "" : sPath; sAddDir += "/"; CString sCurDir; if (sRet.Right(1) == "/") { sRet.RightChomp(); } for (unsigned int a = 0; a < sAddDir.size(); a++) { switch (sAddDir[a]) { case '/': if (sCurDir == "..") { sRet = sRet.substr(0, sRet.rfind('/')); } else if ((sCurDir != "") && (sCurDir != ".")) { sRet += "/" + sCurDir; } sCurDir = ""; break; default: sCurDir += sAddDir[a]; break; } } return (sRet.empty()) ? "/" : sRet; }
// Config Parser. Might drop this. bool CConfig::Parse(CFile& file, CString& sErrorMsg) { CString sLine; unsigned int uLineNum = 0; CConfig *pActiveConfig = this; std::stack<ConfigStackEntry> ConfigStack; bool bCommented = false; // support for /**/ style comments if (!file.Seek(0)) { sErrorMsg = "Could not seek to the beginning of the config."; return false; } while (file.ReadLine(sLine)) { uLineNum++; #define ERROR(arg) do { \ std::stringstream stream; \ stream << "Error on line " << uLineNum << ": " << arg; \ sErrorMsg = stream.str(); \ m_SubConfigs.clear(); \ m_ConfigEntries.clear(); \ return false; \ } while (0) // Remove all leading spaces and trailing line endings sLine.TrimLeft(); sLine.TrimRight("\r\n"); if (bCommented || sLine.Left(2) == "/*") { /* Does this comment end on the same line again? */ bCommented = (sLine.Right(2) != "*/"); continue; } if ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) { continue; } if ((sLine.Left(1) == "<") && (sLine.Right(1) == ">")) { sLine.LeftChomp(); sLine.RightChomp(); sLine.Trim(); CString sTag = sLine.Token(0); CString sValue = sLine.Token(1, true); sTag.Trim(); sValue.Trim(); if (sTag.Left(1) == "/") { sTag = sTag.substr(1); if (!sValue.empty()) ERROR("Malformated closing tag. Expected \"</" << sTag << ">\"."); if (ConfigStack.empty()) ERROR("Closing tag \"" << sTag << "\" which is not open."); const struct ConfigStackEntry& entry = ConfigStack.top(); CConfig myConfig(entry.Config); CString sName(entry.sName); if (!sTag.Equals(entry.sTag)) ERROR("Closing tag \"" << sTag << "\" which is not open."); // This breaks entry ConfigStack.pop(); if (ConfigStack.empty()) pActiveConfig = this; else pActiveConfig = &ConfigStack.top().Config; SubConfig &conf = pActiveConfig->m_SubConfigs[sTag.AsLower()]; SubConfig::const_iterator it = conf.find(sName); if (it != conf.end()) ERROR("Duplicate entry for tag \"" << sTag << "\" name \"" << sName << "\"."); conf[sName] = CConfigEntry(myConfig); } else { if (sValue.empty()) ERROR("Empty block name at begin of block."); ConfigStack.push(ConfigStackEntry(sTag.AsLower(), sValue)); pActiveConfig = &ConfigStack.top().Config; } continue; } // If we have a regular line, figure out where it goes CString sName = sLine.Token(0, false, "="); CString sValue = sLine.Token(1, true, "="); // Only remove the first space, people might want // leading spaces (e.g. in the MOTD). if (sValue.Left(1) == " ") sValue.LeftChomp(); // We don't have any names with spaces, trim all // leading/trailing spaces. sName.Trim(); if (sName.empty() || sValue.empty()) ERROR("Malformed line"); CString sNameLower = sName.AsLower(); pActiveConfig->m_ConfigEntries[sNameLower].push_back(sValue); } if (bCommented) ERROR("Comment not closed at end of file."); if (!ConfigStack.empty()) { const CString& sTag = ConfigStack.top().sTag; ERROR("Not all tags are closed at the end of the file. Inner-most open tag is \"" << sTag << "\"."); } return true; }
bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { TOption<const CString&> StringOptions[] = { { "nick", &CUser::SetNick }, { "quitmsg", &CUser::SetQuitMsg }, { "altnick", &CUser::SetAltNick }, { "ident", &CUser::SetIdent }, { "realname", &CUser::SetRealName }, { "chanmodes", &CUser::SetDefaultChanModes }, { "bindhost", &CUser::SetBindHost }, { "vhost", &CUser::SetBindHost }, { "dccbindhost", &CUser::SetDCCBindHost }, { "dccvhost", &CUser::SetDCCBindHost }, { "timestampformat", &CUser::SetTimestampFormat }, { "skin", &CUser::SetSkinName }, { "language", &CUser::SetLanguage }, }; size_t numStringOptions = sizeof(StringOptions) / sizeof(StringOptions[0]); TOption<unsigned int> UIntOptions[] = { { "jointries", &CUser::SetJoinTries }, { "maxjoins", &CUser::SetMaxJoins }, }; size_t numUIntOptions = sizeof(UIntOptions) / sizeof(UIntOptions[0]); TOption<bool> BoolOptions[] = { { "keepbuffer", &CUser::SetKeepBuffer }, { "multiclients", &CUser::SetMultiClients }, { "denyloadmod", &CUser::SetDenyLoadMod }, { "admin", &CUser::SetAdmin }, { "denysetbindhost", &CUser::SetDenySetBindHost }, { "denysetvhost", &CUser::SetDenySetBindHost }, { "appendtimestamp", &CUser::SetTimestampAppend }, { "prependtimestamp", &CUser::SetTimestampPrepend }, { "ircconnectenabled", &CUser::SetIRCConnectEnabled }, }; size_t numBoolOptions = sizeof(BoolOptions) / sizeof(BoolOptions[0]); for (size_t i = 0; i < numStringOptions; i++) { CString sValue; if (pConfig->FindStringEntry(StringOptions[i].name, sValue)) (this->*StringOptions[i].pSetter)(sValue); } for (size_t i = 0; i < numUIntOptions; i++) { CString sValue; if (pConfig->FindStringEntry(UIntOptions[i].name, sValue)) (this->*UIntOptions[i].pSetter)(sValue.ToUInt()); } for (size_t i = 0; i < numBoolOptions; i++) { CString sValue; if (pConfig->FindStringEntry(BoolOptions[i].name, sValue)) (this->*BoolOptions[i].pSetter)(sValue.ToBool()); } VCString vsList; VCString::const_iterator vit; pConfig->FindStringVector("allow", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { AddAllowedHost(*vit); } pConfig->FindStringVector("ctcpreply", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { const CString& sValue = *vit; AddCTCPReply(sValue.Token(0), sValue.Token(1, true)); } CString sValue; CString sDCCLookupValue; pConfig->FindStringEntry("dcclookupmethod", sDCCLookupValue); if (pConfig->FindStringEntry("bouncedccs", sValue)) { if (sValue.ToBool()) { CUtils::PrintAction("Loading Module [bouncedcc]"); CString sModRet; bool bModRet = GetModules().LoadModule("bouncedcc", "", CModInfo::UserModule, this, NULL, sModRet); CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { sError = sModRet; return false; } if (sDCCLookupValue.Equals("Client")) { GetModules().FindModule("bouncedcc")->SetNV("UseClientIP", "1"); } } } if (pConfig->FindStringEntry("buffer", sValue)) SetBufferCount(sValue.ToUInt(), true); if (pConfig->FindStringEntry("awaysuffix", sValue)) { CUtils::PrintMessage("WARNING: AwaySuffix has been depricated, instead try -> LoadModule = awaynick %nick%_" + sValue); } if (pConfig->FindStringEntry("autocycle", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle"); } if (pConfig->FindStringEntry("keepnick", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: KeepNick has been deprecated, instead try -> LoadModule = keepnick"); } if (pConfig->FindStringEntry("statusprefix", sValue)) { if (!SetStatusPrefix(sValue)) { sError = "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces."; CUtils::PrintError(sError); return false; } } if (pConfig->FindStringEntry("timezoneoffset", sValue)) { SetTimezoneOffset(sValue.ToDouble()); } if (pConfig->FindStringEntry("timestamp", sValue)) { if (!sValue.Trim_n().Equals("true")) { if (sValue.Trim_n().Equals("append")) { SetTimestampAppend(true); SetTimestampPrepend(false); } else if (sValue.Trim_n().Equals("prepend")) { SetTimestampAppend(false); SetTimestampPrepend(true); } else if (sValue.Trim_n().Equals("false")) { SetTimestampAppend(false); SetTimestampPrepend(false); } else { SetTimestampFormat(sValue); } } } pConfig->FindStringEntry("pass", sValue); // There are different formats for this available: // Pass = <plain text> // Pass = <md5 hash> - // Pass = plain#<plain text> // Pass = <hash name>#<hash> // Pass = <hash name>#<salted hash>#<salt># // 'Salted hash' means hash of 'password' + 'salt' // Possible hashes are md5 and sha256 if (sValue.Right(1) == "-") { sValue.RightChomp(); sValue.Trim(); SetPass(sValue, CUser::HASH_MD5); } else { CString sMethod = sValue.Token(0, false, "#"); CString sPass = sValue.Token(1, true, "#"); if (sMethod == "md5" || sMethod == "sha256") { CUser::eHashType type = CUser::HASH_MD5; if (sMethod == "sha256") type = CUser::HASH_SHA256; CString sSalt = sPass.Token(1, false, "#"); sPass = sPass.Token(0, false, "#"); SetPass(sPass, type, sSalt); } else if (sMethod == "plain") { SetPass(sPass, CUser::HASH_NONE); } else { SetPass(sValue, CUser::HASH_NONE); } } CConfig::SubConfig subConf; CConfig::SubConfig::const_iterator subIt; pConfig->FindSubConfig("pass", subConf); if (!sValue.empty() && !subConf.empty()) { sError = "Password defined more than once"; CUtils::PrintError(sError); return false; } subIt = subConf.begin(); if (subIt != subConf.end()) { CConfig* pSubConf = subIt->second.m_pSubConfig; CString sHash; CString sMethod; CString sSalt; CUser::eHashType method; pSubConf->FindStringEntry("hash", sHash); pSubConf->FindStringEntry("method", sMethod); pSubConf->FindStringEntry("salt", sSalt); if (sMethod.empty() || sMethod.Equals("plain")) method = CUser::HASH_NONE; else if (sMethod.Equals("md5")) method = CUser::HASH_MD5; else if (sMethod.Equals("sha256")) method = CUser::HASH_SHA256; else { sError = "Invalid hash method"; CUtils::PrintError(sError); return false; } SetPass(sHash, method, sSalt); if (!pSubConf->empty()) { sError = "Unhandled lines in config!"; CUtils::PrintError(sError); CZNC::DumpConfig(pSubConf); return false; } subIt++; } if (subIt != subConf.end()) { sError = "Password defined more than once"; CUtils::PrintError(sError); return false; } pConfig->FindSubConfig("network", subConf); for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { const CString& sNetworkName = subIt->first; CIRCNetwork *pNetwork = FindNetwork(sNetworkName); if (!pNetwork) { pNetwork = new CIRCNetwork(this, sNetworkName); } if (!pNetwork->ParseConfig(subIt->second.m_pSubConfig, sError)) { return false; } } if (pConfig->FindStringVector("server", vsList, false) || pConfig->FindStringVector("chan", vsList, false) || pConfig->FindSubConfig("chan", subConf, false)) { CIRCNetwork *pNetwork = FindNetwork("user"); if (!pNetwork) { pNetwork = AddNetwork("user"); } if (pNetwork) { CUtils::PrintMessage("NOTICE: Found deprecated config, upgrading to a network"); if (!pNetwork->ParseConfig(pConfig, sError, true)) { return false; } } } pConfig->FindStringVector("loadmodule", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { sValue = *vit; CString sModName = sValue.Token(0); // XXX Legacy crap, added in ZNC 0.089 if (sModName == "discon_kick") { CUtils::PrintMessage("NOTICE: [discon_kick] was renamed, loading [disconkick] instead"); sModName = "disconkick"; } // XXX Legacy crap, added in ZNC 0.099 if (sModName == "fixfreenode") { CUtils::PrintMessage("NOTICE: [fixfreenode] doesn't do anything useful anymore, ignoring it"); continue; } CUtils::PrintAction("Loading Module [" + sModName + "]"); CString sModRet; CString sArgs = sValue.Token(1, true); bool bModRet; CModInfo ModInfo; if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sModName, sModRet)) { sError = "Unable to find modinfo [" + sModName + "] [" + sModRet + "]"; return false; } if (!ModInfo.SupportsType(CModInfo::UserModule) && ModInfo.SupportsType(CModInfo::NetworkModule)) { CUtils::PrintMessage("NOTICE: Module [" + sModName + "] is a network module, loading module for all networks in user."); // Do they have old NV? CFile fNVFile = CFile(GetUserPath() + "/moddata/" + sModName + "/.registry"); for (vector<CIRCNetwork*>::iterator it = m_vIRCNetworks.begin(); it != m_vIRCNetworks.end(); ++it) { if (fNVFile.Exists()) { CString sNetworkModPath = (*it)->GetNetworkPath() + "/moddata/" + sModName; if (!CFile::Exists(sNetworkModPath)) { CDir::MakeDir(sNetworkModPath); } fNVFile.Copy(sNetworkModPath + "/.registry"); } bModRet = (*it)->GetModules().LoadModule(sModName, sArgs, CModInfo::NetworkModule, this, *it, sModRet); if (!bModRet) { break; } } } else { bModRet = GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, this, NULL, sModRet); } CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { sError = sModRet; return false; } continue; } return true; }
bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { TOption<const CString&> StringOptions[] = { { "nick", &CUser::SetNick }, { "quitmsg", &CUser::SetQuitMsg }, { "altnick", &CUser::SetAltNick }, { "ident", &CUser::SetIdent }, { "realname", &CUser::SetRealName }, { "chanmodes", &CUser::SetDefaultChanModes }, { "bindhost", &CUser::SetBindHost }, { "vhost", &CUser::SetBindHost }, { "dccbindhost", &CUser::SetDCCBindHost }, { "dccvhost", &CUser::SetDCCBindHost }, { "timestampformat", &CUser::SetTimestampFormat }, { "skin", &CUser::SetSkinName }, }; size_t numStringOptions = sizeof(StringOptions) / sizeof(StringOptions[0]); TOption<unsigned int> UIntOptions[] = { { "jointries", &CUser::SetJoinTries }, { "maxjoins", &CUser::SetMaxJoins }, }; size_t numUIntOptions = sizeof(UIntOptions) / sizeof(UIntOptions[0]); TOption<bool> BoolOptions[] = { { "keepbuffer", &CUser::SetKeepBuffer }, { "multiclients", &CUser::SetMultiClients }, { "bouncedccs", &CUser::SetBounceDCCs }, { "denyloadmod", &CUser::SetDenyLoadMod }, { "admin", &CUser::SetAdmin }, { "denysetbindhost", &CUser::SetDenySetBindHost }, { "denysetvhost", &CUser::SetDenySetBindHost }, { "appendtimestamp", &CUser::SetTimestampAppend }, { "prependtimestamp", &CUser::SetTimestampPrepend }, { "ircconnectenabled", &CUser::SetIRCConnectEnabled }, }; size_t numBoolOptions = sizeof(BoolOptions) / sizeof(BoolOptions[0]); for (size_t i = 0; i < numStringOptions; i++) { CString sValue; if (pConfig->FindStringEntry(StringOptions[i].name, sValue)) (this->*StringOptions[i].pSetter)(sValue); } for (size_t i = 0; i < numUIntOptions; i++) { CString sValue; if (pConfig->FindStringEntry(UIntOptions[i].name, sValue)) (this->*UIntOptions[i].pSetter)(sValue.ToUInt()); } for (size_t i = 0; i < numBoolOptions; i++) { CString sValue; if (pConfig->FindStringEntry(BoolOptions[i].name, sValue)) (this->*BoolOptions[i].pSetter)(sValue.ToBool()); } VCString vsList; VCString::const_iterator vit; pConfig->FindStringVector("allow", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { AddAllowedHost(*vit); } pConfig->FindStringVector("ctcpreply", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { const CString& sValue = *vit; AddCTCPReply(sValue.Token(0), sValue.Token(1, true)); } pConfig->FindStringVector("server", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { CUtils::PrintAction("Adding Server [" + *vit + "]"); CUtils::PrintStatus(AddServer(*vit)); } pConfig->FindStringVector("chan", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { AddChan(*vit, true); } CString sValue; if (pConfig->FindStringEntry("buffer", sValue)) SetBufferCount(sValue.ToUInt(), true); if (pConfig->FindStringEntry("awaysuffix", sValue)) { CUtils::PrintMessage("WARNING: AwaySuffix has been depricated, instead try -> LoadModule = awaynick %nick%_" + sValue); } if (pConfig->FindStringEntry("autocycle", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle"); } if (pConfig->FindStringEntry("keepnick", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: KeepNick has been deprecated, instead try -> LoadModule = keepnick"); } if (pConfig->FindStringEntry("statusprefix", sValue)) { if (!SetStatusPrefix(sValue)) { sError = "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces."; CUtils::PrintError(sError); return false; } } if (pConfig->FindStringEntry("timezoneoffset", sValue)) { SetTimezoneOffset(sValue.ToDouble()); } if (pConfig->FindStringEntry("timestamp", sValue)) { if (!sValue.Trim_n().Equals("true")) { if (sValue.Trim_n().Equals("append")) { SetTimestampAppend(true); SetTimestampPrepend(false); } else if (sValue.Trim_n().Equals("prepend")) { SetTimestampAppend(false); SetTimestampPrepend(true); } else if (sValue.Trim_n().Equals("false")) { SetTimestampAppend(false); SetTimestampPrepend(false); } else { SetTimestampFormat(sValue); } } } if (pConfig->FindStringEntry("dcclookupmethod", sValue)) SetUseClientIP(sValue.Equals("Client")); pConfig->FindStringEntry("pass", sValue); // There are different formats for this available: // Pass = <plain text> // Pass = <md5 hash> - // Pass = plain#<plain text> // Pass = <hash name>#<hash> // Pass = <hash name>#<salted hash>#<salt># // 'Salted hash' means hash of 'password' + 'salt' // Possible hashes are md5 and sha256 if (sValue.Right(1) == "-") { sValue.RightChomp(); sValue.Trim(); SetPass(sValue, CUser::HASH_MD5); } else { CString sMethod = sValue.Token(0, false, "#"); CString sPass = sValue.Token(1, true, "#"); if (sMethod == "md5" || sMethod == "sha256") { CUser::eHashType type = CUser::HASH_MD5; if (sMethod == "sha256") type = CUser::HASH_SHA256; CString sSalt = sPass.Token(1, false, "#"); sPass = sPass.Token(0, false, "#"); SetPass(sPass, type, sSalt); } else if (sMethod == "plain") { SetPass(sPass, CUser::HASH_NONE); } else { SetPass(sValue, CUser::HASH_NONE); } } CConfig::SubConfig subConf; CConfig::SubConfig::const_iterator subIt; pConfig->FindSubConfig("chan", subConf); for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { const CString& sChanName = subIt->first; CConfig* pSubConf = subIt->second.m_pSubConfig; CChan* pChan = new CChan(sChanName, this, true, pSubConf); if (!pSubConf->empty()) { sError = "Unhandled lines in config for User [" + GetUserName() + "], Channel [" + sChanName + "]!"; CUtils::PrintError(sError); CZNC::DumpConfig(pSubConf); return false; } // Save the channel name, because AddChan // deletes the CChannel*, if adding fails sError = pChan->GetName(); if (!AddChan(pChan)) { sError = "Channel [" + sError + "] defined more than once"; CUtils::PrintError(sError); return false; } sError.clear(); } pConfig->FindStringVector("loadmodule", vsList); for (vit = vsList.begin(); vit != vsList.end(); ++vit) { sValue = *vit; CString sModName = sValue.Token(0); // XXX Legacy crap, added in znc 0.089 if (sModName == "discon_kick") { CUtils::PrintMessage("NOTICE: [discon_kick] was renamed, loading [disconkick] instead"); sModName = "disconkick"; } CUtils::PrintAction("Loading Module [" + sModName + "]"); CString sModRet; CString sArgs = sValue.Token(1, true); bool bModRet = GetModules().LoadModule(sModName, sArgs, this, sModRet); CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { sError = sModRet; return false; } continue; } return true; }
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")) {
bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { TOption<const CString&> StringOptions[] = { { "nick", &CUser::SetNick }, { "quitmsg", &CUser::SetQuitMsg }, { "altnick", &CUser::SetAltNick }, { "ident", &CUser::SetIdent }, { "realname", &CUser::SetRealName }, { "chanmodes", &CUser::SetDefaultChanModes }, { "bindhost", &CUser::SetBindHost }, { "vhost", &CUser::SetBindHost }, { "dccbindhost", &CUser::SetDCCBindHost }, { "dccvhost", &CUser::SetDCCBindHost }, { "timestampformat", &CUser::SetTimestampFormat }, { "skin", &CUser::SetSkinName }, { "clientencoding", &CUser::SetClientEncoding }, }; TOption<unsigned int> UIntOptions[] = { { "jointries", &CUser::SetJoinTries }, { "maxnetworks", &CUser::SetMaxNetworks }, { "maxquerybuffers", &CUser::SetMaxQueryBuffers }, { "maxjoins", &CUser::SetMaxJoins }, }; TOption<bool> BoolOptions[] = { { "keepbuffer", &CUser::SetKeepBuffer }, // XXX compatibility crap from pre-0.207 { "autoclearchanbuffer", &CUser::SetAutoClearChanBuffer }, { "autoclearquerybuffer", &CUser::SetAutoClearQueryBuffer }, { "multiclients", &CUser::SetMultiClients }, { "denyloadmod", &CUser::SetDenyLoadMod }, { "admin", &CUser::SetAdmin }, { "denysetbindhost", &CUser::SetDenySetBindHost }, { "denysetvhost", &CUser::SetDenySetBindHost }, { "appendtimestamp", &CUser::SetTimestampAppend }, { "prependtimestamp", &CUser::SetTimestampPrepend }, }; for (const auto& Option : StringOptions) { CString sValue; if (pConfig->FindStringEntry(Option.name, sValue)) (this->*Option.pSetter)(sValue); } for (const auto& Option : UIntOptions) { CString sValue; if (pConfig->FindStringEntry(Option.name, sValue)) (this->*Option.pSetter)(sValue.ToUInt()); } for (const auto& Option : BoolOptions) { CString sValue; if (pConfig->FindStringEntry(Option.name, sValue)) (this->*Option.pSetter)(sValue.ToBool()); } VCString vsList; pConfig->FindStringVector("allow", vsList); for (const CString& sHost : vsList) { AddAllowedHost(sHost); } pConfig->FindStringVector("ctcpreply", vsList); for (const CString& sReply : vsList) { AddCTCPReply(sReply.Token(0), sReply.Token(1, true)); } CString sValue; CString sDCCLookupValue; pConfig->FindStringEntry("dcclookupmethod", sDCCLookupValue); if (pConfig->FindStringEntry("bouncedccs", sValue)) { if (sValue.ToBool()) { CUtils::PrintAction("Loading Module [bouncedcc]"); CString sModRet; bool bModRet = GetModules().LoadModule("bouncedcc", "", CModInfo::UserModule, this, nullptr, sModRet); CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { sError = sModRet; return false; } if (sDCCLookupValue.Equals("Client")) { GetModules().FindModule("bouncedcc")->SetNV("UseClientIP", "1"); } } } if (pConfig->FindStringEntry("buffer", sValue)) SetBufferCount(sValue.ToUInt(), true); if (pConfig->FindStringEntry("awaysuffix", sValue)) { CUtils::PrintMessage("WARNING: AwaySuffix has been deprecated, instead try -> LoadModule = awaynick %nick%_" + sValue); } if (pConfig->FindStringEntry("autocycle", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle"); } if (pConfig->FindStringEntry("keepnick", sValue)) { if (sValue.Equals("true")) CUtils::PrintError("WARNING: KeepNick has been deprecated, instead try -> LoadModule = keepnick"); } if (pConfig->FindStringEntry("statusprefix", sValue)) { if (!SetStatusPrefix(sValue)) { sError = "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces."; CUtils::PrintError(sError); return false; } } if (pConfig->FindStringEntry("timezone", sValue)) { SetTimezone(sValue); } if (pConfig->FindStringEntry("timezoneoffset", sValue)) { if (fabs(sValue.ToDouble()) > 0.1) { CUtils::PrintError("WARNING: TimezoneOffset has been deprecated, now you can set your timezone by name"); } } if (pConfig->FindStringEntry("timestamp", sValue)) { if (!sValue.Trim_n().Equals("true")) { if (sValue.Trim_n().Equals("append")) { SetTimestampAppend(true); SetTimestampPrepend(false); } else if (sValue.Trim_n().Equals("prepend")) { SetTimestampAppend(false); SetTimestampPrepend(true); } else if (sValue.Trim_n().Equals("false")) { SetTimestampAppend(false); SetTimestampPrepend(false); } else { SetTimestampFormat(sValue); } } } pConfig->FindStringEntry("pass", sValue); // There are different formats for this available: // Pass = <plain text> // Pass = <md5 hash> - // Pass = plain#<plain text> // Pass = <hash name>#<hash> // Pass = <hash name>#<salted hash>#<salt># // 'Salted hash' means hash of 'password' + 'salt' // Possible hashes are md5 and sha256 if (sValue.Right(1) == "-") { sValue.RightChomp(); sValue.Trim(); SetPass(sValue, CUser::HASH_MD5); } else { CString sMethod = sValue.Token(0, false, "#"); CString sPass = sValue.Token(1, true, "#"); if (sMethod == "md5" || sMethod == "sha256") { CUser::eHashType type = CUser::HASH_MD5; if (sMethod == "sha256") type = CUser::HASH_SHA256; CString sSalt = sPass.Token(1, false, "#"); sPass = sPass.Token(0, false, "#"); SetPass(sPass, type, sSalt); } else if (sMethod == "plain") { SetPass(sPass, CUser::HASH_NONE); } else { SetPass(sValue, CUser::HASH_NONE); } } CConfig::SubConfig subConf; CConfig::SubConfig::const_iterator subIt; pConfig->FindSubConfig("pass", subConf); if (!sValue.empty() && !subConf.empty()) { sError = "Password defined more than once"; CUtils::PrintError(sError); return false; } subIt = subConf.begin(); if (subIt != subConf.end()) { CConfig* pSubConf = subIt->second.m_pSubConfig; CString sHash; CString sMethod; CString sSalt; CUser::eHashType method; pSubConf->FindStringEntry("hash", sHash); pSubConf->FindStringEntry("method", sMethod); pSubConf->FindStringEntry("salt", sSalt); if (sMethod.empty() || sMethod.Equals("plain")) method = CUser::HASH_NONE; else if (sMethod.Equals("md5")) method = CUser::HASH_MD5; else if (sMethod.Equals("sha256")) method = CUser::HASH_SHA256; else { sError = "Invalid hash method"; CUtils::PrintError(sError); return false; } SetPass(sHash, method, sSalt); if (!pSubConf->empty()) { sError = "Unhandled lines in config!"; CUtils::PrintError(sError); CZNC::DumpConfig(pSubConf); return false; } ++subIt; } if (subIt != subConf.end()) { sError = "Password defined more than once"; CUtils::PrintError(sError); return false; } pConfig->FindSubConfig("network", subConf); for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { const CString& sNetworkName = subIt->first; CUtils::PrintMessage("Loading network [" + sNetworkName + "]"); CIRCNetwork *pNetwork = FindNetwork(sNetworkName); if (!pNetwork) { pNetwork = new CIRCNetwork(this, sNetworkName); } if (!pNetwork->ParseConfig(subIt->second.m_pSubConfig, sError)) { return false; } } if (pConfig->FindStringVector("server", vsList, false) || pConfig->FindStringVector("chan", vsList, false) || pConfig->FindSubConfig("chan", subConf, false)) { CIRCNetwork *pNetwork = FindNetwork("default"); if (!pNetwork) { CString sErrorDummy; pNetwork = AddNetwork("default", sErrorDummy); } if (pNetwork) { CUtils::PrintMessage("NOTICE: Found deprecated config, upgrading to a network"); if (!pNetwork->ParseConfig(pConfig, sError, true)) { return false; } } } pConfig->FindStringVector("loadmodule", vsList); for (const CString& sMod : vsList) { CString sModName = sMod.Token(0); CString sNotice = "Loading user module [" + sModName + "]"; // XXX Legacy crap, added in ZNC 0.089 if (sModName == "discon_kick") { sNotice = "NOTICE: [discon_kick] was renamed, loading [disconkick] instead"; sModName = "disconkick"; } // XXX Legacy crap, added in ZNC 0.099 if (sModName == "fixfreenode") { sNotice = "NOTICE: [fixfreenode] doesn't do anything useful anymore, ignoring it"; continue; } // XXX Legacy crap, added in ZNC 0.207 if (sModName == "admin") { sNotice = "NOTICE: [admin] module was renamed, loading [controlpanel] instead"; sModName = "controlpanel"; } // XXX Legacy crap, should have been added ZNC 0.207, but added only in 1.1 :( if (sModName == "away") { sNotice = "NOTICE: [away] was renamed, loading [awaystore] instead"; sModName = "awaystore"; } // XXX Legacy crap, added in 1.1; fakeonline module was dropped in 1.0 and returned in 1.1 if (sModName == "fakeonline") { sNotice = "NOTICE: [fakeonline] was renamed, loading [modules_online] instead"; sModName = "modules_online"; } // XXX Legacy crap, added in 1.3 if (sModName == "charset") { CUtils::PrintAction("NOTICE: Charset support was moved to core, importing old charset module settings"); size_t uIndex = 1; if (sMod.Token(uIndex).Equals("-force")) { uIndex++; } VCString vsClient, vsServer; sMod.Token(uIndex).Split(",", vsClient); sMod.Token(uIndex + 1).Split(",", vsServer); if (vsClient.empty() || vsServer.empty()) { CUtils::PrintStatus(false, "charset module was loaded with wrong parameters."); continue; } SetClientEncoding(vsClient[0]); for (CIRCNetwork* pNetwork : m_vIRCNetworks) { pNetwork->SetEncoding(vsServer[0]); } CUtils::PrintStatus(true, "Using [" + vsClient[0] + "] for clients, and [" + vsServer[0] + "] for servers"); continue; } CString sModRet; CString sArgs = sMod.Token(1, true); bool bModRet = LoadModule(sModName, sArgs, sNotice, sModRet); CUtils::PrintStatus(bModRet, sModRet); if (!bModRet) { // XXX The awaynick module was retired in 1.6 (still available as external module) if (sModName == "awaynick") { // load simple_away instead, unless it's already on the list if (std::find(vsList.begin(), vsList.end(), "simple_away") == vsList.end()) { sNotice = "Loading [simple_away] module instead"; sModName = "simple_away"; // not a fatal error if simple_away is not available LoadModule(sModName, sArgs, sNotice, sModRet); } } else { sError = sModRet; return false; } } continue; } // Move ircconnectenabled to the networks if (pConfig->FindStringEntry("ircconnectenabled", sValue)) { for (CIRCNetwork* pNetwork : m_vIRCNetworks) { pNetwork->SetIRCConnectEnabled(sValue.ToBool()); } } return true; }
CString CDir::ChangeDir(const CString& sPathIn, const CString& sAddIn, const CString& sHomeIn) { /* this function is pretty crappy but needed to fix ZNC's strange way of handling file paths */ CString sResult; CString sPath(sPathIn); CString sAdd(sAddIn); CString sHomeDir(sHomeIn); if (sHomeDir.empty()) { // use the default home dir, if no custom home dir has been passed in. sHomeDir = CZNC::Get().GetHomePath(); } // we want to use backslashes for this function's inner workings. sHomeDir.Replace("/", "\\"); sPath.Replace("/", "\\"); sAdd.Replace("/", "\\"); if (sAdd == "~") { // if the add dir is the home dir (why?!), use that as result... sResult = sHomeDir; } else { if(!PathIsRelative(sAdd.c_str())) { // if add already is an absolute path, use it for the result... // ... however, it can still contain ./ or ../ stuff, // which will be resolved later. sResult = sAdd; } else { // if the first part of the path (sPath) is relative, // make it absolute, starting from the znc.exe directory: if(PathIsRelative(sPath.c_str())) { char szLocalPath[1024] = {0}; // get the full path to znc.exe and strip off "znc.exe" from the end: if(GetModuleFileName(NULL, szLocalPath, 1023) != 0 && szLocalPath[0]) { PathRemoveFileSpec(szLocalPath); if(PathIsDirectory(szLocalPath)) { // append the relative sPath to our znc.exe dir, // thereby making it absolute. char szAbsolutePathBuffer[1024] = {0}; PathCombine(szAbsolutePathBuffer, szLocalPath, sPath.c_str()); // PathCombine will also resolve any ./ or ../ parts in the path. // use the now-absolute path: sPath = szAbsolutePathBuffer; } } } // append the (relative) sAdd path to the (absolute) sPath path: char szAbsoluteResultBuffer[1024] = {0}; PathCombine(szAbsoluteResultBuffer, sPath.c_str(), sAdd.c_str()); sResult = szAbsoluteResultBuffer; } } char szResultBuffer[1024] = {0}; // make sure no ./ or ../ stuff survives this function. never. if(!sResult.empty() && PathCanonicalize(szResultBuffer, sResult.c_str())) { if(sAdd.empty() || sAdd[sAdd.length() - 1] != '\\') { PathRemoveBackslash(szResultBuffer); } else { PathAddBackslash(szResultBuffer); } sResult = szResultBuffer; sResult.Replace("\\", "/"); return sResult; } else { abort(); return ""; // to shut up compiler warning } #else CString CDir::ChangeDir(const CString& sPath, const CString& sAdd, const CString& sHome) { CString sHomeDir(sHome); if (sHomeDir.empty()) { sHomeDir = CZNC::Get().GetHomePath(); } if (sAdd == "~") { return sHomeDir; } CString sAddDir(sAdd); if (sAddDir.Left(2) == "~/") { sAddDir.LeftChomp(); sAddDir = sHomeDir + sAddDir; } CString sRet = ((sAddDir.size()) && (sAddDir[0] == '/')) ? "" : sPath; sAddDir += "/"; CString sCurDir; if (sRet.Right(1) == "/") { sRet.RightChomp(); } for (unsigned int a = 0; a < sAddDir.size(); a++) { switch (sAddDir[a]) { case '/': if (sCurDir == "..") { sRet = sRet.substr(0, sRet.rfind('/')); } else if ((sCurDir != "") && (sCurDir != ".")) { sRet += "/" + sCurDir; } sCurDir = ""; break; default: sCurDir += sAddDir[a]; break; } } return (sRet.empty()) ? "/" : sRet; #endif }