mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
Overhaul the config parsing
This moves stuff to a two-step model. First, the new class CConfig reads the config file, parses it and creates a in-memory model of stuff. Only then do we actually go forward and apply the stuff. The upside of this is that some config errors are caught before we change anything on the running upside. Let's see how much stuff this broke... Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,6 +30,8 @@ Makefile
|
||||
/modules/modpython/*.pyc
|
||||
/modules/*.pyc
|
||||
|
||||
/test/ConfigTest
|
||||
|
||||
# Compiled Object files
|
||||
*.o
|
||||
|
||||
|
||||
20
Chan.cpp
20
Chan.cpp
@@ -10,8 +10,9 @@
|
||||
#include "IRCSock.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include "Config.h"
|
||||
|
||||
CChan::CChan(const CString& sName, CUser* pUser, bool bInConfig) {
|
||||
CChan::CChan(const CString& sName, CUser* pUser, bool bInConfig, CConfig *pConfig) {
|
||||
m_sName = sName.Token(0);
|
||||
m_sKey = sName.Token(1);
|
||||
m_pUser = pUser;
|
||||
@@ -27,6 +28,23 @@ CChan::CChan(const CString& sName, CUser* pUser, bool bInConfig) {
|
||||
m_bKeepBuffer = m_pUser->KeepBuffer();
|
||||
m_bDisabled = false;
|
||||
Reset();
|
||||
|
||||
if (pConfig) {
|
||||
CString sValue;
|
||||
if (pConfig->FindStringEntry("buffer", sValue))
|
||||
SetBufferCount(sValue.ToUInt(), true);
|
||||
if (pConfig->FindStringEntry("keepbuffer", sValue))
|
||||
SetKeepBuffer(sValue.ToBool());
|
||||
if (pConfig->FindStringEntry("detached", sValue))
|
||||
SetDetached(sValue.ToBool());
|
||||
if (pConfig->FindStringEntry("autocycle", sValue))
|
||||
if (sValue.Equals("true"))
|
||||
CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle " + sName);
|
||||
if (pConfig->FindStringEntry("key", sValue))
|
||||
SetKey(sValue);
|
||||
if (pConfig->FindStringEntry("modes", sValue))
|
||||
SetDefaultModes(sValue);
|
||||
}
|
||||
}
|
||||
|
||||
CChan::~CChan() {
|
||||
|
||||
3
Chan.h
3
Chan.h
@@ -24,6 +24,7 @@ using std::set;
|
||||
// Forward Declarations
|
||||
class CUser;
|
||||
class CClient;
|
||||
class CConfig;
|
||||
// !Forward Declarations
|
||||
|
||||
class CChan {
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
M_Except = 'e'
|
||||
} EModes;
|
||||
|
||||
CChan(const CString& sName, CUser* pUser, bool bInConfig);
|
||||
CChan(const CString& sName, CUser* pUser, bool bInConfig, CConfig *pConfig = NULL);
|
||||
~CChan();
|
||||
|
||||
void Reset();
|
||||
|
||||
179
Config.cpp
Normal file
179
Config.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2011 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
|
||||
struct ConfigStackEntry {
|
||||
CString sTag;
|
||||
CString sName;
|
||||
CConfig Config;
|
||||
|
||||
ConfigStackEntry(const CString& Tag, const CString Name) {
|
||||
sTag = Tag;
|
||||
sName = Name;
|
||||
}
|
||||
};
|
||||
|
||||
CConfig::CConfigEntry::CConfigEntry()
|
||||
: m_pSubConfig(NULL) {
|
||||
}
|
||||
|
||||
CConfig::CConfigEntry::CConfigEntry(const CConfig& Config)
|
||||
: m_pSubConfig(new CConfig(Config)) {
|
||||
}
|
||||
|
||||
CConfig::CConfigEntry::CConfigEntry(const CConfigEntry& other)
|
||||
: m_pSubConfig(NULL) {
|
||||
if (other.m_pSubConfig)
|
||||
m_pSubConfig = new CConfig(*other.m_pSubConfig);
|
||||
}
|
||||
|
||||
CConfig::CConfigEntry::~CConfigEntry()
|
||||
{
|
||||
delete m_pSubConfig;
|
||||
}
|
||||
|
||||
CConfig::CConfigEntry& CConfig::CConfigEntry::operator=(const CConfigEntry& other) {
|
||||
delete m_pSubConfig;
|
||||
if (other.m_pSubConfig)
|
||||
m_pSubConfig = new CConfig(*other.m_pSubConfig);
|
||||
else
|
||||
m_pSubConfig = NULL;
|
||||
return *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))
|
||||
return "Could not seek to the beginning of the config.";
|
||||
|
||||
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 ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sLine.Left(2) == "/*") {
|
||||
if (sLine.Right(2) != "*/") {
|
||||
bCommented = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bCommented) {
|
||||
if (sLine.Right(2) == "*/") {
|
||||
bCommented = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
92
Config.h
Normal file
92
Config.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2011 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include "ZNCString.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
class CConfig {
|
||||
public:
|
||||
struct CConfigEntry {
|
||||
CConfigEntry();
|
||||
CConfigEntry(const CConfig& Config);
|
||||
CConfigEntry(const CConfigEntry& other);
|
||||
~CConfigEntry();
|
||||
CConfigEntry& operator=(const CConfigEntry& other);
|
||||
|
||||
CConfig* m_pSubConfig;
|
||||
};
|
||||
|
||||
typedef map<CString, VCString> EntryMap;
|
||||
typedef map<CString, CConfigEntry> SubConfig;
|
||||
typedef map<CString, SubConfig> SubConfigMap;
|
||||
|
||||
typedef EntryMap::const_iterator EntryMapIterator;
|
||||
typedef SubConfigMap::const_iterator SubConfigMapIterator;
|
||||
|
||||
EntryMapIterator BeginEntries() const {
|
||||
return m_ConfigEntries.begin();
|
||||
}
|
||||
EntryMapIterator EndEntries() const {
|
||||
return m_ConfigEntries.end();
|
||||
}
|
||||
|
||||
SubConfigMapIterator BeginSubConfigs() const {
|
||||
return m_SubConfigs.begin();
|
||||
}
|
||||
SubConfigMapIterator EndSubConfigs() const {
|
||||
return m_SubConfigs.end();
|
||||
}
|
||||
|
||||
bool FindStringVector(const CString& sName, VCString& vsList) {
|
||||
EntryMap::iterator it = m_ConfigEntries.find(sName);
|
||||
vsList.clear();
|
||||
if (it == m_ConfigEntries.end())
|
||||
return false;
|
||||
vsList = it->second;
|
||||
m_ConfigEntries.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindStringEntry(const CString& sName, CString& sRes) {
|
||||
EntryMap::iterator it = m_ConfigEntries.find(sName);
|
||||
sRes.clear();
|
||||
if (it == m_ConfigEntries.end() || it->second.empty())
|
||||
return false;
|
||||
sRes = it->second.front();
|
||||
it->second.erase(it->second.begin());
|
||||
if (it->second.empty())
|
||||
m_ConfigEntries.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindSubConfig(const CString& sName, SubConfig& Config) {
|
||||
SubConfigMap::iterator it = m_SubConfigs.find(sName);
|
||||
if (it == m_SubConfigs.end()) {
|
||||
Config.clear();
|
||||
return false;
|
||||
}
|
||||
Config = it->second;
|
||||
m_SubConfigs.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return m_ConfigEntries.empty() && m_SubConfigs.empty();
|
||||
}
|
||||
|
||||
bool Parse(CFile& file, CString& sErrorMsg);
|
||||
|
||||
private:
|
||||
EntryMap m_ConfigEntries;
|
||||
SubConfigMap m_SubConfigs;
|
||||
};
|
||||
|
||||
#endif // !CONFIG_H
|
||||
@@ -31,7 +31,7 @@ INSTALL_DATA := @INSTALL_DATA@
|
||||
LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp User.cpp IRCSock.cpp Client.cpp DCCBounce.cpp \
|
||||
DCCSock.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \
|
||||
FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp \
|
||||
WebModules.cpp Listener.cpp
|
||||
WebModules.cpp Listener.cpp Config.cpp
|
||||
BIN_SRCS := main.cpp
|
||||
LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS))
|
||||
BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS))
|
||||
|
||||
212
User.cpp
212
User.cpp
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "User.h"
|
||||
#include "Chan.h"
|
||||
#include "Config.h"
|
||||
#include "DCCSock.h"
|
||||
#include "IRCSock.h"
|
||||
#include "Server.h"
|
||||
@@ -111,6 +112,217 @@ CUser::~CUser() {
|
||||
CZNC::Get().GetManager().DelCronByAddr(m_pUserTimer);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct TOption {
|
||||
const char *name;
|
||||
void (CUser::*pSetter)(T);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CUser::DelModules() {
|
||||
delete m_pModules;
|
||||
m_pModules = NULL;
|
||||
|
||||
3
User.h
3
User.h
@@ -22,6 +22,7 @@ using std::vector;
|
||||
|
||||
class CChan;
|
||||
class CClient;
|
||||
class CConfig;
|
||||
class CIRCSock;
|
||||
class CUserTimer;
|
||||
class CServer;
|
||||
@@ -33,6 +34,8 @@ public:
|
||||
CUser(const CString& sUserName);
|
||||
~CUser();
|
||||
|
||||
bool ParseConfig(CConfig* Config, CString& sError);
|
||||
|
||||
enum eHashType {
|
||||
HASH_NONE,
|
||||
HASH_MD5,
|
||||
|
||||
@@ -474,6 +474,7 @@ AC_CONFIG_FILES([man/Makefile])
|
||||
AC_CONFIG_FILES([znc.pc])
|
||||
AC_CONFIG_FILES([znc-uninstalled.pc])
|
||||
AC_CONFIG_FILES([modules/Makefile])
|
||||
AC_CONFIG_FILES([test/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
echo
|
||||
|
||||
164
test/ConfigTest.cpp
Normal file
164
test/ConfigTest.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2011 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include <cstdlib>
|
||||
|
||||
class CConfigTest {
|
||||
public:
|
||||
CConfigTest(const CString& sConfig) : m_sConfig(sConfig) { }
|
||||
virtual ~CConfigTest() { m_File.Delete(); }
|
||||
|
||||
virtual bool Test() = 0;
|
||||
|
||||
protected:
|
||||
CFile& WriteFile() {
|
||||
char sName[] = "./temp-XXXXXX";
|
||||
int fd = mkstemp(sName);
|
||||
m_File.Open(sName, O_RDWR);
|
||||
close(fd);
|
||||
|
||||
m_File.Write(m_sConfig);
|
||||
|
||||
return m_File;
|
||||
}
|
||||
|
||||
private:
|
||||
CFile m_File;
|
||||
CString m_sConfig;
|
||||
};
|
||||
|
||||
class CConfigErrorTest : public CConfigTest {
|
||||
public:
|
||||
CConfigErrorTest(const CString& sConfig, const CString& sError)
|
||||
: CConfigTest(sConfig), m_sError(sError) { }
|
||||
|
||||
bool Test() {
|
||||
CFile &File = WriteFile();
|
||||
|
||||
CConfig conf;
|
||||
CString sError;
|
||||
bool res = conf.Parse(File, sError);
|
||||
if (res) {
|
||||
std::cout << "Didn't error out!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sError != m_sError) {
|
||||
std::cout << "Wrong error\n Expected: " << m_sError << "\n Got: " << sError << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
CString m_sError;
|
||||
};
|
||||
|
||||
class CConfigSuccessTest : public CConfigTest {
|
||||
public:
|
||||
CConfigSuccessTest(const CString& sConfig, const CString& sExpectedOutput)
|
||||
: CConfigTest(sConfig), m_sOutput(sExpectedOutput) { }
|
||||
|
||||
bool Test() {
|
||||
CFile &File = WriteFile();
|
||||
// Verify that Parse() rewinds the file
|
||||
File.Seek(12);
|
||||
|
||||
CConfig conf;
|
||||
CString sError;
|
||||
bool res = conf.Parse(File, sError);
|
||||
if (!res) {
|
||||
std::cout << "Error'd out! (" + sError + ")\n";
|
||||
return false;
|
||||
}
|
||||
if (!sError.empty()) {
|
||||
std::cout << "Non-empty error string!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
CString sOutput;
|
||||
ToString(sOutput, conf);
|
||||
|
||||
if (sOutput != m_sOutput) {
|
||||
std::cout << "Wrong output\n Expected: " << m_sOutput << "\n Got: " << sOutput << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ToString(CString& sRes, CConfig& conf) {
|
||||
CConfig::EntryMapIterator it = conf.BeginEntries();
|
||||
while (it != conf.EndEntries()) {
|
||||
const CString& sKey = it->first;
|
||||
const VCString& vsEntries = it->second;
|
||||
VCString::const_iterator i = vsEntries.begin();
|
||||
if (i == vsEntries.end())
|
||||
sRes += sKey + " <- Error, empty list!\n";
|
||||
else
|
||||
while (i != vsEntries.end()) {
|
||||
sRes += sKey + "=" + *i + "\n";
|
||||
i++;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
CConfig::SubConfigMapIterator it2 = conf.BeginSubConfigs();
|
||||
while (it2 != conf.EndSubConfigs()) {
|
||||
map<CString, CConfig::CConfigEntry>::const_iterator it3 = it2->second.begin();
|
||||
|
||||
while (it3 != it2->second.end()) {
|
||||
sRes += "->" + it2->first + "/" + it3->first + "\n";
|
||||
ToString(sRes, *it3->second.m_pSubConfig);
|
||||
sRes += "<-\n";
|
||||
++it3;
|
||||
}
|
||||
|
||||
++it2;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CString m_sOutput;
|
||||
};
|
||||
|
||||
int main() {
|
||||
#define TEST_ERROR(a, b) new CConfigErrorTest(a, b)
|
||||
#define TEST_SUCCESS(a, b) new CConfigSuccessTest(a, b)
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/(sizeof((a)[0])))
|
||||
CConfigTest *tests[] = {
|
||||
TEST_SUCCESS("", ""),
|
||||
/* duplicate entries */
|
||||
TEST_SUCCESS("Foo = bar\nFoo = baz\n", "foo=bar\nfoo=baz\n"),
|
||||
TEST_SUCCESS("Foo = baz\nFoo = bar\n", "foo=baz\nfoo=bar\n"),
|
||||
/* sub configs */
|
||||
TEST_ERROR("</foo>", "Error on line 1: Closing tag \"foo\" which is not open."),
|
||||
TEST_ERROR("<foo a>\n</bar>\n", "Error on line 2: Closing tag \"bar\" which is not open."),
|
||||
TEST_ERROR("<foo bar>", "Error on line 1: Not all tags are closed at the end of the file. Inner-most open tag is \"foo\"."),
|
||||
TEST_ERROR("<foo>\n</foo>", "Error on line 1: Empty block name at begin of block."),
|
||||
TEST_ERROR("<foo 1>\n</foo>\n<foo 1>\n</foo>", "Error on line 4: Duplicate entry for tag \"foo\" name \"1\"."),
|
||||
TEST_SUCCESS("<foo a>\n</foo>", "->foo/a\n<-\n"),
|
||||
TEST_SUCCESS("<a b>\n <c d>\n </c>\n</a>", "->a/b\n->c/d\n<-\n<-\n"),
|
||||
TEST_SUCCESS(" \t <A B>\nfoo = bar\n\tFooO = bar\n</a>", "->a/B\nfoo=bar\nfooo=bar\n<-\n"),
|
||||
/* comments */
|
||||
TEST_SUCCESS("Foo = bar // baz\n// Bar = baz", "foo=bar // baz\n"),
|
||||
TEST_SUCCESS("Foo = bar /* baz */\n/*** Foo = baz ***/\n /**** asdsdfdf \n Some quite invalid stuff ***/\n", "foo=bar /* baz */\n"),
|
||||
TEST_ERROR("<foo foo>\n/* Just a comment\n</foo>", "Error on line 3: Comment not closed at end of file."),
|
||||
};
|
||||
unsigned int i;
|
||||
unsigned int failed = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
if (!tests[i]->Test())
|
||||
failed++;
|
||||
delete tests[i];
|
||||
}
|
||||
|
||||
return failed;
|
||||
}
|
||||
46
test/Makefile.in
Normal file
46
test/Makefile.in
Normal file
@@ -0,0 +1,46 @@
|
||||
SHELL := @SHELL@
|
||||
|
||||
# Support out-of-tree builds
|
||||
srcdir := @srcdir@
|
||||
VPATH := @srcdir@
|
||||
|
||||
CXX := @CXX@
|
||||
CXXFLAGS := @DEFS@ @CPPFLAGS@ @CXXFLAGS@ -I..
|
||||
LDFLAGS := @LDFLAGS@
|
||||
LIBS := @LIBS@
|
||||
|
||||
TARGETS := ConfigTest
|
||||
OBJS := $(addsuffix .o, $(TARGETS))
|
||||
ZNC_OBJS := Config.o FileUtils.o Utils.o ZNCString.o MD5.o SHA256.o
|
||||
ZNC_OBJS := $(addprefix ../, $(ZNC_OBJS))
|
||||
|
||||
ifneq "$(V)" ""
|
||||
VERBOSE=1
|
||||
endif
|
||||
ifeq "$(VERBOSE)" ""
|
||||
Q=@
|
||||
E=@echo
|
||||
C=-s
|
||||
else
|
||||
Q=
|
||||
E=@\#
|
||||
C=
|
||||
endif
|
||||
|
||||
.PHONY: test
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
ConfigTest: ConfigTest.o
|
||||
$(E) Linking $@...
|
||||
$(Q)$(CXX) $(LDFLAGS) -o $@ $< $(ZNC_OBJS) $(LIBS)
|
||||
|
||||
%.o: %.cpp Makefile
|
||||
@mkdir -p .depend
|
||||
$(E) Building $@...
|
||||
$(Q)$(CXX) $(CXXFLAGS) -c -o $@ $< -MMD -MF .depend/$@.dep
|
||||
|
||||
test: $(TARGETS)
|
||||
for test in $(TARGETS) ; do \
|
||||
$$test || exit 1 ; \
|
||||
done
|
||||
922
znc.cpp
922
znc.cpp
@@ -12,6 +12,7 @@
|
||||
#include "Server.h"
|
||||
#include "User.h"
|
||||
#include "Listener.h"
|
||||
#include "Config.h"
|
||||
#include <list>
|
||||
|
||||
static inline CString FormatBindError() {
|
||||
@@ -1008,13 +1009,11 @@ bool CZNC::DoRehash(CString& sError)
|
||||
m_pLockFile = pFile;
|
||||
CFile &File = *pFile;
|
||||
|
||||
// This fd is re-used for rehashing, so we must seek back to the beginning!
|
||||
if (!File.Seek(0)) {
|
||||
sError = "Could not seek to the beginning of the config.";
|
||||
CConfig config;
|
||||
if (!config.Parse(File, sError)) {
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CUtils::PrintStatus(true);
|
||||
|
||||
m_vsBindHosts.clear();
|
||||
@@ -1026,610 +1025,189 @@ bool CZNC::DoRehash(CString& sError)
|
||||
m_vpListeners.erase(m_vpListeners.begin());
|
||||
}
|
||||
|
||||
CString sLine;
|
||||
bool bCommented = false; // support for /**/ style comments
|
||||
CUser* pUser = NULL; // Used to keep track of which user block we are in
|
||||
CUser* pRealUser = NULL; // If we rehash a user, this is the real one
|
||||
CChan* pChan = NULL; // Used to keep track of which chan block we are in
|
||||
unsigned int uLineNum = 0;
|
||||
MCString msModules; // Modules are queued for later loading
|
||||
|
||||
while (File.ReadLine(sLine)) {
|
||||
uLineNum++;
|
||||
const char *szListenerEntries[] = {
|
||||
"listen", "listen6", "listen4",
|
||||
"listener", "listener6", "listener4"
|
||||
};
|
||||
const size_t numListenerEntries = sizeof(szListenerEntries) / sizeof(szListenerEntries[0]);
|
||||
VCString vsList;
|
||||
|
||||
// Remove all leading spaces and trailing line endings
|
||||
sLine.TrimLeft();
|
||||
sLine.TrimRight("\r\n");
|
||||
for (size_t i = 0; i < numListenerEntries; i++) {
|
||||
config.FindStringVector(szListenerEntries[i], vsList);
|
||||
VCString::const_iterator it = vsList.begin();
|
||||
|
||||
if ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) {
|
||||
continue;
|
||||
for (; it != vsList.end(); ++it) {
|
||||
if (!AddListener(szListenerEntries[i] + CString(" ") + *it, sError))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sLine.Left(2) == "/*") {
|
||||
if (sLine.Right(2) != "*/") {
|
||||
bCommented = true;
|
||||
}
|
||||
VCString::const_iterator vit;
|
||||
config.FindStringVector("loadmodule", vsList);
|
||||
for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
|
||||
CString sModName = vit->Token(0);
|
||||
CString sArgs = vit->Token(1, true);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bCommented) {
|
||||
if (sLine.Right(2) == "*/") {
|
||||
bCommented = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((sLine.Left(1) == "<") && (sLine.Right(1) == ">")) {
|
||||
sLine.LeftChomp();
|
||||
sLine.RightChomp();
|
||||
sLine.Trim();
|
||||
|
||||
CString sTag = sLine.substr(0, sLine.find_first_of(" \t\r\n"));
|
||||
CString sValue = (sTag.size() < sLine.size()) ? sLine.substr(sTag.size() +1) : "";
|
||||
|
||||
sTag.Trim();
|
||||
sValue.Trim();
|
||||
|
||||
if (sLine.Left(1) == "/") {
|
||||
sTag = sTag.substr(1);
|
||||
|
||||
if (pUser) {
|
||||
if (pChan) {
|
||||
if (sTag.Equals("Chan")) {
|
||||
// Save the channel name, because AddChan
|
||||
// deletes the CChannel*, if adding fails
|
||||
sError = pChan->GetName();
|
||||
if (!pUser->AddChan(pChan)) {
|
||||
sError = "Channel [" + sError + "] defined more than once";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
sError.clear();
|
||||
pChan = NULL;
|
||||
continue;
|
||||
}
|
||||
} else if (sTag.Equals("User")) {
|
||||
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 = NULL;
|
||||
} else if (!AddUser(pUser, sErr)) {
|
||||
sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
|
||||
}
|
||||
|
||||
if (!sError.empty()) {
|
||||
CUtils::PrintError(sError);
|
||||
if (pUser) {
|
||||
pUser->SetBeingDeleted(true);
|
||||
delete pUser;
|
||||
pUser = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pUser = NULL;
|
||||
pRealUser = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (sTag.Equals("User")) {
|
||||
if (pUser) {
|
||||
sError = "You may not nest <User> tags inside of other <User> tags.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sValue.empty()) {
|
||||
sError = "You must supply a username in the <User> tag.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_msUsers.find(sValue) != m_msUsers.end()) {
|
||||
sError = "User [" + sValue + "] defined more than once.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CUtils::PrintMessage("Loading user [" + sValue + "]");
|
||||
|
||||
// Either create a CUser* or use an existing one
|
||||
map<CString, CUser*>::iterator it = m_msDelUsers.find(sValue);
|
||||
|
||||
if (it != m_msDelUsers.end()) {
|
||||
pRealUser = it->second;
|
||||
m_msDelUsers.erase(it);
|
||||
} else
|
||||
pRealUser = NULL;
|
||||
|
||||
pUser = new CUser(sValue);
|
||||
|
||||
if (!m_sStatusPrefix.empty()) {
|
||||
if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
|
||||
sError = "Invalid StatusPrefix [" + m_sStatusPrefix + "] Must be 1-5 chars, no spaces.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (sTag.Equals("Chan")) {
|
||||
if (!pUser) {
|
||||
sError = "<Chan> tags must be nested inside of a <User> tag.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pChan) {
|
||||
sError = "You may not nest <Chan> tags inside of other <Chan> tags.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
pChan = new CChan(sValue, pUser, true);
|
||||
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())) {
|
||||
if (pUser) {
|
||||
if (pChan) {
|
||||
if (sName.Equals("Buffer")) {
|
||||
pChan->SetBufferCount(sValue.ToUInt(), true);
|
||||
continue;
|
||||
} else if (sName.Equals("KeepBuffer")) {
|
||||
pChan->SetKeepBuffer(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("Detached")) {
|
||||
pChan->SetDetached(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("AutoCycle")) {
|
||||
if (sValue.Equals("true")) {
|
||||
CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle " + pChan->GetName());
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("Key")) {
|
||||
pChan->SetKey(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("Modes")) {
|
||||
pChan->SetDefaultModes(sValue);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (sName.Equals("Buffer")) {
|
||||
pUser->SetBufferCount(sValue.ToUInt(), true);
|
||||
continue;
|
||||
} else if (sName.Equals("KeepBuffer")) {
|
||||
pUser->SetKeepBuffer(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("Nick")) {
|
||||
pUser->SetNick(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("CTCPReply")) {
|
||||
pUser->AddCTCPReply(sValue.Token(0), sValue.Token(1, true));
|
||||
continue;
|
||||
} else if (sName.Equals("QuitMsg")) {
|
||||
pUser->SetQuitMsg(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("AltNick")) {
|
||||
pUser->SetAltNick(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("AwaySuffix")) {
|
||||
CUtils::PrintMessage("WARNING: AwaySuffix has been depricated, instead try -> LoadModule = awaynick %nick%_" + sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("AutoCycle")) {
|
||||
if (sValue.Equals("true")) {
|
||||
CUtils::PrintError("WARNING: AutoCycle has been removed, instead try -> LoadModule = autocycle");
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("Pass")) {
|
||||
// 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();
|
||||
pUser->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, "#");
|
||||
pUser->SetPass(sPass, type, sSalt);
|
||||
} else if (sMethod == "plain") {
|
||||
pUser->SetPass(sPass, CUser::HASH_NONE);
|
||||
} else {
|
||||
pUser->SetPass(sValue, CUser::HASH_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (sName.Equals("MultiClients")) {
|
||||
pUser->SetMultiClients(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("BounceDCCs")) {
|
||||
pUser->SetBounceDCCs(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("Ident")) {
|
||||
pUser->SetIdent(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("DenyLoadMod")) {
|
||||
pUser->SetDenyLoadMod(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("Admin")) {
|
||||
pUser->SetAdmin(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("DenySetBindHost") || sName.Equals("DenySetVHost")) {
|
||||
pUser->SetDenySetBindHost(sValue.Equals("true"));
|
||||
continue;
|
||||
} else if (sName.Equals("StatusPrefix")) {
|
||||
if (!pUser->SetStatusPrefix(sValue)) {
|
||||
sError = "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("DCCLookupMethod")) {
|
||||
pUser->SetUseClientIP(sValue.Equals("Client"));
|
||||
continue;
|
||||
} else if (sName.Equals("RealName")) {
|
||||
pUser->SetRealName(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("KeepNick")) {
|
||||
if (sValue.Equals("true")) {
|
||||
CUtils::PrintError("WARNING: KeepNick has been deprecated, instead try -> LoadModule = keepnick");
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("ChanModes")) {
|
||||
pUser->SetDefaultChanModes(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("BindHost") || sName.Equals("VHost")) {
|
||||
pUser->SetBindHost(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("DCCBindHost") || sName.Equals("DCCVHost")) {
|
||||
pUser->SetDCCBindHost(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("Allow")) {
|
||||
pUser->AddAllowedHost(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("Server")) {
|
||||
CUtils::PrintAction("Adding Server [" + sValue + "]");
|
||||
CUtils::PrintStatus(pUser->AddServer(sValue));
|
||||
continue;
|
||||
} else if (sName.Equals("Chan")) {
|
||||
pUser->AddChan(sValue, true);
|
||||
continue;
|
||||
} else if (sName.Equals("TimestampFormat")) {
|
||||
pUser->SetTimestampFormat(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("AppendTimestamp")) {
|
||||
pUser->SetTimestampAppend(sValue.ToBool());
|
||||
continue;
|
||||
} else if (sName.Equals("PrependTimestamp")) {
|
||||
pUser->SetTimestampPrepend(sValue.ToBool());
|
||||
continue;
|
||||
} else if (sName.Equals("IRCConnectEnabled")) {
|
||||
pUser->SetIRCConnectEnabled(sValue.ToBool());
|
||||
continue;
|
||||
} else if (sName.Equals("Timestamp")) {
|
||||
if (!sValue.Trim_n().Equals("true")) {
|
||||
if (sValue.Trim_n().Equals("append")) {
|
||||
pUser->SetTimestampAppend(true);
|
||||
pUser->SetTimestampPrepend(false);
|
||||
} else if (sValue.Trim_n().Equals("prepend")) {
|
||||
pUser->SetTimestampAppend(false);
|
||||
pUser->SetTimestampPrepend(true);
|
||||
} else if (sValue.Trim_n().Equals("false")) {
|
||||
pUser->SetTimestampAppend(false);
|
||||
pUser->SetTimestampPrepend(false);
|
||||
} else {
|
||||
pUser->SetTimestampFormat(sValue);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("TimezoneOffset")) {
|
||||
pUser->SetTimezoneOffset(sValue.ToDouble()); // there is no ToFloat()
|
||||
continue;
|
||||
} else if (sName.Equals("JoinTries")) {
|
||||
pUser->SetJoinTries(sValue.ToUInt());
|
||||
continue;
|
||||
} else if (sName.Equals("MaxJoins")) {
|
||||
pUser->SetMaxJoins(sValue.ToUInt());
|
||||
continue;
|
||||
} else if (sName.Equals("Skin")) {
|
||||
pUser->SetSkinName(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("LoadModule")) {
|
||||
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 = pUser->GetModules().LoadModule(sModName, sArgs, pUser, sModRet);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sName.Equals("Listen") || sName.Equals("Listen6") || sName.Equals("Listen4")
|
||||
|| sName.Equals("Listener") || sName.Equals("Listener6") || sName.Equals("Listener4")) {
|
||||
EAddrType eAddr = ADDR_ALL;
|
||||
if (sName.Equals("Listen4") || sName.Equals("Listen") || sName.Equals("Listener4")) {
|
||||
eAddr = ADDR_IPV4ONLY;
|
||||
}
|
||||
if (sName.Equals("Listener6")) {
|
||||
eAddr = ADDR_IPV6ONLY;
|
||||
}
|
||||
|
||||
CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
|
||||
if (sValue.TrimPrefix("irc_only "))
|
||||
eAccept = CListener::ACCEPT_IRC;
|
||||
else if (sValue.TrimPrefix("web_only "))
|
||||
eAccept = CListener::ACCEPT_HTTP;
|
||||
|
||||
bool bSSL = false;
|
||||
CString sPort;
|
||||
CString sBindHost;
|
||||
|
||||
if (ADDR_IPV4ONLY == eAddr) {
|
||||
sValue.Replace(":", " ");
|
||||
}
|
||||
|
||||
if (sValue.find(" ") != CString::npos) {
|
||||
sBindHost = sValue.Token(0, false, " ");
|
||||
sPort = sValue.Token(1, true, " ");
|
||||
} else {
|
||||
sPort = sValue;
|
||||
}
|
||||
|
||||
if (sPort.Left(1) == "+") {
|
||||
sPort.LeftChomp();
|
||||
bSSL = true;
|
||||
}
|
||||
|
||||
CString sHostComment;
|
||||
|
||||
if (!sBindHost.empty()) {
|
||||
sHostComment = " on host [" + sBindHost + "]";
|
||||
}
|
||||
|
||||
CString sIPV6Comment;
|
||||
|
||||
switch (eAddr) {
|
||||
case ADDR_ALL:
|
||||
sIPV6Comment = "";
|
||||
break;
|
||||
case ADDR_IPV4ONLY:
|
||||
sIPV6Comment = " using ipv4";
|
||||
break;
|
||||
case ADDR_IPV6ONLY:
|
||||
sIPV6Comment = " using ipv6";
|
||||
}
|
||||
|
||||
unsigned short uPort = sPort.ToUShort();
|
||||
CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") + CString(uPort) + "]" + sHostComment + sIPV6Comment);
|
||||
|
||||
#ifndef HAVE_IPV6
|
||||
if (ADDR_IPV6ONLY == eAddr) {
|
||||
sError = "IPV6 is not enabled";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBSSL
|
||||
if (bSSL) {
|
||||
sError = "SSL is not enabled";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
CString sPemFile = GetPemLocation();
|
||||
|
||||
if (bSSL && !CFile::Exists(sPemFile)) {
|
||||
sError = "Unable to locate pem file: [" + sPemFile + "]";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
|
||||
// If stdin is e.g. /dev/null and we call GetBoolInput(),
|
||||
// we are stuck in an endless loop!
|
||||
if (isatty(0) && CUtils::GetBoolInput("Would you like to create a new pem file?", true)) {
|
||||
sError.clear();
|
||||
WritePemFile();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" + sHostComment + sIPV6Comment);
|
||||
}
|
||||
#endif
|
||||
if (!uPort) {
|
||||
sError = "Invalid port";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
|
||||
|
||||
if (!pListener->Listen()) {
|
||||
sError = FormatBindError();
|
||||
CUtils::PrintStatus(false, sError);
|
||||
delete pListener;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_vpListeners.push_back(pListener);
|
||||
CUtils::PrintStatus(true);
|
||||
|
||||
continue;
|
||||
} else if (sName.Equals("LoadModule")) {
|
||||
CString sModName = sValue.Token(0);
|
||||
CString sArgs = sValue.Token(1, true);
|
||||
|
||||
if (msModules.find(sModName) != msModules.end()) {
|
||||
sError = "Module [" + sModName +
|
||||
"] already loaded";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
CString sModRet;
|
||||
CModule *pOldMod;
|
||||
|
||||
pOldMod = GetModules().FindModule(sModName);
|
||||
if (!pOldMod) {
|
||||
CUtils::PrintAction("Loading Global Module [" + sModName + "]");
|
||||
|
||||
bool bModRet = GetModules().LoadModule(sModName, sArgs, NULL, sModRet);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
} else if (pOldMod->GetArgs() != sArgs) {
|
||||
CUtils::PrintAction("Reloading Global Module [" + sModName + "]");
|
||||
|
||||
bool bModRet = GetModules().ReloadModule(sModName, sArgs, NULL, sModRet);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
|
||||
|
||||
msModules[sModName] = sArgs;
|
||||
continue;
|
||||
// Convert old-style ISpoofFormat's and ISpoofFile to identfile module
|
||||
} else if (sName.Equals("ISpoofFormat") || sName.Equals("ISpoofFile")) {
|
||||
CModule *pIdentFileMod = GetModules().FindModule("identfile");
|
||||
if (!pIdentFileMod) {
|
||||
CUtils::PrintAction("Loading Global Module [identfile]");
|
||||
|
||||
CString sModRet;
|
||||
bool bModRet = GetModules().LoadModule("identfile", "", NULL, sModRet);
|
||||
|
||||
if (bModRet) {
|
||||
sModRet = sModRet.Token(1, true, "identfile] ");
|
||||
}
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
|
||||
pIdentFileMod = GetModules().FindModule("identfile");
|
||||
msModules["identfile"] = "";
|
||||
}
|
||||
|
||||
if (!pIdentFileMod->SetNV(sName.TrimPrefix_n("ISpoof"), sValue)) {
|
||||
sError = "Failed to convert " + sName + " to the identfile module";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
} else if (sName.Equals("MOTD")) {
|
||||
AddMotd(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("BindHost") || sName.Equals("VHost")) {
|
||||
AddBindHost(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("PidFile")) {
|
||||
m_sPidFile = sValue;
|
||||
continue;
|
||||
} else if (sName.Equals("Skin")) {
|
||||
SetSkinName(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("StatusPrefix")) {
|
||||
m_sStatusPrefix = sValue;
|
||||
continue;
|
||||
} else if (sName.Equals("ConnectDelay")) {
|
||||
m_uiConnectDelay = sValue.ToUInt();
|
||||
continue;
|
||||
} else if (sName.Equals("ServerThrottle")) {
|
||||
m_sConnectThrottle.SetTTL(sValue.ToUInt()*1000);
|
||||
continue;
|
||||
} else if (sName.Equals("AnonIPLimit")) {
|
||||
m_uiAnonIPLimit = sValue.ToUInt();
|
||||
continue;
|
||||
} else if (sName.Equals("MaxBufferSize")) {
|
||||
m_uiMaxBufferSize = sValue.ToUInt();
|
||||
continue;
|
||||
} else if (sName.Equals("SSLCertFile")) {
|
||||
m_sSSLCertFile = sValue;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
sError = "Unhandled line " + CString(uLineNum) + " in config: [" + sLine + "]";
|
||||
if (msModules.find(sModName) != msModules.end()) {
|
||||
sError = "Module [" + sModName +
|
||||
"] already loaded";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
CString sModRet;
|
||||
CModule *pOldMod;
|
||||
|
||||
pOldMod = GetModules().FindModule(sModName);
|
||||
if (!pOldMod) {
|
||||
CUtils::PrintAction("Loading Global Module [" + sModName + "]");
|
||||
|
||||
bool bModRet = GetModules().LoadModule(sModName, sArgs, NULL, sModRet);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
} else if (pOldMod->GetArgs() != sArgs) {
|
||||
CUtils::PrintAction("Reloading Global Module [" + sModName + "]");
|
||||
|
||||
bool bModRet = GetModules().ReloadModule(sModName, sArgs, NULL, sModRet);
|
||||
|
||||
// If the module was loaded, sModRet contains
|
||||
// "Loaded Module [name] ..." and we strip away this beginning.
|
||||
if (bModRet)
|
||||
sModRet = sModRet.Token(1, true, sModName + "] ");
|
||||
|
||||
CUtils::PrintStatus(bModRet, sModRet);
|
||||
if (!bModRet) {
|
||||
sError = sModRet;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
|
||||
|
||||
msModules[sModName] = sArgs;
|
||||
}
|
||||
|
||||
config.FindStringVector("motd", vsList);
|
||||
for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
|
||||
AddMotd(*vit);
|
||||
}
|
||||
|
||||
config.FindStringVector("bindhost", vsList);
|
||||
for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
|
||||
AddBindHost(*vit);
|
||||
}
|
||||
config.FindStringVector("vhost", vsList);
|
||||
for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
|
||||
AddBindHost(*vit);
|
||||
}
|
||||
|
||||
CString sVal;
|
||||
if (config.FindStringEntry("pidfile", sVal))
|
||||
m_sPidFile = sVal;
|
||||
if (config.FindStringEntry("statusprefix", sVal))
|
||||
m_sStatusPrefix = sVal;
|
||||
if (config.FindStringEntry("sslcertfile", sVal))
|
||||
m_sSSLCertFile = sVal;
|
||||
if (config.FindStringEntry("skin", sVal))
|
||||
SetSkinName(sVal);
|
||||
if (config.FindStringEntry("connectdelay", sVal))
|
||||
m_uiConnectDelay = sVal.ToUInt();
|
||||
if (config.FindStringEntry("serverthrottle", sVal))
|
||||
m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
|
||||
if (config.FindStringEntry("anoniplimit", sVal))
|
||||
m_uiAnonIPLimit = sVal.ToUInt();
|
||||
if (config.FindStringEntry("maxbuffersize", sVal))
|
||||
m_uiMaxBufferSize = sVal.ToUInt();
|
||||
|
||||
CConfig::SubConfig subConf;
|
||||
CConfig::SubConfig::const_iterator subIt;
|
||||
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 = NULL;
|
||||
|
||||
CUtils::PrintMessage("Loading user [" + sUserName + "]");
|
||||
|
||||
// Either create a CUser* or use an existing one
|
||||
map<CString, CUser*>::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()) {
|
||||
if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
|
||||
sError = "Invalid StatusPrefix [" + m_sStatusPrefix + "] Must be 1-5 chars, no spaces.";
|
||||
CUtils::PrintError(sError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pUser->ParseConfig(pSubConf, sError)) {
|
||||
CUtils::PrintError(sError);
|
||||
delete pUser;
|
||||
pUser = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pSubConf->empty()) {
|
||||
sError = "Unhandled lines in config for User [" + sUserName + "]!";
|
||||
CUtils::PrintError(sError);
|
||||
|
||||
DumpConfig(pSubConf);
|
||||
return false;
|
||||
}
|
||||
|
||||
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 = NULL;
|
||||
} else if (!AddUser(pUser, sErr)) {
|
||||
sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
|
||||
}
|
||||
|
||||
if (!sError.empty()) {
|
||||
CUtils::PrintError(sError);
|
||||
if (pUser) {
|
||||
pUser->SetBeingDeleted(true);
|
||||
delete pUser;
|
||||
pUser = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pUser = NULL;
|
||||
pRealUser = NULL;
|
||||
}
|
||||
|
||||
if (!config.empty()) {
|
||||
sError = "Unhandled lines in config!";
|
||||
CUtils::PrintError(sError);
|
||||
|
||||
DumpConfig(&config);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Unload modules which are no longer in the config
|
||||
set<CString> ssUnload;
|
||||
for (size_t i = 0; i < GetModules().size(); i++) {
|
||||
@@ -1646,20 +1224,6 @@ bool CZNC::DoRehash(CString& sError)
|
||||
CUtils::PrintMessage("Could not unload [" + *it + "]");
|
||||
}
|
||||
|
||||
if (pChan) {
|
||||
sError = "Last <Chan> section not properly closed. File truncated?";
|
||||
CUtils::PrintError(sError);
|
||||
delete pChan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pUser) {
|
||||
sError = "Last <User> section not properly closed. File truncated?";
|
||||
CUtils::PrintError(sError);
|
||||
delete pUser;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_msUsers.empty()) {
|
||||
sError = "You must define at least one user in your config.";
|
||||
CUtils::PrintError(sError);
|
||||
@@ -1680,6 +1244,30 @@ bool CZNC::DoRehash(CString& sError)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CZNC::DumpConfig(const CConfig* pConfig) {
|
||||
CConfig::EntryMapIterator eit = pConfig->BeginEntries();
|
||||
for (; eit != pConfig->EndEntries(); ++eit) {
|
||||
const CString& sKey = eit->first;
|
||||
const VCString& vsList = eit->second;
|
||||
VCString::const_iterator it = vsList.begin();
|
||||
for (; it != vsList.end(); ++it) {
|
||||
CUtils::PrintError(sKey + " = " + *it);
|
||||
}
|
||||
}
|
||||
|
||||
CConfig::SubConfigMapIterator sit = pConfig->BeginSubConfigs();
|
||||
for (; sit != pConfig->EndSubConfigs(); ++sit) {
|
||||
const CString& sKey = sit->first;
|
||||
const CConfig::SubConfig& sSub = sit->second;
|
||||
CConfig::SubConfig::const_iterator it = sSub.begin();
|
||||
|
||||
for (; it != sSub.end(); ++it) {
|
||||
CUtils::PrintError("SubConfig [" + sKey + " " + it->first + "]:");
|
||||
DumpConfig(it->second.m_pSubConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CZNC::ClearBindHosts() {
|
||||
m_vsBindHosts.clear();
|
||||
}
|
||||
@@ -1800,6 +1388,120 @@ CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost, EAddrType
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CZNC::AddListener(const CString& sLine, CString& sError) {
|
||||
CString sName = sLine.Token(0);
|
||||
CString sValue = sLine.Token(1, true);
|
||||
|
||||
EAddrType eAddr = ADDR_ALL;
|
||||
if (sName.Equals("Listen4") || sName.Equals("Listen") || sName.Equals("Listener4")) {
|
||||
eAddr = ADDR_IPV4ONLY;
|
||||
}
|
||||
if (sName.Equals("Listener6")) {
|
||||
eAddr = ADDR_IPV6ONLY;
|
||||
}
|
||||
|
||||
CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
|
||||
if (sValue.TrimPrefix("irc_only "))
|
||||
eAccept = CListener::ACCEPT_IRC;
|
||||
else if (sValue.TrimPrefix("web_only "))
|
||||
eAccept = CListener::ACCEPT_HTTP;
|
||||
|
||||
bool bSSL = false;
|
||||
CString sPort;
|
||||
CString sBindHost;
|
||||
|
||||
if (ADDR_IPV4ONLY == eAddr) {
|
||||
sValue.Replace(":", " ");
|
||||
}
|
||||
|
||||
if (sValue.find(" ") != CString::npos) {
|
||||
sBindHost = sValue.Token(0, false, " ");
|
||||
sPort = sValue.Token(1, true, " ");
|
||||
} else {
|
||||
sPort = sValue;
|
||||
}
|
||||
|
||||
if (sPort.Left(1) == "+") {
|
||||
sPort.LeftChomp();
|
||||
bSSL = true;
|
||||
}
|
||||
|
||||
CString sHostComment;
|
||||
|
||||
if (!sBindHost.empty()) {
|
||||
sHostComment = " on host [" + sBindHost + "]";
|
||||
}
|
||||
|
||||
CString sIPV6Comment;
|
||||
|
||||
switch (eAddr) {
|
||||
case ADDR_ALL:
|
||||
sIPV6Comment = "";
|
||||
break;
|
||||
case ADDR_IPV4ONLY:
|
||||
sIPV6Comment = " using ipv4";
|
||||
break;
|
||||
case ADDR_IPV6ONLY:
|
||||
sIPV6Comment = " using ipv6";
|
||||
}
|
||||
|
||||
unsigned short uPort = sPort.ToUShort();
|
||||
CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") + CString(uPort) + "]" + sHostComment + sIPV6Comment);
|
||||
|
||||
#ifndef HAVE_IPV6
|
||||
if (ADDR_IPV6ONLY == eAddr) {
|
||||
sError = "IPV6 is not enabled";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBSSL
|
||||
if (bSSL) {
|
||||
sError = "SSL is not enabled";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
CString sPemFile = GetPemLocation();
|
||||
|
||||
if (bSSL && !CFile::Exists(sPemFile)) {
|
||||
sError = "Unable to locate pem file: [" + sPemFile + "]";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
|
||||
// If stdin is e.g. /dev/null and we call GetBoolInput(),
|
||||
// we are stuck in an endless loop!
|
||||
if (isatty(0) && CUtils::GetBoolInput("Would you like to create a new pem file?", true)) {
|
||||
sError.clear();
|
||||
WritePemFile();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" + sHostComment + sIPV6Comment);
|
||||
}
|
||||
#endif
|
||||
if (!uPort) {
|
||||
sError = "Invalid port";
|
||||
CUtils::PrintStatus(false, sError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
|
||||
|
||||
if (!pListener->Listen()) {
|
||||
sError = FormatBindError();
|
||||
CUtils::PrintStatus(false, sError);
|
||||
delete pListener;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_vpListeners.push_back(pListener);
|
||||
CUtils::PrintStatus(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZNC::AddListener(CListener* pListener) {
|
||||
if (!pListener->GetRealListener()) {
|
||||
// Listener doesnt actually listen
|
||||
|
||||
4
znc.h
4
znc.h
@@ -21,6 +21,7 @@ using std::map;
|
||||
class CListener;
|
||||
class CUser;
|
||||
class CConnectUserTimer;
|
||||
class CConfig;
|
||||
|
||||
class CZNC {
|
||||
public:
|
||||
@@ -142,12 +143,15 @@ public:
|
||||
// Never call this unless you are CConnectUserTimer::~CConnectUserTimer()
|
||||
void LeakConnectUser(CConnectUserTimer *pTimer);
|
||||
|
||||
static void DumpConfig(const CConfig* Config);
|
||||
|
||||
private:
|
||||
CFile* InitPidFile();
|
||||
bool DoRehash(CString& sError);
|
||||
// Returns true if something was done
|
||||
bool HandleUserDeletion();
|
||||
CString MakeConfigHeader();
|
||||
bool AddListener(const CString& sLine, CString& sError);
|
||||
|
||||
protected:
|
||||
time_t m_TimeStarted;
|
||||
|
||||
Reference in New Issue
Block a user